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
JavaScript
"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