From 1fc82acfd3bd3574144d82c1cc4bdcf27c769d2f Mon Sep 17 00:00:00 2001 From: Paul Crossley Date: Thu, 9 Jan 2025 16:31:04 +0000 Subject: [PATCH] Kew tree crown data - map view --- app/javascript/projects/layer_palette.tsx | 10 ++- .../projects/modelling/components/index.ts | 2 +- app/javascript/projects/reify_layer/kew.ts | 72 ++++++++++++++++--- app/javascript/projects/sidebar.tsx | 58 ++++++++++++++- app/javascript/projects/state.ts | 9 +++ 5 files changed, 133 insertions(+), 18 deletions(-) diff --git a/app/javascript/projects/layer_palette.tsx b/app/javascript/projects/layer_palette.tsx index 878a19f..f7961dc 100644 --- a/app/javascript/projects/layer_palette.tsx +++ b/app/javascript/projects/layer_palette.tsx @@ -7,7 +7,7 @@ import { CompiledDatasetRecord } from './saved_dataset' import { designations } from './modelling/designations' import { IMDProperties } from './reify_layer/imd' import { ProjectPermissions } from './project_editor' -import { KewPointOptions } from './reify_layer/kew' +import { KewPointOptions, KewTreeCrownOptions } from './reify_layer/kew' import { seasonYearOptions } from './modelling/components/kew_samples_component' import { natmap_outputs } from './modelling/components/natmap_soil_component' @@ -131,7 +131,9 @@ export const LayerPalette = ({ addLayer, hide, dbModels, getTeamDatasets, teamNa identifier: "kew:bl_crown_3857", visible: true, opacity: 1, - color: { stroke: [160,82,45, 1], fill: [160,82,45, 1] } + color: { stroke: [160,82,45, 1], fill: [160,82,45, 1] }, + fill: "jet", + metric: KewTreeCrownOptions[0] }} /> } @@ -144,7 +146,9 @@ export const LayerPalette = ({ addLayer, hide, dbModels, getTeamDatasets, teamNa identifier: "kew:conifer_crown_3857", visible: true, opacity: 1, - color: { stroke: [34,139,34, 1], fill: [34,139,34, 1] } + color: { stroke: [34,139,34, 1], fill: [34,139,34, 1] }, + fill: "jet", + metric: KewTreeCrownOptions[0] }} /> } diff --git a/app/javascript/projects/modelling/components/index.ts b/app/javascript/projects/modelling/components/index.ts index 5fa036c..9e2de71 100644 --- a/app/javascript/projects/modelling/components/index.ts +++ b/app/javascript/projects/modelling/components/index.ts @@ -64,7 +64,7 @@ export function createDefaultComponents(saveMapLayer: SaveMapLayer, saveModel: S if (permissions.DefraHedgerows) restrictedComponents.push(new HedgerowComponent(projectProps)) if (permissions.KewSamples) { restrictedComponents.push(new KewSamplesComponent(projectProps)) - restrictedComponents.push(new KewHabsComponent(projectProps)) + //restrictedComponents.push(new KewHabsComponent(projectProps)) restrictedComponents.push(new KewTreesComponent(projectProps)) } if (permissions.NATMAPSoil) restrictedComponents.push(new NatmapSoilComponent(projectProps)) diff --git a/app/javascript/projects/reify_layer/kew.ts b/app/javascript/projects/reify_layer/kew.ts index 67ba44e..25891e6 100644 --- a/app/javascript/projects/reify_layer/kew.ts +++ b/app/javascript/projects/reify_layer/kew.ts @@ -1,7 +1,7 @@ import BaseLayer from "ol/layer/Base" import { KewLayer, KewOption, KewPointLayer, KewShapeLayer } from "../state" import VectorLayer from "ol/layer/Vector" -import { memoize } from "lodash" +import { get, memoize } from "lodash" import VectorSource from "ol/source/Vector" import GeoJSON from "ol/format/GeoJSON" import { bbox } from "ol/loadingstrategy" @@ -11,6 +11,41 @@ import { getColorStops } from "./model_output" import { findColor } from "../analysis_panel_tools/subsection" import { numericDataSocket } from "../modelling/socket_types" +export const KewTreeCrownOptions: any[] = [ + { + name: "none", + label: "None" + }, + { + name: "height", + label: "Height" + }, + { + name: "npoints", + label: "N Points" + }, + { + name: "canpy_d", + label: "Canopy Diameter" + }, + { + name: "jckr_gb", + label: "AGB (Jucker)" + }, + { + name: "wake_gb", + label: "AGB (Wakehurst)" + }, + { + name: "p25", + label: "P 25" + }, + { + name: "p75", + label: "P 75" + } +] + export const KewPointOptions: KewOption[] = [ { value: "carbon", @@ -216,6 +251,22 @@ const getPointStyle = (map: Map, layer: KewPointLayer, min: number | null, max: } ) +const getShapeStyle = (layer: KewShapeLayer, min: number, max: number, col: [r: number, g: number, b: number, a: number], colMap: any[]) => ( + (feature) => { + const metric = layer.metric.name !== "none" ? feature.getProperties()[layer.metric.name] : -99999 + const c = metric !== -99999 ? findColor((metric - min) / (max - min), colMap) : col + + return new Style({ + stroke: new Stroke({ + color: `rgba(0, 0, 0, .6)`, + width: .2 + }), + fill: new Fill({ + color: `rgba(${c[0]}, ${c[1]}, ${c[2]}, 1)` + }) + }) + } +) export function reifyKewLayer (layer: KewLayer, existingLayer: BaseLayer | null , map: Map) { @@ -335,17 +386,16 @@ export function reifyKewPointLayer(layer: KewPointLayer, existingLayer: BaseLaye export function reifyKewShapeLayer(layer: KewShapeLayer, existingLayer: BaseLayer | null, map: Map) { const col = layer.color.fill + const colMap = getColorStops(layer.fill, 100).reverse() + const vectorSource = getSource(layer.identifier) + const { min, max } = layer.metric.name !== "none" ? getMinMaxMetric(vectorSource, layer.metric.name) : { min: 0, max: 100 } + + // update sidebar + layer.min = min ?? 0 + layer.max = max ?? 0 return new VectorLayer({ - source: getSource(layer.identifier), - style: new Style({ - stroke: new Stroke({ - color: `rgba(0, 0, 0, 1)`, - width: 1 - }), - fill: new Fill({ - color: `rgba(${col[0]}, ${col[1]}, ${col[2]}, 1)` - }) - }) + source: vectorSource, + style: getShapeStyle(layer, min!, max!, col, colMap) }) } \ No newline at end of file diff --git a/app/javascript/projects/sidebar.tsx b/app/javascript/projects/sidebar.tsx index a542ebe..e37b172 100644 --- a/app/javascript/projects/sidebar.tsx +++ b/app/javascript/projects/sidebar.tsx @@ -2,12 +2,12 @@ import * as React from 'react' import './sidebar.css' import { ReactSortable } from 'react-sortablejs' import { nevoLevelNames, nevoPropertyNames } from './nevo' -import { AtiLayer, CropMapLayer, DatasetLayer, IMDLayer, KewLayer, KewPointLayer, Layer, ModelOutputLayer, NevoLayer, OverlayLayer, ShapeLayer, State, WFSLayer } from './state' +import { AtiLayer, CropMapLayer, DatasetLayer, IMDLayer, KewLayer, KewPointLayer, KewShapeLayer, Layer, ModelOutputLayer, NevoLayer, OverlayLayer, ShapeLayer, State, WFSLayer } from './state' import { iconForLayerType } from "./util" import { getColorStops } from './reify_layer/model_output' import { TileGridProps, tileGridStats } from './modelling/tile_grid' import { IMDProperties } from './reify_layer/imd' -import { KewPointOptions } from './reify_layer/kew' +import { KewPointOptions, KewTreeCrownOptions } from './reify_layer/kew' import { natmap_outputs } from './modelling/components/natmap_soil_component' const colMapList = <> @@ -317,6 +317,11 @@ interface KewPointLayerSettingsProps { mutate: (data: any) => void } +interface KewShapeLayerSettingsProps { + layer: KewShapeLayer + mutate: (data: any) => void +} + const KewPointLayerSettings = ({ layer, mutate }: KewPointLayerSettingsProps) => { return ( @@ -395,6 +400,33 @@ const KewLayerSettings = ({ layer, mutate }: KewLayerSettingsProps) => { ) } +const KewShapeLayerSettings = ({ layer, mutate }: KewShapeLayerSettingsProps) => { + return <> +
+ Property + +
+ { + layer.metric.name != "none" && +
+ Fill + +
+ } + +} + export function ZoomData({zoom, area, length, units}) { const unit = area < 1 ? "cm²" : (area > 1000000 ? "km²" : "m²") length = area < 1 ? length * 100 : (area > 1000000 ? length / 1000 : length) @@ -841,7 +873,7 @@ export const Sidebar = ({ state, selectLayer, mutateLayer, deleteLayer, setLayer { - (selectedLayer?.type == "ModelOutputLayer" || selectedLayer?.type == "DatasetLayer" || selectedLayer?.type == "IMDLayer" || selectedLayer?.type == "KewPointLayer" || selectedLayer?.type == "WFSLayer") && + (selectedLayer?.type == "ModelOutputLayer" || selectedLayer?.type == "DatasetLayer" || selectedLayer?.type == "IMDLayer" || selectedLayer?.type == "KewPointLayer" || selectedLayer?.type == "WFSLayer" || (selectedLayer?.type == "KewShapeLayer" && selectedLayer.metric.name !== "none")) && (
Layer legend @@ -893,6 +925,16 @@ export const Sidebar = ({ state, selectLayer, mutateLayer, deleteLayer, setLayer toggle={isLegCollapsed} /> } + { + selectedLayer?.type == "KewShapeLayer" && + selectedLayer.metric.name !== "none" && + + }
Layer settings
{ @@ -993,6 +1035,16 @@ export const Sidebar = ({ state, selectLayer, mutateLayer, deleteLayer, setLayer } /> } + { + selectedLayer?.type == "KewShapeLayer" && + state.selectedLayer !== undefined && + mutateLayer(state.selectedLayer, data) + } + /> + } { selectedLayer?.type == "ORValLayer" && <> diff --git a/app/javascript/projects/state.ts b/app/javascript/projects/state.ts index 537cc5b..0c802e4 100644 --- a/app/javascript/projects/state.ts +++ b/app/javascript/projects/state.ts @@ -39,6 +39,11 @@ export interface KewOption { socket?: Socket } +export interface KewCrownOption { + name: string + label: string +} + type fillType = "greyscale" | "heatmap" | "jet" | "hsv" | "hot" | "cool" | "spring" | "summer" | "autumn" | "winter" | "copper" | "WIGnBu" | "greens" | "YIOrRd" | "bluered" | "RdBu" | "picnic" | "rainbow" | "portland" | "blackbody" | "earth" | "electric" | "viridis" | "inferno" | "magma" | "plasma" | "warm" | "cool" | "rainbow-soft" | "bathymetry" | "cdom" | "chlorophyll" | "density" | "freesurface-blue" | "freesurface-red" | "oxygen" | "par" | "phase" | "salinity" | "temperature" | "turbidity" | "velocity-blue" | "velocity-green" | "cubehelix" export interface OsmLayer extends BaseLayer { @@ -99,6 +104,10 @@ export interface KewShapeLayer extends BaseLayer { type: "KewShapeLayer" identifier: string color: StrokeFill + fill: fillType + metric: KewCrownOption + min?: number + max?: number } export interface NevoLayer extends BaseLayer {