terriajs
Version:
Geospatial data visualization platform.
161 lines • 7.73 kB
JavaScript
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