UNPKG

@gooddata/react-components

Version:

GoodData.UI - A powerful JavaScript library for building analytical applications

161 lines (148 loc) • 5.83 kB
// (C) 2019-2020 GoodData Corporation import partial = require("lodash/partial"); import mapboxgl from "mapbox-gl"; import { DEFAULT_CLUSTER_FILTER, DEFAULT_CLUSTER_LABELS_CONFIG, DEFAULT_CLUSTER_LAYER_NAME, DEFAULT_CLUSTER_POINT_BORDERS, DEFAULT_CLUSTER_POINT_COLORS, DEFAULT_CLUSTER_POINT_SIZES, DEFAULT_LAYER_NAME, DEFAULT_PUSHPIN_BORDER_COLOR_VALUE, DEFAULT_PUSHPIN_OPTIONS, PUSHPIN_STYLE_CIRCLE, PUSHPIN_STYLE_CIRCLE_COLOR, PUSHPIN_STYLE_CIRCLE_SIZE, PUSHPIN_STYLE_CIRCLE_STROKE_COLOR, EMPTY_SEGMENT_VALUE, PUSHPIN_SIZE_OPTIONS_MAP, } from "../../../constants/geoChart"; import { IGeoData, IGeoPointsConfig, IGeoConfig } from "../../../interfaces/GeoChart"; import { SEGMENT } from "../../../constants/bucketNames"; import { getMinMax } from "../../../helpers/utils"; function createPushpinSizeOptions( geoData: IGeoData, geoPointsConfig: IGeoPointsConfig, ): mapboxgl.Expression | number { const { size } = geoData; const hasSize = size !== undefined; const defaultRadius = PUSHPIN_SIZE_OPTIONS_MAP.min.default / 2; if (!hasSize || size.data.length === 0) { return defaultRadius; } const { min: minSizeFromData, max: maxSizeFromData } = getMinMax(size.data); if (maxSizeFromData === minSizeFromData) { return defaultRadius; } const { minSize: minSizeFromConfig = "default", maxSize: maxSizeFromConfig = "default" } = geoPointsConfig || {}; const minSizeInPixel = PUSHPIN_SIZE_OPTIONS_MAP.min[minSizeFromConfig]; const maxSizeInPixel = PUSHPIN_SIZE_OPTIONS_MAP.max[maxSizeFromConfig]; const base = Math.pow(maxSizeInPixel / minSizeInPixel, 0.25); const getStopPointSize = partial(getPointSize, minSizeInPixel, base); // The mouseenter event uses queryRenderedFeatures to determine when the mouse is touching a feature // And queryRenderedFeatures is not supporting nested object in expression // https://github.com/mapbox/mapbox-gl-js/issues/7194 return [ "step", ["get", "pushpinSize"], Math.round(minSizeInPixel / 2), // a getStopPointSize(1), Math.round(getStopPointSize(1) / 2), // ar^1 getStopPointSize(2), Math.round(getStopPointSize(2) / 2), // ar^2 getStopPointSize(3), Math.round(getStopPointSize(3) / 2), // ar^3 maxSizeInPixel, Math.round(maxSizeInPixel / 2), // ar^4 ]; } function getPointSize(minSizeInPixel: number, base: number, exponent: number): number { const stepValue = minSizeInPixel * Math.pow(base, exponent); return parseFloat(stepValue.toFixed(2)); } export function createPushpinFilter(selectedSegmentItems: string[]): mapboxgl.Expression { return [ "match", ["get", "uri", ["object", ["get", SEGMENT]]], selectedSegmentItems.length ? selectedSegmentItems : [EMPTY_SEGMENT_VALUE], true, false, ]; // true/false are the output values, from the https://docs.mapbox.com/mapbox-gl-js/style-spec/expressions/#match } function createPushpinColorOptions(): mapboxgl.Expression { return ["string", ["get", "background", ["object", ["get", "color"]]]]; } function createPushpinBorderOptions(): mapboxgl.Expression { return ["string", ["get", "border", ["object", ["get", "color"]]], DEFAULT_PUSHPIN_BORDER_COLOR_VALUE]; } export function createPushpinDataLayer( dataSourceName: string, geoData: IGeoData, config: IGeoConfig, ): mapboxgl.Layer { const { selectedSegmentItems = [], points: geoPointsConfig = {} } = config || {}; const layer: mapboxgl.Layer = { id: DEFAULT_LAYER_NAME, type: PUSHPIN_STYLE_CIRCLE, source: dataSourceName, paint: { ...DEFAULT_PUSHPIN_OPTIONS, [PUSHPIN_STYLE_CIRCLE_COLOR]: createPushpinColorOptions(), [PUSHPIN_STYLE_CIRCLE_STROKE_COLOR]: createPushpinBorderOptions(), [PUSHPIN_STYLE_CIRCLE_SIZE]: createPushpinSizeOptions(geoData, geoPointsConfig), }, }; if (selectedSegmentItems.length > 0) { layer.filter = createPushpinFilter(selectedSegmentItems); } return layer; } /** * Create layer for clustered points/pins which have 'properties.point_count' indicates number of same points is clustered together * @param dataSourceName */ export function createClusterPoints(dataSourceName: string): mapboxgl.Layer { return { id: DEFAULT_CLUSTER_LAYER_NAME, type: PUSHPIN_STYLE_CIRCLE, source: dataSourceName, filter: DEFAULT_CLUSTER_FILTER, paint: { ...DEFAULT_CLUSTER_POINT_BORDERS, [PUSHPIN_STYLE_CIRCLE_COLOR]: DEFAULT_CLUSTER_POINT_COLORS, [PUSHPIN_STYLE_CIRCLE_SIZE]: DEFAULT_CLUSTER_POINT_SIZES, }, }; } /** * Create layer for cluster labels which indicate number of points/pins is clustered * @param dataSourceName */ export function createClusterLabels(dataSourceName: string): mapboxgl.Layer { return { ...DEFAULT_CLUSTER_LABELS_CONFIG, source: dataSourceName, filter: DEFAULT_CLUSTER_FILTER, }; } /** * Create layer for un-clustered points which are not close to others * @param dataSourceName * @param selectedSegmentItems */ export function createUnclusterPoints(dataSourceName: string): mapboxgl.Layer { return { id: DEFAULT_LAYER_NAME, type: PUSHPIN_STYLE_CIRCLE, source: dataSourceName, filter: ["!", DEFAULT_CLUSTER_FILTER], paint: { ...DEFAULT_PUSHPIN_OPTIONS, [PUSHPIN_STYLE_CIRCLE_COLOR]: createPushpinColorOptions(), [PUSHPIN_STYLE_CIRCLE_STROKE_COLOR]: DEFAULT_PUSHPIN_BORDER_COLOR_VALUE, [PUSHPIN_STYLE_CIRCLE_SIZE]: PUSHPIN_SIZE_OPTIONS_MAP.min.default / 2, }, }; }