Skip to content

Commit

Permalink
Merge pull request #323 from wearepal/init-ml
Browse files Browse the repository at this point in the history
Add ML output as model layer
  • Loading branch information
paulthatjazz authored Jan 31, 2024
2 parents 0b48c46 + a7b187d commit daba670
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 31 deletions.
1 change: 1 addition & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ GEM

PLATFORMS
arm64-darwin-21
arm64-darwin-23
x86_64-darwin-21
x86_64-linux

Expand Down
62 changes: 31 additions & 31 deletions app/javascript/projects/layer_palette.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,23 +104,23 @@ export const LayerPalette = ({ addLayer, hide, dbModels, getTeamDatasets, teamNa
<Section title="Designations">
{
Array<{ name: string, identifier: string, fill: [number, number, number, number], stroke: [number, number, number, number] }>(
{
name: "Areas of Outstanding Natural Beauty (AONB)",
identifier: "shapefiles:AONB_UK",
fill: [0, 155, 0, 1],
stroke: [0, 0, 0, 1]
{
name: "Areas of Outstanding Natural Beauty (AONB)",
identifier: "shapefiles:AONB_UK",
fill: [0, 155, 0, 1],
stroke: [0, 0, 0, 1]
},
{
name: "Sites of Special Scientific Interest (SSSI)",
identifier: "shapefiles:SSSI_UK",
fill: [255, 0, 0, 1],
stroke: [0, 0, 0, 1]
{
name: "Sites of Special Scientific Interest (SSSI)",
identifier: "shapefiles:SSSI_UK",
fill: [255, 0, 0, 1],
stroke: [0, 0, 0, 1]
},
{
name: "National Nature Reserves",
identifier: "shapefiles:NNR_UK",
fill: [42, 161, 79, 1],
stroke: [0, 0, 0, 1]
{
name: "National Nature Reserves",
identifier: "shapefiles:NNR_UK",
fill: [42, 161, 79, 1],
stroke: [0, 0, 0, 1]
},
{
name: "Local Nature Reserves",
Expand Down Expand Up @@ -168,13 +168,13 @@ export const LayerPalette = ({ addLayer, hide, dbModels, getTeamDatasets, teamNa
}}
/>
)
}
}
</Section>
<Section title="OS Boundaries">
{
{
Array<{ name: string, identifier: string }>(
{
name: "Historic Counties",
{
name: "Historic Counties",
identifier: "shapefiles:boundary_line_historic_counties"
},
{
Expand All @@ -190,18 +190,18 @@ export const LayerPalette = ({ addLayer, hide, dbModels, getTeamDatasets, teamNa
identifier: "shapefiles:polling_districts_england"
}
).sort((a, b) => (a.name < b.name) ? -1 : 1).map(({ name, identifier }) =>
<AddLayerButton
addLayer={addLayer}
prototype={{
type: "BoundaryLayer",
name,
identifier,
visible: true,
opacity: 1,
}}
/>
)
}
<AddLayerButton
addLayer={addLayer}
prototype={{
type: "BoundaryLayer",
name,
identifier,
visible: true,
opacity: 1,
}}
/>
)
}
</Section>
<Section title="NEVO">
{
Expand Down
2 changes: 2 additions & 0 deletions app/javascript/projects/modelling/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,14 @@ import { Extent } from "ol/extent"
import { ReplaceNaNComponent } from "./replace_nan_component"
import { BiodiversityComponent } from "./biodiversity_component"
import { LehLandCoverComponent } from "./leh_land_cover_component"
import { MlTreeHedgeComponent } from "./ml_tree_hedge_component"

export function createDefaultComponents(saveMapLayer: SaveMapLayer, saveModel: SaveModel, getDatasets: getDatasets, extent: Extent, zoom: number): BaseComponent[] {
return [
// Inputs
new UkcehLandCoverComponent(extent, zoom),
new LehLandCoverComponent(extent, zoom),
new MlTreeHedgeComponent(extent, zoom),
new BiodiversityComponent(extent, zoom),
new NevoLayerComponent(extent, zoom),
new OSMLandUseComponent(extent, zoom),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { createXYZ } from "ol/tilegrid"
import { Node, Output } from "rete"
import { NodeData, WorkerInputs, WorkerOutputs } from "rete/types/core/data"
import { booleanDataSocket, categoricalDataSocket } from "../socket_types"
import { BooleanTileGrid, CategoricalTileGrid } from "../tile_grid"
import { BaseComponent } from "./base_component"
import { retrieveModelDataWCS } from "../model_retrieval"
import { TypedArray } from "d3"
import { Extent } from "ol/extent"

interface Habitat {
agg: number
AC: string
mode: number
LC: string
}

const habitats: Habitat[] = [
//TODO : move to a json or an alternative storage
{ agg: 0, AC: "All", mode: 0, LC: "All" },
{ agg: 1, AC: "Hedge", mode: 1, LC: "Hedge" },
{ agg: 2, AC: "Tree", mode: 2, LC: "Tree" }
]

async function renderCategoricalData(extent: Extent, zoom: number) {
// When testing locally, disable CORS in browser settings

const tileGrid = createXYZ()
const outputTileRange = tileGrid.getTileRangeForExtentAndZ(extent, zoom)

const geotiff = await retrieveModelDataWCS(extent, 'ml:tree_hedge_predictions', outputTileRange)

const rasters = await geotiff.readRasters({ bbox: extent, width: outputTileRange.getWidth(), height: outputTileRange.getHeight() })
const image = await geotiff.getImage()


const map: Map<number, string> = new Map()

habitats.forEach(hab => {
if (hab.mode !== 0) map.set(hab.mode, hab.LC)
})

const result = new CategoricalTileGrid(
zoom,
outputTileRange.minX,
outputTileRange.minY,
outputTileRange.getWidth(),
outputTileRange.getHeight()
)

for (let i = 0; i < (rasters[0] as TypedArray).length; i++) {

let x = (outputTileRange.minX + i % image.getWidth())
let y = (outputTileRange.minY + Math.floor(i / image.getWidth()))

result.set(x, y, rasters[0][i])

}

result.setLabels(map)

return result
}

export class MlTreeHedgeComponent extends BaseComponent {
categoricalData: CategoricalTileGrid | null
outputCache: Map<number, BooleanTileGrid>
projectExtent: Extent
zoom: number

constructor(projectExtent: Extent, projectZoom: number) {
super("ML Model Output")
this.category = "Inputs"
this.categoricalData = null
this.outputCache = new Map()
this.projectExtent = projectExtent
this.zoom = projectZoom
}

async builder(node: Node) {

node.meta.toolTip = "Custom ML outputs."

node.meta.toolTipLink = "https://www.wearepal.ai/lmt.html"

habitats.forEach(hab =>
hab.AC === "All" ? node.addOutput(new Output(hab["mode"].toString(), hab["LC"], categoricalDataSocket)) : node.addOutput(new Output(hab["mode"].toString(), hab["LC"], booleanDataSocket))
)
}

async worker(node: NodeData, inputs: WorkerInputs, outputs: WorkerOutputs, ...args: unknown[]) {
if (this.categoricalData === null) {
this.categoricalData = await renderCategoricalData(this.projectExtent, this.zoom)
}
const categoricalData = this.categoricalData!

habitats.filter(
habitat => node.outputs[habitat.mode].connections.length > 0
).forEach(habitat => {
if (habitat.mode === 0) {

outputs[habitat.mode] = this.categoricalData

} else {
if (this.outputCache.has(habitat.mode)) {
outputs[habitat.mode] = this.outputCache.get(habitat.mode)
}
else {
const out = outputs[habitat.mode] = new BooleanTileGrid(categoricalData.zoom, categoricalData.x, categoricalData.y, categoricalData.width, categoricalData.height)
out.name = habitat.LC

categoricalData.iterate((x, y, value) => out.set(x, y, value === habitat.mode))

this.outputCache.set(habitat.mode, out)
}
}
})
}
}

0 comments on commit daba670

Please sign in to comment.