kepler.gl
Version:
kepler.gl is a webgl based application to visualize large scale location data in the browser
166 lines (149 loc) • 4.86 kB
JavaScript
// Copyright (c) 2018 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import {hexToRgb} from './color-utils';
import uniq from 'lodash.uniq';
import {TRIP_POINT_FIELDS} from 'constants/default-settings';
import {generateHashId} from './utils';
import {validateInputData} from 'processors/data-processor'
;
// apply a color for each dataset
// to use as label colors
const datasetColors = [
'#8F2FBF',
'#005CFF',
'#C06C84',
'#F8B195',
'#547A82',
'#3EACA8',
'#A2D4AB'
].map(hexToRgb);
/**
* Random color generator
*/
function* generateColor() {
let index = 0;
while (index < datasetColors.length + 1) {
if (index === datasetColors.length) {
index = 0;
}
yield datasetColors[index++];
}
}
export const datasetColorMaker = generateColor();
function getNewDatasetColor(datasets) {
const presetColors = datasetColors.map(String);
const usedColors = uniq(
Object.values(datasets).map(d => String(d.color))
).filter(c => presetColors.includes(c));
if (usedColors.length === presetColors.length) {
// if we already depleted the pool of color
return datasetColorMaker.next().value;
}
let color = datasetColorMaker.next().value;
while (usedColors.includes(String(color))) {
color = datasetColorMaker.next().value;
}
return color;
}
export function createNewDataEntry({info = {}, data}, datasets = {}) {
const validatedData = validateInputData(data);
if (!validatedData) {
return {};
}
const allData = validatedData.rows;
const datasetInfo = {
id: generateHashId(4),
label: 'new dataset',
...info
};
const dataId = datasetInfo.id;
// add tableFieldIndex and id to fields
// TODO: don't need id and name and tableFieldIndex anymore
// Add value accessor instead
const fields = validatedData.fields.map((f, i) => ({
...f,
id: f.name,
tableFieldIndex: i + 1
}));
return {
[dataId]: {
...datasetInfo,
color: datasetInfo.color || getNewDatasetColor(datasets),
id: dataId,
allData,
// TODO: no need to make a copy anymore, only save fieldedIndex
data: allData.slice(),
filteredIndex: allData.map((_, i) => i),
filteredIndexForDomain: allData.map((_, i) => i),
fieldPairs: findPointFieldPairs(fields),
fields
}
};
}
export function removeSuffixAndDelimiters(layerName, suffix) {
return layerName
.replace(new RegExp(suffix, 'ig'), '')
.replace(/[_,.]+/g, ' ')
.trim();
}
/**
* Find point fields pairs from fields
*
* @param {Array} fields
* @returns {Array} found point fields
*/
export function findPointFieldPairs(fields) {
const allNames = fields.map(f => f.name.toLowerCase());
// get list of all fields with matching suffixes
return allNames.reduce((carry, fieldName, idx) => {
// This search for pairs will early exit if found.
for (const suffixPair of TRIP_POINT_FIELDS) {
// match first suffix```
if (fieldName.endsWith(suffixPair[0])) {
// match second suffix
const otherPattern = new RegExp(`${suffixPair[0]}\$`);
const partner = fieldName.replace(otherPattern, suffixPair[1]);
const partnerIdx = allNames.findIndex(d => d === partner);
if (partnerIdx > -1) {
const defaultName = removeSuffixAndDelimiters(
fieldName,
suffixPair[0]
);
carry.push({
defaultName,
pair: {
lat: {
fieldIdx: idx,
value: fields[idx].name
},
lng: {
fieldIdx: partnerIdx,
value: fields[partnerIdx].name
}
},
suffix: suffixPair
});
return carry;
}
}
}
return carry;
}, []);
}