kepler.gl
Version:
kepler.gl is a webgl based application to visualize large scale location data in the browser
1,250 lines (1,201 loc) • 154 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _typeof = require("@babel/runtime/helpers/typeof");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.TimestampStepMap = exports.LAYER_FILTERS = exports.FILTER_UPDATER_PROPS = exports.FILTER_ID_LENGTH = exports.FILTER_COMPONENTS = exports.DEFAULT_FILTER_STRUCTURE = void 0;
exports.adjustValueToFilterDomain = adjustValueToFilterDomain;
exports.applyFilterFieldName = applyFilterFieldName;
exports.applyFiltersToDatasets = applyFiltersToDatasets;
exports.canApplyFeatureFilter = canApplyFeatureFilter;
exports.diffFilters = diffFilters;
exports.featureToFilterValue = exports.durationYear = exports.durationWeek = exports.durationSecond = exports.durationMinute = exports.durationHour = exports.durationDay = void 0;
exports.filterDataByFilterTypes = filterDataByFilterTypes;
exports.filterDatasetCPU = filterDatasetCPU;
exports.formatNumberByStep = formatNumberByStep;
exports.generatePolygonFilter = generatePolygonFilter;
exports.getAnimatableVisibleLayers = getAnimatableVisibleLayers;
exports.getAnimatableVisibleLayersByType = getAnimatableVisibleLayersByType;
exports.getColumnFilterProps = getColumnFilterProps;
exports.getDefaultFilter = getDefaultFilter;
exports.getFilterFunction = getFilterFunction;
exports.getFilterIdInFeature = void 0;
exports.getFilterProps = getFilterProps;
exports.getFilterRecord = getFilterRecord;
exports.getFilterScaledTimeline = getFilterScaledTimeline;
exports.getIntervalBasedAnimationLayers = getIntervalBasedAnimationLayers;
exports.getNumericFieldDomain = getNumericFieldDomain;
exports.getNumericStepSize = getNumericStepSize;
exports.getPolygonFilterFunctor = void 0;
exports.getTimeWidgetHintFormatter = getTimeWidgetHintFormatter;
exports.getTimeWidgetTitleFormatter = getTimeWidgetTitleFormatter;
exports.getTimestampFieldDomain = getTimestampFieldDomain;
exports.isFilterValidToSave = isFilterValidToSave;
exports.isInPolygon = isInPolygon;
exports.isInRange = isInRange;
exports.isLayerAnimatable = isLayerAnimatable;
exports.isSideFilter = isSideFilter;
exports.isValidFilterValue = isValidFilterValue;
exports.isValidTimeDomain = isValidTimeDomain;
exports.mergeFilterDomain = mergeFilterDomain;
exports.mergeFilterDomainStep = mergeFilterDomainStep;
exports.mergeFilterWithTimeline = mergeFilterWithTimeline;
exports.mergeTimeDomains = mergeTimeDomains;
exports.removeFilterPlot = removeFilterPlot;
exports.scaleSourceDomainToDestination = scaleSourceDomainToDestination;
exports.shouldApplyFilter = shouldApplyFilter;
exports.updateFilterDataId = updateFilterDataId;
exports.updateFilterPlot = updateFilterPlot;
exports.validateFilter = validateFilter;
exports.validateFilterWithData = validateFilterWithData;
exports.validateFiltersUpdateDatasets = validateFiltersUpdateDatasets;
exports.validatePolygonFilter = validatePolygonFilter;
var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
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 _keymirror = _interopRequireDefault(require("keymirror"));
var _lodash = _interopRequireDefault(require("lodash.get"));
var _lodash2 = _interopRequireDefault(require("lodash.isequal"));
var _d3Array = require("d3-array");
var _booleanWithin = _interopRequireDefault(require("@turf/boolean-within"));
var _helpers = require("@turf/helpers");
var _decimal = require("decimal.js");
var _constants = require("@kepler.gl/constants");
var ScaleUtils = _interopRequireWildcard(require("./data-scale-utils"));
var _h3Js = require("h3-js");
var _commonUtils = require("@kepler.gl/common-utils");
var _utils = require("./utils");
var _dataUtils = require("./data-utils");
var _plot = require("./plot");
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { "default": e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n["default"] = e, t && t.set(e, n), n; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2["default"])(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } // SPDX-License-Identifier: MIT
// Copyright contributors to the kepler.gl project
// import {VisState} from '@kepler.gl/schemas';
var durationSecond = exports.durationSecond = 1000;
var durationMinute = exports.durationMinute = durationSecond * 60;
var durationHour = exports.durationHour = durationMinute * 60;
var durationDay = exports.durationDay = durationHour * 24;
var durationWeek = exports.durationWeek = durationDay * 7;
var durationYear = exports.durationYear = durationDay * 365;
// TODO isolate types - depends on @kepler.gl/schemas
var TimestampStepMap = exports.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
}];
var FILTER_UPDATER_PROPS = exports.FILTER_UPDATER_PROPS = (0, _keymirror["default"])({
dataId: null,
name: null,
layerId: null
});
var FILTER_COMPONENTS = exports.FILTER_COMPONENTS = (0, _defineProperty2["default"])((0, _defineProperty2["default"])((0, _defineProperty2["default"])((0, _defineProperty2["default"])((0, _defineProperty2["default"])({}, _constants.FILTER_TYPES.select, 'SingleSelectFilter'), _constants.FILTER_TYPES.multiSelect, 'MultiSelectFilter'), _constants.FILTER_TYPES.timeRange, 'TimeRangeFilter'), _constants.FILTER_TYPES.range, 'RangeFilter'), _constants.FILTER_TYPES.polygon, 'PolygonFilter');
var DEFAULT_FILTER_STRUCTURE = exports.DEFAULT_FILTER_STRUCTURE = {
dataId: [],
// [string]
id: null,
enabled: true,
// time range filter specific
fixedDomain: false,
view: _constants.FILTER_VIEW_TYPES.side,
isAnimating: false,
animationWindow: _constants.ANIMATION_WINDOW.free,
speed: 1,
// field specific
name: [],
// string
type: null,
fieldIdx: [],
// [integer]
domain: null,
value: null,
// plot
plotType: {
type: _constants.PLOT_TYPES.histogram
},
yAxis: null,
// mode
gpu: false
};
var FILTER_ID_LENGTH = exports.FILTER_ID_LENGTH = 4;
var LAYER_FILTERS = exports.LAYER_FILTERS = [_constants.FILTER_TYPES.polygon];
/**
* Generates a filter with a dataset id as dataId
*/
function getDefaultFilter() {
var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
dataId = _ref.dataId,
id = _ref.id;
return _objectSpread(_objectSpread({}, DEFAULT_FILTER_STRUCTURE), {}, {
// store it as dataId and it could be one or many
dataId: dataId ? (0, _commonUtils.toArray)(dataId) : [],
id: id || (0, _commonUtils.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
*/
function shouldApplyFilter(filter, datasetId) {
var dataIds = (0, _commonUtils.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}
*/
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), {}, {
fieldIdx: []
}),
dataset: dataset
};
}
/**
* Custom filter validators
*/
var filterValidators = (0, _defineProperty2["default"])({}, _constants.FILTER_TYPES.polygon, validatePolygonFilter);
/**
* Default validate filter function
* @param datasets
* @param datasetId
* @param filter
* @return - {filter, dataset}
*/
function validateFilter(datasets, datasetId, filter) {
var _filter$view;
var dataset = datasets[datasetId];
// match filter.dataId
var failed = {
dataset: dataset,
filter: null
};
var filterDataId = (0, _commonUtils.toArray)(filter.dataId);
var filterDatasetIndex = filterDataId.indexOf(dataset.id);
if (filterDatasetIndex < 0 || !(0, _commonUtils.toArray)(filter.name)[filterDatasetIndex]) {
// the current filter is not mapped against the current dataset
return failed;
}
var initializeFilter = _objectSpread(_objectSpread(_objectSpread({}, getDefaultFilter({
dataId: filter.dataId
})), filter), {}, {
dataId: filterDataId,
name: (0, _commonUtils.toArray)(filter.name)
});
var fieldName = initializeFilter.name[filterDatasetIndex];
var _applyFilterFieldName = applyFilterFieldName(initializeFilter, datasets, datasetId, fieldName, filterDatasetIndex, {
mergeDomain: true
}),
updatedFilter = _applyFilterFieldName.filter,
updatedDataset = _applyFilterFieldName.dataset;
if (!updatedFilter) {
return failed;
}
// don't adjust value yet before all datasets are loaded
updatedFilter.view = (_filter$view = filter.view) !== null && _filter$view !== void 0 ? _filter$view : updatedFilter.view;
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
*
* @param datasets
* @param datasetId
* @param filter - filter to be validate
* @param layers - layers
* @return validated filter
*/
function validateFilterWithData(datasets, datasetId, filter, layers) {
return filter.type && Object.prototype.hasOwnProperty.call(filterValidators, filter.type) ? filterValidators[filter.type](datasets[datasetId], filter, layers) : validateFilter(datasets, datasetId, 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 (_ref2) {
var name = _ref2.name,
type = _ref2.type;
return name === yAxis.name && type === yAxis.type;
});
filter = matchedAxis ? _objectSpread(_objectSpread({}, filter), {}, {
yAxis: matchedAxis
}) : filter;
}
return filter;
}
/**
* Get default filter prop based on field type
*
* @param field
* @param fieldDomain
* @returns default filter
*/
function getFilterProps(field, fieldDomain) {
var filterProps = _objectSpread(_objectSpread({}, fieldDomain), {}, {
fieldType: field.type,
view: _constants.FILTER_VIEW_TYPES.side
});
switch (field.type) {
case _constants.ALL_FIELD_TYPES.real:
case _constants.ALL_FIELD_TYPES.integer:
return _objectSpread(_objectSpread({}, filterProps), {}, {
value: fieldDomain.domain,
type: _constants.FILTER_TYPES.range,
// @ts-expect-error
typeOptions: [_constants.FILTER_TYPES.range],
gpu: true
});
case _constants.ALL_FIELD_TYPES["boolean"]:
// @ts-expect-error
return _objectSpread(_objectSpread({}, filterProps), {}, {
type: _constants.FILTER_TYPES.select,
value: true,
gpu: false
});
case _constants.ALL_FIELD_TYPES.string:
case _constants.ALL_FIELD_TYPES.h3:
case _constants.ALL_FIELD_TYPES.date:
// @ts-expect-error
return _objectSpread(_objectSpread({}, filterProps), {}, {
type: _constants.FILTER_TYPES.multiSelect,
value: [],
gpu: false
});
case _constants.ALL_FIELD_TYPES.timestamp:
// @ts-expect-error
return _objectSpread(_objectSpread({}, filterProps), {}, {
type: _constants.FILTER_TYPES.timeRange,
view: _constants.FILTER_VIEW_TYPES.enlarged,
fixedDomain: true,
value: filterProps.domain,
gpu: true,
plotType: {}
});
default:
// @ts-expect-error
return {};
}
}
var getPolygonFilterFunctor = exports.getPolygonFilterFunctor = function getPolygonFilterFunctor(layer, filter, dataContainer) {
var getPosition = layer.getPositionAccessor(dataContainer);
switch (layer.type) {
case _constants.LAYER_TYPES.point:
case _constants.LAYER_TYPES.icon:
return function (data) {
var pos = getPosition(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);
return pos.every(Number.isFinite) && [[pos[0], pos[1]], [pos[3], pos[4]]].every(function (point) {
return isInPolygon(point, filter.value);
});
};
case _constants.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, _h3Js.h3IsValid)(id)) {
return false;
}
var pos = (0, _commonUtils.getCentroid)({
id: id
});
return pos.every(Number.isFinite) && isInPolygon(pos, filter.value);
};
case _constants.LAYER_TYPES.geojson:
return function (data) {
return layer.isInPolygon(data, data.index, filter.value);
};
default:
return function () {
return true;
};
}
};
/**
* Check if a GeoJSON feature filter can be applied to a layer
*/
function canApplyFeatureFilter(feature) {
return Boolean((feature === null || feature === void 0 ? void 0 : feature.geometry) && ['Polygon', 'MultiPolygon'].includes(feature.geometry.type));
}
/**
* @param param An object that represents a row record.
* @param param.index Index of the row in data container.
* @returns Returns true to keep the element, or false otherwise.
*/
/**
* @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
*/
/* eslint-disable complexity */
function getFilterFunction(field, dataId, filter, layers, dataContainer) {
// field could be null in polygon filter
var valueAccessor = field ? field.valueAccessor : function () {
return null;
};
var defaultFunc = function defaultFunc() {
return true;
};
if (filter.enabled === false) {
return defaultFunc;
}
switch (filter.type) {
case _constants.FILTER_TYPES.range:
return function (data) {
return isInRange(valueAccessor(data), filter.value);
};
case _constants.FILTER_TYPES.multiSelect:
return function (data) {
return filter.value.includes(valueAccessor(data));
};
case _constants.FILTER_TYPES.select:
return function (data) {
return valueAccessor(data) === filter.value;
};
case _constants.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 _constants.FILTER_TYPES.polygon:
{
if (!layers || !layers.length || !filter.layerId) {
return defaultFunc;
}
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: dataId
});
}
function filterDataByFilterTypes(_ref3, dataContainer) {
var dynamicDomainFilters = _ref3.dynamicDomainFilters,
cpuFilters = _ref3.cpuFilters,
filterFuncs = _ref3.filterFuncs;
var filteredIndexForDomain = [];
var 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) {
filteredIndexForDomain.push(filterContext.index);
}
var matchForRender = cpuFilters && cpuFilters.every(filterFuncCaller);
if (matchForRender) {
filteredIndex.push(filterContext.index);
}
}
return _objectSpread(_objectSpread({}, dynamicDomainFilters ? {
filteredIndexForDomain: filteredIndexForDomain
} : {}), cpuFilters ? {
filteredIndex: filteredIndex
} : {});
}
/**
* Get a record of filters based on domain type and gpu / cpu
*/
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, _commonUtils.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
*/
function diffFilters(filterRecord) {
var oldFilterRecord = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var filterChanged = {};
Object.entries(filterRecord).forEach(function (_ref4) {
var _ref5 = (0, _slicedToArray2["default"])(_ref4, 2),
record = _ref5[0],
items = _ref5[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);
}
});
});
return _objectSpread(_objectSpread({}, {
dynamicDomain: null,
fixedDomain: null,
cpu: null,
gpu: null
}), filterChanged);
}
/**
* Call by parsing filters from URL
* Check if value of filter within filter domain, if not adjust it to match
* filter domain
*
* @returns value - adjusted value to match filter or null to remove filter
*/
// eslint-disable-next-line complexity
function adjustValueToFilterDomain(value, _ref6) {
var domain = _ref6.domain,
type = _ref6.type;
if (!type) {
return false;
}
// if the current filter is a polygon it will not have any domain
// all other filter types require domain
if (type !== _constants.FILTER_TYPES.polygon && !domain) {
return false;
}
switch (type) {
case _constants.FILTER_TYPES.range:
case _constants.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, _commonUtils.notNullorUndefined)(d) && isInRange(d, domain) ? d : domain[i];
});
case _constants.FILTER_TYPES.multiSelect:
{
if (!Array.isArray(value)) {
return [];
}
var filteredValue = value.filter(function (d) {
return domain.includes(d);
});
return filteredValue.length ? filteredValue : [];
}
case _constants.FILTER_TYPES.select:
return domain.includes(value) ? value : true;
case _constants.FILTER_TYPES.polygon:
return value;
default:
return null;
}
}
/**
* Calculate numeric domain and suitable step
*/
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');
}
return {
domain: domain,
step: step
};
}
/**
* Calculate step size for range and timerange filter
*/
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
*/
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];
// in case equal timestamp add 1 second padding to prevent break
if (!diff) {
domain[1] = domain[0] + 1000;
}
var entry = TimestampStepMap.find(function (f) {
return f.max >= diff;
});
if (entry) {
step = entry.step;
}
return {
domain: domain,
step: step,
mappedValue: mappedValue,
defaultTimeFormat: defaultTimeFormat
};
}
/**
* 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;
}
if (Array.isArray(val)) {
return domain[0] <= val[0] && val[1] <= domain[1];
}
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 (!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';
}
/**
* Sanity check on filters to prepare for save
* @type {typeof import('./filter-utils').isFilterValidToSave}
*/
function isFilterValidToSave(filter) {
return (filter === null || filter === void 0 ? void 0 : filter.type) && Array.isArray(filter === null || filter === void 0 ? void 0 : filter.name) && ((filter === null || filter === void 0 ? void 0 : filter.name.length) || (filter === null || filter === void 0 ? void 0 : filter.layerId.length));
}
/**
* 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 _constants.FILTER_TYPES.select:
return value === true || value === false;
case _constants.FILTER_TYPES.range:
case _constants.FILTER_TYPES.timeRange:
return Array.isArray(value) && value.every(function (v) {
return v !== null && !isNaN(v);
});
case _constants.FILTER_TYPES.multiSelect:
return Array.isArray(value) && Boolean(value.length);
case _constants.FILTER_TYPES.input:
return Boolean(value.length);
case _constants.FILTER_TYPES.polygon:
{
var coordinates = (0, _lodash["default"])(value, ['geometry', 'coordinates']);
return Boolean(value && value.id && coordinates);
}
default:
return true;
}
}
function getColumnFilterProps(filter, dataset) {
var _filter$plotType;
if (((_filter$plotType = filter.plotType) === null || _filter$plotType === void 0 ? void 0 : _filter$plotType.type) === _constants.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.warn(`yAxis ${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 (_ref7) {
var x = _ref7.x,
y = _ref7.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 updateFilterPlot(datasets, filter) {
var dataId = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : undefined;
if (dataId) {
filter = removeFilterPlot(filter, dataId);
}
if (filter.type === _constants.FILTER_TYPES.timeRange) {
return (0, _plot.updateTimeFilterPlotType)(filter, filter.plotType, datasets);
} else if (filter.type === _constants.FILTER_TYPES.range) {
return (0, _plot.updateRangeFilterPlotType)(filter, filter.plotType, datasets);
}
return filter;
}
/**
*
* @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
*/
function applyFiltersToDatasets(datasetIds, datasets, filters, layers) {
var dataIds = (0, _commonUtils.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 filter and update both filter and dataset
* @param filter - to be applied the new field name on
* @param datasets - All datasets
* @param datasetId - Id of the dataset the field belongs to
* @param fieldName - field.name
* @param filterDatasetIndex - field.name
* @param option
* @return - {filter, datasets}
*/
function applyFilterFieldName(filter, datasets, datasetId, fieldName) {
var filterDatasetIndex = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0;
var option = arguments.length > 5 ? arguments[5] : undefined;
// using filterDatasetIndex we can filter only the specified dataset
var mergeDomain = option && Object.prototype.hasOwnProperty.call(option, 'mergeDomain') ? option.mergeDomain : false;
var dataset = datasets[datasetId];
var fieldIndex = dataset === null || dataset === void 0 ? void 0 : dataset.getColumnFieldIdx(fieldName);
// if no field with same name is found, move to the next datasets
if (!dataset || 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(_objectSpread({}, filter), filterProps), {}, {
name: Object.assign((0, _toConsumableArray2["default"])((0, _commonUtils.toArray)(filter.name)), (0, _defineProperty2["default"])({}, filterDatasetIndex, fieldName)),
fieldIdx: Object.assign((0, _toConsumableArray2["default"])((0, _commonUtils.toArray)(filter.fieldIdx)), (0, _defineProperty2["default"])({}, filterDatasetIndex, fieldIndex))
}, filter.plotType ? {
plotType: filter.plotType
} : {});
if (mergeDomain) {
var _mergeFilterDomain;
var domainSteps = (_mergeFilterDomain = mergeFilterDomain(newFilter, datasets)) !== null && _mergeFilterDomain !== void 0 ? _mergeFilterDomain : {};
if (domainSteps) {
var domain = domainSteps.domain,
step = domainSteps.step;
newFilter.domain = domain;
if (newFilter.step !== step) {
newFilter.step = step;
}
}
}
// TODO: if we don't set filter value in filterProps, we don't need to do this
if (filterDatasetIndex > 0) {
// don't reset the filter value if we are just adding a synced dataset
newFilter = _objectSpread(_objectSpread({}, newFilter), {}, {
value: filter.value
});
}
return {
filter: newFilter,
dataset: dataset
};
}
/**
* Merge the domains of a filter in case it applies to multiple datasets
*/
function mergeFilterDomain(filter, datasets) {
var _filter$dataId;
var domainSteps = null;
if (!(filter !== null && filter !== void 0 && (_filter$dataId = filter.dataId) !== null && _filter$dataId !== void 0 && _filter$dataId.length)) {
return filter;
}
filter.dataId.forEach(function (filterDataId, idx) {
var _domainSteps;
var dataset = datasets[filterDataId];
var filterProps = dataset.getColumnFilterProps(filter.name[idx]);
domainSteps = mergeFilterDomainStep((_domainSteps = domainSteps) !== null && _domainSteps !== void 0 ? _domainSteps : {}, filterProps);
});
return domainSteps;
}
/**
* Merge one filter with other filter prop domain
*/
/* 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 sortedDomain = !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), {}, {
// use min max as default domain
domain: [sortedDomain[0], sortedDomain[sortedDomain.length - 1]]
});
switch (filterProps.fieldType) {
case _constants.ALL_FIELD_TYPES.string:
case _constants.ALL_FIELD_TYPES.h3:
case _constants.ALL_FIELD_TYPES.date:
return _objectSpread(_objectSpread({}, newFilter), {}, {
domain: (0, _dataUtils.unique)(sortedDomain)
});
case _constants.ALL_FIELD_TYPES.timestamp:
{
var step = filter.step < filterProps.step ? filter.step : filterProps.step;
return _objectSpread(_objectSpread({}, newFilter), {}, {
step: step
});
}
case _constants.ALL_FIELD_TYPES.real:
case _constants.ALL_FIELD_TYPES.integer:
default:
return newFilter;
}
}
/* eslint-enable complexity */
/**
* Generates polygon filter
*/
var featureToFilterValue = exports.featureToFilterValue = function featureToFilterValue(feature, filterId, properties) {
return _objectSpread(_objectSpread({}, feature), {}, {
id: feature.id,
properties: _objectSpread(_objectSpread(_objectSpread({}, feature.properties), properties), {}, {
filterId: filterId
})
});
};
var getFilterIdInFeature = exports.getFilterIdInFeature = function getFilterIdInFeature(f) {
return (0, _lodash["default"])(f, ['properties', 'filterId']);
};
/**
* Generates polygon filter
*/
function generatePolygonFilter(layers, feature) {
var dataId = layers.map(function (l) {
return l.config.dataId;
}).filter(_commonUtils.notNullorUndefined);
var layerId = layers.map(function (l) {
return l.id;
});
var name = layers.map(function (l) {
return l.config.label;
});
var filter = getDefaultFilter({
dataId: dataId
});
return _objectSpread(_objectSpread({}, filter), {}, {
fixedDomain: true,
type: _constants.FILTER_TYPES.polygon,
name: name,
layerId: layerId,
value: featureToFilterValue(feature, filter.id, {
isVisible: true
})
});
}
/**
* Run filter entirely on CPU
*/
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
*/
function validateFiltersUpdateDatasets(state) {
var filtersToValidate = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
// TODO Better Typings here
var validated = [];
var failed = [];
var datasets = state.datasets,
layers = state.layers;
var updatedDatasets = datasets;
// merge filters
filtersToValidate.forEach(function (filterToValidate) {
// we can only look for datasets define in the filter dataId
var datasetIds = (0, _commonUtils.toArray)(filterToValidate.dataId);
// we can merge a filter only if all datasets in filter.dataId are loaded
if (datasetIds.every(function (d) {
return datasets[d] && !state.isMergingDatasets[d];
})) {
// all datasetIds in filter must be present the state datasets
var _datasetIds$reduce = datasetIds.reduce(function (acc, datasetId) {
var dataset = updatedDatasets[datasetId];
var datasetLayers = layers.filter(function (l) {
return l.config.dataId === dataset.id;
});
var toValidate = acc.validatedFilter || filterToValidate;
var _validateFilterWithDa = validateFilterWithData(_objectSpread(_objectSpread(_objectSpread({}, updatedDatasets), acc.augmentedDatasets), {}, (0, _defineProperty2["default"])({}, datasetId, acc.augmentedDatasets[datasetId] || dataset)), datasetId, toValidate, datasetLayers),
updatedFilter = _validateFilterWithDa.filter,
updatedDataset = _validateFilterWithDa.dataset;
if (updatedFilter) {
// merge filter domain step
return {
validatedFilter: updatedFilter,
applyToDatasets: [].concat((0, _toConsumableArray2["default"])(acc.applyToDatasets), [datasetId]),
augmentedDatasets: _objectSpread(_objectSpread({}, acc.augmentedDatasets), {}, (0, _defineProperty2["default"])({}, datasetId, updatedDataset))
};
}
return acc;
}, {
validatedFilter: null,
applyToDatasets: [],
augmentedDatasets: {}
}),
validatedFilter = _datasetIds$reduce.validatedFilter,
applyToDatasets = _datasetIds$reduce.applyToDatasets,
augmentedDatasets = _datasetIds$reduce.augmentedDatasets;
if (validatedFilter && (0, _lodash2["default"])(datasetIds, applyToDatasets)) {
var domain = validatedFilter.domain;
if (validatedFilter.syncedWithLayerTimeline) {
var animatableLayers = getAnimatableVisibleLayers(layers);
domain = mergeTimeDomains([].concat((0, _toConsumableArray2["default"])(animatableLayers.map(function (l) {
return l.config.animation.domain || [0, 0];
})), [validatedFilter.domain]));
}
validatedFilter.value = adjustValueToFilterDomain(filterToValidate.value, _objectSpread(_objectSpread({}, validatedFilter), {}, {
domain: domain
}));
validated.push(updateFilterPlot(datasets, validatedFilter));
updatedDatasets = _objectSpread(_objectSpread({}, updatedDatasets), augmentedDatasets);
} else {
failed.push(filterToValidate);
}
} else {
failed.push(filterToValidate);
}
});
return {
validated: validated,
failed: failed,
updatedDatasets: updatedDatasets
};
}
function removeFilterPlot(filter, dataId) {
var nextFilter = filter;
var rangeFilter = filter;
if (rangeFilter.bins && rangeFilter.bins[dataId]) {
var _rangeFilter$bins = rangeFilter.bins,
_delete = _rangeFilter$bins[dataId],
nextBins = (0, _objectWithoutProperties2["default"])(_rangeFilter$bins, [dataId].map(_toPropertyKey));
nextFilter = _objectSpread(_objectSpread({}, rangeFilter), {}, {
bins: nextBins
});
}
var timeFilter = filter;
if (timeFilter.timeBins && timeFilter.timeBins[dataId]) {
var _timeFilter$timeBins = timeFilter.timeBins,
__delete = _timeFilter$timeBins[dataId],
nextTimeBins = (0, _objectWithoutProperties2["default"])(_timeFilter$timeBins, [dataId].map(_toPropertyKey));
nextFilter = _objectSpread(_objectSpread({}, nextFilter), {}, {
timeBins: nextTimeBins
});
}
return nextFilter;
}
function isValidTimeDomain(domain) {
return Array.isArray(domain) && domain.every(Number.isFinite);
}
function getTimeWidgetHintFormatter(domain) {
if (!isValidTimeDomain(domain)) {
return undefined;
}
var diff = domain[1] - domain[0];
return diff > durationWeek ? 'L' : diff > durationDay ? 'L LT' : diff > durationHour ? 'LT' : 'LTS';
}
function isSideFilter(filter) {
return filter.view === _constants.FILTER_VIEW_TYPES.side;
}
function mergeTimeDomains(domains) {
return domains.reduce(function (acc, domain) {
var _domain$, _domain$2;
return [Math.min(acc[0], (_domain$ = domain === null || domain === void 0 ? void 0 : domain[0]) !== null && _domain$ !== void 0 ? _domain$ : Infinity), Math.max(acc[1], (_domain$2 = domain === null || domain === void 0 ? void 0 : domain[1]) !== null && _domain$2 !== void 0 ? _domain$2 : -Infinity)];
}, [Number(Infinity), -Infinity]);
}
/**
* @param {Layer} layer
*/
function isLayerAnimatable(layer) {
var _layer$config$animati;
return ((_layer$config$animati = layer.config.animation) === null || _layer$config$animati === void 0 ? void 0 : _layer$config$animati.enabled) && Array.isArray(layer.config.animation.domain);
}
/**
* @param {Layer[]} layers
* @returns {Layer[]}
*/
function getAnimatableVisibleLayers(layers) {
return layers.filter(function (l) {
return isLayerAnimatable(l) && l.config.isVisible;
});
}
/**
* @param {Layer[]} layers
* @param {string} type
* @returns {Layer[]}
*/
function getAnimatableVisibleLayersByType(layers, type) {
return getAnimatableVisibleLayers(layers).filter(function (l) {
return l.type === type;
});
}
/**
* @param {Layer[]} layers
* @returns {Layer[]}
*/
function getIntervalBasedAnimationLayers(layers) {
// @ts-ignore
return getAnimatableVisibleLayers(layers).filter(function (l) {
var _l$config$animation;
return (_l$config$animation = l.config.animation) === null || _l$config$animation === void 0 ? void 0 : _l$config$animation.timeSteps;
});
}
function mergeFilterWithTimeline(filter, animationConfig) {
if ((filter === null || filter === void 0 ? void 0 : filter.type) === _constants.FILTER_TYPES.timeRange && filter.syncedWithLayerTimeline && animationConfig && Array.isArray(animationConfig.domain)) {
var domain = mergeTimeDomains([filter.domain, animationConfig.domain]);
return {
filter: _objectSpread(_objectSpread({}, filter), {}, {
domain: domain
}),
animationConfig: _objectSpread(_objectSpread({}, animationConfig), {}, {
domain: domain
})
};
}
return {
filter: filter,
animationConfig: animationConfig
};
}
function scaleSourceDomainToDestination(sourceDomain, destinationDomain) {
// 0 -> 100: merged domains t1 - t0 === 100% filter may already have this info which is good
var sourceDomainSize = sourceDomain[1] - sourceDomain[0];
// 10 -> 20: animationConfig domain d1 - d0 === animationConfig size
var destinationDomainSize = destinationDomain[1] - destinationDomain[0];
// scale animationConfig size using domain size
var scaledSourceDomainSize = sourceDomainSize / destinationDomainSize * 100;
// scale d0 - t0 using domain size to find starting point
var offset = sourceDomain[0] - destinationDomain[0];
var scaledOffset = offset / destinationDomainSize * 100;
return [scaledOffset, scaledSourceDomainSize + scaledOffset];
}
function getFilterScaledTimeline(filter, animationConfig) {
if (!(filter.syncedWithLayerTimeline && animationConfig !== null && animationConfig !== void 0 && animationConfig.domain)) {
return [];
}
return scaleSourceDomainToDestination(animationConfig.domain, filter.domain);
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfa2V5bWlycm9yIiwiX2ludGVyb3BSZXF1aXJlRGVmYXVsdCIsInJlcXVpcmUiLCJfbG9kYXNoIiwiX2xvZGFzaDIiLCJfZDNBcnJheSIsIl9ib29sZWFuV2l0aGluIiwiX2hlbHBlcnMiLCJfZGVjaW1hbCIsIl9jb25zdGFudHMiLCJTY2FsZVV0aWxzIiwiX2ludGVyb3BSZXF1aXJlV2lsZGNhcmQiLCJfaDNKcyIsIl9jb21tb25VdGlscyIsIl91dGlscyIsIl9kYXRhVXRpbHMiLCJfcGxvdCIsIl9nZXRSZXF1aXJlV2lsZGNhcmRDYWNoZSIsImUiLCJXZWFrTWFwIiwiciIsInQiLCJfX2VzTW9kdWxlIiwiX3R5cGVvZiIsImhhcyIsImdldCIsIm4iLCJfX3Byb3RvX18iLCJhIiwiT2JqZWN0IiwiZGVmaW5lUHJvcGVydHkiLCJnZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IiLCJ1IiwiaGFzT3duUHJvcGVydHkiLCJjYWxsIiwiaSIsInNldCIsIl90b1Byb3BlcnR5S2V5IiwiX3RvUHJpbWl0aXZlIiwiU3ltYm9sIiwidG9QcmltaXRpdmUiLCJUeXBlRXJyb3IiLCJTdHJpbmciLCJOdW1iZXIiLCJvd25LZXlzIiwia2V5cyIsImdldE93blByb3BlcnR5U3ltYm9scyIsIm8iLCJmaWx0ZXIiLCJlbnVtZXJhYmxlIiwicHVzaCIsImFwcGx5IiwiX29iamVjdFNwcmVhZCIsImFyZ3VtZW50cyIsImxlbmd0aCIsImZvckVhY2giLCJfZGVmaW5lUHJvcGVydHkyIiwiZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9ycyIsImRlZmluZVByb3BlcnRpZXMiLCJkdXJhdGlvblNlY29uZCIsImV4cG9ydHMiLCJkdXJhdGlvbk1pbnV0ZSIsImR1cmF0aW9uSG91ciIsImR1cmF0aW9uRGF5IiwiZHVyYXRpb25XZWVrIiwiZHVyYXRpb25ZZWFyIiwiVGltZXN0YW1wU3RlcE1hcCIsIm1heCIsInN0ZXAiLCJQT1NJVElWRV9JTkZJTklUWSIsIkZJTFRFUl9VUERBVEVSX1BST1BTIiwia2V5TWlycm9yIiwiZGF0YUlkIiwibmFtZSIsImxheWVySWQiLCJGSUxURVJfQ09NUE9ORU5UUyIsIkZJTFRFUl9UWVBFUyIsInNlbGVjdCIsIm11bHRpU2VsZWN0IiwidGltZVJhbmdlIiwicmFuZ2UiLCJwb2x5Z29uIiwiREVGQVVMVF9GSUxURVJfU1RSVUNUVVJFIiwiaWQiLCJlbmFibGVkIiwiZml4ZWREb21haW4iLCJ2aWV3IiwiRklMVEVSX1ZJRVdfVFlQRVMiLCJzaWRlIiwiaXNBbmltYXRpbmciLCJhbmltYXRpb25XaW5kb3ciLCJBTklNQVRJT05fV0lORE9XIiwiZnJlZSIsInNwZWVkIiwidHlwZSIsImZpZWxkSWR4IiwiZG9tYWluIiwidmFsdWUiLCJwbG90VHlwZSIsIlBMT1RfVFlQRVMiLCJoaXN0b2dyYW0iLCJ5QXhpcyIsImdwdSIsIkZJTFRFUl9JRF9MRU5HVEgiLCJMQVlFUl9GSUxURVJTIiwiZ2V0RGVmYXVsdEZpbHRlciIsIl9yZWYiLCJ1bmRlZmluZWQiLCJ0b0FycmF5IiwiZ2VuZXJhdGVIYXNoSWQiLCJzaG91bGRBcHBseUZpbHRlciIsImRhdGFzZXRJZCIsImRhdGFJZHMiLCJpbmNsdWRlcyIsInZhbGlkYXRlUG9seWdvbkZpbHRlciIsImRhdGFzZXQiLCJsYXllcnMiLCJmYWlsZWQiLCJpc1ZhbGlkRmlsdGVyVmFsdWUiLCJpc1ZhbGlkRGF0YXNldCIsImxheWVyIiwiZmluZCIsImwiLCJmaWx0ZXJWYWxpZGF0b3JzIiwidmFsaWRhdGVGaWx0ZXIiLCJkYXRhc2V0cyIsIl9maWx0ZXIkdmlldyIsImZpbHRlckRhdGFJZCIsImZpbHRlckRhdGFzZXRJbmRleCIsImluZGV4T2YiLCJpbml0aWFsaXplRmlsdGVyIiwiZmllbGROYW1lIiwiX2FwcGx5RmlsdGVyRmllbGROYW1lIiwiYXBwbHlGaWx0ZXJGaWVsZE5hbWUiLCJtZXJnZURvbWFpbiIsInVwZGF0ZWRGaWx0ZXIiLCJ1cGRhdGVkRGF0YXNldCIsInZhbGlkYXRlRmlsdGVyWUF4aXMiLCJ2YWxpZGF0ZUZpbHRlcldpdGhEYXRhIiwicHJvdG90eXBlIiwiZmllbGRzIiwiX2ZpbHRlciIsIm1hdGNoZWRBeGlzIiwiX3JlZjIiLCJnZXRGaWx0ZXJQcm9wcyIsImZpZWxkIiwiZmllbGREb21haW4iLCJmaWx0ZXJQcm9wcyIsImZpZWxkVHlwZSIsIkFMTF9GSUVMRF9UWVBFUyIsInJlYWwiLCJpbnRlZ2VyIiwidHlwZU9wdGlvbnMiLCJzdHJpbmciLCJoMyIsImRhdGUiLCJ0aW1lc3RhbXAiLCJlbmxhcmdlZCIsImdldFBvbHlnb25GaWx0ZXJGdW5jdG9yIiwiZGF0YUNvbnRhaW5lciIsImdldFBvc2l0aW9uIiwiZ2V0UG9zaXRpb25BY2Nlc3NvciIsIkxBWUVSX1RZUEVTIiwicG9pbnQiLCJpY29uIiwiZGF0YSIsInBvcyIsImV2ZXJ5IiwiaXNGaW5pdGUiLCJpc0luUG9seWdvbiIsImFyYyIsImxpbmUiLCJoZXhhZ29uSWQiLCJkYXRhVG9GZWF0dXJlIiwiY2VudHJvaWRzIiwiY2VudHJvaWQiLCJpbmRleCIsImgzSXNWYWxpZCIsImdldENlbnRyb2lkIiwiZ2VvanNvbiIsImNhbkFwcGx5RmVhdHVyZUZpbHRlciIsImZlYXR1cmUiLCJCb29sZWFuIiwiZ2VvbWV0cnkiLCJnZXRGaWx0ZXJGdW5jdGlvbiIsInZhbHVlQWNjZXNzb3IiLCJkZWZhdWx0RnVuYyIsImlzSW5SYW5nZSIsIm1hcHBlZFZhbHVlIiwiYWNjZXNzb3IiLCJBcnJheSIsImlzQXJyYXkiLCJ0aW1lVG9Vbml4TWlsbGkiLCJmb3JtYXQiLCJsYXllckZpbHRlckZ1bmN0aW9ucyIsIm1hcCIsImNvbmZpZyIsImZpbHRlckZ1bmMiLCJ1cGRhdGVGaWx0ZXJEYXRhSWQiLCJmaWx0ZXJEYXRhQnlGaWx0ZXJUeXBlcyIsIl9yZWYzIiwiZHluYW1pY0RvbWFpbkZpbHRlcnMiLCJjcHVGaWx0ZXJzIiwiZmlsdGVyRnVuY3MiLCJmaWx0ZXJlZEluZGV4Rm9yRG9tYWluIiwiZmlsdGVyZWRJbmRleCIsImZpbHRlckNvbnRleHQiLCJmaWx0ZXJGdW5jQ2FsbGVyIiwibnVtUm93cyIsIm1hdGNoRm9yRG9tYWluIiwibWF0Y2hGb3JSZW5kZXIiLCJnZXRGaWx0ZXJSZWNvcmQiLCJmaWx0ZXJzIiwib3B0IiwiZmlsdGVyUmVjb3JkIiwiZHluYW1pY0RvbWFpbiIsImNwdSIsImYiLCJpZ25vcmVEb21haW4iLCJjcHVPbmx5IiwiZGlmZkZpbHRlcnMiLCJvbGRGaWx0ZXJSZWNvcmQiLCJmaWx0ZXJDaGFuZ2VkIiwiZW50cmllcyIsIl9yZWY0IiwiX3JlZjUiLCJfc2xpY2VkVG9BcnJheTIiLCJyZWNvcmQiLCJpdGVtcyIsIm9sZEZpbHRlciIsInByb3AiLCJjb25jYXQiLCJhZGp1c3RWYWx1ZVRvRmlsdGVyRG9tYWluIiwiX3JlZjYiLCJkIiwibm90TnVsbG9yVW5kZWZpbmVkIiwiZmlsdGVyZWRWYWx1ZSIsImdldE51bWVyaWNGaWVsZERvbWFpbiIsIm1hcEluZGV4IiwiZ2V0TGluZWFyRG9tYWluIiwiZGlmZiIsImdldE51bWVyaWNTdGVwU2l6ZSIsImZvcm1hdE51bWJlckJ5U3RlcCIsIk1hdGgiLCJhYnMiLCJ4IiwiZXhwb25lbnRpYWxGb3JtIiwidG9FeHBvbmVudGlhbCIsImV4cG9uZW50IiwicGFyc2VGbG9hdCIsInNwbGl0IiwiRGVjaW1hbCIsInBvdyIsInRvTnVtYmVyIiwiZ2V0VGltZXN0YW1wRmllbGREb21haW4iLCJkZWZhdWx0VGltZUZvcm1hdCIsImdldFRpbWVXaWRnZXRUaXRsZUZvcm1hdHRlciIsImVudHJ5IiwidmFsIiwiYm91bmQiLCJmbG9vciIsImNlaWwiLCJib29sZWFuV2l0aGluIiwidHVyZlBvaW50IiwiaXNWYWxpZFRpbWVEb21haW4iLCJpc0ZpbHRlclZhb