UNPKG

kepler.gl.geoiq

Version:

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

1,283 lines (1,073 loc) 131 kB
"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.PLOT_TYPES = exports.LIMITED_FILTER_EFFECT_PROPS = exports.LAYER_FILTERS = exports.FILTER_UPDATER_PROPS = exports.FILTER_TYPES = exports.FILTER_ID_LENGTH = exports.FILTER_COMPONENTS = exports.DEFAULT_FILTER_STRUCTURE = void 0; exports.adjustValueToFilterDomain = adjustValueToFilterDomain; exports.applyFilterApiFieldName = applyFilterApiFieldName; exports.applyFilterFieldName = applyFilterFieldName; exports.applyFilterToDataset = applyFilterToDataset; exports.applyFiltersToDatasets = applyFiltersToDatasets; exports.featureToFilterValue = exports.enlargedHistogramBins = void 0; exports.filterData = filterData; exports.filterTileLayerData = filterTileLayerData; exports.formatNumberByStep = formatNumberByStep; exports.generatePolygonFilter = generatePolygonFilter; exports.getDatasetFieldIndexForFilter = getDatasetFieldIndexForFilter; exports.getDatasetIndexForFilter = getDatasetIndexForFilter; exports.getDefaultFilter = getDefaultFilter; exports.getDefaultFilterPlotType = getDefaultFilterPlotType; exports.getFieldDomain = getFieldDomain; exports.getFilterApiProps = getFilterApiProps; exports.getFilterIdInFeature = void 0; exports.getFilterPlot = getFilterPlot; exports.getFilterProps = getFilterProps; exports.getNumericFieldDomain = getNumericFieldDomain; exports.getTimeWidgetHintFormatter = getTimeWidgetHintFormatter; exports.getTimeWidgetTitleFormatter = getTimeWidgetTitleFormatter; exports.getTimestampFieldDomain = getTimestampFieldDomain; exports.histogramBins = void 0; exports.histogramConstruct = histogramConstruct; exports.isDataMatchFilter = isDataMatchFilter; exports.isInPolygon = isInPolygon; exports.isInRange = isInRange; exports.isValidFilterValue = isValidFilterValue; exports.mergeFilterProps = mergeFilterProps; exports.shouldApplyfilter = shouldApplyfilter; exports.updatePolygonFilter = updatePolygonFilter; exports.validateFilter = validateFilter; exports.validateFilterWithData = validateFilterWithData; exports.validatePolygonFilter = validatePolygonFilter; var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray")); var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _moment = _interopRequireDefault(require("moment")); 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 _defaultSettings = require("../constants/default-settings"); var _dataUtils = require("./data-utils"); var ScaleUtils = _interopRequireWildcard(require("./data-scale-utils")); var _utils = require("./utils"); var _FILTER_TYPES$timeRan, _FILTER_TYPES$range, _SupportedPlotType, _FILTER_COMPONENTS, _filterDataMatchers; function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; } 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; } 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 histogramBins = exports.histogramBins = 30; var enlargedHistogramBins = exports.enlargedHistogramBins = 100; var durationSecond = 1000; var durationMinute = durationSecond * 60; var durationHour = durationMinute * 60; var durationDay = durationHour * 24; var durationWeek = durationDay * 7; var durationYear = durationDay * 365; var FILTER_TYPES = exports.FILTER_TYPES = (0, _keymirror["default"])({ range: null, select: null, timeRange: null, multiSelect: null, polygon: null }); var PLOT_TYPES = exports.PLOT_TYPES = (0, _keymirror["default"])({ histogram: null, lineChart: null }); var FILTER_UPDATER_PROPS = exports.FILTER_UPDATER_PROPS = (0, _keymirror["default"])({ dataId: null, name: null, layerId: null }); var LIMITED_FILTER_EFFECT_PROPS = exports.LIMITED_FILTER_EFFECT_PROPS = (0, _keymirror["default"])((0, _defineProperty2["default"])({}, FILTER_UPDATER_PROPS.name, null)); var SupportedPlotType = (_SupportedPlotType = {}, (0, _defineProperty2["default"])(_SupportedPlotType, 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, 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 = exports.FILTER_COMPONENTS = (_FILTER_COMPONENTS = {}, (0, _defineProperty2["default"])(_FILTER_COMPONENTS, FILTER_TYPES.select, 'SingleSelectFilter'), (0, _defineProperty2["default"])(_FILTER_COMPONENTS, FILTER_TYPES.multiSelect, 'MultiSelectFilter'), (0, _defineProperty2["default"])(_FILTER_COMPONENTS, FILTER_TYPES.timeRange, 'TimeRangeFilter'), (0, _defineProperty2["default"])(_FILTER_COMPONENTS, FILTER_TYPES.range, 'RangeFilter'), (0, _defineProperty2["default"])(_FILTER_COMPONENTS, FILTER_TYPES.polygon, 'PolygonFilter'), _FILTER_COMPONENTS); var DEFAULT_FILTER_STRUCTURE = exports.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 }; var FILTER_ID_LENGTH = exports.FILTER_ID_LENGTH = 4; var LAYER_FILTERS = exports.LAYER_FILTERS = [FILTER_TYPES.polygon]; /** * Generates a filter with a dataset id as dataId * @param {[string]} dataId * @return {object} filter */ 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 {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; } function validatePolygonFilter(dataset, filter, layers) { var value = filter.value, layerId = filter.layerId, type = filter.type, dataId = filter.dataId; if (!(value && value.id && layerId)) { return null; } var isValidDataset = dataId.includes(dataset.id); if (!isValidDataset) { return null; } var layer = layers.find(function (l) { return layerId.includes(l.id); }); if (!layer) { return null; } if (!isValidFilterValue({ type: type, value: value })) { return null; } return _objectSpread(_objectSpread({}, filter), {}, { freeze: true, fieldIdx: [0] }); } var filterValidators = (0, _defineProperty2["default"])({}, FILTER_TYPES.polygon, validatePolygonFilter); function validateFilter(dataset, filter) { var fields = dataset.fields, allData = dataset.allData; // match filter.name to field.name var filterDataId = (0, _utils.toArray)(filter.dataId); var filterNames = (0, _utils.toArray)(filter.name); var filterDatasetIndex = Array.isArray(filter.dataId) ? getDatasetIndexForFilter(dataset, filter) : 0; var fieldIndex = fields.findIndex(function (_ref) { var name = _ref.name; return name === filterNames[filterDatasetIndex]; }); if (fieldIndex < 0) { return null; } var field = fields[fieldIndex]; if (filterDatasetIndex === -1) { // the current filter is not mapped against the current dataset return null; } // update fieldIdx with the current value var newFieldIdx = (0, _utils.toArray)(filter.fieldIdx); newFieldIdx[filterDatasetIndex] = fieldIndex; // return filter type, default value, fieldType and fieldDomain from field var filterPropsFromField = getFilterProps(allData, field); var matchedFilter = _objectSpread(_objectSpread(_objectSpread(_objectSpread({}, getDefaultFilter(filter.dataId)), filter), filterPropsFromField), {}, { dataId: filterDataId, freeze: true, fieldIdx: Object.assign((0, _toConsumableArray2["default"])(newFieldIdx), (0, _defineProperty2["default"])({}, filterDatasetIndex, fieldIndex)), name: Object.assign((0, _toConsumableArray2["default"])(filterNames), (0, _defineProperty2["default"])({}, filterDatasetIndex, field.name)) }); var _matchedFilter = matchedFilter, yAxis = _matchedFilter.yAxis; // TODO: validate yAxis against other datasets if (yAxis) { var matcheAxis = fields.find(function (_ref2) { var name = _ref2.name, type = _ref2.type; return name === yAxis.name && type === yAxis.type; }); matchedFilter = matcheAxis ? _objectSpread(_objectSpread({}, matchedFilter), {}, { yAxis: matcheAxis }, getFilterPlot(_objectSpread(_objectSpread({}, matchedFilter), {}, { yAxis: matcheAxis }), allData)) : matchedFilter; } matchedFilter.value = adjustValueToFilterDomain(filter.value, matchedFilter); if (matchedFilter.value === null) { // cannot adjust saved value to filter return null; } return matchedFilter; } /** * Validate saved filter config with new data, * calculate domain and fieldIdx based new fields and data * * @param {Array<Object>} dataset.fields * @param {Array<Object>} dataset.allData * @param {Object} filter - filter to be validate * @param {Array<Object>} layers - existing layers * @return {Object | null} - validated filter */ function validateFilterWithData(dataset, filter, layers) { return filterValidators.hasOwnProperty(filter.type) ? filterValidators[filter.type](dataset, filter, layers) : validateFilter(dataset, filter); } function updatePolygonFilter(filter, feature) { var polygon = (0, _helpers.polygon)(feature.geometry.coordinates); return _objectSpread(_objectSpread({}, filter), {}, { // we merge both turf and feature properties into one value: _objectSpread(_objectSpread({}, polygon), {}, { id: feature.id, properties: _objectSpread(_objectSpread({}, feature.properties), polygon.properties) }) }); } var featureToFilterValue = exports.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 }) }); }; var getFilterIdInFeature = exports.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 */ function generatePolygonFilter(layers, feature) { var _layers$reduce = layers.reduce(function (acc, layer) { return _objectSpread(_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(_objectSpread({}, filter), {}, { fixedDomain: true, type: FILTER_TYPES.polygon, name: name, layerId: layerId, value: featureToFilterValue(feature, filter.id, { isVisible: true }) }); } /** * Get default filter prop based on field type * * @param {Object[]} data * @param {object} field * @returns {object} default filter */ function getFilterProps(data, field) { var filterProp = _objectSpread(_objectSpread({}, getFieldDomain(data, field)), {}, { fieldType: field.type }); switch (field.type) { case _defaultSettings.ALL_FIELD_TYPES.real: case _defaultSettings.ALL_FIELD_TYPES.integer: return _objectSpread(_objectSpread({}, filterProp), {}, { value: filterProp.domain, type: FILTER_TYPES.range, typeOptions: [FILTER_TYPES.range] }); case _defaultSettings.ALL_FIELD_TYPES["boolean"]: return _objectSpread(_objectSpread({}, filterProp), {}, { type: FILTER_TYPES.select, value: true }); case _defaultSettings.ALL_FIELD_TYPES.string: case _defaultSettings.ALL_FIELD_TYPES.date: return _objectSpread(_objectSpread({}, filterProp), {}, { type: FILTER_TYPES.multiSelect, value: [] }); case _defaultSettings.ALL_FIELD_TYPES.timestamp: return _objectSpread(_objectSpread({}, filterProp), {}, { type: FILTER_TYPES.timeRange, enlarged: true, fixedDomain: true, value: filterProp.domain }); default: return {}; } } /** * Get default filter prop based on field type * * @param {Object[]} data * @param {object} field * @returns {object} default filter */ function getFilterApiProps(domain, histogram, field) { var step = 0.1; var filterProp = { domain: domain, histogram: histogram, fieldType: field.type, step: step }; switch (field.type) { case _defaultSettings.ALL_FIELD_TYPES.real: case _defaultSettings.ALL_FIELD_TYPES.integer: return _objectSpread(_objectSpread({}, filterProp), {}, { value: filterProp.domain, type: FILTER_TYPES.range, typeOptions: [FILTER_TYPES.range] }); case _defaultSettings.ALL_FIELD_TYPES["boolean"]: return _objectSpread(_objectSpread({}, filterProp), {}, { type: FILTER_TYPES.select, value: true }); case _defaultSettings.ALL_FIELD_TYPES.string: case _defaultSettings.ALL_FIELD_TYPES.date: return _objectSpread(_objectSpread({}, filterProp), {}, { type: FILTER_TYPES.multiSelect, value: [] }); case _defaultSettings.ALL_FIELD_TYPES.timestamp: return _objectSpread(_objectSpread({}, filterProp), {}, { type: FILTER_TYPES.timeRange, enlarged: true, fixedDomain: true, value: filterProp.domain }); default: return {}; } } /** * Calculate field domain based on field type and data * * @param {Object[]} data * @param {object} field * @returns {object} with domain as key */ function getFieldDomain(data, 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; // console.log('data and field in getFieldDomain', data, field); switch (field.type) { case _defaultSettings.ALL_FIELD_TYPES.real: case _defaultSettings.ALL_FIELD_TYPES.integer: // calculate domain and step return getNumericFieldDomain(data, 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(data, valueAccessor); return { domain: domain }; case _defaultSettings.ALL_FIELD_TYPES.timestamp: return getTimestampFieldDomain(data, valueAccessor); default: return { domain: ScaleUtils.getOrdinalDomain(data, valueAccessor) }; } } /** * Filter data based on an array of filters * @param {Object} dataset to perform the filter on * @param {Object[]} filters list of filters to use against dataset * @param {Object[]} layers list of layers to perform filter on */ function filterData(dataset, filters, layers) { var data = dataset.allData, fields = dataset.fields; if (!filters.length) { var defaultValues = data.map(function (d, i) { return i; }); return { data: data, filteredIndex: defaultValues, filteredIndexForDomain: defaultValues }; } var appliedFilters = filters.filter(function (d) { return shouldApplyfilter(d, dataset.id); }); // Map filter against current dataset field var filtersToFields = filters.reduce(function (acc, filter) { var fieldIndex = getDatasetFieldIndexForFilter(dataset, filter); return _objectSpread(_objectSpread({}, acc), fieldIndex !== -1 ? (0, _defineProperty2["default"])({}, filter.id, fields[fieldIndex]) : {}); }, {// [filterId]: field }); var _appliedFilters$reduc = appliedFilters.reduce(function (accu, f) { (f.fixedDomain ? accu[1] : accu[0]).push(f); return accu; }, [[], []]), _appliedFilters$reduc2 = (0, _slicedToArray2["default"])(_appliedFilters$reduc, 2), dynamicDomainFilters = _appliedFilters$reduc2[0], fixedDomainFilters = _appliedFilters$reduc2[1]; // we save a reference of allData index here to access dataToFeature // in geojson and hexagonId layer var _data$reduce = data.reduce(function (accu, d, i) { // generate 2 sets of // filter data used to calculate layer Domain var matchForDomain = dynamicDomainFilters.every(function (filter) { return isDataMatchFilter(d, filter, i, filtersToFields[filter.id], layers); }); if (matchForDomain) { accu.filteredIndexForDomain.push(i); // filter data for render var matchForRender = fixedDomainFilters.every(function (filter) { return isDataMatchFilter(d, filter, i, filtersToFields[filter.id], layers); }); if (matchForRender) { accu.filtered.push(d); accu.filteredIndex.push(i); } } return accu; }, { filtered: [], filteredIndex: [], filteredIndexForDomain: [] }), filtered = _data$reduce.filtered, filteredIndex = _data$reduce.filteredIndex, filteredIndexForDomain = _data$reduce.filteredIndexForDomain; return { data: filtered, filteredIndex: filteredIndex, filteredIndexForDomain: filteredIndexForDomain }; } var filterDataMatchers = (_filterDataMatchers = {}, (0, _defineProperty2["default"])(_filterDataMatchers, FILTER_TYPES.range, function (data, filter, index, field) { var val = field ? data[field.tableFieldIndex - 1] : null; return isInRange(val, filter.value); }), (0, _defineProperty2["default"])(_filterDataMatchers, FILTER_TYPES.timeRange, function (data, filter, index, field) { var val = field ? data[field.tableFieldIndex - 1] : null; var timeVal = field && field.filterProp && Array.isArray(field.filterProp.mappedValue) ? field.filterProp.mappedValue[index] : _moment["default"].utc(val).valueOf(); return isInRange(timeVal, filter.value); }), (0, _defineProperty2["default"])(_filterDataMatchers, FILTER_TYPES.multiSelect, function (data, filter, index, field) { var val = field ? data[field.tableFieldIndex - 1] : null; return filter.value.includes(val); }), (0, _defineProperty2["default"])(_filterDataMatchers, FILTER_TYPES.select, function (data, filter, index, field) { var val = field ? data[field.tableFieldIndex - 1] : null; return val === filter.value; }), (0, _defineProperty2["default"])(_filterDataMatchers, FILTER_TYPES.polygon, function (data, filter, index, field, layers) { if (!(layers || layers.length === 0)) { return true; } // determine which layers to apply the filter on var currentLayers = filter.layerId.map(function (id) { return layers.find(function (l) { return l.id === id; }); } // we may get null value because filter.layerId may contain layers from other datasets ).filter(function (l) { return Boolean(l); }); return currentLayers.every(function (layer) { var _layer$config$columns = layer.config.columns, lat = _layer$config$columns.lat, lng = _layer$config$columns.lng; var point = [data[lng.fieldIdx], data[lat.fieldIdx]]; return isInPolygon(point, filter.value); }); }), _filterDataMatchers); /** * Check if value is in range of filter * * @param {Object[]} data * @param {Object} filter * @param {number} i * @param {field} field containing values to test data against. This is used only when * testing timestamp filters * @param layers to perform filters upon * @returns {Boolean} - whether value falls in the range of the filter */ function isDataMatchFilter(data, filter, i, field) { var layers = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : null; return !filter.type || !filterDataMatchers.hasOwnProperty(filter.type) ? true : filterDataMatchers[filter.type](data, filter, i, field, layers); } /** * Call by parsing filters from URL * Check if value of filter within filter domain, if not adjust it to match * filter domain * * @param {string[] | string | number | 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, _ref4) { var domain = _ref4.domain, type = _ref4.type; if (!domain || !type) { return false; } switch (type) { case FILTER_TYPES.range: case 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 FILTER_TYPES.multiSelect: if (!Array.isArray(value)) { return []; } var filteredValue = value.filter(function (d) { return domain.includes(d); }); return filteredValue.length ? filteredValue : []; case 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) { if (diff > 100) { return 1; } else if (diff < 20 && diff > 3) { return 0.01; } else if (diff <= 3) { return 0.001; } } /** * Calculate timestamp domain and suitable step * * @param {Object[]} data * @param {function} valueAccessor * @returns {object} domain and step */ 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 }; } 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 {number[]} domain * @param {Object[]} mappedValue * @returns {Array[]} 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) { var convertedPoint = (0, _helpers.point)(point); var present = (0, _booleanWithin["default"])(convertedPoint, polygon); return present; } 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(_ref5) { var type = _ref5.type, value = _ref5.value; if (!type) { return false; } switch (type) { case FILTER_TYPES.select: return value === true || value === false; case FILTER_TYPES.range: case FILTER_TYPES.timeRange: return Array.isArray(value) && value.every(function (v) { return v !== null && !isNaN(v); }); case FILTER_TYPES.multiSelect: return Array.isArray(value) && Boolean(value.length); case FILTER_TYPES.input: return Boolean(value.length); case FILTER_TYPES.polygon: return Boolean(value && value.id && value.geometry && value.geometry.coordinates); default: return true; } } /* eslint-enable complexity */ 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; } /** * Apply a list of filters to a given dataset * @param dataset * @param filters * @return {Object} filtered dataset */ function applyFilterToDataset(dataset, filters, layers) { return _objectSpread(_objectSpread({}, dataset), filterData(dataset, filters, layers)); } /** * * @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, dataIdentifier) { var layersToFilter = (layers || []).filter(function (l) { return l.config.dataId === dataIdentifier; }); return _objectSpread(_objectSpread({}, acc), {}, (0, _defineProperty2["default"])({}, dataIdentifier, applyFilterToDataset(datasets[dataIdentifier], filters, 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 datasets * @param fieldName * @return {object} {filter, datasets} */ function applyFilterFieldName(filter, datasets, fieldName) { var filterDatasetIndex = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0; // using filterDatasetIndex we can filter only the specified dataset var dataId = filter.dataId; var dataIdentifier = dataId[filterDatasetIndex]; var _datasets$dataIdentif = datasets[dataIdentifier], fields = _datasets$dataIdentif.fields, allData = _datasets$dataIdentif.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: ".concat(fieldName)); } var newFilter = _objectSpread(_objectSpread({}, filter), {}, { // TODO, since we allow to add multiple fields to a filter we can no longer freeze the filter freeze: true }); // TODO: validate field type var field = fields[fieldIndex]; var filterProps = field.hasOwnProperty('filterProps') ? field.filterProps : getFilterProps(allData, field); // Update Filter field idx var filterName = (0, _utils.toArray)(filter.name); var filterIdx = (0, _utils.toArray)(filter.fieldIdx); var filterWithProps = _objectSpread(_objectSpread({}, mergeFilterProps(newFilter, filterProps)), {}, { name: Object.assign((0, _toConsumableArray2["default"])(filterName), (0, _defineProperty2["default"])({}, filterDatasetIndex, field.name)), fieldIdx: Object.assign((0, _toConsumableArray2["default"])(filterIdx), (0, _defineProperty2["default"])({}, filterDatasetIndex, field.tableFieldIndex - 1)) }); var fieldWithFilterProps = _objectSpread(_objectSpread({}, field), {}, { filterProp: filterProps }); var newFields = fields.map(function (d, i) { return i === fieldIndex ? fieldWithFilterProps : d; }); return { filter: filterWithProps, datasets: _objectSpread(_objectSpread({}, datasets), {}, (0, _defineProperty2["default"])({}, dataIdentifier, _objectSpread(_objectSpread({}, datasets[dataIdentifier]), {}, { fields: newFields }))) }; } /** * 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 datasets * @param fieldName * @return {object} {filter, datasets} */ function applyFilterApiFieldName(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; var histogram = arguments.length > 5 ? arguments[5] : undefined; var domain = arguments.length > 6 ? arguments[6] : undefined; // 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 }; } var newFilter = _objectSpread(_objectSpread({}, filter), {}, { // TODO, since we allow to add multiple fields to a filter we can no longer freeze the filter freeze: true }); // TODO: validate field type var field = fields[fieldIndex]; var filterProps = field.hasOwnProperty('filterProps') ? field.filterProps : getFilterApiProps(domain, histogram, field); var filterWithProps = _objectSpread(_objectSpread({}, mergeDomain ? mergeFilterDomainStep(newFilter, filterProps) : _objectSpread(_objectSpread({}, newFilter), 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)) }); var fieldWithFilterProps = _objectSpread(_objectSpread({}, field), {}, { filterProps: filterProps }); var newFields = fields.map(function (d, i) { return i === fieldIndex ? fieldWithFilterProps : d; }); return { filter: filterWithProps, dataset: _objectSpread(_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 mergeFilterProps(filter, filterProps) { if (!filter) { return filterProps; } if (!filterProps) { return filter; } if (!filterProps || 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: var step = filter.step < filterProps.step ? filter.step : filterProps.step; return _objectSpread(_objectSpread({}, newFilter), {}, { step: step // step: Math.min(filter.step, filterProps.step) }); case _defaultSettings.ALL_FIELD_TYPES.real: case _defaultSettings.ALL_FIELD_TYPES.integer: default: return newFilter; } } /* eslint-enable complexity */ /** * Return filter dataset index from filter.dataId * @param dataset * @param filter * @return {*} */ function getDatasetIndexForFilter(dataset, filter) { var dataId = filter.dataId; var dataIds = (0, _utils.toArray)(dataId); // dataId is an array return dataIds.findIndex(function (id) { return id === dataset.id; }); } /** * Return dataset field index from filter.fieldIdx * The index matches the same dataset index for filter.dataId * @param dataset * @param filter * @return {*} */ function getDatasetFieldIndexForFilter(dataset, filter) { var datasetIndex = getDatasetIndexForFilter(dataset, filter); if (datasetIndex === -1) { return datasetIndex; } var fieldIndex = filter.fieldIdx[datasetIndex]; return (0, _dataUtils.notNullorUndefined)(fieldIndex) ? fieldIndex : -1; } /** * Filter data based on an array of filters * * @param {Object[]} data * @param {string} dataId * @param {Object[]} filters * @returns {Object[]} data * @returns {Number[]} filteredIndex */ function filterTileLayerData(data, dataId, filters) { if (!data || !dataId) { // why would there not be any data? are we over doing this? return { data: [], filteredIndex: [] }; } if (!filters.length) { return { data: data, filteredIndex: data.map(function (_, i) { return i; }), filteredIndexForDomain: data.map(function (_, i) { return i; }) }; } var appliedFilters = filters.filter(function (d) { return d.dataId === dataId && d.fieldIdx > -1 && d.value !== null; }); var _appliedFilters$reduc3 = appliedFilters.reduce(function (accu, f) { if (f.dataId === dataId && f.fieldIdx > -1 && f.value !== null) { (f.fixedDomain ? accu[1] : accu[0]).push(f); } return accu; }, [[], []]), _appliedFilters$reduc4 = (0, _slicedToArray2["default"])(_appliedFilters$reduc3, 2), dynamicDomainFilters = _appliedFilters$reduc4[0], fixedDomainFilters = _appliedFilters$reduc4[1]; // console.log(dynamicDomainFilters) // console.log(fixedDomainFilters) // we save a reference of allData index here to access dataToFeature // in geojson and hexgonId layer // console.time('filterData'); var _data$reduce2 = data.reduce(function (accu, d, i) { // generate 2 sets of // filter data used to calculate layer Domain var matchForDomain = dynamicDomainFilters.every(function (filter) { return isDataMatchFilter(d, filter, i); }); if (matchForDomain) { accu.filteredIndexForDomain.push(i); // filter data for render var matchForRender = fixedDomainFilters.every(function (filter) { return isDataMatchFilter(d, filter, i); }); if (matchForRender) { accu.filtered.push(d); accu.filteredIndex.push(i); } } return accu; }, { filtered: [], filteredIndex: [], filteredIndexForDomain: [] }), filtered = _data$reduce2.filtered, filteredIndex = _data$reduce2.filteredIndex, filteredIndexForDomain = _data$reduce2.filteredIndexForDomain; // console.log('data==', data.length) // console.log('filtered==', filtered.length) // console.log('filteredIndex==', filteredIndex.length) // console.log('filteredIndexForDomain==', filteredIndexForDomain.length) // // console.timeEnd('filterData'); return { data: filtered, filteredIndex: filteredIndex, filteredIndexForDomain: filteredIndexForDomain }; } //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy91dGlscy9maWx0ZXItdXRpbHMuanMiXSwibmFtZXMiOlsiVGltZXN0YW1wU3RlcE1hcCIsIm1heCIsInN0ZXAiLCJOdW1iZXIiLCJQT1NJVElWRV9JTkZJTklUWSIsImhpc3RvZ3JhbUJpbnMiLCJlbmxhcmdlZEhpc3RvZ3JhbUJpbnMiLCJkdXJhdGlvblNlY29uZCIsImR1cmF0aW9uTWludXRlIiwiZHVyYXRpb25Ib3VyIiwiZHVyYXRpb25EYXkiLCJkdXJhdGlvbldlZWsiLCJkdXJhdGlvblllYXIiLCJGSUxURVJfVFlQRVMiLCJyYW5nZSIsInNlbGVjdCIsInRpbWVSYW5nZSIsIm11bHRpU2VsZWN0IiwicG9seWdvbiIsIlBMT1RfVFlQRVMiLCJoaXN0b2dyYW0iLCJsaW5lQ2hhcnQiLCJGSUxURVJfVVBEQVRFUl9QUk9QUyIsImRhdGFJZCIsIm5hbWUiLCJsYXllcklkIiwiTElNSVRFRF9GSUxURVJfRUZGRUNUX1BST1BTIiwiU3VwcG9ydGVkUGxvdFR5cGUiLCJBTExfRklFTERfVFlQRVMiLCJpbnRlZ2VyIiwicmVhbCIsIkZJTFRFUl9DT01QT05FTlRTIiwiREVGQVVMVF9GSUxURVJfU1RSVUNUVVJFIiwiZnJlZXplIiwiaWQiLCJmaXhlZERvbWFpbiIsImVubGFyZ2VkIiwiaXNBbmltYXRpbmciLCJzcGVlZCIsInR5cGUiLCJmaWVsZElkeCIsImRvbWFpbiIsInZhbHVlIiwicGxvdFR5cGUiLCJ5QXhpcyIsImludGVydmFsIiwiZ3B1IiwiRklMVEVSX0lEX0xFTkdUSCIsIkxBWUVSX0ZJTFRFUlMiLCJnZXREZWZhdWx0RmlsdGVyIiwic2hvdWxkQXBwbHlmaWx0ZXIiLCJmaWx0ZXIiLCJkYXRhc2V0SWQiLCJkYXRhSWRzIiwiaW5jbHVkZXMiLCJ2YWxpZGF0ZVBvbHlnb25GaWx0ZXIiLCJkYXRhc2V0IiwibGF5ZXJzIiwiaXNWYWxpZERhdGFzZXQiLCJsYXllciIsImZpbmQiLCJsIiwiaXNWYWxpZEZpbHRlclZhbHVlIiwiZmlsdGVyVmFsaWRhdG9ycyIsInZhbGlkYXRlRmlsdGVyIiwiZmllbGRzIiwiYWxsRGF0YSIsImZpbHRlckRhdGFJZCIsImZpbHRlck5hbWVzIiwiZmlsdGVyRGF0YXNldEluZGV4IiwiQXJyYXkiLCJpc0FycmF5IiwiZ2V0RGF0YXNldEluZGV4Rm9yRmlsdGVyIiwiZmllbGRJbmRleCIsImZpbmRJbmRleCIsImZpZWxkIiwibmV3RmllbGRJZHgiLCJmaWx0ZXJQcm9wc0Zyb21GaWVsZCIsImdldEZpbHRlclByb3BzIiwibWF0Y2hlZEZpbHRlciIsIk9iamVjdCIsImFzc2lnbiIsIm1hdGNoZUF4aXMiLCJnZXRGaWx0ZXJQbG90IiwiYWRqdXN0VmFsdWVUb0ZpbHRlckRvbWFpbiIsInZhbGlkYXRlRmlsdGVyV2l0aERhdGEiLCJoYXNPd25Qcm9wZXJ0eSIsInVwZGF0ZVBvbHlnb25GaWx0ZXIiLCJmZWF0dXJlIiwiZ2VvbWV0cnkiLCJjb29yZGluYXRlcyIsInByb3BlcnRpZXMiLCJmZWF0dXJlVG9GaWx0ZXJWYWx1ZSIsImZpbHRlcklkIiwiZ2V0RmlsdGVySWRJbkZlYXR1cmUiLCJmIiwiZ2VuZXJhdGVQb2x5Z29uRmlsdGVyIiwicmVkdWNlIiwiYWNjIiwiY29uZmlnIiwibGFiZWwiLCJpc1Zpc2libGUiLCJkYXRhIiwiZmlsdGVyUHJvcCIsImdldEZpZWxkRG9tYWluIiwiZmllbGRUeXBlIiwidHlwZU9wdGlvbnMiLCJzdHJpbmciLCJkYXRlIiwidGltZXN0YW1wIiwiZ2V0RmlsdGVyQXBpUHJvcHMiLCJ0YWJsZUZpZWxkSW5kZXgiLCJpc1RpbWUiLCJ2YWx1ZUFjY2Vzc29yIiwibWF5YmVUb0RhdGUiLCJiaW5kIiwiZm9ybWF0IiwiZ2V0TnVtZXJpY0ZpZWxkRG9tYWluIiwiU2NhbGVVdGlscyIsImdldE9yZGluYWxEb21haW4iLCJnZXRUaW1lc3RhbXBGaWVsZERvbWFpbiIsImZpbHRlckRhdGEiLCJmaWx0ZXJzIiwibGVuZ3RoIiwiZGVmYXVsdFZhbHVlcyIsIm1hcCIsImQiLCJpIiwiZmlsdGVyZWRJbmRleCIsImZpbHRlcmVkSW5kZXhGb3JEb21haW4iLCJhcHBsaWVkRmlsdGVycyIsImZpbHRlcnNUb0ZpZWxkcyIsImdldERhdGFzZXRGaWVsZEluZGV4Rm9yRmlsdGVyIiwiYWNjdSIsInB1c2giLCJkeW5hbWljRG9tYWluRmlsdGVycyIsImZpeGVkRG9tYWluRmlsdGVycyIsIm1hdGNoRm9yRG9tYWluIiwiZXZlcnkiLCJpc0RhdGFNYXRjaEZpbHRlciIsIm1hdGNoRm9yUmVuZGVyIiwiZmlsdGVyZWQiLCJmaWx0ZXJEYXRhTWF0Y2hlcnMiLCJpbmRleCIsInZhbCIsImlzSW5SYW5nZSIsInRpbWVWYWwiLCJtYXBwZWRWYWx1ZSIsIm1vbWVudCIsInV0YyIsInZhbHVlT2YiLCJjdXJyZW50TGF5ZXJzIiwiQm9vbGVhbiIsImNvbHVtbnMiLCJsYXQiLCJsbmciLCJwb2ludCIsImlzSW5Qb2x5Z29uIiwiZmlsdGVyZWRWYWx1ZSIsImdldExpbmVhckRvbWFpbiIsImRpZmYiLCJnZXROdW1lcmljU3RlcFNpemUiLCJmb3JtYXROdW1iZXJCeVN0ZXAiLCJnZXRIaXN0b2dyYW0iLCJlbmxhcmdlZEhpc3RvZ3JhbSIsImVudHJ5IiwiaGlzdG9ncmFtQ29uc3RydWN0IiwiYmlucyIsInRocmVzaG9sZHMiLCJiaW4iLCJjb3VudCIsIngwIiwieDEiLCJib3VuZCIsIk1hdGgiLCJmbG9vciIsImNlaWwiLCJjb252ZXJ0ZWRQb2ludCIsInByZXNlbnQiLCJnZXRUaW1lV2lkZ2V0VGl0bGVGb3JtYXR0ZXIiLCJnZXRUaW1lV2lkZ2V0SGludEZvcm1hdHRlciIsInYiLCJpc05hTiIsImlucHV0Iiwic2VyaWVzIiwieCIsInkiLCJpc0Zpbml0ZSIsInNvcnQiLCJhIiwiYiIsInlEb21haW4iLCJ4RG9tYWluIiwiZ2V0RGVmYXVsdEZpbHRlclBsb3RUeXBlIiwiZmlsdGVyUGxvdFR5cGVzIiwiYXBwbHlGaWx0ZXJUb0RhdGFzZXQiLCJhcHBseUZpbHRlcnNUb0RhdGFzZXRzIiwiZGF0YXNldElkcyIsImRhdGFzZXRzIiwiZGF0YUlkZW50aWZpZXIiLCJsYXllcnNUb0ZpbHRlciIsImFwcGx5RmlsdGVyRmllbGROYW1lIiwiZmllbGROYW1lIiwiRXJyb3IiLCJuZXdGaWx0ZXIiLCJmaWx0ZXJQcm9wcyIsImZpbHRlck5hbWUiLCJmaWx0ZXJJZHgiLCJmaWx0ZXJXaXRoUHJvcHMiLCJtZXJnZUZpbHRlclByb3BzIiwiZmllbGRXaXRoRmlsdGVyUHJvcHMiLCJuZXdGaWVsZHMiLCJhcHBseUZpbHRlckFwaUZpZWxkTmFtZSIsIm1lcmdlRG9tYWluIiwibWVyZ2VGaWx0ZXJEb21haW5TdGVwIiwiY29uY2F0IiwiY29tYmluZWREb21haW4iLCJkYXRhc2V0SW5kZXgiLCJmaWx0ZXJUaWxlTGF5ZXJEYXRhIiwiXyJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQW9CQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7Ozs7Ozs7Ozs7O0FBR08sSUFBTUEsZ0JBQWdCLDhCQUFHLENBQzlCO0FBQUNDLEVBQUFBLEdBQUcsRUFBRSxDQUFOO0FBQVNDLEVBQUFBLElBQUksRUFBRTtBQUFmLENBRDhCLEVBRTlCO0FBQUNELEVBQUFBLEdBQUcsRUFBRSxFQUFOO0FBQVVDLEVBQUFBLElBQUksRUFBRTtBQUFoQixDQUY4QixFQUc5QjtBQUFDRCxFQUFBQSxHQUFHLEVBQUUsR0FBTjtBQUFXQyxFQUFBQSxJQUFJLEVBQUU7QUFBakIsQ0FIOEIsRUFJOUI7QUFBQ0QsRUFBQUEsR0FBRyxFQUFFLEdBQU47QUFBV0MsRUFBQUEsSUFBSSxFQUFFO0FBQWpCLENBSjhCLEVBSzlCO0FBQUNELEVBQUFBLEdBQUcsRUFBRSxJQUFOO0FBQVlDLEVBQUFBLElBQUksRUFBRTtBQUFsQixDQUw4QixFQU05QjtBQUFDRCxFQUFBQSxHQUFHLEVBQUUsSUFBTjtBQUFZQyxFQUFBQSxJQUFJLEVBQUU7QUFBbEIsQ0FOOEIsRUFP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