UNPKG

kepler.gl

Version:

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

865 lines (795 loc) 20.1 kB
// Copyright (c) 2020 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 keyMirror from 'keymirror'; import {EditorModes} from 'react-map-gl-draw'; import { scaleLinear, scaleQuantize, scaleQuantile, scaleOrdinal, scaleSqrt, scaleLog, scalePoint } from 'd3-scale'; import { Layers, FilterFunnel, Settings, CursorClick, Pin, ArrowDown, ArrowUp, Clipboard, Cancel } from 'components/common/icons'; import {getHTMLMapModeTileUrl} from 'utils/utils'; export const ACTION_PREFIX = '@@kepler.gl/'; export const CLOUDFRONT = 'https://d1a3f4spazzrp4.cloudfront.net/kepler.gl'; export const ICON_PREFIX = `${CLOUDFRONT}/geodude`; export const DEFAULT_MAPBOX_API_URL = 'https://api.mapbox.com'; // Modal Ids /** * Modal id: data table * @constant * @type {string} * @public */ export const DATA_TABLE_ID = 'dataTable'; /** * Modal id: delete dataset confirm dialog * @constant * @type {string} * @public */ export const DELETE_DATA_ID = 'deleteData'; /** * Modal id: add data modal * @constant * @type {string} * @public */ export const ADD_DATA_ID = 'addData'; /** * Modal id: export image modal * @constant * @type {string} * @public */ export const EXPORT_IMAGE_ID = 'exportImage'; /** * Modal id: export data modal * @constant * @type {string} * @public */ export const EXPORT_DATA_ID = 'exportData'; /** * Modal id: add custom map style modal * @constant * @type {string} * @public */ export const ADD_MAP_STYLE_ID = 'addMapStyle'; /** * Modal id: export map modal * @constant * @type {string} * @public */ export const EXPORT_MAP_ID = 'exportMap'; /** * Modal id: save map modal * @constant * @type {string} * @public */ export const SAVE_MAP_ID = 'saveMap'; /** * Modal id: confirm to overwrite saved map * @constant * @type {string} * @public */ export const OVERWRITE_MAP_ID = 'overwriteMap'; /** * Modal id: share map url modal * @constant * @type {string} * @public */ export const SHARE_MAP_ID = 'shareMap'; export const KEPLER_GL_NAME = 'kepler.gl'; // __PACKAGE_VERSION__ is automatically injected by Babel/Webpack during the building process // Since we are injecting this during the build process with babel // while developing VERSION is not defined, we capture the exception and return // an empty string which will allow us to retrieve the latest umd version export const KEPLER_GL_VERSION = '__PACKAGE_VERSION__'; export const KEPLER_GL_WEBSITE = 'http://kepler.gl/'; export const DIMENSIONS = { sidePanel: { width: 300, margin: {top: 20, left: 20, bottom: 30, right: 20}, headerHeight: 96 }, mapControl: { width: 204, padding: 12 } }; /** * Theme name that can be passed to `KeplerGl` `prop.theme`. * Available themes are `Theme.light` and `Theme.dark`. Default theme is `Theme.dark` * @constant * @type {string} * @public * @example * ```js * const Map = () => <KeplerGl theme={THEME.light} id="map"/> * ``` */ export const THEME = keyMirror({ light: null, dark: null, base: null }); /** * Localization can be passed to `KeplerGl` via uiState `locale`. * Available languages are `en` and `fi`. Default language is `en` * @constant * @type {string} * @public * @example * ```js * import {combineReducers} from 'redux'; * import keplerGlReducer from 'kepler.gl/reducers'; * import {LOCALES} from 'kepler.gl/constants'; * * const customizedKeplerGlReducer = keplerGlReducer * .initialState({ * uiState: { * // use Finnish locale * locale: LOCALES.fi * } * }); * * const reducers = combineReducers({ * keplerGl: customizedKeplerGlReducer, * app: appReducer * }); * ``` */ export const LOCALES = keyMirror({ en: null, fi: null }); export const SIDEBAR_PANELS = [ { id: 'layer', label: 'sidebar.panels.layer', iconComponent: Layers }, { id: 'filter', label: 'sidebar.panels.filter', iconComponent: FilterFunnel }, { id: 'interaction', label: 'sidebar.panels.interaction', iconComponent: CursorClick }, { id: 'map', label: 'sidebar.panels.basemap', iconComponent: Settings } ]; // backward compatibility export const PANELS = SIDEBAR_PANELS; // MAP STYLES export const DEFAULT_LAYER_GROUPS = [ { slug: 'label', filter: ({id}) => id.match(/(?=(label|place-|poi-))/), defaultVisibility: true }, { slug: 'road', filter: ({id}) => id.match(/(?=(road|railway|tunnel|street|bridge))(?!.*label)/), defaultVisibility: true }, { slug: 'border', filter: ({id}) => id.match(/border|boundaries/), defaultVisibility: false }, { slug: 'building', filter: ({id}) => id.match(/building/), defaultVisibility: true }, { slug: 'water', filter: ({id}) => id.match(/(?=(water|stream|ferry))/), defaultVisibility: true }, { slug: 'land', filter: ({id}) => id.match(/(?=(parks|landcover|industrial|sand|hillshade))/), defaultVisibility: true }, { slug: '3d building', filter: () => false, defaultVisibility: false } ]; export const DEFAULT_MAP_STYLES = [ { id: 'dark', label: 'Dark', url: 'mapbox://styles/uberdata/cjoqbbf6l9k302sl96tyvka09', icon: `${ICON_PREFIX}/UBER_DARK_V2.png`, layerGroups: DEFAULT_LAYER_GROUPS }, { id: 'light', label: 'Light', url: 'mapbox://styles/uberdata/cjoqb9j339k1f2sl9t5ic5bn4', icon: `${ICON_PREFIX}/UBER_LIGHT_V2.png`, layerGroups: DEFAULT_LAYER_GROUPS }, { id: 'muted', label: 'Muted Light', url: 'mapbox://styles/uberdata/cjfyl03kp1tul2smf5v2tbdd4', icon: `${ICON_PREFIX}/UBER_MUTED_LIGHT.png`, layerGroups: DEFAULT_LAYER_GROUPS }, { id: 'muted_night', label: 'Muted Night', url: 'mapbox://styles/uberdata/cjfxhlikmaj1b2soyzevnywgs', icon: `${ICON_PREFIX}/UBER_MUTED_NIGHT.png`, layerGroups: DEFAULT_LAYER_GROUPS }, { id: 'satellite', label: 'Satellite', url: `mapbox://styles/mapbox/satellite-v9`, icon: `${ICON_PREFIX}/UBER_SATELLITE.png` } ]; export const GEOJSON_FIELDS = { geojson: ['_geojson', 'all_points', 'geojson'] }; export const ICON_FIELDS = { icon: ['icon'] }; export const TRIP_POINT_FIELDS = [ ['lat', 'lng'], ['lat', 'lon'], ['latitude', 'longitude'] ]; export const TRIP_ARC_FIELDS = { lat0: 'begintrip', lng0: 'begintrip', lat1: 'dropoff', lng1: 'dropoff' }; export const FILTER_TYPES = keyMirror({ range: null, select: null, timeRange: null, multiSelect: null, polygon: null }); export const SCALE_TYPES = keyMirror({ ordinal: null, quantile: null, quantize: null, linear: null, sqrt: null, log: null, // ordinal domain to linear range point: null }); export const SCALE_FUNC = { [SCALE_TYPES.linear]: scaleLinear, [SCALE_TYPES.quantize]: scaleQuantize, [SCALE_TYPES.quantile]: scaleQuantile, [SCALE_TYPES.ordinal]: scaleOrdinal, [SCALE_TYPES.sqrt]: scaleSqrt, [SCALE_TYPES.log]: scaleLog, [SCALE_TYPES.point]: scalePoint }; export const ALL_FIELD_TYPES = keyMirror({ boolean: null, date: null, geojson: null, integer: null, real: null, string: null, timestamp: null, point: null }); // Data Table export const SORT_ORDER = keyMirror({ ASCENDING: null, DESCENDING: null, UNSORT: null }); export const TABLE_OPTION = keyMirror({ SORT_ASC: null, SORT_DES: null, UNSORT: null, PIN: null, UNPIN: null, COPY: null }); export const TABLE_OPTION_LIST = [ { value: TABLE_OPTION.SORT_ASC, display: 'Sort Ascending', icon: ArrowUp, condition: props => props.sortMode !== SORT_ORDER.ASCENDING }, { value: TABLE_OPTION.SORT_DES, display: 'Sort Descending', icon: ArrowDown, condition: props => props.sortMode !== SORT_ORDER.DESCENDING }, { value: TABLE_OPTION.UNSORT, display: 'Unsort Column', icon: Cancel, condition: props => props.isSorted }, {value: TABLE_OPTION.PIN, display: 'Pin Column', icon: Pin, condition: props => !props.isPinned}, { value: TABLE_OPTION.UNPIN, display: 'Unpin Column', icon: Cancel, condition: props => props.isPinned }, {value: TABLE_OPTION.COPY, display: 'Copy Column', icon: Clipboard} ]; const ORANGE = '248, 194, 28'; const PINK = '231, 189, 194'; const PURPLE = '160, 106, 206'; const BLUE = '140, 210, 205'; const BLUE2 = '106, 160, 206'; const BLUE3 = '0, 172, 237'; const GREEN = '106, 160, 56'; const RED = '237, 88, 106'; export const HIGHLIGH_COLOR_3D = [255, 255, 255, 60]; export const FIELD_COLORS = { default: RED }; export const FILED_TYPE_DISPLAY = { [ALL_FIELD_TYPES.boolean]: { label: 'bool', color: PINK }, [ALL_FIELD_TYPES.date]: { label: 'date', color: PURPLE }, [ALL_FIELD_TYPES.geojson]: { label: 'geo', color: BLUE2 }, [ALL_FIELD_TYPES.integer]: { label: 'int', color: ORANGE }, [ALL_FIELD_TYPES.real]: { label: 'float', color: ORANGE }, [ALL_FIELD_TYPES.string]: { label: 'string', color: BLUE }, [ALL_FIELD_TYPES.timestamp]: { label: 'time', color: GREEN }, // field pairs [ALL_FIELD_TYPES.point]: { label: 'point', color: BLUE3 } }; export const CHANNEL_SCALES = keyMirror({ color: null, radius: null, size: null, colorAggr: null, sizeAggr: null }); export const AGGREGATION_TYPES = { // default count: 'count', // linear average: 'average', maximum: 'maximum', minimum: 'minimum', median: 'median', stdev: 'stdev', sum: 'sum', variance: 'variance', // ordinal mode: 'mode', countUnique: 'count unique' }; export const linearFieldScaleFunctions = { [CHANNEL_SCALES.color]: [SCALE_TYPES.quantize, SCALE_TYPES.quantile], [CHANNEL_SCALES.radius]: [SCALE_TYPES.sqrt], [CHANNEL_SCALES.size]: [SCALE_TYPES.linear, SCALE_TYPES.sqrt, SCALE_TYPES.log] }; export const linearFieldAggrScaleFunctions = { [CHANNEL_SCALES.colorAggr]: { [AGGREGATION_TYPES.average]: [SCALE_TYPES.quantize, SCALE_TYPES.quantile], [AGGREGATION_TYPES.maximum]: [SCALE_TYPES.quantize, SCALE_TYPES.quantile], [AGGREGATION_TYPES.minimum]: [SCALE_TYPES.quantize, SCALE_TYPES.quantile], [AGGREGATION_TYPES.median]: [SCALE_TYPES.quantize, SCALE_TYPES.quantile], [AGGREGATION_TYPES.stdev]: [SCALE_TYPES.quantize, SCALE_TYPES.quantile], [AGGREGATION_TYPES.sum]: [SCALE_TYPES.quantize, SCALE_TYPES.quantile], [AGGREGATION_TYPES.variance]: [SCALE_TYPES.quantize, SCALE_TYPES.quantile] }, [CHANNEL_SCALES.sizeAggr]: { [AGGREGATION_TYPES.average]: [SCALE_TYPES.linear, SCALE_TYPES.sqrt, SCALE_TYPES.log], [AGGREGATION_TYPES.maximum]: [SCALE_TYPES.linear, SCALE_TYPES.sqrt, SCALE_TYPES.log], [AGGREGATION_TYPES.minimum]: [SCALE_TYPES.linear, SCALE_TYPES.sqrt, SCALE_TYPES.log], [AGGREGATION_TYPES.median]: [SCALE_TYPES.linear, SCALE_TYPES.sqrt, SCALE_TYPES.log], [AGGREGATION_TYPES.stdev]: [SCALE_TYPES.linear, SCALE_TYPES.sqrt, SCALE_TYPES.log], [AGGREGATION_TYPES.sum]: [SCALE_TYPES.linear, SCALE_TYPES.sqrt, SCALE_TYPES.log], [AGGREGATION_TYPES.variance]: [SCALE_TYPES.linear, SCALE_TYPES.sqrt, SCALE_TYPES.log] } }; export const ordinalFieldScaleFunctions = { [CHANNEL_SCALES.color]: [SCALE_TYPES.ordinal], [CHANNEL_SCALES.radius]: [SCALE_TYPES.point], [CHANNEL_SCALES.size]: [SCALE_TYPES.point] }; export const ordinalFieldAggrScaleFunctions = { // [CHANNEL_SCALES.colorAggr]: [SCALE_TYPES.ordinal, SCALE_TYPES.linear], [CHANNEL_SCALES.colorAggr]: { [AGGREGATION_TYPES.mode]: [SCALE_TYPES.ordinal], [AGGREGATION_TYPES.countUnique]: [SCALE_TYPES.quantize, SCALE_TYPES.quantile] }, // Currently doesn't support yet [CHANNEL_SCALES.sizeAggr]: {} }; export const notSupportedScaleOpts = { [CHANNEL_SCALES.color]: [], [CHANNEL_SCALES.radius]: [], [CHANNEL_SCALES.size]: [] }; export const notSupportAggrOpts = { [CHANNEL_SCALES.colorAggr]: {}, [CHANNEL_SCALES.sizeAggr]: {} }; /** * Default aggregation are based on ocunt */ export const DEFAULT_AGGREGATION = { [CHANNEL_SCALES.colorAggr]: { [AGGREGATION_TYPES.count]: [SCALE_TYPES.quantize, SCALE_TYPES.quantile] }, [CHANNEL_SCALES.sizeAggr]: { [AGGREGATION_TYPES.count]: [SCALE_TYPES.linear, SCALE_TYPES.sqrt, SCALE_TYPES.log] } }; /** * Define what type of scale operation is allowed on each type of fields */ export const FIELD_OPTS = { string: { type: 'categorical', scale: { ...ordinalFieldScaleFunctions, ...ordinalFieldAggrScaleFunctions }, format: { legend: d => d } }, real: { type: 'numerical', scale: { ...linearFieldScaleFunctions, ...linearFieldAggrScaleFunctions }, format: { legend: d => d } }, timestamp: { type: 'time', scale: { ...linearFieldScaleFunctions, ...notSupportAggrOpts }, format: { legend: d => d } }, integer: { type: 'numerical', scale: { ...linearFieldScaleFunctions, ...linearFieldAggrScaleFunctions }, format: { legend: d => d } }, boolean: { type: 'boolean', scale: { ...ordinalFieldScaleFunctions, ...ordinalFieldAggrScaleFunctions }, format: { legend: d => d } }, date: { scale: { ...ordinalFieldScaleFunctions, ...ordinalFieldAggrScaleFunctions }, format: { legend: d => d } }, geojson: { type: 'geometry', scale: { ...notSupportedScaleOpts, ...notSupportAggrOpts }, format: { legend: d => '...' } } }; export const CHANNEL_SCALE_SUPPORTED_FIELDS = Object.keys(CHANNEL_SCALES).reduce( (accu, key) => ({ ...accu, [key]: Object.keys(FIELD_OPTS).filter(ft => Object.keys(FIELD_OPTS[ft].scale[key]).length) }), {} ); // TODO: shan delete use of LAYER_TYPES export const LAYER_TYPES = keyMirror({ point: null, arc: null, cluster: null, line: null, grid: null, geojson: null, icon: null, heatmap: null, hexagon: null }); export const DEFAULT_LAYER_COLOR = { tripArc: '#9226C6', begintrip_lat: '#1E96BE', dropoff_lat: '#FF991F', request_lat: '#52A353' }; // let user pass in default tooltip fields export const DEFAULT_TOOLTIP_FIELDS = []; export const NO_VALUE_COLOR = [0, 0, 0, 0]; export const LAYER_BLENDINGS = { additive: { label: 'layerBlending.additive', blendFunc: ['SRC_ALPHA', 'DST_ALPHA'], blendEquation: 'FUNC_ADD' }, normal: { // reference to // https://limnu.com/webgl-blending-youre-probably-wrong/ label: 'layerBlending.normal', blendFunc: ['SRC_ALPHA', 'ONE_MINUS_SRC_ALPHA', 'ONE', 'ONE_MINUS_SRC_ALPHA'], blendEquation: ['FUNC_ADD', 'FUNC_ADD'] }, subtractive: { label: 'layerBlending.subtractive', blendFunc: ['ONE', 'ONE_MINUS_DST_COLOR', 'SRC_ALPHA', 'DST_ALPHA'], blendEquation: ['FUNC_SUBTRACT', 'FUNC_ADD'] } }; export const MAX_DEFAULT_TOOLTIPS = 5; export const RESOLUTIONS = keyMirror({ ONE_X: null, TWO_X: null }); export const EXPORT_IMG_RATIOS = keyMirror({ SCREEN: null, FOUR_BY_THREE: null, SIXTEEN_BY_NINE: null, CUSTOM: null }); export const EXPORT_IMG_RATIO_OPTIONS = [ { id: EXPORT_IMG_RATIOS.SCREEN, label: 'modal.exportImage.ratioOriginalScreen', getSize: (screenW, screenH) => ({width: screenW, height: screenH}) }, { id: EXPORT_IMG_RATIOS.CUSTOM, hidden: true, label: 'modal.exportImage.ratioCustom', getSize: (mapW, mapH) => ({width: mapW, height: mapH}) }, { id: EXPORT_IMG_RATIOS.FOUR_BY_THREE, label: 'modal.exportImage.ratio4_3', getSize: (screenW, screenH) => ({ width: screenW, height: Math.round(screenW * 0.75) }) }, { id: EXPORT_IMG_RATIOS.SIXTEEN_BY_NINE, label: 'modal.exportImage.ratio16_9', getSize: (screenW, screenH) => ({ width: screenW, height: Math.round(screenW * 0.5625) }) } ]; export const EXPORT_IMG_RESOLUTION_OPTIONS = [ { id: RESOLUTIONS.ONE_X, label: '1x', available: true, scale: 1, getSize: (screenW, screenH) => ({ width: screenW, height: screenH }) }, { id: RESOLUTIONS.TWO_X, label: '2x', available: true, scale: 2, getSize: (screenW, screenH) => ({ width: screenW * 2, height: screenH * 2 }) } ]; export const EXPORT_DATA_TYPE = keyMirror({ CSV: null // SHAPEFILE: null, // JSON: null, // GEOJSON: null, // TOPOJSON: null }); export const EXPORT_DATA_TYPE_OPTIONS = [ { id: EXPORT_DATA_TYPE.CSV, label: EXPORT_DATA_TYPE.CSV.toLowerCase(), available: true } // { // id: EXPORT_DATA_TYPE.SHAPEFILE, // label: 'shapefile', // available: false // }, // { // id: EXPORT_DATA_TYPE.JSON, // label: 'json', // available: false // }, // { // id: EXPORT_DATA_TYPE.GEOJSON, // label: 'geojson', // available: false // }, // { // id: EXPORT_DATA_TYPE.TOPOJSON, // label: 'topojson', // available: false // } ]; // Export map types export const EXPORT_MAP_FORMATS = keyMirror({ HTML: null, JSON: null }); export const EXPORT_HTML_MAP_MODES = keyMirror({ READ: null, EDIT: null }); // Export map options export const EXPORT_MAP_FORMAT_OPTIONS = Object.entries(EXPORT_MAP_FORMATS).map(entry => ({ id: entry[0], label: entry[1].toLowerCase(), available: true })); export const EXPORT_HTML_MAP_MODE_OPTIONS = Object.entries(EXPORT_HTML_MAP_MODES).map(entry => ({ id: entry[0], label: `modal.exportMap.html.${entry[1].toLowerCase()}`, available: true, url: getHTMLMapModeTileUrl(entry[1]) })); export const DEFAULT_UUID_COUNT = 6; export const DEFAULT_NOTIFICATION_MESSAGE = 'MESSAGE_NOT_PROVIDED'; export const DEFAULT_NOTIFICATION_TYPES = keyMirror({ info: null, error: null, warning: null, success: null }); export const DEFAULT_NOTIFICATION_TOPICS = keyMirror({ global: null, file: null }); // Animation export const BASE_SPEED = 600; export const DEFAULT_TIME_FORMAT = 'MM/DD/YY HH:mm:ssa'; export const SPEED_CONTROL_RANGE = [0, 10]; // We could use directly react-map-gl-draw EditorMode but this would // create a direct dependency with react-map-gl-draw // Created this map to be independent from react-map-gl-draw export const EDITOR_MODES = { READ_ONLY: EditorModes.READ_ONLY, DRAW_POLYGON: EditorModes.DRAW_POLYGON, DRAW_RECTANGLE: EditorModes.DRAW_RECTANGLE, EDIT: EditorModes.EDIT_VERTEX }; export const EDITOR_AVAILABLE_LAYERS = [ LAYER_TYPES.point, LAYER_TYPES.hexagon, LAYER_TYPES.arc, LAYER_TYPES.line ]; // GPU Filtering /** * Max number of filter value buffers that deck.gl provides */ export const MAX_GPU_FILTERS = 4; export const MAP_THUMBNAIL_DIMENSION = { width: 300, height: 200 }; export const MAP_INFO_CHARACTER = { title: 100, description: 100 }; // Load data export const LOADING_METHODS = keyMirror({ upload: null, storage: null }); export const DATASET_FORMATS = keyMirror({ row: null, geojson: null, csv: null, keplergl: null });