UNPKG

terriajs

Version:

Geospatial data visualization platform.

161 lines 7.73 kB
import { runInAction } from "mobx"; import { CircleSymbolizer, GeomType, LineSymbolizer, PolygonSymbolizer } from "protomaps-leaflet"; import TimeInterval from "terriajs-cesium/Source/Core/TimeInterval"; import { isJsonNumber } from "../../../Core/Json"; import { FEATURE_ID_PROP, getStyleReactiveDependencies } from "../../../ModelMixins/GeojsonMixin"; import { isConstantStyleMap } from "../../../Table/TableStyleMap"; import { GEOJSON_SOURCE_LAYER_NAME } from "./ProtomapsGeojsonSource"; /** Helper function to create a function which can be used to generate styles for a given rowID. This ensures that nothing is read outside reactive context */ function getStyleValueFn(tableStyleMap, trait, defaultValue, useRowId = true, transformValue) { const styleMap = runInAction(() => tableStyleMap.styleMap); if (isConstantStyleMap(styleMap)) { return () => { const value = styleMap.style[trait]; if (value === undefined) { return defaultValue; } return transformValue ? transformValue(value) : value; }; } if (useRowId) { return (_, f) => { const rowId = isJsonNumber(f?.props[FEATURE_ID_PROP]) ? f?.props[FEATURE_ID_PROP] : -1; const value = styleMap.mapRowIdToStyle(rowId)[trait]; if (value === undefined) { return defaultValue; } return transformValue ? transformValue(value) : value; }; } const columnName = runInAction(() => tableStyleMap.traits.column); return (_, f) => { const value = styleMap.mapValueToStyle(f?.props[columnName ?? ""])[trait]; if (value === undefined) { return defaultValue; } return transformValue ? transformValue(value) : value; }; } /** Convert TableStyle to protomaps paint/label rules. * It currently supports * - ColorMap (for fill/stroke) * - OutlineStyleMap (color, width and dash) * - PointStyleMap (fill, stroke, radius - which uses height) */ export function tableStyleToProtomaps(catalogItem, useRowIds = true, includePointSymbols = false) { let currentTimeRows; // Access all dependencies so reactivity works getStyleReactiveDependencies(catalogItem); // If time varying, get row indices which match // This is used to filter feature[FEATURE_ID_PROP] if (catalogItem.currentTimeAsJulianDate && catalogItem.activeTableStyle.timeIntervals && catalogItem.activeTableStyle.moreThanOneTimeInterval) { currentTimeRows = catalogItem.activeTableStyle.timeIntervals.reduce((rows, timeInterval, index) => { if (timeInterval && TimeInterval.contains(timeInterval, catalogItem.currentTimeAsJulianDate)) { rows.push(index); } return rows; }, []); } // NOTE: Make sure we don't access computed properties in style functions, as they will be re-computed for every feature const colorValueFunction = runInAction(() => catalogItem.activeTableStyle.colorColumn?.valueFunctionForType); const colorColumnName = runInAction(() => catalogItem.activeTableStyle.colorTraits.colorColumn); const colorMap = runInAction(() => catalogItem.activeTableStyle.colorMap); // Style function const getColorValue = (_z, f) => { let value; if (useRowIds) { const rowId = isJsonNumber(f?.props[FEATURE_ID_PROP]) ? f?.props[FEATURE_ID_PROP] : -1; value = colorValueFunction?.(rowId ?? -1); } else { value = colorColumnName ? f?.props[colorColumnName] : undefined; } return colorMap.mapValueToColor(value).toCssColorString(); }; const useOutlineColorForLineFeatures = catalogItem.useOutlineColorForLineFeatures; const getOutlineWidthValue = getStyleValueFn(catalogItem.activeTableStyle.outlineStyleMap, "width", catalogItem.defaultStyles.polygonStrokeWidth, useRowIds); const getOutlineColorValue = getStyleValueFn(catalogItem.activeTableStyle.outlineStyleMap, "color", catalogItem.terria.baseMapContrastColor, useRowIds); const getOutlineDashValue = getStyleValueFn(catalogItem.activeTableStyle.outlineStyleMap, "dash", [], useRowIds); const getPointRadiusValue = getStyleValueFn(catalogItem.activeTableStyle.pointStyleMap, "height", 2, useRowIds, (v) => (3 * v) / 8 // Divide height by 2 to get radius, and then reduce by 25% to make it look better (most of the time protomaps image tiles are scaled up) ); // Filter features by time if applicable const showFeature = (_z, f) => !currentTimeRows || (isJsonNumber(f?.props[FEATURE_ID_PROP]) && currentTimeRows.includes(f?.props[FEATURE_ID_PROP])); return { currentTimeRows, labelRules: [], paintRules: [ // Polygon features { dataLayer: GEOJSON_SOURCE_LAYER_NAME, symbolizer: new PolygonSymbolizer({ fill: getColorValue, stroke: getOutlineColorValue, width: getOutlineWidthValue }), minzoom: 0, maxzoom: Infinity, filter: (zoom, feature) => { return (feature?.geomType === GeomType.Polygon && showFeature(zoom, feature)); } }, // Line features // Note - line color will use TableColorStyleTraits by default. // If useOutlineColorForLineFeatures is true, then line color will use TableOutlineStyle traits { dataLayer: GEOJSON_SOURCE_LAYER_NAME, symbolizer: new LineSymbolizer({ color: useOutlineColorForLineFeatures ? getOutlineColorValue : getColorValue, width: getOutlineWidthValue, dash: getOutlineDashValue, // Incorrect type upstream dashColor: useOutlineColorForLineFeatures ? getOutlineColorValue : getColorValue, dashWidth: getOutlineWidthValue }), minzoom: 0, maxzoom: Infinity, filter: (zoom, feature) => { return (feature?.geomType === GeomType.Line && showFeature(zoom, feature)); } }, ...(includePointSymbols ? [ { dataLayer: GEOJSON_SOURCE_LAYER_NAME, symbolizer: new CircleSymbolizer({ fill: getColorValue, stroke: getOutlineColorValue, width: getOutlineWidthValue, radius: getPointRadiusValue }), minzoom: 0, maxzoom: Infinity, filter: (_, f) => f.geomType === GeomType.Point } ] : []) ] }; } /** Returns true if any styles have point (or label) styles that aren't supported by protomaps */ export function hasUnsupportedStylesForProtomaps(catalogItem) { return catalogItem.styles.some((style) => { return ([...style.point.enum, ...style.point.bin, style.point.null].some((pointStyle) => pointStyle.marker && pointStyle.marker !== "circle" && pointStyle.marker !== "point") || style.label.enabled || style.trail.enabled); }); } //# sourceMappingURL=tableStyleToProtomaps.js.map