Skip to content

Commit

Permalink
Merge pull request #439 from wearepal/kew-data
Browse files Browse the repository at this point in the history
New format Kew data on map view
  • Loading branch information
paulthatjazz authored Oct 22, 2024
2 parents 3cf45ec + 37e6b50 commit 4bb1b09
Show file tree
Hide file tree
Showing 12 changed files with 319 additions and 110 deletions.
5 changes: 4 additions & 1 deletion app/javascript/controllers/projects_controller.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export default class extends Controller {
projectTeamName: String,
projectDefraHedgerowPermission: Boolean,
projectKewRgb25cmPermission: Boolean,
projectKewSamplesPermission: Boolean,
projectExtents: Array,
backButtonPath: String,
dbModels: Object
Expand All @@ -24,6 +25,7 @@ export default class extends Controller {
declare readonly projectTeamNameValue: string
declare readonly projectDefraHedgerowPermissionValue: boolean
declare readonly projectKewRgb25cmPermissionValue: boolean
declare readonly projectKewSamplesPermissionValue: boolean
declare readonly projectExtentsValue: Array<any>
declare readonly backButtonPathValue: string
declare readonly dbModelsValue: DBModels
Expand All @@ -40,7 +42,8 @@ export default class extends Controller {
permissions={
{
DefraHedgerows: this.projectDefraHedgerowPermissionValue,
KewRgb25cm: this.projectKewRgb25cmPermissionValue
KewRgb25cm: this.projectKewRgb25cmPermissionValue,
KewSamples: this.projectKewSamplesPermissionValue
}
}
teamExtents={this.projectExtentsValue as TeamExtentData[]}
Expand Down
102 changes: 20 additions & 82 deletions app/javascript/projects/layer_palette.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +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'

interface AddLayerButtonProps {
prototype: Layer
Expand Down Expand Up @@ -101,90 +102,26 @@ export const LayerPalette = ({ addLayer, hide, dbModels, getTeamDatasets, teamNa
}}
/>
</Section> */}
<Section title="Kew Samples">
{
Array<{ name: string, location: string, metric: string, loc : string | undefined, periodOptions: KewOption[], typeOptions: KewOption[] }>(
{
name: "SSSI Woodland",
location: "kew:sssi_wood_os_bng_04m_grid",
metric: "grasses",
loc: undefined,
periodOptions: [
{ value: "Summer22", label: "Summer 22", selected: false },
{ value: "Autumn22", label: "Autumn 22", selected: false },
{ value: "Spring23", label: "Spring 23", selected: false },
{ value: "Summer23", label: "Summer 23", selected: false },
{ value: "Autumn23", label: "Autumn 23", selected: true },
],
typeOptions: [
{ value: "grasses", label: "Grass", max : 100 },
{ value: "forbs", label: "Forbs" , max : 100},
{ value: "bryos", label: "Bryos" , max : 100},
{ value: "leaf_litter", label: "Leaf Liter", max : 100 },
{ value: "bare_ground", label: "Bare Ground" , max : 100},
{ value: "canopy_cover", label: "Canopy" , max : 100},
]
},
{
name: "Young Conifer",
location: "kew:young_conifer_os_bng_04m_grid",
metric: "totalCarbon",
loc: "DenseCon",
periodOptions: [
{ value: "Sp21", label: "Spring 2022", selected: false },
{ value: "Aut21", label: "Autumn 2021", selected: false },
{ value: "Su22", label: "Summer 2022", selected: false },
{ value: "Su22_2", label: "Summer 2022 - 2" , selected: false},
{ value: "Aut22", label: "Autumn 2022", selected: false },
{ value: "Sp23", label: "Spring 2023" , selected: false},
{ value: "Su23", label: "Summer 2023", selected: true }
],
typeOptions: [
{ value: "totalCarbon", label: "Total Carbon", max : 7},
{ value: "soil_density", label: "Soil Density", max : 1200 },
{ value: "pH", label: "pH", max : 14},
{ value: "dry_matter", label: "Dry Matter", max : 100},
]
},
{
name: "Coronation Meadow",
location: "kew:coronation_meadow_os_bng_02m_grid",
metric: "totalCarbon",
loc: "Meadow",
periodOptions: [
{ value: "Sp21", label: "Spring 2022", selected: false },
{ value: "Aut21", label: "Autumn 2021", selected: false },
{ value: "Su22", label: "Summer 2022", selected: false },
{ value: "Su22_2", label: "Summer 2022 - 2" , selected: false},
{ value: "Aut22", label: "Autumn 2022", selected: false },
{ value: "Sp23", label: "Spring 2023" , selected: false},
{ value: "Su23", label: "Summer 2023", selected: true }
],
typeOptions: [
{ value: "totalCarbon", label: "Total Carbon", max : 7},
{ value: "soil_density", label: "Soil Density", max : 1200 },
{ value: "pH", label: "pH", max : 14},
{ value: "dry_matter", label: "Dry Matter", max : 100},
]
}
).map(({ name, location, metric, periodOptions, typeOptions, loc }) =>
{
permissions.KewSamples &&
<Section title="Kew Samples">
{
<AddLayerButton
addLayer={addLayer}
prototype={{
type: "KewLayer",
name,
location,
metric,
periodOptions,
typeOptions,
visible: true,
opacity: 1,
loc
}}
addLayer={addLayer}
prototype={{
type: "KewPointLayer",
name: "Wakehurst Soil",
identifier: "kew:wakehurst_soil_rp3857",
fill: "hsv",
metric: KewPointOptions[0],
metricOpts: KewPointOptions,
visible: true,
opacity: 1,
}}
/>
)
}
</Section>
}
</Section>
}
<Section title="Ancient Tree Inventory">
<AddLayerButton
addLayer={addLayer}
Expand Down Expand Up @@ -415,6 +352,7 @@ export const LayerPalette = ({ addLayer, hide, dbModels, getTeamDatasets, teamNa
</Section>
}
{
(dbModels.mapTileLayers.length > 0 || permissions.KewRgb25cm) &&
<Section title="Aerial/Satellite imagery">
{permissions.KewRgb25cm &&
<AddLayerButton
Expand Down
1 change: 1 addition & 0 deletions app/javascript/projects/project_editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export enum Tab {
export interface ProjectPermissions {
DefraHedgerows: boolean
KewRgb25cm: boolean
KewSamples: boolean
}

export interface TeamExtentData {
Expand Down
3 changes: 2 additions & 1 deletion app/javascript/projects/reify_layer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { reifyAtiLayer } from './ati'
import { reifyShapeFileLayer } from './shapefile'
import { reifyBoundaryLayer } from './boundary'
import { reifyGeoserverWMSLayer } from './geoserver'
import { reifyKewLayer } from './kew'
import { reifyKewLayer, reifyKewPointLayer } from './kew'
import { reifyOrvalLayer } from './orval'
import { reifyIMDLayer } from './imd'

Expand All @@ -37,6 +37,7 @@ export const reifyLayer = (layer: Layer, existingLayer: BaseLayer | null, dbMode
case "KewLayer": return reifyKewLayer(layer, existingLayer, map)
case "ORValLayer": return reifyOrvalLayer(layer, existingLayer, map)
case "IMDLayer": return reifyIMDLayer(layer, existingLayer, map)
case "KewPointLayer": return reifyKewPointLayer(layer, existingLayer, map)
default: {
// Ensure this switch statement is exhaustive
const unreachable: never = layerType
Expand Down
162 changes: 160 additions & 2 deletions app/javascript/projects/reify_layer/kew.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,117 @@
import BaseLayer from "ol/layer/Base"
import { KewLayer } from "../state"
import { KewLayer, KewOption, KewPointLayer } from "../state"
import VectorLayer from "ol/layer/Vector"
import { memoize } from "lodash"
import VectorSource from "ol/source/Vector"
import GeoJSON from "ol/format/GeoJSON"
import { bbox } from "ol/loadingstrategy"
import { Fill, Stroke, Style, Text } from "ol/style"
import { Fill, RegularShape, Stroke, Style, Text } from "ol/style"
import { Map, Overlay } from "ol"
import { getColorStops } from "./model_output"
import { findColor } from "../analysis_panel_tools/subsection"

export const KewPointOptions: KewOption[] = [
{
value: "carbon",
label: "Carbon",
},
{
value: "nitrogn",
label: "Nitrogen",
},
{
value: "ph",
label: "pH",
max: 14
},
{
value: "sl_dnst",
label: "Soil Density"
},
{
value: "dry_mtt",
label: "Dry Matter"
},
{
value: "sodium",
label: "Sodium"
},
{
value: "calcium",
label: "Calcium"
},
{
value: "magnesm",
label: "Magnesium"
},
{
value: "potassm",
label: "Potassium"
},
{
value: "sulphat",
label: "Sulphate"
},
{
value: "phsphrs",
label: "Phosphorus"
},
{
value: "cndctvt",
label: "cndctvt"
},
{
value: "ctn_xch",
label: "ctn_xch"
},
{
value: "sand",
label: "Sand"
},
{
value: "silt",
label: "Silt"
},
{
value: "clay",
label: "Clay"
},
{
value: "avrg_dp",
label: "Average Depth"
},
{
value: "crbn_st",
label: "Carbon Stock"
},
{
value: "cn_rati",
label: "cn_rati"
},
{
value: "np_rati",
label: "np_rati"
}
]

function getMinMaxMetric(vectorSource: VectorSource, metricKey: string) {
const features = vectorSource.getFeatures()

const metricValues = features
.map(feature => feature.getProperties()[metricKey])
.filter(value => value !== undefined && value !== null)

if (metricValues.length === 0) {
return { min: null, max: null };
}

const min = Math.min(...metricValues);
const max = Math.max(...metricValues);

return { min, max };
}

// Collection of functions that reify a KewLayer or KewPointLayer into an OpenLayers layer

const getSource = memoize((location: string) => {
const source = new VectorSource({
Expand Down Expand Up @@ -59,6 +162,41 @@ const getStyle = (layer: KewLayer, zoom: number | undefined) => (
}
)

const getPointStyle = (map: Map, layer: KewPointLayer, min: number | null, max: number | null, colMap: any[]) => (
(feature) => {

const props = feature.getProperties()
const metric = props[layer.metric.value] || -99999
const realWorldSize = 4;
const resolution = map.getView().getResolution()!
const pixelSize = realWorldSize / resolution;

let normalizedMetric = 0

if(min !== null && max !== null) {
normalizedMetric = (metric - min) / (max - min)
normalizedMetric = isNaN(normalizedMetric) || metric === -99999 ? 0 : normalizedMetric
}

const col = findColor(normalizedMetric, colMap)

return new Style({
image: new RegularShape({
points: 4,
radius: pixelSize,
angle: Math.PI / 4,
fill: new Fill({
color: `rgba(${col[0]}, ${col[1]}, ${col[2]}, ${metric === -99999 ? 0 : 1})`}
),
stroke: new Stroke({
color: 'rgba(0,0,0,1)',
width: .3 / resolution
})
})
})
}
)


export function reifyKewLayer (layer: KewLayer, existingLayer: BaseLayer | null , map: Map) {

Expand All @@ -69,4 +207,24 @@ export function reifyKewLayer (layer: KewLayer, existingLayer: BaseLayer | null

return vectLayer

}

export function reifyKewPointLayer(layer: KewPointLayer, existingLayer: BaseLayer | null, map: Map) {

const vectorSource = getSource(layer.identifier)

const { min, max } = layer.metric.max ? {min: 0, max: layer.metric.max} : getMinMaxMetric(vectorSource, layer.metric.value)

const colMap = getColorStops(layer.fill, 100).reverse()

layer.min = min ?? 0
layer.max = max ?? 0

const v = new VectorLayer({
source: vectorSource,
style: getPointStyle(map, layer, min, max, colMap)
})

return v

}
Loading

0 comments on commit 4bb1b09

Please sign in to comment.