UNPKG

terriajs

Version:

Geospatial data visualization platform.

219 lines (191 loc) 7.14 kB
import { VectorTileFeature } from "@mapbox/vector-tile"; import { action, runInAction } from "mobx"; import Color from "terriajs-cesium/Source/Core/Color"; import JulianDate from "terriajs-cesium/Source/Core/JulianDate"; import Rectangle from "terriajs-cesium/Source/Core/Rectangle"; import TimeInterval from "terriajs-cesium/Source/Core/TimeInterval"; import ImageryLayerFeatureInfo from "terriajs-cesium/Source/Scene/ImageryLayerFeatureInfo"; import ImageryProvider from "terriajs-cesium/Source/Scene/ImageryProvider"; import isDefined from "../Core/isDefined"; import { isJsonNumber } from "../Core/Json"; import MapboxVectorTileImageryProvider from "../Map/ImageryProvider/MapboxVectorTileImageryProvider"; import { TerriaFeatureData } from "../Models/Feature/FeatureData"; import TableStyle from "./TableStyle"; import { isConstantStyleMap } from "./TableStyleMap"; export default function createRegionMappedImageryProvider( style: TableStyle, currentTime: JulianDate | undefined ): ImageryProvider | undefined { if (!style.isRegions()) { return undefined; } const regionColumn = style.regionColumn; const regionType = regionColumn.regionType; if (regionType === undefined) { return undefined; } 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: number[] | undefined; // If time varying, get row indices which match if (currentTime && style.timeIntervals && style.moreThanOneTimeInterval) { currentTimeRows = style.timeIntervals.reduce<number[]>( (rows, timeInterval, index) => { if (timeInterval && TimeInterval.contains(timeInterval, currentTime!)) { rows.push(index); } return rows; }, [] ); } return new MapboxVectorTileImageryProvider({ url: regionType.server, layerName: regionType.layerName, styleFunc: function (feature: any) { const regionId = feature.properties[regionType.uniqueIdProp]; let rowNumber = getImageryLayerFilteredRow( style, currentTimeRows, valuesAsRegions.regionIdToRowNumbersMap.get(regionId) ); let value: string | number | null = isDefined(rowNumber) ? valueFunction(rowNumber) : null; const color = colorMap.mapValueToColor(value); if (color === undefined) { return undefined; } const outlineStyle = isConstantStyleMap(outlineStyleMap) ? outlineStyleMap.style : outlineStyleMap.mapValueToStyle(rowNumber ?? -1); const outlineColorValue = Color.fromCssColorString( outlineStyle.color ?? runInAction(() => style.tableModel.terria.baseMapContrastColor) ); return { fillStyle: color.toCssColorString(), strokeStyle: value !== null ? outlineColorValue.toCssColorString() : "transparent", lineWidth: outlineStyle.width ?? 1, lineJoin: "miter" }; }, subdomains: regionType.serverSubdomains, rectangle: Array.isArray(regionType.bbox) && regionType.bbox.length >= 4 ? Rectangle.fromDegrees( regionType.bbox[0], regionType.bbox[1], regionType.bbox[2], regionType.bbox[3] ) : undefined, minimumZoom: regionType.serverMinZoom, maximumNativeZoom: regionType.serverMaxNativeZoom, maximumZoom: regionType.serverMaxZoom, uniqueIdProp: regionType.uniqueIdProp, featureInfoFunc: (feature: any) => getImageryLayerFeatureInfo(style, feature, currentTimeRows) }); } /** * Filters row numbers by time (if applicable) - for a given region mapped ImageryLayer */ const getImageryLayerFilteredRow = action( ( style: TableStyle, currentTimeRows: number[] | undefined, rowNumbers: number | readonly number[] | undefined ): number | undefined => { if (!isDefined(rowNumbers)) return; if (!isDefined(currentTimeRows)) { return Array.isArray(rowNumbers) ? rowNumbers[0] : rowNumbers; } if ( typeof rowNumbers === "number" && currentTimeRows.includes(rowNumbers) ) { return rowNumbers; } else if (Array.isArray(rowNumbers)) { const matchingTimeRows: number[] = 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 = action( ( style: TableStyle, feature: VectorTileFeature, currentTimeRows: number[] | undefined ) => { 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; style.tableModel.tableColumns; const rowObject = style.tableModel.tableColumns.reduce<{ [key: string]: string | number | null; }>((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 ); const featureInfo = new ImageryLayerFeatureInfo(); if (isDefined(regionType.nameProp)) { featureInfo.name = featureData[regionType.nameProp] as string; } featureData.id = feature.properties[regionType.uniqueIdProp]; featureInfo.properties = featureData; const terriaFeatureData: TerriaFeatureData = { rowIds: isJsonNumber(regionRows) ? [regionRows] : [...regionRows], type: "terriaFeatureData" }; featureInfo.data = terriaFeatureData; featureInfo.configureDescriptionFromProperties(featureData); featureInfo.configureNameFromProperties(featureData); return featureInfo; } return undefined; } );