UNPKG

kepler.gl

Version:

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

1,286 lines (1,066 loc) 125 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard"); Object.defineProperty(exports, "__esModule", { value: true }); exports.getDefaultFilter = getDefaultFilter; exports.shouldApplyFilter = shouldApplyFilter; exports.validatePolygonFilter = validatePolygonFilter; exports.validateFilter = validateFilter; exports.validateFilterWithData = validateFilterWithData; exports.getFilterProps = getFilterProps; exports.getFilterFunction = getFilterFunction; exports.updateFilterDataId = updateFilterDataId; exports.filterDataByFilterTypes = filterDataByFilterTypes; exports.getFilterRecord = getFilterRecord; exports.diffFilters = diffFilters; exports.adjustValueToFilterDomain = adjustValueToFilterDomain; exports.getNumericFieldDomain = getNumericFieldDomain; exports.getNumericStepSize = getNumericStepSize; exports.getTimestampFieldDomain = getTimestampFieldDomain; exports.histogramConstruct = histogramConstruct; exports.getHistogram = getHistogram; exports.formatNumberByStep = formatNumberByStep; exports.isInRange = isInRange; exports.isInPolygon = isInPolygon; exports.isValidTimeDomain = isValidTimeDomain; exports.getTimeWidgetTitleFormatter = getTimeWidgetTitleFormatter; exports.getTimeWidgetHintFormatter = getTimeWidgetHintFormatter; exports.isValidFilterValue = isValidFilterValue; exports.getFilterPlot = getFilterPlot; exports.getDefaultFilterPlotType = getDefaultFilterPlotType; exports.applyFiltersToDatasets = applyFiltersToDatasets; exports.applyFilterFieldName = applyFilterFieldName; exports.mergeFilterDomainStep = mergeFilterDomainStep; exports.generatePolygonFilter = generatePolygonFilter; exports.filterDatasetCPU = filterDatasetCPU; exports.validateFiltersUpdateDatasets = validateFiltersUpdateDatasets; exports.getIntervalBins = getIntervalBins; exports.getFilterIdInFeature = exports.featureToFilterValue = exports.getPolygonFilterFunctor = exports.LAYER_FILTERS = exports.FILTER_ID_LENGTH = exports.DEFAULT_FILTER_STRUCTURE = exports.FILTER_COMPONENTS = exports.LIMITED_FILTER_EFFECT_PROPS = exports.FILTER_UPDATER_PROPS = exports.PLOT_TYPES = exports.enlargedHistogramBins = exports.histogramBins = exports.TimestampStepMap = 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 _d3Array = require("d3-array"); var _keymirror = _interopRequireDefault(require("keymirror")); var _console = require("global/console"); var _lodash = _interopRequireDefault(require("lodash.get")); var _lodash2 = _interopRequireDefault(require("lodash.isequal")); var _booleanWithin = _interopRequireDefault(require("@turf/boolean-within")); var _helpers = require("@turf/helpers"); var _decimal = require("decimal.js"); var _defaultSettings = require("../constants/default-settings"); var _dataUtils = require("./data-utils"); var ScaleUtils = _interopRequireWildcard(require("./data-scale-utils")); var _types = require("../layers/types"); var _utils = require("./utils"); var _h3Utils = require("../layers/h3-hexagon-layer/h3-utils"); var _FILTER_TYPES$timeRan, _FILTER_TYPES$range, _SupportedPlotType, _FILTER_COMPONENTS; 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; } // TYPE /** @typedef {import('./table-utils/kepler-table').FilterRecord} FilterRecord */ /** @typedef {import('./filter-utils').FilterResult} FilterResult */ var TimestampStepMap = [{ max: 1, step: 0.05 }, { max: 10, step: 0.1 }, { max: 100, step: 1 }, { max: 500, step: 5 }, { max: 1000, step: 10 }, { max: 5000, step: 50 }, { max: Number.POSITIVE_INFINITY, step: 1000 }]; exports.TimestampStepMap = TimestampStepMap; var histogramBins = 30; exports.histogramBins = histogramBins; var enlargedHistogramBins = 100; exports.enlargedHistogramBins = enlargedHistogramBins; var durationSecond = 1000; var durationMinute = durationSecond * 60; var durationHour = durationMinute * 60; var durationDay = durationHour * 24; var durationWeek = durationDay * 7; var durationYear = durationDay * 365; var PLOT_TYPES = (0, _keymirror["default"])({ histogram: null, lineChart: null }); exports.PLOT_TYPES = PLOT_TYPES; var FILTER_UPDATER_PROPS = (0, _keymirror["default"])({ dataId: null, name: null, layerId: null }); exports.FILTER_UPDATER_PROPS = FILTER_UPDATER_PROPS; var LIMITED_FILTER_EFFECT_PROPS = (0, _keymirror["default"])((0, _defineProperty2["default"])({}, FILTER_UPDATER_PROPS.name, null)); /** * Max number of filter value buffers that deck.gl provides */ exports.LIMITED_FILTER_EFFECT_PROPS = LIMITED_FILTER_EFFECT_PROPS; var SupportedPlotType = (_SupportedPlotType = {}, (0, _defineProperty2["default"])(_SupportedPlotType, _defaultSettings.FILTER_TYPES.timeRange, (_FILTER_TYPES$timeRan = { "default": 'histogram' }, (0, _defineProperty2["default"])(_FILTER_TYPES$timeRan, _defaultSettings.ALL_FIELD_TYPES.integer, 'lineChart'), (0, _defineProperty2["default"])(_FILTER_TYPES$timeRan, _defaultSettings.ALL_FIELD_TYPES.real, 'lineChart'), _FILTER_TYPES$timeRan)), (0, _defineProperty2["default"])(_SupportedPlotType, _defaultSettings.FILTER_TYPES.range, (_FILTER_TYPES$range = { "default": 'histogram' }, (0, _defineProperty2["default"])(_FILTER_TYPES$range, _defaultSettings.ALL_FIELD_TYPES.integer, 'lineChart'), (0, _defineProperty2["default"])(_FILTER_TYPES$range, _defaultSettings.ALL_FIELD_TYPES.real, 'lineChart'), _FILTER_TYPES$range)), _SupportedPlotType); var FILTER_COMPONENTS = (_FILTER_COMPONENTS = {}, (0, _defineProperty2["default"])(_FILTER_COMPONENTS, _defaultSettings.FILTER_TYPES.select, 'SingleSelectFilter'), (0, _defineProperty2["default"])(_FILTER_COMPONENTS, _defaultSettings.FILTER_TYPES.multiSelect, 'MultiSelectFilter'), (0, _defineProperty2["default"])(_FILTER_COMPONENTS, _defaultSettings.FILTER_TYPES.timeRange, 'TimeRangeFilter'), (0, _defineProperty2["default"])(_FILTER_COMPONENTS, _defaultSettings.FILTER_TYPES.range, 'RangeFilter'), (0, _defineProperty2["default"])(_FILTER_COMPONENTS, _defaultSettings.FILTER_TYPES.polygon, 'PolygonFilter'), _FILTER_COMPONENTS); exports.FILTER_COMPONENTS = FILTER_COMPONENTS; var DEFAULT_FILTER_STRUCTURE = { dataId: [], // [string] freeze: false, id: null, // time range filter specific fixedDomain: false, enlarged: false, isAnimating: false, animationWindow: _defaultSettings.ANIMATION_WINDOW.free, speed: 1, // field specific name: [], // string type: null, fieldIdx: [], // [integer] domain: null, value: null, // plot plotType: PLOT_TYPES.histogram, yAxis: null, interval: null, // mode gpu: false }; exports.DEFAULT_FILTER_STRUCTURE = DEFAULT_FILTER_STRUCTURE; var FILTER_ID_LENGTH = 4; exports.FILTER_ID_LENGTH = FILTER_ID_LENGTH; var LAYER_FILTERS = [_defaultSettings.FILTER_TYPES.polygon]; /** * Generates a filter with a dataset id as dataId * @type {typeof import('./filter-utils').getDefaultFilter} */ exports.LAYER_FILTERS = LAYER_FILTERS; function getDefaultFilter(dataId) { return _objectSpread(_objectSpread({}, DEFAULT_FILTER_STRUCTURE), {}, { // store it as dataId and it could be one or many dataId: (0, _utils.toArray)(dataId), id: (0, _utils.generateHashId)(FILTER_ID_LENGTH) }); } /** * Check if a filter is valid based on the given dataId * @param filter to validate * @param datasetId id to validate filter against * @return true if a filter is valid, false otherwise * @type {typeof import('./filter-utils').shouldApplyFilter} */ function shouldApplyFilter(filter, datasetId) { var dataIds = (0, _utils.toArray)(filter.dataId); return dataIds.includes(datasetId) && filter.value !== null; } /** * Validates and modifies polygon filter structure * @param dataset * @param filter * @param layers * @return - {filter, dataset} * @type {typeof import('./filter-utils').validatePolygonFilter} */ function validatePolygonFilter(dataset, filter, layers) { var failed = { dataset: dataset, filter: null }; var value = filter.value, layerId = filter.layerId, type = filter.type, dataId = filter.dataId; if (!layerId || !isValidFilterValue(type, value)) { return failed; } var isValidDataset = dataId.includes(dataset.id); if (!isValidDataset) { return failed; } var layer = layers.find(function (l) { return layerId.includes(l.id); }); if (!layer) { return failed; } return { filter: _objectSpread(_objectSpread({}, filter), {}, { freeze: true, fieldIdx: [] }), dataset: dataset }; } /** * Custom filter validators */ var filterValidators = (0, _defineProperty2["default"])({}, _defaultSettings.FILTER_TYPES.polygon, validatePolygonFilter); /** * Default validate filter function * @param dataset * @param filter * @return - {filter, dataset} * @type {typeof import('./filter-utils').validateFilter} */ function validateFilter(dataset, filter) { // match filter.dataId var failed = { dataset: dataset, filter: null }; var filterDataId = (0, _utils.toArray)(filter.dataId); var filterDatasetIndex = filterDataId.indexOf(dataset.id); if (filterDatasetIndex < 0) { // the current filter is not mapped against the current dataset return failed; } var initializeFilter = _objectSpread(_objectSpread(_objectSpread({}, getDefaultFilter(filter.dataId)), filter), {}, { dataId: filterDataId, name: (0, _utils.toArray)(filter.name) }); var fieldName = initializeFilter.name[filterDatasetIndex]; var _applyFilterFieldName = applyFilterFieldName(initializeFilter, dataset, fieldName, filterDatasetIndex, { mergeDomain: true }), updatedFilter = _applyFilterFieldName.filter, updatedDataset = _applyFilterFieldName.dataset; if (!updatedFilter) { return failed; } updatedFilter.value = adjustValueToFilterDomain(filter.value, updatedFilter); updatedFilter.enlarged = typeof filter.enlarged === 'boolean' ? filter.enlarged : updatedFilter.enlarged; if (updatedFilter.value === null) { // cannot adjust saved value to filter return failed; } return { filter: validateFilterYAxis(updatedFilter, updatedDataset), dataset: updatedDataset }; } /** * Validate saved filter config with new data, * calculate domain and fieldIdx based new fields and data * * @param dataset * @param filter - filter to be validate * @param layers - layers * @return validated filter * @type {typeof import('./filter-utils').validateFilterWithData} */ function validateFilterWithData(dataset, filter, layers) { // @ts-ignore return filterValidators.hasOwnProperty(filter.type) ? filterValidators[filter.type](dataset, filter, layers) : validateFilter(dataset, filter); } /** * Validate YAxis * @param filter * @param dataset * @return {*} */ function validateFilterYAxis(filter, dataset) { // TODO: validate yAxis against other datasets var fields = dataset.fields; var _filter = filter, yAxis = _filter.yAxis; // TODO: validate yAxis against other datasets if (yAxis) { var matchedAxis = fields.find(function (_ref) { var name = _ref.name, type = _ref.type; return name === yAxis.name && type === yAxis.type; }); filter = matchedAxis ? _objectSpread(_objectSpread({}, filter), {}, { yAxis: matchedAxis }, getFilterPlot(_objectSpread(_objectSpread({}, filter), {}, { yAxis: matchedAxis }), dataset)) : filter; } return filter; } /** * Get default filter prop based on field type * * @param field * @param fieldDomain * @returns default filter * @type {typeof import('./filter-utils').getFilterProps} */ function getFilterProps(field, fieldDomain) { var filterProps = _objectSpread(_objectSpread({}, fieldDomain), {}, { fieldType: field.type }); switch (field.type) { case _defaultSettings.ALL_FIELD_TYPES.real: case _defaultSettings.ALL_FIELD_TYPES.integer: return _objectSpread(_objectSpread({}, filterProps), {}, { value: fieldDomain.domain, type: _defaultSettings.FILTER_TYPES.range, typeOptions: [_defaultSettings.FILTER_TYPES.range], gpu: true }); case _defaultSettings.ALL_FIELD_TYPES["boolean"]: return _objectSpread(_objectSpread({}, filterProps), {}, { type: _defaultSettings.FILTER_TYPES.select, value: true, gpu: false }); case _defaultSettings.ALL_FIELD_TYPES.string: case _defaultSettings.ALL_FIELD_TYPES.date: return _objectSpread(_objectSpread({}, filterProps), {}, { type: _defaultSettings.FILTER_TYPES.multiSelect, value: [], gpu: false }); case _defaultSettings.ALL_FIELD_TYPES.timestamp: return _objectSpread(_objectSpread({}, filterProps), {}, { type: _defaultSettings.FILTER_TYPES.timeRange, enlarged: true, fixedDomain: true, value: filterProps.domain, gpu: true }); default: return {}; } } var getPolygonFilterFunctor = function getPolygonFilterFunctor(layer, filter, dataContainer) { var getPosition = layer.getPositionAccessor(dataContainer); switch (layer.type) { case _types.LAYER_TYPES.point: case _types.LAYER_TYPES.icon: return function (data) { var pos = getPosition(data); return pos.every(Number.isFinite) && isInPolygon(pos, filter.value); }; case _types.LAYER_TYPES.arc: case _types.LAYER_TYPES.line: return function (data) { var pos = getPosition(data); return pos.every(Number.isFinite) && [[pos[0], pos[1]], [pos[3], pos[4]]].every(function (point) { return isInPolygon(point, filter.value); }); }; case _types.LAYER_TYPES.hexagonId: if (layer.dataToFeature && layer.dataToFeature.centroids) { return function (data) { // null or getCentroid({id}) var centroid = layer.dataToFeature.centroids[data.index]; return centroid && isInPolygon(centroid, filter.value); }; } return function (data) { var id = getPosition(data); if (!(0, _h3Utils.h3IsValid)(id)) { return false; } var pos = (0, _h3Utils.getCentroid)({ id: id }); return pos.every(Number.isFinite) && isInPolygon(pos, filter.value); }; default: return function () { return true; }; } }; /** * @param field dataset Field * @param dataId Dataset id * @param filter Filter object * @param layers list of layers to filter upon * @param dataContainer Data container * @return filterFunction * @type {typeof import('./filter-utils').getFilterFunction} */ exports.getPolygonFilterFunctor = getPolygonFilterFunctor; function getFilterFunction(field, dataId, filter, layers, dataContainer) { // field could be null in polygon filter var valueAccessor = field ? field.valueAccessor : function (data) { return null; }; var defaultFunc = function defaultFunc(d) { return true; }; switch (filter.type) { case _defaultSettings.FILTER_TYPES.range: return function (data) { return isInRange(valueAccessor(data), filter.value); }; case _defaultSettings.FILTER_TYPES.multiSelect: return function (data) { return filter.value.includes(valueAccessor(data)); }; case _defaultSettings.FILTER_TYPES.select: return function (data) { return valueAccessor(data) === filter.value; }; case _defaultSettings.FILTER_TYPES.timeRange: if (!field) { return defaultFunc; } var mappedValue = (0, _lodash["default"])(field, ['filterProps', 'mappedValue']); var accessor = Array.isArray(mappedValue) ? function (data) { return mappedValue[data.index]; } : function (data) { return (0, _dataUtils.timeToUnixMilli)(valueAccessor(data), field.format); }; return function (data) { return isInRange(accessor(data), filter.value); }; case _defaultSettings.FILTER_TYPES.polygon: if (!layers || !layers.length) { return defaultFunc; } // @ts-ignore var layerFilterFunctions = filter.layerId.map(function (id) { return layers.find(function (l) { return l.id === id; }); }).filter(function (l) { return l && l.config.dataId === dataId; }).map(function (layer) { return getPolygonFilterFunctor(layer, filter, dataContainer); }); return function (data) { return layerFilterFunctions.every(function (filterFunc) { return filterFunc(data); }); }; default: return defaultFunc; } } function updateFilterDataId(dataId) { return getDefaultFilter(dataId); } /** * @type {typeof import('./filter-utils').filterDataByFilterTypes} */ function filterDataByFilterTypes(_ref2, dataContainer) { var dynamicDomainFilters = _ref2.dynamicDomainFilters, cpuFilters = _ref2.cpuFilters, filterFuncs = _ref2.filterFuncs; var result = _objectSpread(_objectSpread({}, dynamicDomainFilters ? { filteredIndexForDomain: [] } : {}), cpuFilters ? { filteredIndex: [] } : {}); var filterContext = { index: -1, dataContainer: dataContainer }; var filterFuncCaller = function filterFuncCaller(filter) { return filterFuncs[filter.id](filterContext); }; var numRows = dataContainer.numRows(); for (var i = 0; i < numRows; ++i) { filterContext.index = i; var matchForDomain = dynamicDomainFilters && dynamicDomainFilters.every(filterFuncCaller); if (matchForDomain) { // @ts-ignore result.filteredIndexForDomain.push(filterContext.index); } var matchForRender = cpuFilters && cpuFilters.every(filterFuncCaller); if (matchForRender) { // @ts-ignore result.filteredIndex.push(filterContext.index); } } return result; } /** * Get a record of filters based on domain type and gpu / cpu * @type {typeof import('./filter-utils').getFilterRecord} */ function getFilterRecord(dataId, filters) { var opt = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; /** * @type {FilterRecord} */ var filterRecord = { dynamicDomain: [], fixedDomain: [], cpu: [], gpu: [] }; filters.forEach(function (f) { if (isValidFilterValue(f.type, f.value) && (0, _utils.toArray)(f.dataId).includes(dataId)) { (f.fixedDomain || opt.ignoreDomain ? filterRecord.fixedDomain : filterRecord.dynamicDomain).push(f); (f.gpu && !opt.cpuOnly ? filterRecord.gpu : filterRecord.cpu).push(f); } }); return filterRecord; } /** * Compare filter records to get what has changed * @type {typeof import('./filter-utils').diffFilters} */ function diffFilters(filterRecord) { var oldFilterRecord = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var filterChanged = {}; Object.entries(filterRecord).forEach(function (_ref3) { var _ref4 = (0, _slicedToArray2["default"])(_ref3, 2), record = _ref4[0], items = _ref4[1]; items.forEach(function (filter) { var oldFilter = (oldFilterRecord[record] || []).find(function (f) { return f.id === filter.id; }); if (!oldFilter) { // added filterChanged = (0, _utils.set)([record, filter.id], 'added', filterChanged); } else { // check what has changed ['name', 'value', 'dataId'].forEach(function (prop) { if (filter[prop] !== oldFilter[prop]) { filterChanged = (0, _utils.set)([record, filter.id], "".concat(prop, "_changed"), filterChanged); } }); } }); (oldFilterRecord[record] || []).forEach(function (oldFilter) { // deleted if (!items.find(function (f) { return f.id === oldFilter.id; })) { filterChanged = (0, _utils.set)([record, oldFilter.id], 'deleted', filterChanged); } }); if (!filterChanged[record]) { filterChanged[record] = null; } }); // @ts-ignore return filterChanged; } /** * Call by parsing filters from URL * Check if value of filter within filter domain, if not adjust it to match * filter domain * * @type {typeof import('./filter-utils').adjustValueToFilterDomain} * @returns value - adjusted value to match filter or null to remove filter */ /* eslint-disable complexity */ function adjustValueToFilterDomain(value, _ref5) { var domain = _ref5.domain, type = _ref5.type; if (!domain || !type) { return false; } switch (type) { case _defaultSettings.FILTER_TYPES.range: case _defaultSettings.FILTER_TYPES.timeRange: if (!Array.isArray(value) || value.length !== 2) { return domain.map(function (d) { return d; }); } return value.map(function (d, i) { return (0, _dataUtils.notNullorUndefined)(d) && isInRange(d, domain) ? d : domain[i]; }); case _defaultSettings.FILTER_TYPES.multiSelect: if (!Array.isArray(value)) { return []; } var filteredValue = value.filter(function (d) { return domain.includes(d); }); return filteredValue.length ? filteredValue : []; case _defaultSettings.FILTER_TYPES.select: return domain.includes(value) ? value : true; default: return null; } } /* eslint-enable complexity */ /** * Calculate numeric domain and suitable step * * @type {typeof import('./filter-utils').getNumericFieldDomain} */ function getNumericFieldDomain(dataContainer, valueAccessor) { var domain = [0, 1]; var step = 0.1; var mappedValue = dataContainer.mapIndex(valueAccessor); if (dataContainer.numRows() > 1) { domain = ScaleUtils.getLinearDomain(mappedValue); var diff = domain[1] - domain[0]; // in case equal domain, [96, 96], which will break quantize scale if (!diff) { domain[1] = domain[0] + 1; } step = getNumericStepSize(diff) || step; domain[0] = formatNumberByStep(domain[0], step, 'floor'); domain[1] = formatNumberByStep(domain[1], step, 'ceil'); } // @ts-ignore var _getHistogram = getHistogram(domain, mappedValue), histogram = _getHistogram.histogram, enlargedHistogram = _getHistogram.enlargedHistogram; return { domain: domain, step: step, histogram: histogram, enlargedHistogram: enlargedHistogram }; } /** * Calculate step size for range and timerange filter * * @type {typeof import('./filter-utils').getNumericStepSize} */ function getNumericStepSize(diff) { diff = Math.abs(diff); if (diff > 100) { return 1; } else if (diff > 3) { return 0.01; } else if (diff > 1) { return 0.001; } // Try to get at least 1000 steps - and keep the step size below that of // the (diff > 1) case. var x = diff / 1000; // Find the exponent and truncate to 10 to the power of that exponent var exponentialForm = x.toExponential(); var exponent = parseFloat(exponentialForm.split('e')[1]); // Getting ready for node 12 // this is why we need decimal.js // Math.pow(10, -5) = 0.000009999999999999999 // the above result shows in browser and node 10 // node 12 behaves correctly return new _decimal.Decimal(10).pow(exponent).toNumber(); } /** * Calculate timestamp domain and suitable step * @type {typeof import('./filter-utils').getTimestampFieldDomain} */ function getTimestampFieldDomain(dataContainer, valueAccessor) { // to avoid converting string format time to epoch // every time we compare we store a value mapped to int in filter domain var mappedValue = dataContainer.mapIndex(valueAccessor); var domain = ScaleUtils.getLinearDomain(mappedValue); var defaultTimeFormat = getTimeWidgetTitleFormatter(domain); var step = 0.01; var diff = domain[1] - domain[0]; var entry = TimestampStepMap.find(function (f) { return f.max >= diff; }); if (entry) { step = entry.step; } var _getHistogram2 = getHistogram(domain, mappedValue), histogram = _getHistogram2.histogram, enlargedHistogram = _getHistogram2.enlargedHistogram; return { domain: domain, step: step, mappedValue: mappedValue, histogram: histogram, enlargedHistogram: enlargedHistogram, defaultTimeFormat: defaultTimeFormat }; } /** * * @type {typeof import('./filter-utils').histogramConstruct} */ function histogramConstruct(domain, mappedValue, bins) { return (0, _d3Array.histogram)().thresholds((0, _d3Array.ticks)(domain[0], domain[1], bins)).domain(domain)(mappedValue).map(function (bin) { return { count: bin.length, x0: bin.x0, x1: bin.x1 }; }); } /** * Calculate histogram from domain and array of values * * @type {typeof import('./filter-utils').getHistogram} */ function getHistogram(domain, mappedValue) { var histogram = histogramConstruct(domain, mappedValue, histogramBins); var enlargedHistogram = histogramConstruct(domain, mappedValue, enlargedHistogramBins); return { histogram: histogram, enlargedHistogram: enlargedHistogram }; } /** * round number based on step * * @param {Number} val * @param {Number} step * @param {string} bound * @returns {Number} rounded number */ function formatNumberByStep(val, step, bound) { if (bound === 'floor') { return Math.floor(val * (1 / step)) / (1 / step); } return Math.ceil(val * (1 / step)) / (1 / step); } /** * * @type {typeof import('./filter-utils').isInRange} */ function isInRange(val, domain) { if (!Array.isArray(domain)) { return false; } return val >= domain[0] && val <= domain[1]; } /** * Determines whether a point is within the provided polygon * * @param point as input search [lat, lng] * @param polygon Points must be within these (Multi)Polygon(s) * @return {boolean} */ function isInPolygon(point, polygon) { return (0, _booleanWithin["default"])((0, _helpers.point)(point), polygon); } function isValidTimeDomain(domain) { return Array.isArray(domain) && domain.every(Number.isFinite); } function getTimeWidgetTitleFormatter(domain) { if (!isValidTimeDomain(domain)) { return null; } var diff = domain[1] - domain[0]; // Local aware formats // https://momentjs.com/docs/#/parsing/string-format return diff > durationYear ? 'L' : diff > durationDay ? 'L LT' : 'L LTS'; } function getTimeWidgetHintFormatter(domain) { if (!isValidTimeDomain(domain)) { return null; } var diff = domain[1] - domain[0]; return diff > durationWeek ? 'L' : diff > durationDay ? 'L LT' : diff > durationHour ? 'LT' : 'LTS'; } /** * Sanity check on filters to prepare for save * @type {typeof import('./filter-utils').isValidFilterValue} */ /* eslint-disable complexity */ function isValidFilterValue(type, value) { if (!type) { return false; } switch (type) { case _defaultSettings.FILTER_TYPES.select: return value === true || value === false; case _defaultSettings.FILTER_TYPES.range: case _defaultSettings.FILTER_TYPES.timeRange: return Array.isArray(value) && value.every(function (v) { return v !== null && !isNaN(v); }); case _defaultSettings.FILTER_TYPES.multiSelect: return Array.isArray(value) && Boolean(value.length); case _defaultSettings.FILTER_TYPES.input: return Boolean(value.length); case _defaultSettings.FILTER_TYPES.polygon: var coordinates = (0, _lodash["default"])(value, ['geometry', 'coordinates']); return Boolean(value && value.id && coordinates); default: return true; } } /** * * @type {typeof import('./filter-utils').getFilterPlot} */ function getFilterPlot(filter, dataset) { if (filter.plotType === PLOT_TYPES.histogram || !filter.yAxis) { // histogram should be calculated when create filter return {}; } var _filter$mappedValue = filter.mappedValue, mappedValue = _filter$mappedValue === void 0 ? [] : _filter$mappedValue; var yAxis = filter.yAxis; var fieldIdx = dataset.getColumnFieldIdx(yAxis.name); if (fieldIdx < 0) { _console.console.warn("yAxis ".concat(yAxis.name, " does not exist in dataset")); return { lineChart: {}, yAxis: yAxis }; } // return lineChart var series = dataset.dataContainer.map(function (row, rowIndex) { return { x: mappedValue[rowIndex], y: row.valueAt(fieldIdx) }; }, true).filter(function (_ref6) { var x = _ref6.x, y = _ref6.y; return Number.isFinite(x) && Number.isFinite(y); }).sort(function (a, b) { return (0, _d3Array.ascending)(a.x, b.x); }); var yDomain = (0, _d3Array.extent)(series, function (d) { return d.y; }); var xDomain = [series[0].x, series[series.length - 1].x]; return { lineChart: { series: series, yDomain: yDomain, xDomain: xDomain }, yAxis: yAxis }; } function getDefaultFilterPlotType(filter) { var filterPlotTypes = SupportedPlotType[filter.type]; if (!filterPlotTypes) { return null; } if (!filter.yAxis) { return filterPlotTypes["default"]; } return filterPlotTypes[filter.yAxis.type] || null; } /** * * @param datasetIds list of dataset ids to be filtered * @param datasets all datasets * @param filters all filters to be applied to datasets * @return datasets - new updated datasets * @type {typeof import('./filter-utils').applyFiltersToDatasets} */ function applyFiltersToDatasets(datasetIds, datasets, filters, layers) { var dataIds = (0, _utils.toArray)(datasetIds); return dataIds.reduce(function (acc, dataId) { var layersToFilter = (layers || []).filter(function (l) { return l.config.dataId === dataId; }); var appliedFilters = filters.filter(function (d) { return shouldApplyFilter(d, dataId); }); var table = datasets[dataId]; return _objectSpread(_objectSpread({}, acc), {}, (0, _defineProperty2["default"])({}, dataId, table.filterTable(appliedFilters, layersToFilter, {}))); }, datasets); } /** * Applies a new field name value to fielter and update both filter and dataset * @param filter - to be applied the new field name on * @param dataset - dataset the field belongs to * @param fieldName - field.name * @param filterDatasetIndex - field.name * @param option * @return - {filter, datasets} * @type {typeof import('./filter-utils').applyFilterFieldName} */ function applyFilterFieldName(filter, dataset, fieldName) { var filterDatasetIndex = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0; var option = arguments.length > 4 ? arguments[4] : undefined; // using filterDatasetIndex we can filter only the specified dataset var mergeDomain = option && option.hasOwnProperty('mergeDomain') ? option.mergeDomain : false; var fieldIndex = dataset.getColumnFieldIdx(fieldName); // if no field with same name is found, move to the next datasets if (fieldIndex === -1) { // throw new Error(`fieldIndex not found. Dataset must contain a property with name: ${fieldName}`); return { filter: null, dataset: dataset }; } // TODO: validate field type var filterProps = dataset.getColumnFilterProps(fieldName); var newFilter = _objectSpread(_objectSpread({}, mergeDomain ? mergeFilterDomainStep(filter, filterProps) : _objectSpread(_objectSpread({}, filter), filterProps)), {}, { name: Object.assign((0, _toConsumableArray2["default"])((0, _utils.toArray)(filter.name)), (0, _defineProperty2["default"])({}, filterDatasetIndex, fieldName)), fieldIdx: Object.assign((0, _toConsumableArray2["default"])((0, _utils.toArray)(filter.fieldIdx)), (0, _defineProperty2["default"])({}, filterDatasetIndex, fieldIndex)), // TODO, since we allow to add multiple fields to a filter we can no longer freeze the filter freeze: true }); return { filter: newFilter, dataset: dataset }; } /** * Merge one filter with other filter prop domain * @type {typeof import('./filter-utils').mergeFilterDomainStep} */ /* eslint-disable complexity */ function mergeFilterDomainStep(filter, filterProps) { if (!filter) { return null; } if (!filterProps) { return filter; } if (filter.fieldType && filter.fieldType !== filterProps.fieldType || !filterProps.domain) { return filter; } var combinedDomain = !filter.domain ? filterProps.domain : [].concat((0, _toConsumableArray2["default"])(filter.domain || []), (0, _toConsumableArray2["default"])(filterProps.domain || [])).sort(function (a, b) { return a - b; }); var newFilter = _objectSpread(_objectSpread(_objectSpread({}, filter), filterProps), {}, { domain: [combinedDomain[0], combinedDomain[combinedDomain.length - 1]] }); switch (filterProps.fieldType) { case _defaultSettings.ALL_FIELD_TYPES.string: case _defaultSettings.ALL_FIELD_TYPES.date: return _objectSpread(_objectSpread({}, newFilter), {}, { domain: (0, _dataUtils.unique)(combinedDomain).sort() }); case _defaultSettings.ALL_FIELD_TYPES.timestamp: // @ts-ignore var step = filter.step < filterProps.step ? filter.step : filterProps.step; return _objectSpread(_objectSpread({}, newFilter), {}, { step: step }); case _defaultSettings.ALL_FIELD_TYPES.real: case _defaultSettings.ALL_FIELD_TYPES.integer: default: return newFilter; } } /* eslint-enable complexity */ /** * Generates polygon filter * @type {typeof import('./filter-utils').featureToFilterValue} */ var featureToFilterValue = function featureToFilterValue(feature, filterId) { var properties = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; return _objectSpread(_objectSpread({}, feature), {}, { id: feature.id, properties: _objectSpread(_objectSpread(_objectSpread({}, feature.properties), properties), {}, { filterId: filterId }) }); }; /** * @type {typeof import('./filter-utils').getFilterIdInFeature} */ exports.featureToFilterValue = featureToFilterValue; var getFilterIdInFeature = function getFilterIdInFeature(f) { return (0, _lodash["default"])(f, ['properties', 'filterId']); }; /** * Generates polygon filter * @type {typeof import('./filter-utils').generatePolygonFilter} */ exports.getFilterIdInFeature = getFilterIdInFeature; function generatePolygonFilter(layers, feature) { var dataId = layers.map(function (l) { return l.config.dataId; }).filter(function (d) { return d; }); var layerId = layers.map(function (l) { return l.id; }); var name = layers.map(function (l) { return l.config.label; }); // @ts-ignore var filter = getDefaultFilter(dataId); return _objectSpread(_objectSpread({}, filter), {}, { fixedDomain: true, type: _defaultSettings.FILTER_TYPES.polygon, name: name, layerId: layerId, value: featureToFilterValue(feature, filter.id, { isVisible: true }) }); } /** * Run filter entirely on CPU * @type {typeof import('./filter-utils').filterDatasetCPU} */ function filterDatasetCPU(state, dataId) { var datasetFilters = state.filters.filter(function (f) { return f.dataId.includes(dataId); }); var dataset = state.datasets[dataId]; if (!dataset) { return state; } var cpuFilteredDataset = dataset.filterTableCPU(datasetFilters, state.layers); return (0, _utils.set)(['datasets', dataId], cpuFilteredDataset, state); } /** * Validate parsed filters with datasets and add filterProps to field * @type {typeof import('./filter-utils').validateFiltersUpdateDatasets} */ function validateFiltersUpdateDatasets(state) { var filtersToValidate = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; var validated = []; var failed = []; var datasets = state.datasets; var updatedDatasets = datasets; // merge filters filtersToValidate.forEach(function (filter) { // we can only look for datasets define in the filter dataId var datasetIds = (0, _utils.toArray)(filter.dataId); // we can merge a filter only if all datasets in filter.dataId are loaded if (datasetIds.every(function (d) { return datasets[d]; })) { // all datasetIds in filter must be present the state datasets var _datasetIds$reduce = datasetIds.reduce(function (acc, datasetId) { var dataset = updatedDatasets[datasetId]; var layers = state.layers.filter(function (l) { return l.config.dataId === dataset.id; }); var _validateFilterWithDa = validateFilterWithData(acc.augmentedDatasets[datasetId] || dataset, filter, layers), updatedFilter = _validateFilterWithDa.filter, updatedDataset = _validateFilterWithDa.dataset; if (updatedFilter) { return _objectSpread(_objectSpread({}, acc), {}, { // merge filter props filter: acc.filter ? _objectSpread(_objectSpread({}, acc.filter), mergeFilterDomainStep(acc, updatedFilter)) : updatedFilter, applyToDatasets: [].concat((0, _toConsumableArray2["default"])(acc.applyToDatasets), [datasetId]), augmentedDatasets: _objectSpread(_objectSpread({}, acc.augmentedDatasets), {}, (0, _defineProperty2["default"])({}, datasetId, updatedDataset)) }); } return acc; }, { filter: null, applyToDatasets: [], augmentedDatasets: {} }), validatedFilter = _datasetIds$reduce.filter, applyToDatasets = _datasetIds$reduce.applyToDatasets, augmentedDatasets = _datasetIds$reduce.augmentedDatasets; if (validatedFilter && (0, _lodash2["default"])(datasetIds, applyToDatasets)) { validated.push(validatedFilter); updatedDatasets = _objectSpread(_objectSpread({}, updatedDatasets), augmentedDatasets); } } else { failed.push(filter); } }); return { validated: validated, failed: failed, updatedDatasets: updatedDatasets }; } /** * Retrieve interval bins for time filter * @type {typeof import('./filter-utils').getIntervalBins} */ function getIntervalBins(filter) { var _filter$plotType; var bins = filter.bins; var interval = (_filter$plotType = filter.plotType) === null || _filter$plotType === void 0 ? void 0 : _filter$plotType.interval; if (!interval || !bins || Object.keys(bins).length === 0) { return null; } var values = Object.values(bins); return values[0] ? values[0][interval] : null; } //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy91dGlscy9maWx0ZXItdXRpbHMuanMiXSwibmFtZXMiOlsiVGltZXN0YW1wU3RlcE1hcCIsIm1heCIsInN0ZXAiLCJOdW1iZXIiLCJQT1NJVElWRV9JTkZJTklUWSIsImhpc3RvZ3JhbUJpbnMiLCJlbmxhcmdlZEhpc3RvZ3JhbUJpbnMiLCJkdXJhdGlvblNlY29uZCIsImR1cmF0aW9uTWludXRlIiwiZHVyYXRpb25Ib3VyIiwiZHVyYXRpb25EYXkiLCJkdXJhdGlvbldlZWsiLCJkdXJhdGlvblllYXIiLCJQTE9UX1RZUEVTIiwiaGlzdG9ncmFtIiwibGluZUNoYXJ0IiwiRklMVEVSX1VQREFURVJfUFJPUFMiLCJkYXRhSWQiLCJuYW1lIiwibGF5ZXJJZCIsIkxJTUlURURfRklMVEVSX0VGRkVDVF9QUk9QUyIsIlN1cHBvcnRlZFBsb3RUeXBlIiwiRklMVEVSX1RZUEVTIiwidGltZVJhbmdlIiwiQUxMX0ZJRUxEX1RZUEVTIiwiaW50ZWdlciIsInJlYWwiLCJyYW5nZSIsIkZJTFRFUl9DT01QT05FTlRTIiwic2VsZWN0IiwibXVsdGlTZWxlY3QiLCJwb2x5Z29uIiwiREVGQVVMVF9GSUxURVJfU1RSVUNUVVJFIiwiZnJlZXplIiwiaWQiLCJmaXhlZERvbWFpbiIsImVubGFyZ2VkIiwiaXNBbmltYXRpbmciLCJhbmltYXRpb25XaW5kb3ciLCJBTklNQVRJT05fV0lORE9XIiwiZnJlZSIsInNwZWVkIiwidHlwZSIsImZpZWxkSWR4IiwiZG9tYWluIiwidmFsdWUiLCJwbG90VHlwZSIsInlBeGlzIiwiaW50ZXJ2YWwiLCJncHUiLCJGSUxURVJfSURfTEVOR1RIIiwiTEFZRVJfRklMVEVSUyIsImdldERlZmF1bHRGaWx0ZXIiLCJzaG91bGRBcHBseUZpbHRlciIsImZpbHRlciIsImRhdGFzZXRJZCIsImRhdGFJZHMiLCJpbmNsdWRlcyIsInZhbGlkYXRlUG9seWdvbkZpbHRlciIsImRhdGFzZXQiLCJsYXllcnMiLCJmYWlsZWQiLCJpc1ZhbGlkRmlsdGVyVmFsdWUiLCJpc1ZhbGlkRGF0YXNldCIsImxheWVyIiwiZmluZCIsImwiLCJmaWx0ZXJWYWxpZGF0b3JzIiwidmFsaWRhdGVGaWx0ZXIiLCJmaWx0ZXJEYXRhSWQiLCJmaWx0ZXJEYXRhc2V0SW5kZXgiLCJpbmRleE9mIiwiaW5pdGlhbGl6ZUZpbHRlciIsImZpZWxkTmFtZSIsImFwcGx5RmlsdGVyRmllbGROYW1lIiwibWVyZ2VEb21haW4iLCJ1cGRhdGVkRmlsdGVyIiwidXBkYXRlZERhdGFzZXQiLCJhZGp1c3RWYWx1ZVRvRmlsdGVyRG9tYWluIiwidmFsaWRhdGVGaWx0ZXJZQXhpcyIsInZhbGlkYXRlRmlsdGVyV2l0aERhdGEiLCJoYXNPd25Qcm9wZXJ0eSIsImZpZWxkcyIsIm1hdGNoZWRBeGlzIiwiZ2V0RmlsdGVyUGxvdCIsImdldEZpbHRlclByb3BzIiwiZmllbGQiLCJmaWVsZERvbWFpbiIsImZpbHRlclByb3BzIiwiZmllbGRUeXBlIiwidHlwZU9wdGlvbnMiLCJzdHJpbmciLCJkYXRlIiwidGltZXN0YW1wIiwiZ2V0UG9seWdvbkZpbHRlckZ1bmN0b3IiLCJkYXRhQ29udGFpbmVyIiwiZ2V0UG9zaXRpb24iLCJnZXRQb3NpdGlvbkFjY2Vzc29yIiwiTEFZRVJfVFlQRVMiLCJwb2ludCIsImljb24iLCJkYXRhIiwicG9zIiwiZXZlcnkiLCJpc0Zpbml0ZSIsImlzSW5Qb2x5Z29uIiwiYXJjIiwibGluZSIsImhleGFnb25JZCIsImRhdGFUb0ZlYXR1cmUiLCJjZW50cm9pZHMiLCJjZW50cm9pZCIsImluZGV4IiwiZ2V0RmlsdGVyRnVuY3Rpb24iLCJ2YWx1ZUFjY2Vzc29yIiwiZGVmYXVsdEZ1bmMiLCJkIiwiaXNJblJhbmdlIiwibWFwcGVkVmFsdWUiLCJhY2Nlc3NvciIsIkFycmF5IiwiaXNBcnJheSIsImZvcm1hdCIsImxlbmd0aCIsImxheWVyRmlsdGVyRnVuY3Rpb25zIiwibWFwIiwiY29uZmlnIiwiZmlsdGVyRnVuYyIsInVwZGF0ZUZpbHRlckRhdGFJZCIsImZpbHRlckRhdGFCeUZpbHRlclR5cGVzIiwiZHluYW1pY0RvbWFpbkZpbHRlcnMiLCJjcHVGaWx0ZXJzIiwiZmlsdGVyRnVuY3MiLCJyZXN1bHQiLCJmaWx0ZXJlZEluZGV4Rm9yRG9tYWluIiwiZmlsdGVyZWRJbmRleCIsImZpbHRlckNvbnRleHQiLCJmaWx0ZXJGdW5jQ2FsbGVyIiwibnVtUm93cyIsImkiLCJtYXRjaEZvckRvbWFpbiIsInB1c2giLCJtYXRjaEZvclJlbmRlciIsImdldEZpbHRlclJlY29yZCIsImZpbHRlcnMiLCJvcHQiLCJmaWx0ZXJSZWNvcmQiLCJkeW5hbWljRG9tYWluIiwiY3B1IiwiZm9yRWFjaCIsImYiLCJpZ25vcmVEb21haW4iLCJjcHVPbmx5IiwiZGlmZkZpbHRlcnMiLCJvbGRGaWx0ZXJSZWNvcmQiLCJmaWx0ZXJDaGFuZ2VkIiwiT2JqZWN0IiwiZW50cmllcyIsInJlY29yZCIsIml0ZW1zIiwib2xkRmlsdGVyIiwicHJvcCIsImZpbHRlcmVkVmFsdWUiLCJnZXROdW1lcmljRmllbGREb21haW4iLCJtYXBJbmRleCIsIlNjYWxlVXRpbHMiLCJnZXRMaW5lYXJEb21haW4iLCJkaWZmIiwiZ2V0TnVtZXJpY1N0ZXBTaXplIiwiZm9ybWF0TnVtYmVyQnlTdGVwIiwiZ2V0SGlzdG9ncmFtIiwiZW5sYXJnZWRIaXN0b2dyYW0iLCJNYXRoIiwiYWJzIiwieCIsImV4cG9uZW50aWFsRm9ybSIsInRvRXhwb25lbnRpYWwiLCJleHBvbmVudCIsInBhcnNlRmxvYXQiLCJzcGxpdCIsIkRlY2ltYWwiLCJwb3ciLCJ0b051bWJlciIsImdldFRpbWVzdGFtcEZpZWxkRG9tYWluIiwiZGVmYXVsdFRpbWVGb3JtYXQiLCJnZXRUaW1lV2lkZ2V0VGl0bGVGb3JtYXR0ZXIiLCJlbnRyeSIsImhpc3RvZ3JhbUNvbnN0cnVjdCIsImJpbnMiLCJ0aHJlc2hvbGRzIiwiYmluIiwiY291bnQiLCJ4MCIsIngxIiwidmFsIiwiYm91bmQiLCJmbG9vciIsImNlaWwiLCJpc1ZhbGlkVGltZURvbWFpbiIsImdldFRpbWVXaWRnZXRIaW50Rm9ybWF0dGVyIiwidiIsImlzTmFOIiwiQm9vbGVhbiIsImlucHV0IiwiY29vcmRpbmF0ZXMiLCJnZXRDb2x1bW5GaWVsZElkeCIsIkNvbnNvbGUiLCJ3YXJuIiwic2VyaWVzIiwicm93Iiwicm93SW5kZXgiLCJ5IiwidmFsdWVBdCIsInNvcnQiLCJhIiwiYiIsInlEb21haW4iLCJ4RG9tYWluIiwiZ2V0RGVmYXVsdEZpbHRlclBsb3RUeXBlIiwiZmlsdGVyUGxvdFR5cGVzIiwiYXBwbHlGaWx0ZXJzVG9EYXRhc2V0cyIsImRhdGFzZXRJZHMiLCJkYXRhc2V0cyIsInJlZHVjZSIsImFjYyIsImxheWVyc1RvRmlsdGVyIiwiYXBwbGllZEZpbHRlcnMiLCJ0YWJsZSIsImZpbHRlclRhYmxlIiwib3B0aW9uIiwiZmllbGRJbmRleCIsImdldENvbHVtbkZpbHRlclByb3BzIiwibmV3RmlsdGVyIiwibWVyZ2VGaWx0ZXJEb21haW5TdGVwIiwiYXNzaWduIiwiY29tYmluZWREb21haW4iLCJmZWF0dXJlVG9GaWx0ZXJWYWx1ZSIsImZlYXR1cmUiLCJmaWx0ZXJJZCIsInByb3BlcnRpZXMiLCJnZXRGaWx0ZXJJZEluRmVhdHVyZSIsImdlbmVyYXRlUG9seWdvbkZpbHRlciIsImxhYmVsIiwiaXNWaXNpYmxlIiwiZmlsdGVyRGF0YXNldENQVSIsInN0YXRlIiwiZGF0YXNldEZpbHRlcnMiLCJjcHVGaWx0ZXJlZERhdGFzZXQiLCJmaWx0ZXJUYWJsZUNQVSIsInZhbGlkYXRlRmlsdGVyc1VwZGF0ZURhdGFzZXRzIiwiZmlsdGVyc1RvVmFsaWRhdGUiLCJ2YWxpZGF0ZWQiLCJ1cGRhdGVkRGF0YXNldHMiLCJhdWdtZW50ZWREYXRhc2V0cyIsImFwcGx5VG9EYXRhc2V0cyIsInZhbGlkYXRlZEZpbHRlciIsImdldEludGVydmFsQmlucyIsImtleXMiLCJ2YWx1ZXMiXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBb0JB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