kepler.gl
Version:
kepler.gl is a webgl based application to visualize large scale location data in the browser
1,124 lines (1,085 loc) • 213 kB
JavaScript
"use strict";
var _typeof3 = require("@babel/runtime/helpers/typeof");
var _regeneratorRuntime2 = require("@babel/runtime/regenerator");
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.layerColors = exports.defaultGetFieldValue = exports["default"] = exports.colorMaker = exports.OVERLAY_TYPE_CONST = exports.LAYER_ID_LENGTH = void 0;
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _core = require("@deck.gl/core");
var _deckglArrowLayers = require("@kepler.gl/deckgl-arrow-layers");
var _extensions = require("@deck.gl/extensions");
var _layers = require("@deck.gl/layers");
var _window = require("global/window");
var _keymirror = _interopRequireDefault(require("keymirror"));
var arrow = _interopRequireWildcard(require("apache-arrow"));
var _defaultLayerIcon = _interopRequireDefault(require("./default-layer-icon"));
var _layerUpdate = require("./layer-update");
var _layerUtils = require("./layer-utils");
var _constants = require("@kepler.gl/constants");
var _utils = require("@kepler.gl/utils");
var _commonUtils = require("@kepler.gl/common-utils");
var _lodash = _interopRequireDefault(require("lodash.memoize"));
var _getApplicationConfig; // SPDX-License-Identifier: MIT
// Copyright contributors to the kepler.gl project
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof3(e) && "function" != typeof e) return { "default": e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n["default"] = e, t && t.set(e, n), n; }
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; }
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2["default"])(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
var _marked = /*#__PURE__*/_regeneratorRuntime2.mark(generateColor);
/**
* Approx. number of points to sample in a large data set
*/
var LAYER_ID_LENGTH = exports.LAYER_ID_LENGTH = 6;
var MAX_SAMPLE_SIZE = 5000;
var defaultDomain = [0, 1];
var dataFilterExtension = new _extensions.DataFilterExtension({
filterSize: _constants.MAX_GPU_FILTERS,
// `countItems` option. It enables the GPU to report the number of objects that pass the filter criteria via the `onFilteredItemsChange` callback.
// @ts-expect-error not typed
countItems: (_getApplicationConfig = (0, _utils.getApplicationConfig)().useOnFilteredItemsChange) !== null && _getApplicationConfig !== void 0 ? _getApplicationConfig : false
});
// eslint-disable-next-line @typescript-eslint/no-unused-vars
var defaultDataAccessor = function defaultDataAccessor(dc) {
return function (d) {
return d;
};
};
var identity = function identity(d) {
return d;
};
// Can't use fiedValueAccesor because need the raw data to render tooltip
// SHAN: Revisit here
var defaultGetFieldValue = exports.defaultGetFieldValue = function defaultGetFieldValue(field, d) {
return field.valueAccessor(d);
};
var OVERLAY_TYPE_CONST = exports.OVERLAY_TYPE_CONST = (0, _keymirror["default"])({
deckgl: null,
mapboxgl: null
});
var layerColors = exports.layerColors = Object.values(_constants.DataVizColors).map(_utils.hexToRgb);
function generateColor() {
var index;
return _regenerator["default"].wrap(function generateColor$(_context) {
while (1) switch (_context.prev = _context.next) {
case 0:
index = 0;
case 1:
if (!(index < layerColors.length + 1)) {
_context.next = 7;
break;
}
if (index === layerColors.length) {
index = 0;
}
_context.next = 5;
return layerColors[index++];
case 5:
_context.next = 1;
break;
case 7:
case "end":
return _context.stop();
}
}, _marked);
}
var colorMaker = exports.colorMaker = generateColor();
var Layer = /*#__PURE__*/function () {
function Layer(props) {
(0, _classCallCheck2["default"])(this, Layer);
(0, _defineProperty2["default"])(this, "id", void 0);
(0, _defineProperty2["default"])(this, "meta", void 0);
(0, _defineProperty2["default"])(this, "visConfigSettings", void 0);
(0, _defineProperty2["default"])(this, "config", void 0);
// TODO: define _oldDataUpdateTriggers
(0, _defineProperty2["default"])(this, "_oldDataUpdateTriggers", void 0);
(0, _defineProperty2["default"])(this, "isValid", void 0);
(0, _defineProperty2["default"])(this, "errorMessage", void 0);
(0, _defineProperty2["default"])(this, "filteredItemCount", void 0);
this.id = props.id || (0, _commonUtils.generateHashId)(LAYER_ID_LENGTH);
// meta
this.meta = {};
// visConfigSettings
this.visConfigSettings = {};
this.config = this.getDefaultLayerConfig(props);
// set columnMode from supported columns
if (!this.config.columnMode) {
var supportedColumnModes = this.supportedColumnModes;
if (supportedColumnModes !== null && supportedColumnModes !== void 0 && supportedColumnModes.length) {
var _supportedColumnModes;
this.config.columnMode = (_supportedColumnModes = supportedColumnModes[0]) === null || _supportedColumnModes === void 0 ? void 0 : _supportedColumnModes.key;
}
}
// then set column, columnMode should already been set
this.config.columns = this.getLayerColumns(props.columns);
// false indicates that the layer caused an error, and was disabled
this.isValid = true;
this.errorMessage = null;
// item count
this.filteredItemCount = {};
}
return (0, _createClass2["default"])(Layer, [{
key: "layerIcon",
get: function get() {
return _defaultLayerIcon["default"];
}
}, {
key: "overlayType",
get: function get() {
return OVERLAY_TYPE_CONST.deckgl;
}
}, {
key: "type",
get: function get() {
return null;
}
}, {
key: "name",
get: function get() {
return this.type;
}
}, {
key: "isAggregated",
get: function get() {
return false;
}
}, {
key: "requiredLayerColumns",
get: function get() {
var supportedColumnModes = this.supportedColumnModes;
if (supportedColumnModes) {
return supportedColumnModes.reduce(function (acc, obj) {
return obj.requiredColumns ? acc.concat(obj.requiredColumns) : acc;
}, []);
}
return [];
}
}, {
key: "optionalColumns",
get: function get() {
var supportedColumnModes = this.supportedColumnModes;
if (supportedColumnModes) {
return supportedColumnModes.reduce(function (acc, obj) {
return obj.optionalColumns ? acc.concat(obj.optionalColumns) : acc;
}, []);
}
return [];
}
}, {
key: "noneLayerDataAffectingProps",
get: function get() {
return ['label', 'opacity', 'thickness', 'isVisible', 'hidden'];
}
}, {
key: "visualChannels",
get: function get() {
return {
color: {
property: 'color',
field: 'colorField',
scale: 'colorScale',
domain: 'colorDomain',
range: 'colorRange',
key: 'color',
channelScaleType: _constants.CHANNEL_SCALES.color,
nullValue: _constants.NO_VALUE_COLOR,
defaultValue: function defaultValue(config) {
return config.color;
}
},
size: {
property: 'size',
field: 'sizeField',
scale: 'sizeScale',
domain: 'sizeDomain',
range: 'sizeRange',
key: 'size',
channelScaleType: _constants.CHANNEL_SCALES.size,
nullValue: 0,
defaultValue: 1
}
};
}
}, {
key: "columnValidators",
get: function get() {
return {};
}
/*
* Column pairs maps layer column to a specific field pairs,
* By default, it is set to null
*/
}, {
key: "columnPairs",
get: function get() {
return null;
}
/**
* Column labels if its different than column key
*/
}, {
key: "columnLabels",
get: function get() {
return null;
}
/*
* Default point column pairs, can be used for point based layers: point, icon etc.
*/
}, {
key: "defaultPointColumnPairs",
get: function get() {
return {
lat: {
pair: ['lng', 'altitude'],
fieldPairKey: 'lat'
},
lng: {
pair: ['lat', 'altitude'],
fieldPairKey: 'lng'
},
altitude: {
pair: ['lng', 'lat'],
fieldPairKey: 'altitude'
}
};
}
/*
* Default link column pairs, can be used for link based layers: arc, line etc
*/
}, {
key: "defaultLinkColumnPairs",
get: function get() {
return {
lat: {
pair: ['lng', 'alt'],
fieldPairKey: 'lat'
},
lng: {
pair: ['lat', 'alt'],
fieldPairKey: 'lng'
},
alt: {
pair: ['lng', 'lat'],
fieldPairKey: 'altitude'
},
lat0: {
pair: 'lng0',
fieldPairKey: 'lat'
},
lng0: {
pair: 'lat0',
fieldPairKey: 'lng'
},
alt0: {
pair: ['lng0', 'lat0'],
fieldPairKey: 'altitude'
},
lat1: {
pair: 'lng1',
fieldPairKey: 'lat'
},
lng1: {
pair: 'lat1',
fieldPairKey: 'lng'
},
alt1: {
pair: ['lng1', 'lat1'],
fieldPairKey: 'altitude'
}
};
}
/**
* Return a React component for to render layer instructions in a modal
* @returns {object} - an object
* @example
* return {
* id: 'iconInfo',
* template: IconInfoModal,
* modalProps: {
* title: 'How to draw icons'
* };
* }
*/
}, {
key: "layerInfoModal",
get: function get() {
return null;
}
/**
* Returns which column modes this layer supports
*/
}, {
key: "supportedColumnModes",
get: function get() {
return null;
}
}, {
key: "supportedDatasetTypes",
get: function get() {
return null;
}
/*
* Given a dataset, automatically find props to create layer based on it
* and return the props and previous found layers.
* By default, no layers will be found
*/
}, {
key: "getDefaultLayerConfig",
value: function getDefaultLayerConfig(props) {
var _props$isVisible, _props$isConfigActive, _props$hidden;
return _objectSpread({
dataId: props.dataId,
label: props.label || _constants.DEFAULT_LAYER_LABEL,
color: props.color || colorMaker.next().value,
// set columns later
columns: {},
isVisible: (_props$isVisible = props.isVisible) !== null && _props$isVisible !== void 0 ? _props$isVisible : true,
isConfigActive: (_props$isConfigActive = props.isConfigActive) !== null && _props$isConfigActive !== void 0 ? _props$isConfigActive : false,
highlightColor: props.highlightColor || _constants.DEFAULT_HIGHLIGHT_COLOR,
hidden: (_props$hidden = props.hidden) !== null && _props$hidden !== void 0 ? _props$hidden : false,
// TODO: refactor this into separate visual Channel config
// color by field, domain is set by filters, field, scale type
colorField: null,
colorDomain: [0, 1],
colorScale: _constants.SCALE_TYPES.quantile,
// color by size, domain is set by filters, field, scale type
sizeDomain: [0, 1],
sizeScale: _constants.SCALE_TYPES.linear,
sizeField: null,
visConfig: {},
textLabel: [_constants.DEFAULT_TEXT_LABEL],
colorUI: {
color: _constants.DEFAULT_COLOR_UI,
colorRange: _constants.DEFAULT_COLOR_UI
},
animation: {
enabled: false
}
}, props.columnMode ? {
columnMode: props.columnMode
} : {});
}
/**
* Get the description of a visualChannel config
* @param key
* @returns
*/
}, {
key: "getVisualChannelDescription",
value: function getVisualChannelDescription(key) {
// e.g. label: Color, measure: Vehicle Type
var channel = this.visualChannels[key];
if (!channel) return {
label: '',
measure: undefined
};
var rangeSettings = this.visConfigSettings[channel.range];
var fieldSettings = this.config[channel.field];
var label = rangeSettings === null || rangeSettings === void 0 ? void 0 : rangeSettings.label;
return {
label: typeof label === 'function' ? label(this.config) : label || '',
measure: fieldSettings ? fieldSettings.displayName || fieldSettings.name : channel.defaultMeasure
};
}
/**
* Assign a field to layer column, return column config
*/
}, {
key: "assignColumn",
value: function assignColumn(key, field) {
var _this$config$columns;
// field value could be null for optional columns
var update = field ? {
value: field.name,
fieldIdx: field.fieldIdx
} : {
value: null,
fieldIdx: -1
};
return _objectSpread(_objectSpread({}, this.config.columns), {}, (0, _defineProperty2["default"])({}, key, _objectSpread(_objectSpread({}, (_this$config$columns = this.config.columns) === null || _this$config$columns === void 0 ? void 0 : _this$config$columns[key]), update)));
}
/**
* Assign a field pair to column config, return column config
*/
}, {
key: "assignColumnPairs",
value: function assignColumnPairs(key, fieldPairs) {
var _this$columnPairs, _this$columnPairs2;
if (!this.columnPairs || !((_this$columnPairs = this.columnPairs) !== null && _this$columnPairs !== void 0 && _this$columnPairs[key])) {
// should not end in this state
return this.config.columns;
}
// key = 'lat'
var _ref = ((_this$columnPairs2 = this.columnPairs) === null || _this$columnPairs2 === void 0 ? void 0 : _this$columnPairs2[key]) || {},
pair = _ref.pair,
fieldPairKey = _ref.fieldPairKey;
if (typeof fieldPairKey === 'string' && !fieldPairs[fieldPairKey]) {
// do not allow `key: undefined` to creep into the `updatedColumn` object
return this.config.columns;
}
// pair = ['lng', 'alt] | 'lng'
var updatedColumn = _objectSpread(_objectSpread({}, this.config.columns), {}, (0, _defineProperty2["default"])({}, key, fieldPairs[fieldPairKey]));
var partnerKeys = (0, _commonUtils.toArray)(pair);
var _iterator = _createForOfIteratorHelper(partnerKeys),
_step;
try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
var _this$columnPairs3, _this$columnPairs4;
var partnerKey = _step.value;
if (this.config.columns[partnerKey] && (_this$columnPairs3 = this.columnPairs) !== null && _this$columnPairs3 !== void 0 && _this$columnPairs3[partnerKey] &&
// @ts-ignore
fieldPairs[(_this$columnPairs4 = this.columnPairs) === null || _this$columnPairs4 === void 0 ? void 0 : _this$columnPairs4[partnerKey].fieldPairKey]) {
var _this$columnPairs5;
// @ts-ignore
updatedColumn[partnerKey] = fieldPairs[(_this$columnPairs5 = this.columnPairs) === null || _this$columnPairs5 === void 0 ? void 0 : _this$columnPairs5[partnerKey].fieldPairKey];
}
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
return updatedColumn;
}
/**
* Calculate a radius zoom multiplier to render points, so they are visible in all zoom level
* @param {object} mapState
* @param {number} mapState.zoom - actual zoom
* @param {number | void} mapState.zoomOffset - zoomOffset when render in the plot container for export image
* @returns {number}
*/
}, {
key: "getZoomFactor",
value: function getZoomFactor(_ref2) {
var zoom = _ref2.zoom,
_ref2$zoomOffset = _ref2.zoomOffset,
zoomOffset = _ref2$zoomOffset === void 0 ? 0 : _ref2$zoomOffset;
return Math.pow(2, Math.max(14 - zoom + zoomOffset, 0));
}
/**
* Calculate a elevation zoom multiplier to render points, so they are visible in all zoom level
* @param {object} mapState
* @param {number} mapState.zoom - actual zoom
* @param {number=} mapState.zoomOffset - zoomOffset when render in the plot container for export image
* @returns {number}
*/
}, {
key: "getElevationZoomFactor",
value: function getElevationZoomFactor(_ref3) {
var zoom = _ref3.zoom,
_ref3$zoomOffset = _ref3.zoomOffset,
zoomOffset = _ref3$zoomOffset === void 0 ? 0 : _ref3$zoomOffset;
// enableElevationZoomFactor is used to support existing maps
var _this$config$visConfi = this.config.visConfig,
fixedHeight = _this$config$visConfi.fixedHeight,
enableElevationZoomFactor = _this$config$visConfi.enableElevationZoomFactor;
return fixedHeight || enableElevationZoomFactor === false ? 1 : Math.pow(2, Math.max(8 - zoom + zoomOffset, 0));
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
}, {
key: "formatLayerData",
value: function formatLayerData(datasets, oldLayerData, animationConfig) {
return {};
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
}, {
key: "renderLayer",
value: function renderLayer() {
return [];
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
}, {
key: "getHoverData",
value: function getHoverData(object, dataContainer,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
fields,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
animationConfig,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
hoverInfo) {
if (!object) {
return null;
}
// By default, each entry of layerData should have an index of a row in the original data container.
// Each layer can implement its own getHoverData method
return dataContainer.row(object.index);
}
}, {
key: "getFilteredItemCount",
value: function getFilteredItemCount() {
// use first layer
if (Object.keys(this.filteredItemCount).length) {
var firstLayer = Object.keys(this.filteredItemCount)[0];
return this.filteredItemCount[firstLayer];
}
return null;
}
/**
* When change layer type, try to copy over layer configs as much as possible
* @param configToCopy - config to copy over
* @param visConfigSettings - visConfig settings of config to copy
* @param datasets - current datasets.
* @param defaultLayerProps - default layer creation configurations for current layer and datasets.
*/
}, {
key: "assignConfigToLayer",
value: function assignConfigToLayer(configToCopy, visConfigSettings, datasets, defaultLayerProps) {
var _this = this;
// don't deep merge visualChannel field
// don't deep merge color range, reversed: is not a key by default
var shallowCopy = ['colorRange', 'strokeColorRange'].concat(Object.values(this.visualChannels).map(function (v) {
return v.field;
}));
// don't copy over domain and animation
var notToCopy = ['animation'].concat(Object.values(this.visualChannels).map(function (v) {
return v.domain;
}));
// if range is for the same property group copy it, otherwise, not to copy
Object.values(this.visualChannels).forEach(function (v) {
if (configToCopy.visConfig[v.range] && _this.visConfigSettings[v.range] && visConfigSettings[v.range].group !== _this.visConfigSettings[v.range].group) {
notToCopy.push(v.range);
}
});
// don't copy over visualChannel range
var currentConfig = this.config;
var copied = this.copyLayerConfig(currentConfig, configToCopy, {
shallowCopy: shallowCopy,
notToCopy: notToCopy
});
// update columNode based on new columns
if (this.config.columnMode && this.supportedColumnModes) {
var _satisfiedColumnMode;
var dataset = datasets === null || datasets === void 0 ? void 0 : datasets[this.config.dataId];
// try to find a mode with all requied columns from the source config
var satisfiedColumnMode = (0, _layerUtils.getSatisfiedColumnMode)(this.supportedColumnModes, copied.columns, dataset === null || dataset === void 0 ? void 0 : dataset.fields);
// if no suitable column mode found or no such columMode exists for the layer
// then try use one of the automatically detected layer configs
if (!satisfiedColumnMode) {
var options = [].concat((0, _toConsumableArray2["default"])((defaultLayerProps === null || defaultLayerProps === void 0 ? void 0 : defaultLayerProps.props) || []), (0, _toConsumableArray2["default"])((defaultLayerProps === null || defaultLayerProps === void 0 ? void 0 : defaultLayerProps.altProps) || []));
if (options.length) {
// Use the first of the default configurations
var defaultColumnConfig = options[0].columns;
satisfiedColumnMode = (0, _layerUtils.getSatisfiedColumnMode)(this.supportedColumnModes, defaultColumnConfig, dataset === null || dataset === void 0 ? void 0 : dataset.fields);
if (satisfiedColumnMode) {
copied.columns = _objectSpread(_objectSpread({}, copied.columns), defaultColumnConfig);
}
}
}
copied.columnMode = ((_satisfiedColumnMode = satisfiedColumnMode) === null || _satisfiedColumnMode === void 0 ? void 0 : _satisfiedColumnMode.key) || copied.columnMode;
}
this.updateLayerConfig(copied);
// validate visualChannel field type and scale types
Object.keys(this.visualChannels).forEach(function (channel) {
_this.validateVisualChannel(channel);
});
}
/*
* Recursively copy config over to an empty layer
* when received saved config, or copy config over from a different layer type
* make sure to only copy over value to existing keys
* @param {object} currentConfig - existing config to be override
* @param {object} configToCopy - new Config to copy over
* @param {string[]} shallowCopy - array of properties to not to be deep copied
* @param {string[]} notToCopy - array of properties not to copy
* @returns {object} - copied config
*/
}, {
key: "copyLayerConfig",
value: function copyLayerConfig(currentConfig, configToCopy) {
var _this2 = this;
var _ref4 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {},
_ref4$shallowCopy = _ref4.shallowCopy,
shallowCopy = _ref4$shallowCopy === void 0 ? [] : _ref4$shallowCopy,
_ref4$notToCopy = _ref4.notToCopy,
notToCopy = _ref4$notToCopy === void 0 ? [] : _ref4$notToCopy;
var copied = {};
Object.keys(currentConfig).forEach(function (key) {
if ((0, _utils.isPlainObject)(currentConfig[key]) && (0, _utils.isPlainObject)(configToCopy[key]) && !shallowCopy.includes(key) && !notToCopy.includes(key)) {
// recursively assign object value
copied[key] = _this2.copyLayerConfig(currentConfig[key], configToCopy[key], {
shallowCopy: shallowCopy,
notToCopy: notToCopy
});
} else if ((0, _commonUtils.notNullorUndefined)(configToCopy[key]) && !notToCopy.includes(key)) {
// copy
copied[key] = configToCopy[key];
} else {
// keep existing
copied[key] = currentConfig[key];
}
});
return copied;
}
}, {
key: "registerVisConfig",
value: function registerVisConfig(layerVisConfigs) {
var _this3 = this;
Object.keys(layerVisConfigs).forEach(function (item) {
var configItem = layerVisConfigs[item];
if (typeof configItem === 'string' && _constants.LAYER_VIS_CONFIGS[configItem]) {
// if assigned one of default LAYER_CONFIGS
_this3.config.visConfig[item] = _constants.LAYER_VIS_CONFIGS[configItem].defaultValue;
_this3.visConfigSettings[item] = _constants.LAYER_VIS_CONFIGS[configItem];
} else if ((0, _typeof2["default"])(configItem) === 'object' && ['type', 'defaultValue'].every(function (p) {
return Object.prototype.hasOwnProperty.call(configItem, p);
})) {
// if provided customized visConfig, and has type && defaultValue
// TODO: further check if customized visConfig is valid
_this3.config.visConfig[item] = configItem.defaultValue;
_this3.visConfigSettings[item] = configItem;
}
});
}
}, {
key: "getLayerColumns",
value: function getLayerColumns() {
var propsColumns = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var columnValidators = this.columnValidators || {};
var required = this.requiredLayerColumns.reduce(function (accu, key) {
var _propsColumns$key$val, _propsColumns$key, _propsColumns$key$fie, _propsColumns$key2, _propsColumns$key$val2, _propsColumns$key3, _propsColumns$key$fie2, _propsColumns$key4;
return _objectSpread(_objectSpread({}, accu), {}, (0, _defineProperty2["default"])({}, key, columnValidators[key] ? {
value: (_propsColumns$key$val = (_propsColumns$key = propsColumns[key]) === null || _propsColumns$key === void 0 ? void 0 : _propsColumns$key.value) !== null && _propsColumns$key$val !== void 0 ? _propsColumns$key$val : null,
fieldIdx: (_propsColumns$key$fie = (_propsColumns$key2 = propsColumns[key]) === null || _propsColumns$key2 === void 0 ? void 0 : _propsColumns$key2.fieldIdx) !== null && _propsColumns$key$fie !== void 0 ? _propsColumns$key$fie : -1,
validator: columnValidators[key]
} : {
value: (_propsColumns$key$val2 = (_propsColumns$key3 = propsColumns[key]) === null || _propsColumns$key3 === void 0 ? void 0 : _propsColumns$key3.value) !== null && _propsColumns$key$val2 !== void 0 ? _propsColumns$key$val2 : null,
fieldIdx: (_propsColumns$key$fie2 = (_propsColumns$key4 = propsColumns[key]) === null || _propsColumns$key4 === void 0 ? void 0 : _propsColumns$key4.fieldIdx) !== null && _propsColumns$key$fie2 !== void 0 ? _propsColumns$key$fie2 : -1
}));
}, {});
var optional = this.optionalColumns.reduce(function (accu, key) {
var _propsColumns$key$val3, _propsColumns$key5, _propsColumns$key$fie3, _propsColumns$key6;
return _objectSpread(_objectSpread({}, accu), {}, (0, _defineProperty2["default"])({}, key, {
value: (_propsColumns$key$val3 = (_propsColumns$key5 = propsColumns[key]) === null || _propsColumns$key5 === void 0 ? void 0 : _propsColumns$key5.value) !== null && _propsColumns$key$val3 !== void 0 ? _propsColumns$key$val3 : null,
fieldIdx: (_propsColumns$key$fie3 = (_propsColumns$key6 = propsColumns[key]) === null || _propsColumns$key6 === void 0 ? void 0 : _propsColumns$key6.fieldIdx) !== null && _propsColumns$key$fie3 !== void 0 ? _propsColumns$key$fie3 : -1,
optional: true
}));
}, {});
var columns = _objectSpread(_objectSpread({}, required), optional);
return columns;
}
}, {
key: "updateLayerConfig",
value: function updateLayerConfig(newConfig) {
this.config = _objectSpread(_objectSpread({}, this.config), newConfig);
return this;
}
}, {
key: "updateLayerVisConfig",
value: function updateLayerVisConfig(newVisConfig) {
this.config.visConfig = _objectSpread(_objectSpread({}, this.config.visConfig), newVisConfig);
return this;
}
}, {
key: "updateLayerColorUI",
value: function updateLayerColorUI(prop, newConfig) {
var _this$config = this.config,
previous = _this$config.colorUI,
visConfig = _this$config.visConfig;
if (!(0, _utils.isPlainObject)(newConfig) || typeof prop !== 'string') {
return this;
}
var colorUIProp = Object.entries(newConfig).reduce(function (accu, _ref5) {
var _ref6 = (0, _slicedToArray2["default"])(_ref5, 2),
key = _ref6[0],
value = _ref6[1];
return _objectSpread(_objectSpread({}, accu), {}, (0, _defineProperty2["default"])({}, key, (0, _utils.isPlainObject)(accu[key]) && (0, _utils.isPlainObject)(value) ? _objectSpread(_objectSpread({}, accu[key]), value) : value));
}, previous[prop] || _constants.DEFAULT_COLOR_UI);
var colorUI = _objectSpread(_objectSpread({}, previous), {}, (0, _defineProperty2["default"])({}, prop, colorUIProp));
this.updateLayerConfig({
colorUI: colorUI
});
// if colorUI[prop] is colorRange
var isColorRange = visConfig[prop] && visConfig[prop].colors;
if (isColorRange) {
// if open dropdown and prop is color range
// Automatically set colorRangeConfig's step and reversed
this.updateColorUIByColorRange(newConfig, prop);
// if changes in UI is made to 'reversed', 'steps' or steps
// update current layer colorRange
this.updateColorRangeByColorUI(newConfig, previous, prop);
// if set colorRangeConfig to custom
// initiate customPalette to be edited in the ui
this.updateCustomPalette(newConfig, previous, prop);
}
return this;
}
// if set colorRangeConfig to custom palette or custom breaks
// initiate customPalette to be edited in the ui
}, {
key: "updateCustomPalette",
value: function updateCustomPalette(newConfig, previous, prop) {
var _newConfig$colorRange, _newConfig$colorRange2;
if (!((_newConfig$colorRange = newConfig.colorRangeConfig) !== null && _newConfig$colorRange !== void 0 && _newConfig$colorRange.custom) && !((_newConfig$colorRange2 = newConfig.colorRangeConfig) !== null && _newConfig$colorRange2 !== void 0 && _newConfig$colorRange2.customBreaks)) {
return;
}
if (newConfig.customPalette) {
// if new config also set customPalette, no need to initiate new
return;
}
var _this$config2 = this.config,
colorUI = _this$config2.colorUI,
visConfig = _this$config2.visConfig;
if (!visConfig[prop]) return;
// make copy of current color range to customPalette
var customPalette = _objectSpread({}, visConfig[prop]);
if (newConfig.colorRangeConfig.customBreaks && !customPalette.colorMap) {
// find visualChanel
var visualChannels = this.visualChannels;
var channelKey = Object.keys(visualChannels).find(function (key) {
return visualChannels[key].range === prop;
});
if (!channelKey) {
// should never happn
_window.console.warn("updateColorUI: Can't find visual channel which range is ".concat(prop));
return;
}
// add name|type|category to updateCustomPalette if customBreaks, so that
// colors will not be override as well when inverse palette with custom break
// initiate colorMap from current scale
var colorMap = (0, _utils.initializeLayerColorMap)(this, visualChannels[channelKey]);
customPalette = (0, _utils.initializeCustomPalette)(visConfig[prop], colorMap);
} else if (newConfig.colorRangeConfig.custom) {
customPalette = (0, _utils.initializeCustomPalette)(visConfig[prop]);
}
this.updateLayerConfig({
colorUI: _objectSpread(_objectSpread({}, colorUI), {}, (0, _defineProperty2["default"])({}, prop, _objectSpread(_objectSpread({}, colorUI[prop]), {}, {
customPalette: customPalette
})))
});
}
/**
* if open dropdown and prop is color range
* Automatically set colorRangeConfig's step and reversed
* @param {*} newConfig
* @param {*} prop
*/
}, {
key: "updateColorUIByColorRange",
value: function updateColorUIByColorRange(newConfig, prop) {
var _newConfig$customPale;
var _this$config3 = this.config,
colorUI = _this$config3.colorUI,
visConfig = _this$config3.visConfig;
// when custom palette adds/removes step, the number in "Steps" input control
// should be updated as well
var isCustom = ((_newConfig$customPale = newConfig.customPalette) === null || _newConfig$customPale === void 0 ? void 0 : _newConfig$customPale.category) === 'Custom';
var customStepsChanged = isCustom ? newConfig.customPalette.colors.length !== visConfig[prop].colors.length : false;
if (typeof newConfig.showDropdown !== 'number' && !customStepsChanged) return;
this.updateLayerConfig({
colorUI: _objectSpread(_objectSpread({}, colorUI), {}, (0, _defineProperty2["default"])({}, prop, _objectSpread(_objectSpread({}, colorUI[prop]), {}, {
colorRangeConfig: _objectSpread(_objectSpread({}, colorUI[prop].colorRangeConfig), {}, {
steps: customStepsChanged ? colorUI[prop].customPalette.colors.length : visConfig[prop].colors.length,
reversed: Boolean(visConfig[prop].reversed)
})
})))
});
}
}, {
key: "updateColorRangeByColorUI",
value: function updateColorRangeByColorUI(newConfig, previous, prop) {
// only update colorRange if changes in UI is made to 'reversed', 'steps' or steps
var shouldUpdate = newConfig.colorRangeConfig && ['reversed', 'steps', 'colorBlindSafe', 'type'].some(function (key) {
return Object.prototype.hasOwnProperty.call(newConfig.colorRangeConfig, key) && newConfig.colorRangeConfig[key] !== (previous[prop] || _constants.DEFAULT_COLOR_UI).colorRangeConfig[key];
});
if (!shouldUpdate) return;
var _this$config4 = this.config,
colorUI = _this$config4.colorUI,
visConfig = _this$config4.visConfig;
// for custom palette, one can only 'reverse' the colors in custom palette.
// changing 'steps', 'colorBindSafe', 'type' should fall back to predefined palette.
var isCustomColorReversed = visConfig.colorRange.category === 'Custom' && newConfig.colorRangeConfig && Object.prototype.hasOwnProperty.call(newConfig.colorRangeConfig, 'reversed');
var update = isCustomColorReversed ? (0, _utils.updateCustomColorRangeByColorUI)(visConfig[prop], colorUI[prop].colorRangeConfig) : (0, _utils.updateColorRangeByMatchingPalette)(visConfig[prop], colorUI[prop].colorRangeConfig);
if (update) {
this.updateLayerVisConfig((0, _defineProperty2["default"])({}, prop, update));
}
}
}, {
key: "hasColumnValue",
value: function hasColumnValue(column) {
return Boolean(column && column.value && column.fieldIdx > -1);
}
}, {
key: "hasRequiredColumn",
value: function hasRequiredColumn(column) {
return Boolean(column && (column.optional || this.hasColumnValue(column)));
}
/**
* Check whether layer has all columns
* @returns yes or no
*/
}, {
key: "hasAllColumns",
value: function hasAllColumns() {
var _this4 = this;
var _this$config5 = this.config,
columns = _this$config5.columns,
columnMode = _this$config5.columnMode;
// if layer has different column mode, check if have all required columns of current column Mode
if (columnMode) {
var _currentColumnModes$r;
var currentColumnModes = (this.supportedColumnModes || []).find(function (colMode) {
return colMode.key === columnMode;
});
return Boolean(currentColumnModes !== undefined && ((_currentColumnModes$r = currentColumnModes.requiredColumns) === null || _currentColumnModes$r === void 0 ? void 0 : _currentColumnModes$r.every(function (colKey) {
return _this4.hasColumnValue(columns[colKey]);
})));
}
return Boolean(columns && Object.values(columns).every(function (column) {
return _this4.hasRequiredColumn(column);
}));
}
/**
* Check whether layer has data
*
* @param {Array | Object} layerData
* @returns {boolean} yes or no
*/
}, {
key: "hasLayerData",
value: function hasLayerData(layerData) {
if (!layerData) {
return false;
}
return Boolean(layerData.data && (layerData.data.length || layerData.data.numRows));
}
}, {
key: "isValidToSave",
value: function isValidToSave() {
return Boolean(this.type && this.hasAllColumns());
}
}, {
key: "shouldRenderLayer",
value: function shouldRenderLayer(data) {
return Boolean(this.type) && this.hasAllColumns() && this.hasLayerData(data) && typeof this.renderLayer === 'function';
}
}, {
key: "getColorScale",
value: function getColorScale(colorScale, colorDomain, colorRange) {
if (colorScale === _constants.SCALE_TYPES.customOrdinal) {
return (0, _utils.getCategoricalColorScale)(colorDomain, colorRange);
}
if ((0, _utils.hasColorMap)(colorRange) && colorScale === _constants.SCALE_TYPES.custom) {
var _colorRange$colorMap;
var cMap = new Map();
(_colorRange$colorMap = colorRange.colorMap) === null || _colorRange$colorMap === void 0 || _colorRange$colorMap.forEach(function (_ref7) {
var _ref8 = (0, _slicedToArray2["default"])(_ref7, 2),
k = _ref8[0],
v = _ref8[1];
cMap.set(k, typeof v === 'string' ? (0, _utils.hexToRgb)(v) : v);
});
var scaleType = colorScale === _constants.SCALE_TYPES.custom ? colorScale : _constants.SCALE_TYPES.ordinal;
var scale = (0, _utils.getScaleFunction)(scaleType, cMap.values(), cMap.keys(), false);
scale.unknown(cMap.get(_constants.UNKNOWN_COLOR_KEY) || _constants.NO_VALUE_COLOR);
return scale;
}
return this.getVisChannelScale(colorScale, colorDomain, colorRange.colors.map(_utils.hexToRgb));
}
}, {
key: "accessVSFieldValue",
value: function accessVSFieldValue(_field, _indexKey) {
return defaultGetFieldValue;
}
/**
* Mapping from visual channels to deck.gl accesors
* @param param Parameters
* @param param.dataAccessor Access kepler.gl layer data from deck.gl layer
* @param param.dataContainer DataContainer to use use with dataAccessor
* @return {Object} attributeAccessors - deck.gl layer attribute accessors
*/
}, {
key: "getAttributeAccessors",
value: function getAttributeAccessors(_ref9) {
var _this5 = this;
var _ref9$dataAccessor = _ref9.dataAccessor,
dataAccessor = _ref9$dataAccessor === void 0 ? defaultDataAccessor : _ref9$dataAccessor,
dataContainer = _ref9.dataContainer,
indexKey = _ref9.indexKey;
var attributeAccessors = {};
Object.keys(this.visualChannels).forEach(function (channel) {
var _this5$visualChannels = _this5.visualChannels[channel],
field = _this5$visualChannels.field,
fixed = _this5$visualChannels.fixed,
scale = _this5$visualChannels.scale,
domain = _this5$visualChannels.domain,
range = _this5$visualChannels.range,
accessor = _this5$visualChannels.accessor,
defaultValue = _this5$visualChannels.defaultValue,
getAttributeValue = _this5$visualChannels.getAttributeValue,
nullValue = _this5$visualChannels.nullValue,
channelScaleType = _this5$visualChannels.channelScaleType;
if (accessor) {
var shouldGetScale = _this5.config[field];
if (shouldGetScale) {
var isFixed = fixed && _this5.config.visConfig[fixed];
var scaleFunction = channelScaleType === _constants.CHANNEL_SCALES.color ? _this5.getColorScale(_this5.config[scale], _this5.config[domain], _this5.config.visConfig[range]) : _this5.getVisChannelScale(_this5.config[scale], _this5.config[domain], _this5.config.visConfig[range], isFixed);
var getFieldValue = _this5.accessVSFieldValue(_this5.config[field], indexKey);
if (scaleFunction) {
attributeAccessors[accessor] = scaleFunction.byZoom ? (0, _lodash["default"])(function (z) {
var scaleFunc = scaleFunction(z);
return function (d) {
return _this5.getEncodedChannelValue(scaleFunc, dataAccessor(dataContainer)(d), _this5.config[field], nullValue, getFieldValue);
};
}) : function (d) {
return _this5.getEncodedChannelValue(scaleFunction, dataAccessor(dataContainer)(d), _this5.config[field], nullValue, getFieldValue);
};
// set getFillColorByZoom to true
if (scaleFunction.byZoom) {
attributeAccessors["".concat(accessor, "ByZoom")] = true;
}
}
} else if (typeof getAttributeValue === 'function') {
attributeAccessors[accessor] = getAttributeValue(_this5.config);
} else {
attributeAccessors[accessor] = typeof defaultValue === 'function' ? defaultValue(_this5.config) : defaultValue;
}
if (!attributeAccessors[accessor]) {
_window.console.warn("Failed to provide accessor function for ".concat(accessor || channel));
}
}
});
return attributeAccessors;
}
}, {
key: "getVisChannelScale",
value: function getVisChannelScale(scale, domain, range, fixed) {
// if quantile is provided per zoom
if ((0, _utils.isDomainQuantile)(domain) && scale === _constants.SCALE_TYPES.quantile) {
var zSteps = domain.z;
var getScale = function getScaleByZoom(z) {
var scaleDomain = (0, _utils.getDomainStepsbyZoom)(domain.quantiles, zSteps, z);
var thresholds = (0, _utils.getThresholdsFromQuantiles)(scaleDomain, range.length);
return (0, _utils.getScaleFunction)('threshold', range, thresholds, false);
};
getScale.byZoom = true;
return getScale;
} else if ((0, _utils.isDomainStops)(domain)) {
// color is based on zoom
var _zSteps = domain.z;
// get scale function by z
// {
// z: [z, z, z],
// stops: [[min, max], [min, max]],
// interpolation: 'interpolate'
// }
var _getScale = function getScaleByZoom(z) {
var scaleDomain = (0, _utils.getDomainStepsbyZoom)(domain.stops, _zSteps, z);
return (0, _utils.getScaleFunction)(scale, range, scaleDomain, fixed);
};
_getScale.byZoom = true;
return _getScale;
}
return _constants.SCALE_FUNC[fixed ? 'linear' : scale]().domain(domain).range(fixed ? domain : range);
}
/**
* Get longitude and latitude bounds of the data.
*/
}, {
key: "getPointsBounds",
value: function getPointsBounds(dataContainer) {
var getPosition = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : identity;
// no need to loop through the entire dataset
// get a sample of data to calculate bounds
var sampleData = dataContainer.numRows() > MAX_SAMPLE_SIZE ? (0, _utils.getSampleContainerData)(dataContainer, MAX_SAMPLE_SIZE) : dataContainer;
var points = getPosition ? sampleData.mapIndex(getPosition) : [];
var latBounds = (0, _utils.getLatLngBounds)(points, 1, [-90, 90]);
var lngBounds = (0, _utils.getLatLngBounds)(points, 0, [-180, 180]);
if (!latBounds || !lngBounds) {
return null;
}
return [lngBounds[0], latBounds[0], lngBounds[1], latBounds[1]];
}
}, {
key: "getChangedTriggers",
value: function getChangedTriggers(dataUpdateTriggers) {
var triggerChanged = (0, _layerUpdate.diffUpdateTriggers)(dataUpdateTriggers, this._oldDataUpdateTriggers);
this._oldDataUpdateTriggers = dataUpdateTriggers;
return triggerChanged;
}
}, {
key: "getEncodedChannelValue",
value: function getEncodedChannelValue(scale, data, field) {
var nullValue = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : _constants.NO_VALUE_COLOR;
var getValue = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : defaultGetFieldValue;
var value = getValue(field, data);
if (!(0, _commonUtils.notNullorUndefined)(value)) {
return nullValue;
}
var attributeValue;
if (Array.isArray(value)) {
attributeValue = value.map(scale);
} else {
attributeValue = scale(value);
}
if (!(0, _commonUtils.notNullorUndefined)(attributeValue)) {
attributeValue = nullValue;
}
return attributeValue;
}
}, {
key: "updateMeta",
value: function updateMeta(meta) {
this.meta = _objectSpread(_objectSpread({}, this.meta), meta);
}
}, {
key: "getDataUpdateTriggers",
value: function getDataUpdateTriggers(_ref10) {
var filteredIndex = _ref10.filteredIndex,
id = _ref10.id,
dataContainer = _ref10.dataContainer;
var columns = this.config.columns;
return _objectSpread({
getData: {
datasetId: id,
dataContainer: dataContainer,
columns: columns,
filteredIndex: filteredIndex
},
getMeta: {
datasetId: id,
dataContainer: dataContainer,
columns: columns
}
}, (this.config.textLabel || []).reduce(function (accu, tl, i) {
return _objectSpread(_objectSpread({}, accu), {}, (0, _defineProperty2["default"])({}, "getLabelCharacterSet-".concat(i), tl.field ? tl.field.name : null));
}, {}));
}
}, {
key: "updateData",
value: function updateData(datasets, oldLayerData) {
if (!this.config.dataId) {
return {};
}
var layerDataset = datasets[this.config.dataId];
var dataContainer = layerDataset.dataContainer;
var getPosition = this.getPositionAccessor(dataContainer);
var dataUpdateTriggers = this.getDataUpdateTriggers(layerDataset);
var triggerChanged = this.getChangedTriggers(dataUpdateTriggers);
if (triggerChanged && (triggerChanged.getMeta || triggerChanged.getData)) {
this.updateLayerMeta(layerDataset, getPosition);
// reset filteredItemCount
this.filteredItemCount = {};
}
var data = [];
if (!(triggerChanged && triggerChanged.getData) && oldLayerData && oldLayerData.data) {
// same data
data = oldLayerData.data;
} else {
data = this.calculateDataAttribute(layerDataset, getPosition);
}
return {
data: data,
triggerChanged: triggerChanged
};
}
/**
* h