terriajs
Version:
Geospatial data visualization platform.
168 lines • 7.61 kB
JavaScript
import { action, runInAction } from "mobx";
import { GeomType, PolygonSymbolizer } from "protomaps-leaflet";
import JulianDate from "terriajs-cesium/Source/Core/JulianDate";
import TimeInterval from "terriajs-cesium/Source/Core/TimeInterval";
import isDefined from "../Core/isDefined";
import { isJsonNumber } from "../Core/Json";
import ProtomapsImageryProvider from "../Map/ImageryProvider/ProtomapsImageryProvider";
import { isConstantStyleMap } from "./TableStyleMap";
import isReadOnlyArray from "../Core/isReadOnlyArray";
export default function createRegionMappedImageryProvider(style, currentTime) {
if (!style.isRegions()) {
return undefined;
}
const regionColumn = style.regionColumn;
const regionType = regionColumn.regionType;
if (regionType === undefined) {
return undefined;
}
const terria = style.tableModel.terria;
const colorColumn = style.colorColumn;
const valueFunction = colorColumn !== undefined ? colorColumn.valueFunctionForType : () => null;
const colorMap = style.colorMap;
const valuesAsRegions = regionColumn.valuesAsRegions;
const outlineStyleMap = style.outlineStyleMap.styleMap;
let currentTimeRows;
// If time varying, get row indices which match
if (currentTime && style.timeIntervals && style.moreThanOneTimeInterval) {
currentTimeRows = style.timeIntervals.reduce((rows, timeInterval, index) => {
if (timeInterval && TimeInterval.contains(timeInterval, currentTime)) {
rows.push(index);
}
return rows;
}, []);
}
const getRowNumber = (_zoom, f) => {
const regionId = f?.props[regionType.uniqueIdProp];
return isJsonNumber(regionId)
? getImageryLayerFilteredRow(style, currentTimeRows, valuesAsRegions.regionIdToRowNumbersMap.get(regionId))
: undefined;
};
const showFeature = (zoom, f) => isDefined(getRowNumber(zoom, f));
const getColorValue = (zoom, f) => {
const rowNumber = getRowNumber(zoom, f);
return colorMap
.mapValueToColor(isDefined(rowNumber) ? valueFunction(rowNumber) : null)
.toCssColorString();
};
const getOutlineColorValue = (zoom, f) => {
const defaultOutlineColor = runInAction(() => terria.baseMapContrastColor);
const rowNumber = getRowNumber(zoom, f);
if (!isDefined(rowNumber))
return defaultOutlineColor;
return ((isConstantStyleMap(outlineStyleMap)
? outlineStyleMap.style.color
: outlineStyleMap.mapRowIdToStyle(rowNumber ?? -1).color) ??
defaultOutlineColor);
};
const getOutlineWidthValue = (zoom, f) => {
const rowNumber = getRowNumber(zoom, f);
if (!isDefined(rowNumber))
return 1;
return ((isConstantStyleMap(outlineStyleMap)
? outlineStyleMap.style.width
: outlineStyleMap.mapRowIdToStyle(rowNumber ?? -1).width) ?? 1);
};
return new ProtomapsImageryProvider({
terria,
// Use the URL as the id, this is needed for backward compatibility with MapboxImageryProvider, for when picking features (as it uses the URL as the id)
id: regionType.server,
data: regionType.server,
minimumZoom: regionType.serverMinZoom,
maximumNativeZoom: regionType.serverMaxNativeZoom,
maximumZoom: regionType.serverMaxZoom,
idProperty: regionType.uniqueIdProp,
paintRules: [
// Polygon features
{
dataLayer: regionType.layerName,
symbolizer: new PolygonSymbolizer({
fill: getColorValue,
stroke: getOutlineColorValue,
width: getOutlineWidthValue
}),
minzoom: 0,
maxzoom: Infinity,
filter: (zoom, feature) => {
return (feature?.geomType === GeomType.Polygon && showFeature(zoom, feature));
}
}
],
labelRules: [],
processPickedFeatures: async (features) => features
.map((feature) => getImageryLayerFeatureInfo(style, feature, currentTimeRows))
.filter(isDefined)
});
}
/**
* Filters row numbers by time (if applicable) - for a given region mapped ImageryLayer
*/
const getImageryLayerFilteredRow = action((style, currentTimeRows, rowNumbers) => {
if (!isDefined(rowNumbers))
return;
if (!isDefined(currentTimeRows)) {
return isReadOnlyArray(rowNumbers) ? rowNumbers[0] : rowNumbers;
}
if (typeof rowNumbers === "number" &&
currentTimeRows.includes(rowNumbers)) {
return rowNumbers;
}
else if (isReadOnlyArray(rowNumbers)) {
const matchingTimeRows = rowNumbers.filter((row) => currentTimeRows.includes(row));
if (matchingTimeRows.length <= 1) {
return matchingTimeRows[0];
}
// In a time-varying dataset, intervals may
// overlap at their endpoints (i.e. the end of one interval is the start of the next).
// In that case, we want the later interval to apply.
return matchingTimeRows.reduce((latestRow, currentRow) => {
const currentInterval = style.timeIntervals?.[currentRow]?.stop;
const latestInterval = style.timeIntervals?.[latestRow]?.stop;
if (currentInterval &&
latestInterval &&
JulianDate.lessThan(latestInterval, currentInterval)) {
return currentRow;
}
return latestRow;
}, matchingTimeRows[0]);
}
});
/**
* Get ImageryLayerFeatureInfo for a given ImageryLayer input and feature.
*/
const getImageryLayerFeatureInfo = (style, feature, currentTimeRows) => {
if (isDefined(style.regionColumn?.regionType?.uniqueIdProp)) {
const regionType = style.regionColumn.regionType;
const regionRows = style.regionColumn.valuesAsRegions.regionIdToRowNumbersMap.get(feature.properties[regionType.uniqueIdProp]) ?? [];
const rowId = getImageryLayerFilteredRow(style, currentTimeRows, regionRows);
if (!isDefined(rowId))
return;
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
style.tableModel.tableColumns;
const rowObject = style.tableModel.tableColumns.reduce((obj, column) => {
obj[column.name] = column.valueFunctionForType(rowId);
return obj;
}, {});
// Preserve values from d and insert feature properties after entries from d
const featureData = Object.assign({}, rowObject, feature.properties, rowObject);
if (isDefined(regionType.nameProp)) {
feature.name = featureData[regionType.nameProp];
}
featureData.id = feature.properties[regionType.uniqueIdProp];
feature.properties = featureData;
const terriaFeatureData = {
rowIds: isJsonNumber(regionRows) ? [regionRows] : [...regionRows],
type: "terriaFeatureData"
};
feature.data = terriaFeatureData;
feature.configureDescriptionFromProperties(featureData);
feature.configureNameFromProperties(featureData);
// Set name of feature to region name if it exists
if (isDefined(regionType.nameProp) &&
typeof featureData[regionType.nameProp] === "string")
feature.name = featureData[regionType.nameProp];
return feature;
}
return undefined;
};
//# sourceMappingURL=createRegionMappedImageryProvider.js.map