kepler.gl
Version:
kepler.gl is a webgl based application to visualize large scale location data in the browser
1,301 lines (1,092 loc) • 126 kB
JavaScript
"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.getFieldDomain = getFieldDomain;
exports.getFilterFunction = getFilterFunction;
exports.updateFilterDataId = updateFilterDataId;
exports.filterDataset = filterDataset;
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.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.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 _lodash = _interopRequireDefault(require("lodash.get"));
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 _constants = require("../constants");
var _utils = require("./utils");
var _gpuFilterUtils = require("./gpu-filter-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; }
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,
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
* @param {[string]} dataId
* @return {object} filter
*/
exports.LAYER_FILTERS = LAYER_FILTERS;
function getDefaultFilter(dataId) {
return _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 {object} filter to validate
* @param {string} dataset id to validate filter against
* @return {boolean} true if a filter is valid, false otherwise
*/
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 {object}
*/
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({}, filter, {
freeze: true,
fieldIdx: []
}),
dataset: dataset
};
}
/**
* Custom filter validators
* @type {Function}
*/
var filterValidators = (0, _defineProperty2["default"])({}, _defaultSettings.FILTER_TYPES.polygon, validatePolygonFilter);
/**
* Default validate filter function
* @param dataset
* @param filter
* @return {*}
*/
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({}, 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);
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 {Object} dataset
* @param {Object} filter - filter to be validate
* @return {Object | null} - validated filter
*/
function validateFilterWithData(dataset, filter, layers) {
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,
allData = dataset.allData;
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({}, filter, {
yAxis: matchedAxis
}, getFilterPlot(_objectSpread({}, filter, {
yAxis: matchedAxis
}), allData)) : filter;
}
return filter;
}
/**
* Get default filter prop based on field type
*
* @param {Array<Array>} allData
* @param {Object} field
* @returns {Object} default filter
*/
function getFilterProps(allData, field) {
var filterProps = _objectSpread({}, getFieldDomain(allData, field), {
fieldType: field.type
});
switch (field.type) {
case _defaultSettings.ALL_FIELD_TYPES.real:
case _defaultSettings.ALL_FIELD_TYPES.integer:
return _objectSpread({}, filterProps, {
value: filterProps.domain,
type: _defaultSettings.FILTER_TYPES.range,
typeOptions: [_defaultSettings.FILTER_TYPES.range],
gpu: true
});
case _defaultSettings.ALL_FIELD_TYPES["boolean"]:
return _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({}, filterProps, {
type: _defaultSettings.FILTER_TYPES.multiSelect,
value: [],
gpu: false
});
case _defaultSettings.ALL_FIELD_TYPES.timestamp:
return _objectSpread({}, filterProps, {
type: _defaultSettings.FILTER_TYPES.timeRange,
enlarged: true,
fixedDomain: true,
value: filterProps.domain,
gpu: true
});
default:
return {};
}
}
/**
* Calculate field domain based on field type and data
*
* @param {Array<Array>} allData
* @param {Object} field
* @returns {Object} with domain as key
*/
function getFieldDomain(allData, field) {
var fieldIdx = field.tableFieldIndex - 1;
var isTime = field.type === _defaultSettings.ALL_FIELD_TYPES.timestamp;
var valueAccessor = _dataUtils.maybeToDate.bind(null, isTime, fieldIdx, field.format);
var domain;
switch (field.type) {
case _defaultSettings.ALL_FIELD_TYPES.real:
case _defaultSettings.ALL_FIELD_TYPES.integer:
// calculate domain and step
return getNumericFieldDomain(allData, valueAccessor);
case _defaultSettings.ALL_FIELD_TYPES["boolean"]:
return {
domain: [true, false]
};
case _defaultSettings.ALL_FIELD_TYPES.string:
case _defaultSettings.ALL_FIELD_TYPES.date:
domain = ScaleUtils.getOrdinalDomain(allData, valueAccessor);
return {
domain: domain
};
case _defaultSettings.ALL_FIELD_TYPES.timestamp:
return getTimestampFieldDomain(allData, valueAccessor);
default:
return {
domain: ScaleUtils.getOrdinalDomain(allData, valueAccessor)
};
}
}
var getPolygonFilterFunctor = function getPolygonFilterFunctor(layer, filter) {
var getPosition = layer.getPositionAccessor();
switch (layer.type) {
case _constants.LAYER_TYPES.point:
case _constants.LAYER_TYPES.icon:
return function (data) {
var pos = getPosition({
data: data
});
return pos.every(Number.isFinite) && isInPolygon(pos, filter.value);
};
case _constants.LAYER_TYPES.arc:
case _constants.LAYER_TYPES.line:
return function (data) {
var pos = getPosition({
data: data
});
return pos.every(Number.isFinite) && [[pos[0], pos[1]], [pos[3], pos[4]]].every(function (point) {
return isInPolygon(point, 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
* @return {*}
*/
exports.getPolygonFilterFunctor = getPolygonFilterFunctor;
function getFilterFunction(field, dataId, filter, layers) {
// field could be null
var valueAccessor = function valueAccessor(data) {
return field ? data[field.tableFieldIndex - 1] : null;
};
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:
var mappedValue = (0, _lodash["default"])(field, ['filterProps', 'mappedValue']);
var accessor = Array.isArray(mappedValue) ? function (data, index) {
return mappedValue[index];
} : function (data) {
return (0, _dataUtils.timeToUnixMilli)(valueAccessor(data), field.format);
};
return function (data, index) {
return isInRange(accessor(data, index), filter.value);
};
case _defaultSettings.FILTER_TYPES.polygon:
if (!layers || !layers.length) {
return function () {
return true;
};
}
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);
});
return function (data) {
return layerFilterFunctions.every(function (filterFunc) {
return filterFunc(data);
});
};
default:
return function () {
return true;
};
}
}
function updateFilterDataId(dataId) {
return getDefaultFilter(dataId);
}
/**
* Filter data based on an array of filters
*
* @param {Object} dataset
* @param {Array<Object>} filters
* @param {Object} opt
* @param {Object} opt.cpuOnly only allow cpu filtering
* @param {Object} opt.ignoreDomain ignore filter for domain calculation
* @returns {Object} dataset
* @returns {Array<Number>} dataset.filteredIndex
* @returns {Array<Number>} dataset.filteredIndexForDomain
*/
function filterDataset(dataset, filters, layers) {
var opt = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
var allData = dataset.allData,
dataId = dataset.id,
oldFilterRecord = dataset.filterRecord,
fields = dataset.fields; // if there is no filters
var filterRecord = getFilterRecord(dataId, filters, opt);
var newDataset = (0, _utils.set)(['filterRecord'], filterRecord, dataset);
if (!filters.length) {
return _objectSpread({}, newDataset, {
gpuFilter: (0, _gpuFilterUtils.getGpuFilterProps)(filters, dataId, fields),
filteredIndex: dataset.allIndexes,
filteredIndexForDomain: dataset.allIndexes
});
}
var changedFilters = diffFilters(filterRecord, oldFilterRecord); // generate 2 sets of filter result
// filteredIndex used to calculate layer data
// filteredIndexForDomain used to calculate layer Domain
var shouldCalDomain = Boolean(changedFilters.dynamicDomain);
var shouldCalIndex = Boolean(changedFilters.cpu);
var filterResult = {};
if (shouldCalDomain || shouldCalIndex) {
var dynamicDomainFilters = shouldCalDomain ? filterRecord.dynamicDomain : null;
var cpuFilters = shouldCalIndex ? filterRecord.cpu : null;
var filterFuncs = filters.reduce(function (acc, filter) {
var fieldIndex = (0, _gpuFilterUtils.getDatasetFieldIndexForFilter)(dataset.id, filter);
var field = fieldIndex !== -1 ? fields[fieldIndex] : null;
return _objectSpread({}, acc, (0, _defineProperty2["default"])({}, filter.id, getFilterFunction(field, dataset.id, filter, layers)));
}, {});
filterResult = filterDataByFilterTypes({
dynamicDomainFilters: dynamicDomainFilters,
cpuFilters: cpuFilters,
filterFuncs: filterFuncs
}, allData);
}
return _objectSpread({}, newDataset, {}, filterResult, {
gpuFilter: (0, _gpuFilterUtils.getGpuFilterProps)(filters, dataId, fields)
});
}
/**
*
* @param {Object} filters
* @param {Array|null} filters.dynamicDomainFilters
* @param {Array|null} filters.cpuFilters
* @param {Object} filters.filterFuncs
* @returns {{filteredIndex: Array, filteredIndexForDomain: Array}} filteredIndex and filteredIndexForDomain
*/
function filterDataByFilterTypes(_ref2, allData) {
var dynamicDomainFilters = _ref2.dynamicDomainFilters,
cpuFilters = _ref2.cpuFilters,
filterFuncs = _ref2.filterFuncs;
var result = _objectSpread({}, dynamicDomainFilters ? {
filteredIndexForDomain: []
} : {}, {}, cpuFilters ? {
filteredIndex: []
} : {});
var _loop = function _loop(i) {
var d = allData[i];
var matchForDomain = dynamicDomainFilters && dynamicDomainFilters.every(function (filter) {
return filterFuncs[filter.id](d, i);
});
if (matchForDomain) {
result.filteredIndexForDomain.push(i);
}
var matchForRender = cpuFilters && cpuFilters.every(function (filter) {
return filterFuncs[filter.id](d, i);
});
if (matchForRender) {
result.filteredIndex.push(i);
}
};
for (var i = 0; i < allData.length; i++) {
_loop(i);
}
return result;
}
/**
* Get a record of filters based on domain type and gpu / cpu
* @param {string} dataId
* @param {Array<Object>} filters
* @param {Object} opt.cpuOnly only allow cpu filtering
* @param {Object} opt.ignoreDomain ignore filter for domain calculation
* @returns {{dynamicDomain: Array, fixedDomain: Array, cpu: Array, gpu: Array}} filterRecord
*/
function getFilterRecord(dataId, filters) {
var opt = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
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
* @param {Object} filterRecord
* @param {Object} oldFilterRecord
* @returns {{dynamicDomain: Object, fixedDomain: Object, cpu: Object, gpu: Object}} changed filters based on type
*/
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;
}
});
return filterChanged;
}
/**
* Call by parsing filters from URL
* Check if value of filter within filter domain, if not adjust it to match
* filter domain
*
* @param {Array<string> | string | Number | Array<Number>} value
* @param {Array} filter.domain
* @param {String} filter.type
* @returns {*} - 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
*
* @param {Object[]} data
* @param {function} valueAccessor
* @returns {object} domain and step
*/
function getNumericFieldDomain(data, valueAccessor) {
var domain = [0, 1];
var step = 0.1;
var mappedValue = Array.isArray(data) ? data.map(valueAccessor) : [];
if (Array.isArray(data) && data.length > 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');
}
var _getHistogram = getHistogram(domain, mappedValue),
histogram = _getHistogram.histogram,
enlargedHistogram = _getHistogram.enlargedHistogram;
return {
domain: domain,
step: step,
histogram: histogram,
enlargedHistogram: enlargedHistogram
};
}
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;
} else if (diff <= 1) {
// 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
*
* @param {Array<Array>} data
* @param {Function} valueAccessor
* @returns {{
* domain: Array<Number>,
* step: Number,
* mappedValue: Array<Number>,
* histogram: Array<Object>,
* enlargedHistogram: Array<Object>
* }} timestamp field domain
*/
function getTimestampFieldDomain(data, 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 = Array.isArray(data) ? data.map(valueAccessor) : [];
var domain = ScaleUtils.getLinearDomain(mappedValue);
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
};
}
/**
*
* @param {Array<Number>} domain
* @param {Array<Number>} mappedValue
* @param {Number} bins
* @returns {Array<{count: Number, x0: Number, x1: number}>} histogram
*/
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
*
* @param {Array<Number>} domain
* @param {Array<Object>} mappedValue
* @returns {{histogram: Array<Object>, enlargedHistogram: Array<Object>}} 2 sets of histogram
*/
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);
}
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 getTimeWidgetTitleFormatter(domain) {
if (!Array.isArray(domain)) {
return null;
}
var diff = domain[1] - domain[0];
return diff > durationYear ? 'MM/DD/YY' : diff > durationDay ? 'MM/DD/YY hh:mma' : 'MM/DD/YY hh:mm:ssa';
}
function getTimeWidgetHintFormatter(domain) {
if (!Array.isArray(domain)) {
return null;
}
var diff = domain[1] - domain[0];
return diff > durationYear ? 'MM/DD/YY' : diff > durationWeek ? 'MM/DD' : diff > durationDay ? 'MM/DD hha' : diff > durationHour ? 'hh:mma' : 'hh:mm:ssa';
}
/**
* Sanity check on filters to prepare for save
* @param {String} type - filter type
* @param {*} value - filter value
* @returns {boolean} whether filter is value
*/
/* 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;
}
}
function getFilterPlot(filter, allData) {
if (filter.plotType === PLOT_TYPES.histogram || !filter.yAxis) {
// histogram should be calculated when create filter
return {};
}
var mappedValue = filter.mappedValue;
var yAxis = filter.yAxis; // return lineChart
var series = allData.map(function (d, i) {
return {
x: mappedValue[i],
y: d[yAxis.tableFieldIndex - 1]
};
}).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 {{[datasetId: string]: Object}} datasets - new updated datasets
*/
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);
});
return _objectSpread({}, acc, (0, _defineProperty2["default"])({}, dataId, filterDataset(datasets[dataId], appliedFilters, layersToFilter)));
}, datasets);
}
/**
* Applies a new field name value to fielter and update both filter and dataset
* @param {Object} filter - to be applied the new field name on
* @param {Object} dataset - dataset the field belongs to
* @param {string} fieldName - field.name
* @param {Number} filterDatasetIndex - field.name
* @param {Number} filters - current
* @param {Object} option
* @return {Object} {filter, datasets}
*/
function applyFilterFieldName(filter, dataset, fieldName) {
var filterDatasetIndex = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
var _ref7 = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {},
_ref7$mergeDomain = _ref7.mergeDomain,
mergeDomain = _ref7$mergeDomain === void 0 ? false : _ref7$mergeDomain;
// using filterDatasetIndex we can filter only the specified dataset
var fields = dataset.fields,
allData = dataset.allData;
var fieldIndex = fields.findIndex(function (f) {
return f.name === 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 field = fields[fieldIndex];
var filterProps = field.hasOwnProperty('filterProps') ? field.filterProps : getFilterProps(allData, field);
var newFilter = _objectSpread({}, mergeDomain ? mergeFilterDomainStep(filter, filterProps) : _objectSpread({}, filter, {}, filterProps), {
name: Object.assign([].concat(filter.name), (0, _defineProperty2["default"])({}, filterDatasetIndex, field.name)),
fieldIdx: Object.assign([].concat(filter.fieldIdx), (0, _defineProperty2["default"])({}, filterDatasetIndex, field.tableFieldIndex - 1)),
// TODO, since we allow to add multiple fields to a filter we can no longer freeze the filter
freeze: true
});
var fieldWithFilterProps = _objectSpread({}, field, {
filterProps: filterProps
});
var newFields = Object.assign([].concat(fields), (0, _defineProperty2["default"])({}, fieldIndex, fieldWithFilterProps));
return {
filter: newFilter,
dataset: _objectSpread({}, dataset, {
fields: newFields
})
};
}
/**
* Merge one filter with other filter prop domain
* @param filter
* @param filterProps
* @param fieldIndex
* @param datasetIndex
* @return {*}
*/
/* 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({}, 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({}, newFilter, {
domain: (0, _dataUtils.unique)(combinedDomain).sort()
});
case _defaultSettings.ALL_FIELD_TYPES.timestamp:
var step = filter.step < filterProps.step ? filter.step : filterProps.step;
return _objectSpread({}, newFilter, {
step: step
});
case _defaultSettings.ALL_FIELD_TYPES.real:
case _defaultSettings.ALL_FIELD_TYPES.integer:
default:
return newFilter;
}
}
/* eslint-enable complexity */
var featureToFilterValue = function featureToFilterValue(feature, filterId) {
var properties = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
return _objectSpread({}, feature, {
id: feature.id,
properties: _objectSpread({}, feature.properties, {}, properties, {
filterId: filterId
})
});
};
exports.featureToFilterValue = featureToFilterValue;
var getFilterIdInFeature = function getFilterIdInFeature(f) {
return (0, _lodash["default"])(f, ['properties', 'filterId']);
};
/**
* Generates polygon filter
* @param layers array of layers
* @param feature polygon to use
* @return {object} filter
*/
exports.getFilterIdInFeature = getFilterIdInFeature;
function generatePolygonFilter(layers, feature) {
var _layers$reduce = layers.reduce(function (acc, layer) {
return _objectSpread({}, acc, {
dataId: [].concat((0, _toConsumableArray2["default"])(acc.dataId), [layer.config.dataId]),
layerId: [].concat((0, _toConsumableArray2["default"])(acc.layerId), [layer.id]),
name: [].concat((0, _toConsumableArray2["default"])(acc.name), [layer.config.label])
});
}, {
dataId: [],
layerId: [],
name: []
}),
dataId = _layers$reduce.dataId,
layerId = _layers$reduce.layerId,
name = _layers$reduce.name;
var filter = getDefaultFilter(dataId);
return _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
* @param {Object} state - visState
* @param {string} dataId
* @return {Object} state state with updated datasets
*/
function filterDatasetCPU(state, dataId) {
var datasetFilters = state.filters.filter(function (f) {
return f.dataId.includes(dataId);
});
var selectedDataset = state.datasets[dataId];
if (!selectedDataset) {
return state;
}
var opt = {
cpuOnly: true,
ignoreDomain: true
};
if (!datasetFilters.length) {
// no filter
var _filtered = _objectSpread({}, selectedDataset, {
filteredIdxCPU: selectedDataset.allIndexes,
filterRecordCPU: getFilterRecord(dataId, state.filters, opt)
});
return (0, _utils.set)(['datasets', dataId], _filtered, state);
} // no gpu filter
if (!datasetFilters.find(function (f) {
return f.gpu;
})) {
var _filtered2 = _objectSpread({}, selectedDataset, {
filteredIdxCPU: selectedDataset.filteredIndex,
filterRecordCPU: getFilterRecord(dataId, state.filters, opt)
});
return (0, _utils.set)(['datasets', dataId], _filtered2, state);
} // make a copy for cpu filtering
var copied = _objectSpread({}, selectedDataset, {
filterRecord: selectedDataset.filterRecordCPU,
filteredIndex: selectedDataset.filteredIdxCPU
});
var filtered = filterDataset(copied, state.filters, state.layers, opt);
var cpuFilteredDataset = _objectSpread({}, selectedDataset, {
filteredIdxCPU: filtered.filteredIndex,
filterRecordCPU: filtered.filterRecord
});
return (0, _utils.set)(['datasets', dataId], cpuFilteredDataset, state);
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy91dGlscy9maWx0ZXItdXRpbHMuanMiXSwibmFtZXMiOlsiVGltZXN0YW1wU3RlcE1hcCIsIm1heCIsInN0ZXAiLCJOdW1iZXIiLCJQT1NJVElWRV9JTkZJTklUWSIsImhpc3RvZ3JhbUJpbnMiLCJlbmxhcmdlZEhpc3RvZ3JhbUJpbnMiLCJkdXJhdGlvblNlY29uZCIsImR1cmF0aW9uTWludXRlIiwiZHVyYXRpb25Ib3VyIiwiZHVyYXRpb25EYXkiLCJkdXJhdGlvbldlZWsiLCJkdXJhdGlvblllYXIiLCJQTE9UX1RZUEVTIiwiaGlzdG9ncmFtIiwibGluZUNoYXJ0IiwiRklMVEVSX1VQREFURVJfUFJPUFMiLCJkYXRhSWQiLCJuYW1lIiwibGF5ZXJJZCIsIkxJTUlURURfRklMVEVSX0VGRkVDVF9QUk9QUyIsIlN1cHBvcnRlZFBsb3RUeXBlIiwiRklMVEVSX1RZUEVTIiwidGltZVJhbmdlIiwiQUxMX0ZJRUxEX1RZUEVTIiwiaW50ZWdlciIsInJlYWwiLCJyYW5nZSIsIkZJTFRFUl9DT01QT05FTlRTIiwic2VsZWN0IiwibXVsdGlTZWxlY3QiLCJwb2x5Z29uIiwiREVGQVVMVF9GSUxURVJfU1RSVUNUVVJFIiwiZnJlZXplIiwiaWQiLCJmaXhlZERvbWFpbiIsImVubGFyZ2VkIiwiaXNBbmltYXRpbmciLCJzcGVlZCIsInR5cGUiLCJmaWVsZElkeCIsImRvbWFpbiIsInZhbHVlIiwicGxvdFR5cGUiLCJ5QXhpcyIsImludGVydmFsIiwiZ3B1IiwiRklMVEVSX0lEX0xFTkdUSCIsIkxBWUVSX0ZJTFRFUlMiLCJnZXREZWZhdWx0RmlsdGVyIiwic2hvdWxkQXBwbHlGaWx0ZXIiLCJmaWx0ZXIiLCJkYXRhc2V0SWQiLCJkYXRhSWRzIiwiaW5jbHVkZXMiLCJ2YWxpZGF0ZVBvbHlnb25GaWx0ZXIiLCJkYXRhc2V0IiwibGF5ZXJzIiwiZmFpbGVkIiwiaXNWYWxpZEZpbHRlclZhbHVlIiwiaXNWYWxpZERhdGFzZXQiLCJsYXllciIsImZpbmQiLCJsIiwiZmlsdGVyVmFsaWRhdG9ycyIsInZhbGlkYXRlRmlsdGVyIiwiZmlsdGVyRGF0YUlkIiwiZmlsdGVyRGF0YXNldEluZGV4IiwiaW5kZXhPZiIsImluaXRpYWxpemVGaWx0ZXIiLCJmaWVsZE5hbWUiLCJhcHBseUZpbHRlckZpZWxkTmFtZSIsIm1lcmdlRG9tYWluIiwidXBkYXRlZEZpbHRlciIsInVwZGF0ZWREYXRhc2V0IiwiYWRqdXN0VmFsdWVUb0ZpbHRlckRvbWFpbiIsInZhbGlkYXRlRmlsdGVyWUF4aXMiLCJ2YWxpZGF0ZUZpbHRlcldpdGhEYXRhIiwiaGFzT3duUHJvcGVydHkiLCJmaWVsZHMiLCJhbGxEYXRhIiwibWF0Y2hlZEF4aXMiLCJnZXRGaWx0ZXJQbG90IiwiZ2V0RmlsdGVyUHJvcHMiLCJmaWVsZCIsImZpbHRlclByb3BzIiwiZ2V0RmllbGREb21haW4iLCJmaWVsZFR5cGUiLCJ0eXBlT3B0aW9ucyIsInN0cmluZyIsImRhdGUiLCJ0aW1lc3RhbXAiLCJ0YWJsZUZpZWxkSW5kZXgiLCJpc1RpbWUiLCJ2YWx1ZUFjY2Vzc29yIiwibWF5YmVUb0RhdGUiLCJiaW5kIiwiZm9ybWF0IiwiZ2V0TnVtZXJpY0ZpZWxkRG9tYWluIiwiU2NhbGVVdGlscyIsImdldE9yZGluYWxEb21haW4iLCJnZXRUaW1lc3RhbXBGaWVsZERvbWFpbiIsImdldFBvbHlnb25GaWx0ZXJGdW5jdG9yIiwiZ2V0UG9zaXRpb24iLCJnZXRQb3NpdGlvbkFjY2Vzc29yIiwiTEFZRVJfVFlQRVMiLCJwb2ludCIsImljb24iLCJkYXRhIiwicG9zIiwiZXZlcnkiLCJpc0Zpbml0ZSIsImlzSW5Qb2x5Z29uIiwiYXJjIiwibGluZSIsImdldEZpbHRlckZ1bmN0aW9uIiwiaXNJblJhbmdlIiwibWFwcGVkVmFsdWUiLCJhY2Nlc3NvciIsIkFycmF5IiwiaXNBcnJheSIsImluZGV4IiwibGVuZ3RoIiwibGF5ZXJGaWx0ZXJGdW5jdGlvbnMiLCJtYXAiLCJjb25maWciLCJmaWx0ZXJGdW5jIiwidXBkYXRlRmlsdGVyRGF0YUlkIiwiZmlsdGVyRGF0YXNldCIsImZpbHRlcnMiLCJvcHQiLCJvbGRGaWx0ZXJSZWNvcmQiLCJmaWx0ZXJSZWNvcmQiLCJnZXRGaWx0ZXJSZWNvcmQiLCJuZXdEYXRhc2V0IiwiZ3B1RmlsdGVyIiwiZmlsdGVyZWRJbmRleCIsImFsbEluZGV4ZXMiLCJmaWx0ZXJlZEluZGV4Rm9yRG9tYWluIiwiY2hhbmdlZEZpbHRlcnMiLCJkaWZmRmlsdGVycyIsInNob3VsZENhbERvbWFpbiIsIkJvb2xlYW4iLCJkeW5hbWljRG9tYWluIiwic2hvdWxkQ2FsSW5kZXgiLCJjcHUiLCJmaWx0ZXJSZXN1bHQiLCJkeW5hbWljRG9tYWluRmlsdGVycyIsImNwdUZpbHRlcnMiLCJmaWx0ZXJGdW5jcyIsInJlZHVjZSIsImFjYyIsImZpZWxkSW5kZXgiLCJmaWx0ZXJEYXRhQnlGaWx0ZXJUeXBlcyIsInJlc3VsdCIsImkiLCJkIiwibWF0Y2hGb3JEb21haW4iLCJwdXNoIiwibWF0Y2hGb3JSZW5kZXIiLCJmb3JFYWNoIiwiZiIsImlnbm9yZURvbWFpbiIsImNwdU9ubHkiLCJmaWx0ZXJDaGFuZ2VkIiwiT2JqZWN0IiwiZW50cmllcyIsInJlY29yZCIsIml0ZW1zIiwib2xkRmlsdGVyIiwicHJvcCIsImZpbHRlcmVkVmFsdWUiLCJnZXRMaW5lYXJEb21haW4iLCJkaWZmIiwiZ2V0TnVtZXJpY1N0ZXBTaXplIiwiZm9ybWF0TnVtYmVyQnlTdGVwIiwiZ2V0SGlzdG9ncmFtIiwiZW5sYXJnZWRIaXN0b2dyYW0iLCJNYXRoIiwiYWJzIiwieCIsImV4cG9uZW50aWFsRm9ybSIsInRvRXhwb25lbnRpYWwiLCJleHBvbmVudCIsInBhcnNlRmxvYXQiLCJzcGxpdCIsIkRlY2ltYWwiLCJwb3ciLCJ0b051bWJlciIsImVudHJ5IiwiaGlzdG9ncmFtQ29uc3RydWN0IiwiYmlucyIsInRocmVzaG9sZHMiLCJiaW4iLCJjb3VudCIsIngwIiwieDEiLCJ2YWwiLCJib3VuZCIsImZsb29yIiwiY2VpbCIsImdldFRpbWVXaWRnZXRUaXRsZUZvcm1hdHRlciIsImdldFRpbWVXaWRnZXRIaW50Rm9ybWF0dGVyIiwidiIsImlzTmFOIiwiaW5wdXQiLCJjb29yZGluYXRlcyIsInNlcmllcyIsInkiLCJzb3J0IiwiYSIsImIiLCJ5RG9tYWluIiwieERvbWFpbiIsImdldERlZmF1bHRGaWx0ZXJQbG90VHlwZSIsImZpbHRlclBsb3RUeXBlcyIsImFwcGx5RmlsdGVyc1RvRGF0YXNldHMiLCJkYXRhc2V0SWRzIiwiZGF0YXNldHMiLCJsYXllcnNUb0ZpbHRlciIsImFwcGxpZWRGaWx0ZXJzIiwiZmluZEluZGV4IiwibmV3RmlsdGVyIiwibWVyZ2VGaWx0ZXJEb21haW5TdGVwIiwiYXNzaWduIiwiY29uY2F0IiwiZmllbGRXaXRoRmlsdGVyUHJvcHMiLCJuZXdGaWVsZHMiLCJjb21iaW5lZERvbWFpbiIsImZlYXR1cmVUb0ZpbHRlclZhbHVlIiwiZmVhdHVyZSIsImZpbHRlcklkIiwicHJvcGVydGllcyIsImdldEZpbHRlcklkSW5GZWF0dXJlIiwiZ2VuZXJhdGVQb2x5Z29uRmlsdGVyIiwibGFiZWwiLCJpc1Zpc2libGUiLCJmaWx0ZXJEYXRhc2V0Q1BVIiwic3RhdGUiLCJkYXRhc2V0RmlsdGVycyIsInNlbGVjdGVkRGF0YXNldCIsImZpbHRlcmVkIiwiZmlsdGVyZWRJZHhDUFUiLCJmaWx0ZXJSZWNvcmRDUFUiLCJjb3BpZWQiLCJjcHVGaWx0ZXJlZERhdGFzZXQiXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQW9CQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7Ozs7Ozs7QUFFTyxJQUFNQSxnQkFBZ0IsR0FBRyxDQUM5QjtBQUFDQyxFQUFBQSxHQUFHLEVBQUUsQ0FBTjtBQUFTQyxFQUFBQSxJQUFJLEVBQUU7QUFBZixDQUQ4QixFQUU5QjtBQUFDRCxFQUFBQSxHQUFHLEVBQUUsRUFBTjtBQUFV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