kepler.gl
Version:
kepler.gl is a webgl based application to visualize large scale location data in the browser
173 lines (147 loc) • 4.5 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 geojsonExtent from '@mapbox/geojson-extent';
import wktParser from 'wellknown';
import normalize from '@mapbox/geojson-normalize';
import {getSampleData} from 'utils/data-utils';
/**
* Parse raw data to geojson feature
* @param allData
* @param getFeature
* @returns {{}}
*/
export function getGeojsonDataMaps(allData, getFeature) {
const acceptableTypes = [
'Point',
'MultiPoint',
'LineString',
'MultiLineString',
'Polygon',
'MultiPolygon',
'GeometryCollection'
];
const dataToFeature = {};
allData.forEach((d, index) => {
dataToFeature[index] = null;
const rawFeature = getFeature(d);
let feature = null;
// parse feature from field
if (Array.isArray(rawFeature)) {
// Support geojson as an array of points
feature = {
type: 'Feature',
geometry: {
// why do we need to flip it...
coordinates: rawFeature.map(pts => [pts[1], pts[0]]),
type: 'LineString'
}
};
} else if (typeof rawFeature === 'string') {
feature = parseGeometryFromString(rawFeature);
} else if (typeof rawFeature === 'object') {
// Support geojson feature as object
// probably need to normalize it as well
const normalized = normalize(rawFeature);
if (!normalized || !Array.isArray(normalized.features)) {
// fail to normalize geojson
return null;
}
feature = normalized.features[0];
}
if (
feature &&
feature.geometry &&
acceptableTypes.includes(feature.geometry.type)
) {
// store index of the data in feature properties
feature.properties = {
...(feature.properties || {}),
index
};
dataToFeature[index] = feature;
}
});
return dataToFeature;
}
/**
* Parse geojson from string
* @param {String} geoString
* @returns {null | Object} geojson object or null if failed
*/
export function parseGeometryFromString(geoString) {
let parsedGeo;
// try parse as geojson string
// {"type":"Polygon","coordinates":[[[-74.158491,40.83594]]]}
try {
parsedGeo = JSON.parse(geoString);
} catch (e) {
// keep trying to parse
}
// try parse as wkt
if (!parsedGeo) {
try {
parsedGeo = wktParser(geoString);
} catch (e) {
return null;
}
}
if (!parsedGeo) {
return null;
}
const normalized = normalize(parsedGeo);
if (!normalized || !Array.isArray(normalized.features)) {
// fail to normalize geojson
return null;
}
return normalized.features[0];
}
export function getGeojsonBounds(features = []) {
// calculate feature bounds is computation heavy
// here we only pick couple
const samples =
features.length > 500 ? getSampleData(features, 500) : features;
const nonEmpty = samples.filter(
d =>
d && d.geometry && d.geometry.coordinates && d.geometry.coordinates.length
);
try {
return geojsonExtent({
type: 'FeatureCollection',
features: nonEmpty
});
} catch (e) {
return null;
}
}
export function featureToDeckGlGeoType(type) {
switch (type) {
case 'Point':
case 'MultiPoint':
return 'point';
case 'LineString':
case 'MultiLineString':
return 'line';
case 'Polygon':
case 'MultiPolygon':
return 'polygon';
default:
return null;
}
}