UNPKG

kepler.gl

Version:

kepler.gl is a webgl based application to visualize large scale location data in the browser

1,336 lines (1,183 loc) 142 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports["default"] = exports.colorMaker = exports.layerColors = exports.OVERLAY_TYPE = exports.LAYER_ID_LENGTH = void 0; var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray")); var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator")); var _window = require("global/window"); var _keymirror = _interopRequireDefault(require("keymirror")); var _extensions = require("@deck.gl/extensions"); var _core = require("@deck.gl/core"); var _layers = require("@deck.gl/layers"); var _defaultLayerIcon = _interopRequireDefault(require("./default-layer-icon")); var _layerUpdate = require("./layer-update"); var _defaultSettings = require("../constants/default-settings"); var _colorRanges = require("../constants/color-ranges"); var _customColorRanges = require("../constants/custom-color-ranges"); var _layerFactory = require("./layer-factory"); var _utils = require("../utils/utils"); var _dataUtils = require("../utils/data-utils"); var _dataContainerUtils = require("../utils/table-utils/data-container-utils"); var _colorUtils = require("../utils/color-utils"); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { (0, _defineProperty2["default"])(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } var _marked = /*#__PURE__*/_regenerator["default"].mark(generateColor); /** @typedef {import('./index').Layer} LayerClass} */ /** * Approx. number of points to sample in a large data set * @type {number} */ var LAYER_ID_LENGTH = 6; exports.LAYER_ID_LENGTH = LAYER_ID_LENGTH; var MAX_SAMPLE_SIZE = 5000; var defaultDomain = [0, 1]; var dataFilterExtension = new _extensions.DataFilterExtension({ filterSize: _defaultSettings.MAX_GPU_FILTERS }); var defaultDataAccessor = function defaultDataAccessor(dc) { return function (d) { return d; }; }; var defaultGetFieldValue = function defaultGetFieldValue(field, d) { return field.valueAccessor(d); }; var OVERLAY_TYPE = (0, _keymirror["default"])({ deckgl: null, mapboxgl: null }); exports.OVERLAY_TYPE = OVERLAY_TYPE; var layerColors = Object.values(_customColorRanges.DataVizColors).map(_colorUtils.hexToRgb); exports.layerColors = layerColors; 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 = generateColor(); /** @type {LayerClass} */ exports.colorMaker = colorMaker; var Layer = /*#__PURE__*/function () { function Layer() { var props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; (0, _classCallCheck2["default"])(this, Layer); this.id = props.id || (0, _utils.generateHashId)(LAYER_ID_LENGTH); // meta this.meta = {}; // visConfigSettings this.visConfigSettings = {}; // @ts-ignore this.config = this.getDefaultLayerConfig(_objectSpread({ columns: this.getLayerColumns() }, props)); } (0, _createClass2["default"])(Layer, [{ key: "layerIcon", get: function get() { return _defaultLayerIcon["default"]; } }, { key: "overlayType", get: function get() { return OVERLAY_TYPE.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() { return []; } }, { key: "optionalColumns", get: function get() { 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: _defaultSettings.CHANNEL_SCALES.color, nullValue: _defaultSettings.NO_VALUE_COLOR, defaultValue: function defaultValue(config) { return config.color; } }, size: { property: 'size', field: 'sizeField', scale: 'sizeScale', domain: 'sizeDomain', range: 'sizeRange', key: 'size', channelScaleType: _defaultSettings.CHANNEL_SCALES.size, nullValue: 0, defaultValue: 1 } }; } /* * Column pairs maps layer column to a specific field pairs, * By default, it is set to null */ }, { key: "columnPairs", 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', fieldPairKey: 'lat' }, lng: { pair: 'lat', fieldPairKey: 'lng' } }; } /* * Default link column pairs, can be used for link based layers: arc, line etc */ }, { key: "defaultLinkColumnPairs", get: function get() { return { lat0: { pair: 'lng0', fieldPairKey: 'lat' }, lng0: { pair: 'lat0', fieldPairKey: 'lng' }, lat1: { pair: 'lng1', fieldPairKey: 'lat' }, lng1: { pair: 'lat1', fieldPairKey: 'lng' } }; } /** * 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; } /* * 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() { var props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; return { dataId: props.dataId || null, label: props.label || _layerFactory.DEFAULT_LAYER_LABEL, color: props.color || colorMaker.next().value, columns: props.columns || null, isVisible: props.isVisible || false, isConfigActive: props.isConfigActive || false, highlightColor: props.highlightColor || _layerFactory.DEFAULT_HIGHLIGHT_COLOR, hidden: 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: _defaultSettings.SCALE_TYPES.quantile, // color by size, domain is set by filters, field, scale type sizeDomain: [0, 1], sizeScale: _defaultSettings.SCALE_TYPES.linear, sizeField: null, visConfig: {}, textLabel: [_layerFactory.DEFAULT_TEXT_LABEL], colorUI: { color: _layerFactory.DEFAULT_COLOR_UI, colorRange: _layerFactory.DEFAULT_COLOR_UI }, animation: { enabled: false } }; } /** * Get the description of a visualChannel config * @param key * @returns {{label: string, measure: (string|string)}} */ }, { key: "getVisualChannelDescription", value: function getVisualChannelDescription(key) { // e.g. label: Color, measure: Vehicle Type return { label: this.visConfigSettings[this.visualChannels[key].range].label, measure: this.config[this.visualChannels[key].field] ? this.config[this.visualChannels[key].field].displayName || this.config[this.visualChannels[key].field].name : this.visualChannels[key].defaultMeasure }; } /** * Assign a field to layer column, return column config * @param key - Column Key * @param field - Selected field * @returns {{}} - Column config */ }, { key: "assignColumn", value: function assignColumn(key, field) { // 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[key]), update))); } /** * Assign a field pair to column config, return column config * @param key - Column Key * @param pair - field Pair * @returns {object} - Column config */ }, { key: "assignColumnPairs", value: function assignColumnPairs(key, pair) { var _this$columnPairs, _this$columnPairs2, _this$columnPairs3, _objectSpread3; 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; } var _this$columnPairs$key = (_this$columnPairs2 = this.columnPairs) === null || _this$columnPairs2 === void 0 ? void 0 : _this$columnPairs2[key], partnerKey = _this$columnPairs$key.pair, fieldPairKey = _this$columnPairs$key.fieldPairKey; var _this$columnPairs$par = (_this$columnPairs3 = this.columnPairs) === null || _this$columnPairs3 === void 0 ? void 0 : _this$columnPairs3[partnerKey], partnerFieldPairKey = _this$columnPairs$par.fieldPairKey; return _objectSpread(_objectSpread({}, this.config.columns), {}, (_objectSpread3 = {}, (0, _defineProperty2["default"])(_objectSpread3, key, pair[fieldPairKey]), (0, _defineProperty2["default"])(_objectSpread3, partnerKey, pair[partnerFieldPairKey]), _objectSpread3)); } /** * 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(_ref) { var zoom = _ref.zoom, _ref$zoomOffset = _ref.zoomOffset, zoomOffset = _ref$zoomOffset === void 0 ? 0 : _ref$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 | void} mapState.zoomOffset - zoomOffset when render in the plot container for export image * @returns {number} */ }, { key: "getElevationZoomFactor", value: function getElevationZoomFactor(_ref2) { var zoom = _ref2.zoom, _ref2$zoomOffset = _ref2.zoomOffset, zoomOffset = _ref2$zoomOffset === void 0 ? 0 : _ref2$zoomOffset; return this.config.visConfig.enableElevationZoomFactor ? Math.pow(2, Math.max(8 - zoom + zoomOffset, 0)) : 1; } }, { key: "formatLayerData", value: function formatLayerData(datasets, filteredIndex) { return {}; } }, { key: "renderLayer", value: function renderLayer() { return []; } }, { key: "getHoverData", value: function getHoverData(object, dataContainer) { 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); } /** * 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 */ }, { key: "assignConfigToLayer", value: function assignConfigToLayer(configToCopy, visConfigSettings) { 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 }); 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 _ref3 = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}, _ref3$shallowCopy = _ref3.shallowCopy, shallowCopy = _ref3$shallowCopy === void 0 ? [] : _ref3$shallowCopy, _ref3$notToCopy = _ref3.notToCopy, notToCopy = _ref3$notToCopy === void 0 ? [] : _ref3$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, _dataUtils.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) { if (typeof item === 'string' && _layerFactory.LAYER_VIS_CONFIGS[layerVisConfigs[item]]) { // if assigned one of default LAYER_CONFIGS _this3.config.visConfig[item] = _layerFactory.LAYER_VIS_CONFIGS[layerVisConfigs[item]].defaultValue; _this3.visConfigSettings[item] = _layerFactory.LAYER_VIS_CONFIGS[layerVisConfigs[item]]; } else if (['type', 'defaultValue'].every(function (p) { return layerVisConfigs[item].hasOwnProperty(p); })) { // if provided customized visConfig, and has type && defaultValue // TODO: further check if customized visConfig is valid _this3.config.visConfig[item] = layerVisConfigs[item].defaultValue; _this3.visConfigSettings[item] = layerVisConfigs[item]; } }); } }, { key: "getLayerColumns", value: function getLayerColumns() { var columnValidators = this.columnValidators || {}; var required = this.requiredLayerColumns.reduce(function (accu, key) { return _objectSpread(_objectSpread({}, accu), {}, (0, _defineProperty2["default"])({}, key, columnValidators[key] ? { value: null, fieldIdx: -1, validator: columnValidators[key] } : { value: null, fieldIdx: -1 })); }, {}); var optional = this.optionalColumns.reduce(function (accu, key) { return _objectSpread(_objectSpread({}, accu), {}, (0, _defineProperty2["default"])({}, key, { value: null, fieldIdx: -1, optional: true })); }, {}); return _objectSpread(_objectSpread({}, required), optional); } }, { 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, _ref4) { var _ref5 = (0, _slicedToArray2["default"])(_ref4, 2), key = _ref5[0], value = _ref5[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] || _layerFactory.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) { this.updateColorUIByColorRange(newConfig, prop); this.updateColorRangeByColorUI(newConfig, previous, prop); this.updateCustomPalette(newConfig, previous, prop); } return this; } }, { key: "updateCustomPalette", value: function updateCustomPalette(newConfig, previous, prop) { if (!newConfig.colorRangeConfig || !newConfig.colorRangeConfig.custom) { return; } var _this$config2 = this.config, colorUI = _this$config2.colorUI, visConfig = _this$config2.visConfig; if (!visConfig[prop]) return; var colors = visConfig[prop].colors; var customPalette = _objectSpread(_objectSpread({}, colorUI[prop].customPalette), {}, { name: 'Custom Palette', colors: (0, _toConsumableArray2["default"])(colors) }); 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) { if (typeof newConfig.showDropdown !== 'number') return; var _this$config3 = this.config, colorUI = _this$config3.colorUI, visConfig = _this$config3.visConfig; this.updateLayerConfig({ colorUI: _objectSpread(_objectSpread({}, colorUI), {}, (0, _defineProperty2["default"])({}, prop, _objectSpread(_objectSpread({}, colorUI[prop]), {}, { colorRangeConfig: _objectSpread(_objectSpread({}, colorUI[prop].colorRangeConfig), {}, { steps: 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'].some(function (key) { return newConfig.colorRangeConfig.hasOwnProperty(key) && newConfig.colorRangeConfig[key] !== (previous[prop] || _layerFactory.DEFAULT_COLOR_UI).colorRangeConfig[key]; }); if (!shouldUpdate) return; var _this$config4 = this.config, colorUI = _this$config4.colorUI, visConfig = _this$config4.visConfig; var _colorUI$prop$colorRa = colorUI[prop].colorRangeConfig, steps = _colorUI$prop$colorRa.steps, reversed = _colorUI$prop$colorRa.reversed; var colorRange = visConfig[prop]; // find based on step or reversed var update; if (newConfig.colorRangeConfig.hasOwnProperty('steps')) { var group = (0, _colorUtils.getColorGroupByName)(colorRange); if (group) { var sameGroup = _colorRanges.COLOR_RANGES.filter(function (cr) { return (0, _colorUtils.getColorGroupByName)(cr) === group; }); update = sameGroup.find(function (cr) { return cr.colors.length === steps; }); if (update && colorRange.reversed) { update = (0, _colorUtils.reverseColorRange)(true, update); } } } if (newConfig.colorRangeConfig.hasOwnProperty('reversed')) { update = (0, _colorUtils.reverseColorRange)(reversed, update || colorRange); } if (update) { this.updateLayerVisConfig((0, _defineProperty2["default"])({}, prop, update)); } } /** * Check whether layer has all columns * @returns {boolean} yes or no */ }, { key: "hasAllColumns", value: function hasAllColumns() { var columns = this.config.columns; return columns && Object.values(columns).every(function (v) { return Boolean(v.optional || v.value && v.fieldIdx > -1); }); } /** * 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); } }, { key: "isValidToSave", value: function isValidToSave() { return this.type && this.hasAllColumns(); } }, { key: "shouldRenderLayer", value: function shouldRenderLayer(data) { return this.type && this.hasAllColumns() && this.hasLayerData(data) && typeof this.renderLayer === 'function'; } }, { key: "getColorScale", value: function getColorScale(colorScale, colorDomain, colorRange) { if (Array.isArray(colorRange.colorMap)) { var cMap = new Map(); colorRange.colorMap.forEach(function (_ref6) { var _ref7 = (0, _slicedToArray2["default"])(_ref6, 2), k = _ref7[0], v = _ref7[1]; cMap.set(k, typeof v === 'string' ? (0, _colorUtils.hexToRgb)(v) : v); }); // @ts-ignore d3 scale var scale = _defaultSettings.SCALE_FUNC[_defaultSettings.SCALE_TYPES.ordinal]().domain(cMap.keys()).range(cMap.values()).unknown(cMap.get(_layerFactory.UNKNOWN_COLOR_KEY) || _defaultSettings.NO_VALUE_COLOR); return scale; } return this.getVisChannelScale(colorScale, colorDomain, colorRange.colors.map(_colorUtils.hexToRgb)); } /** * Mapping from visual channels to deck.gl accesors * @param {Object} param Parameters * @param {Function} param.dataAccessor Access kepler.gl layer data from deck.gl layer * @param {import('utils/table-utils/data-container-interface').DataContainerInterface} param.dataContainer DataContainer to use use with dataAccessor * @return {Object} attributeAccessors - deck.gl layer attribute accessors */ }, { key: "getAttributeAccessors", value: function getAttributeAccessors(_ref8) { var _this4 = this; var _ref8$dataAccessor = _ref8.dataAccessor, dataAccessor = _ref8$dataAccessor === void 0 ? defaultDataAccessor : _ref8$dataAccessor, dataContainer = _ref8.dataContainer; var attributeAccessors = {}; Object.keys(this.visualChannels).forEach(function (channel) { var _this4$visualChannels = _this4.visualChannels[channel], field = _this4$visualChannels.field, fixed = _this4$visualChannels.fixed, scale = _this4$visualChannels.scale, domain = _this4$visualChannels.domain, range = _this4$visualChannels.range, accessor = _this4$visualChannels.accessor, defaultValue = _this4$visualChannels.defaultValue, getAttributeValue = _this4$visualChannels.getAttributeValue, nullValue = _this4$visualChannels.nullValue, channelScaleType = _this4$visualChannels.channelScaleType; var shouldGetScale = _this4.config[field]; if (shouldGetScale) { var args = [_this4.config[scale], _this4.config[domain], _this4.config.visConfig[range]]; var isFixed = fixed && _this4.config.visConfig[fixed]; var scaleFunction = channelScaleType === _defaultSettings.CHANNEL_SCALES.color ? _this4.getColorScale.apply(_this4, args) : _this4.getVisChannelScale.apply(_this4, args.concat([isFixed])); attributeAccessors[accessor] = function (d) { return _this4.getEncodedChannelValue(scaleFunction, dataAccessor(dataContainer)(d), _this4.config[field], nullValue); }; } else if (typeof getAttributeValue === 'function') { attributeAccessors[accessor] = getAttributeValue(_this4.config); } else { attributeAccessors[accessor] = typeof defaultValue === 'function' ? defaultValue(_this4.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) { // @ts-ignore d3-scale type return _defaultSettings.SCALE_FUNC[fixed ? 'linear' : scale]().domain(domain).range(fixed ? domain : range); } /** * Get longitude and latitude bounds of the data. * @param {import('utils/table-utils/data-container-interface').DataContainerInterface} dataContainer DataContainer to calculate bounds for. * @param {(d: {index: number}, dc: import('utils/table-utils/data-container-interface').DataContainerInterface) => number[]} getPosition Access kepler.gl layer data from deck.gl layer * @return {number[]|null} bounds of the data. */ }, { key: "getPointsBounds", value: function getPointsBounds(dataContainer, getPosition) { // no need to loop through the entire dataset // get a sample of data to calculate bounds var sampleData = dataContainer.numRows() > MAX_SAMPLE_SIZE ? (0, _dataContainerUtils.getSampleData)(dataContainer, MAX_SAMPLE_SIZE) : dataContainer; var points = sampleData.mapIndex(getPosition); var latBounds = (0, _dataUtils.getLatLngBounds)(points, 1, [-90, 90]); var lngBounds = (0, _dataUtils.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] : _defaultSettings.NO_VALUE_COLOR; var getValue = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : defaultGetFieldValue; var type = field.type; var value = getValue(field, data); if (!(0, _dataUtils.notNullorUndefined)(value)) { return nullValue; } var attributeValue; if (type === _defaultSettings.ALL_FIELD_TYPES.timestamp) { // shouldn't need to convert here // scale Function should take care of it attributeValue = scale(new Date(value)); } else { attributeValue = scale(value); } if (!(0, _dataUtils.notNullorUndefined)(attributeValue)) { attributeValue = nullValue; } return attributeValue; } }, { key: "updateMeta", value: function updateMeta(meta) { this.meta = _objectSpread(_objectSpread({}, this.meta), meta); } }, { key: "getDataUpdateTriggers", value: function getDataUpdateTriggers(_ref9) { var filteredIndex = _ref9.filteredIndex, id = _ref9.id, allData = _ref9.allData; var columns = this.config.columns; return _objectSpread({ getData: { datasetId: id, allData: allData, columns: columns, filteredIndex: filteredIndex }, getMeta: { datasetId: id, allData: allData, 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.getMeta) { this.updateLayerMeta(dataContainer, getPosition); } var data = []; if (!triggerChanged.getData && oldLayerData && oldLayerData.data) { // same data data = oldLayerData.data; } else { data = this.calculateDataAttribute(layerDataset, getPosition); } return { data: data, triggerChanged: triggerChanged }; } /** * helper function to update one layer domain when state.data changed * if state.data change is due ot update filter, newFiler will be passed * called by updateAllLayerDomainData * @param {Object} datasets * @param {Object} newFilter * @returns {object} layer */ }, { key: "updateLayerDomain", value: function updateLayerDomain(datasets, newFilter) { var _this5 = this; var table = this.getDataset(datasets); if (!table) { return this; } Object.values(this.visualChannels).forEach(function (channel) { var scale = channel.scale; var scaleType = _this5.config[scale]; // ordinal domain is based on dataContainer, if only filter changed // no need to update ordinal domain if (!newFilter || scaleType !== _defaultSettings.SCALE_TYPES.ordinal) { var domain = channel.domain; var updatedDomain = _this5.calculateLayerDomain(table, channel); _this5.updateLayerConfig((0, _defineProperty2["default"])({}, domain, updatedDomain)); } }); return this; } }, { key: "getDataset", value: function getDataset(datasets) { return this.config.dataId ? datasets[this.config.dataId] : null; } /** * Validate visual channel field and scales based on supported field & scale type * @param channel */ }, { key: "validateVisualChannel", value: function validateVisualChannel(channel) { this.validateFieldType(channel); this.validateScale(channel); } /** * Validate field type based on channelScaleType */ }, { key: "validateFieldType", value: function validateFieldType(channel) { var visualChannel = this.visualChannels[channel]; var field = visualChannel.field, channelScaleType = visualChannel.channelScaleType, supportedFieldTypes = visualChannel.supportedFieldTypes; if (this.config[field]) { // if field is selected, check if field type is supported var channelSupportedFieldTypes = supportedFieldTypes || _defaultSettings.CHANNEL_SCALE_SUPPORTED_FIELDS[channelScaleType]; if (!channelSupportedFieldTypes.includes(this.config[field].type)) { // field type is not supported, set it back to null // set scale back to default this.updateLayerConfig((0, _defineProperty2["default"])({}, field, null)); } } } /** * Validate scale type based on aggregation */ }, { key: "validateScale", value: function validateScale(channel) { var visualChannel = this.visualChannels[channel]; var scale = visualChannel.scale; if (!scale) { // visualChannel doesn't have scale return; } var scaleOptions = this.getScaleOptions(channel); // check if current selected scale is // supported, if not, change to default if (!scaleOptions.includes(this.config[scale])) { this.updateLayerConfig((0, _defineProperty2["default"])({}, scale, scaleOptions[0])); } } /** * Get scale options based on current field * @param {string} channel * @returns {string[]} */ }, { key: "getScaleOptions", value: function getScaleOptions(channel) { var visualChannel = this.visualChannels[channel]; var field = visualChannel.field, scale = visualChannel.scale, channelScaleType = visualChannel.channelScaleType; return this.config[field] ? _defaultSettings.FIELD_OPTS[this.config[field].type].scale[channelScaleType] : [this.getDefaultLayerConfig()[scale]]; } }, { key: "updateLayerVisualChannel", value: function updateLayerVisualChannel(dataset, channel) { var visualChannel = this.visualChannels[channel]; this.validateVisualChannel(channel); // calculate layer channel domain var updatedDomain = this.calculateLayerDomain(dataset, visualChannel); this.updateLayerConfig((0, _defineProperty2["default"])({}, visualChannel.domain, updatedDomain)); } }, { key: "getVisualChannelUpdateTriggers", value: function getVisualChannelUpdateTriggers() { var _this6 = this; var updateTriggers = {}; Object.values(this.visualChannels).forEach(function (visualChannel) { var _objectSpread11; // field range scale domain var accessor = visualChannel.accessor, field = visualChannel.field, scale = visualChannel.scale, domain = visualChannel.domain, range = visualChannel.range, defaultValue = visualChannel.defaultValue, fixed = visualChannel.fixed; updateTriggers[accessor] = _objectSpread((_objectSpread11 = {}, (0, _defineProperty2["default"])(_objectSpread11, field, _this6.config[field]), (0, _defineProperty2["default"])(_objectSpread11, scale, _this6.config[scale]), (0, _defineProperty2["default"])(_objectSpread11, domain, _this6.config[domain]), (0, _defineProperty2["default"])(_objectSpread11, range, _this6.config.visConfig[range]), (0, _defineProperty2["default"])(_objectSpread11, "defaultValue", typeof defaultValue === 'function' ? defaultValue(_this6.config) : defaultValue), _objectSpread11), fixed ? (0, _defineProperty2["default"])({}, fixed, _this6.config.visConfig[fixed]) : {}); }); return updateTriggers; } }, { key: "calculateLayerDomain", value: function calculateLayerDomain(dataset, visualChannel) { var scale = visualChannel.scale; var scaleType = this.config[scale]; var field = this.config[visualChannel.field]; if (!field) { // if colorField or sizeField were set back to null return defaultDomain; } return dataset.getColumnLayerDomain(field, scaleType) || defaultDomain; } }, { key: "hasHoveredObject", value: function hasHoveredObject(objectInfo) { return this.isLayerHovered(objectInfo) && objectInfo.object ? objectInfo.object : null; } }, { key: "isLayerHovered", value: function isLayerHovered(objectInfo) { var _objectInfo$layer, _objectInfo$layer$pro; return (objectInfo === null || objectInfo === void 0 ? void 0 : objectInfo.picked) && (objectInfo === null || objectInfo === void 0 ? void 0 : (_objectInfo$layer = objectInfo.layer) === null || _objectInfo$layer === void 0 ? void 0 : (_objectInfo$layer$pro = _objectInfo$layer.props) === null || _objectInfo$layer$pro === void 0 ? void 0 : _objectInfo$layer$pro.id) === this.id; } }, { key: "getRadiusScaleByZoom", value: function getRadiusScaleByZoom(mapState, fixedRadius) { var radiusChannel = Object.values(this.visualChannels).find(function (vc) { return vc.property === 'radius'; }); if (!radiusChannel) { return 1; } var field = radiusChannel.field; var fixed = fixedRadius === undefined ? this.config.visConfig.fixedRadius : fixedRadius; var radius = this.config.visConfig.radius; // @ts-ignore return fixed ? 1 : (this.config[field] ? 1 : radius) * this.getZoomFactor(mapState); } }, { key: "shouldCalculateLayerData", value: function shouldCalculateLayerData(props) { var _this7 = this; return props.some(function (p) { return !_this7.noneLayerDataAffectingProps.includes(p); }); } }, { key: "getBrushingExtensionProps", value: function getBrushingExtensionProps(interactionConfig, brushingTarget) { var brush = interactionConfig.brush; return { // brushing autoHighlight: !brush.enabled, brushingRadius: brush.config.size * 1000, brushingTarget: brushingTarget || 'source', brushingEnabled: brush.enabled }; } }, { key: "getDefaultDeckLayerProps", value: function getDefaultDeckLayerProps(_ref11) { var idx = _ref11.idx, gpuFilter = _ref11.gpuFilter, mapState = _ref11.mapState, visible = _ref11.visible; return { id: this.id, idx: idx, coordinateSystem: _core.COORDINATE_SYSTEM.LNGLAT, pickable: true, wrapLongitude: true, parameters: { depthTest: Boolean(mapState.dragRotate || this.config.visConfig.enable3d) }, hidden: this.config.hidden, // visconfig opacity: this.config.visConfig.opacity, highlightColor: this.config.highlightColor, // data filtering extensions: [dataFilterExtension], filterRange: gpuFilter ? gpuFilter.filterRange : undefined, // layer should be visible and if splitMap, shown in to one of panel visible: this.config.isVisible && visible }; } }, { key: "getDefaultHoverLayerProps", value: function getDefaultHoverLayerProps() { return { id: "".concat(this.id, "-hovered"), pickable: false, wrapLongitude: true, coordinateSystem: _core.COORDINATE_SYSTEM.LNGLAT }; } }, { key: "renderTextLabelLayer", value: function renderTextLabelLayer(_ref12, renderOpts) { var _this8 = this; var getPosition = _ref12.getPosition, getPixelOffset = _ref12.getPixelOffset, updateTriggers = _ref12.updateTriggers, sharedProps = _ref12.sharedProps; var data = renderOpts.data, mapState = renderOpts.mapState; var textLabel = this.config.textLabel; return data.textLabels.reduce(function (accu, d, i) { if (d.getText) { var _textLabel$i$field, _textLabel$i$field2; accu.push(new _layers.TextLayer(_objectSpread(_objectSpread({}, sharedProps), {}, { id: "".concat(_this8.id, "-label-").concat((_textLabel$i$field = textLabel[i].field) === null || _textLabel$i$field === void 0 ? void 0 : _textLabel$i$field.name), data: data.data, getText: d.getText, getPosition: getPosition, characterSet: d.characterSet, getPixelOffset: getPixelOffset(textLabel[i]), getSize: 1, sizeScale: textLabel[i].size, getTextAnchor: textLabel[i].anchor, getAlignmentBaseline: textLabel[i].alignment, getColor: textLabel[i].color, parameters: { // text will always show on top of all layers depthTest: false }, getFilterValue: data.getFilterValue, updateTriggers: _objectSpread(_objectSpread({}, updateTriggers), {}, { getText: (_textLabel$i$field2 = textLabel[i].field) === null || _textLabel$i$field2 === void 0 ? void 0 : _textLabel$i$field2.name, getPixelOffset: _objectSpread(_objectSpread({}, updateTriggers.getRadius), {}, { mapState: mapState, anchor: textLabel[i].anchor, alignment: textLabel[i].alignment }), getTextAnchor: textLabel[i].anchor, getAlignmentBaseline: textLabel[i].alignment, getColor: textLabel[i].color }) }))); } return accu; }, []); } }, { key: "calculateDataAttribute", value: function calculateDataAttribute(keplerTable, getPosition) { // implemented in subclasses return []; } }, { key: "updateLayerMeta", value: function updateLayerMeta(dataContainer, getPosition) {// implemented in subclasses } }, { key: "getPositionAccessor", value: function getPositionAccessor(dataContainer) { // implemented in subclasses return function () { return null; }; } }], [{ key: "findDefaultLayerProps", value: function findDefaultLayerProps(dataset, foundLayers) { return { props: [], foundLayers: foundLayers }; } /** * Given a array of preset required column names * found field that has the same name to set as layer column * * @param {object} defaultFields * @param {object[]} allFields * @returns {object[] | null} all possible required layer column pairs */ }, { key: "findDefaultColumnField", value: function findDefaultColumnField(defaultFields, allFields) { // find all matched fields for each required col var requiredColumns = Object.keys(defaultFields).reduce(function (prev, key) { var requiredFields = allFields.filter(function (f) { return f.name === defaultFields[key] || defaultFields[key].includes(f.name); }); prev[key] = requiredFields.length ? requiredFields.map(function (f) { return { value: f.name, fieldIdx: f.fieldIdx }; }) : null; return prev; }, {}); if (!Object.values(requiredColumns).every(Boolean)) { // if any field missing, return null return null; } return this.getAllPossibleColumnParis(requiredColumns); } }, { key: "getAllPossibleColumnParis", value: function getAllPossibleColumnParis(requiredColumns) { // for multiple matched field for one required column, return multiple // combinations, e. g. if column a has 2 matched, column b has 3 matched // 6 possible column pairs will be returned var allKeys = Object.keys(requiredColumns); var pointers = allKeys.map(function (k, i) { return i === allKeys.length - 1 ? -1 : 0; }); var countPerKey = allKeys.map(function (k) { return requiredColumns[k].length; }); var pairs = []; /* eslint-disable no-loop-func */ while (incrementPointers(pointers, countPerKey, pointers.length - 1)) { var newPair = pointers.reduce(function (prev, cuur, i) { prev[allKeys[i]] = requiredColumns[allKeys[i]][cuur]; return prev; }, {}); pairs.push(newPair); } /* eslint-enable no-loop-func */ // recursively increment pointers function incrementPointers(pts, counts, index) { if (index === 0 && pts[0] === counts[0] - 1) { // nothing to increment return false; } if (pts[index] + 1 < counts[index]) { pts[index] = pts[index] + 1; return true; } pts[index] = 0; return incrementPointers(pts, counts, index - 1); } return pairs; } }, { key: "hexToRgb", value: function hexToRgb(c) { return (0, _colorUtils.hexToRgb)(c); } }]); return Layer; }(); var _default = Layer; exports["default"] = _default; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9sYXllcnMvYmFzZS1sYXllci5qcyJdLCJuYW1lcyI6WyJnZW5lcmF0ZUNvbG9yIiwiTEFZRVJfSURfTEVOR1RIIiwiTUFYX1NBTVBMRV9TSVpFIiwiZGVmYXVsdERvbWFpbiIsImRhdGFGaWx0ZXJFeHRlbnNpb24iLCJEYXRhRmlsdGVyRXh0ZW5zaW9uIiwiZmlsdGVyU2l6ZSIsIk1BWF9HUFVfRklMVEVSUyIsImRlZmF1bHREYXRhQWNjZXNzb3IiLCJkYyIsImQiLCJkZWZhdWx0R2V0RmllbGRWYWx1ZSIsImZpZWxkIiwidmFsdWVBY2Nlc3NvciIsIk9WRVJMQVlfVFlQRSIsImRlY2tnbCIsIm1hcGJveGdsIiwibGF5ZXJDb2xvcnMiLCJPYmplY3QiLCJ2YWx1ZXMiLCJEYXRhVml6Q29sb3JzIiwibWFwIiwiaGV4VG9SZ2IiLCJpbmRleCIsImxlbmd0aCIsImNvbG9yTWFrZXIiLCJMYXllciIsInByb3BzIiwiaWQiLCJtZXRhIiwidmlzQ29uZmlnU2V0dGluZ3MiLCJjb25maWciLCJnZXREZWZhdWx0TGF5ZXJDb25maWciLCJjb2x1bW5zIiwiZ2V0TGF5ZXJDb2x1bW5zIiwiRGVmYXVsdExheWVySWNvbiIsInR5cGUiLCJjb2xvciIsInByb3BlcnR5Iiwic2NhbGUiLCJkb21haW4iLCJyYW5nZSIsImtleSIsImNoYW5uZWxTY2FsZVR5cGUiLCJDSEFOTkVMX1NDQUxFUyIsIm51bGxWYWx1ZSIsIk5PX1ZBTFVFX0NPTE9SIiwiZGVmYXVsdFZhbHVlIiwic2l6ZSIsImxhdCIsInBhaXIiLCJmaWVsZFBhaXJLZXkiLCJsbmciLCJsYXQwIiwibG5nMCIsImxhdDEiLCJsbmcxIiwiZGF0YUlkIiwibGFiZWwiLCJERUZBVUxUX0xBWUVSX0xBQkVMIiwibmV4dCIsInZhbHVlIiwiaXNWaXNpYmxlIiwiaXNDb25maWdBY3RpdmUiLCJoaWdobGlnaHRDb2xvciIsIkRFRkFVTFRfSElHSExJR0hUX0NPTE9SIiwiaGlkZGVuIiwiY29sb3JGaWVsZCIsImNvbG9yRG9tYWluIiwiY29sb3JTY2FsZSIsIlNDQUxFX1RZUEVTIiwicXVhbnRpbGUiLCJzaXplRG9tYWluIiwic2l6ZVNjYWxlIiwibGluZWFyIiwic2l6ZUZpZWxkIiwidmlzQ29uZmlnIiwidGV4dExhYmVsIiwiREVGQVVMVF9URVhUX0xBQkVMIiwiY29sb3JVSSIsIkRFRkFVTFRfQ09MT1JfVUkiLCJjb2xvclJhbmdlIiwiYW5pbWF0aW9uIiwiZW5hYmxlZCIsInZpc3VhbENoYW5uZWxzIiwibWVhc3VyZSIsImRpc3BsYXlOYW1lIiwibmFtZSIsImRlZmF1bHRNZWFzdXJlIiwidXBkYXRlIiwiZmllbGRJZHgiLCJjb2x1bW5QYWlycyIsInBhcnRuZXJLZXkiLCJwYXJ0bmVyRmllbGRQYWlyS2V5Iiwiem9vbSIsInpvb21PZmZzZXQiLCJNYXRoIiwicG93IiwibWF4IiwiZW5hYmxlRWxldmF0aW9uWm9vbUZhY3RvciIsImRhdGFzZXRzIiwiZmlsdGVyZWRJbmRleCIsIm9