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
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.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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