Skip to content

Commit

Permalink
Merge pull request #487 from wearepal/kew-soil-preds
Browse files Browse the repository at this point in the history
Added Kew Soil Carbon Component, improved error handling on AI Model Component
  • Loading branch information
paulthatjazz authored Jan 31, 2025
2 parents ec5b5ed + 60f05a7 commit 4f9a815
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 1 deletion.
7 changes: 7 additions & 0 deletions app/javascript/projects/modelling/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import { UnitComponent } from "./units_component"
import { areas, units } from "../tile_grid"
import { KewHabsComponent } from "./kew_habs_component"
import { KewTreesComponent } from "./kew_trees_component"
import { RasterComponent } from "./raster_component"

export interface ProjectProperties {
extent: Extent
Expand All @@ -66,6 +67,12 @@ export function createDefaultComponents(saveMapLayer: SaveMapLayer, saveModel: S
restrictedComponents.push(new KewSamplesComponent(projectProps))
//restrictedComponents.push(new KewHabsComponent(projectProps))
restrictedComponents.push(new KewTreesComponent(projectProps))
restrictedComponents.push(new RasterComponent(projectProps, 'Wakehurst Soil Carbon', 'Kew', [
{ name: 'Carbon 0-15cm (t/ha)' , source: 'kew:predicted_carbon_015' },
{ name: 'Carbon 0-30cm (t/ha)' , source: 'kew:predicted_carbon_030' },
{ name: 'Carbon 15-30cm (t/ha)' , source: 'kew:predicted_carbon_1530' },
{ name: 'Carbon 30-45cm (t/ha)' , source: 'kew:predicted_carbon_3045' },
]))
}
if (permissions.NATMAPSoil) restrictedComponents.push(new NatmapSoilComponent(projectProps))

Expand Down
91 changes: 91 additions & 0 deletions app/javascript/projects/modelling/components/raster_component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { BaseComponent } from "./base_component"
import { NodeData, WorkerInputs, WorkerOutputs } from 'rete/types/core/data'
import { Input, Node, Output, Socket } from 'rete'
import { booleanDataSocket, numericDataSocket } from "../socket_types"
import { ProjectProperties } from "."

import { maskFromExtentAndShape } from "../bounding_box"
import { createXYZ } from "ol/tilegrid"
import { retrieveModelDataWCS } from "../model_retrieval"
import { NumericTileGrid } from "../tile_grid"
import { TypedArray } from "d3"

// Generic component class for handling raster datasets

interface RasterComponentOption {
name: string
source: string
}

export class RasterComponent extends BaseComponent {
projectProps: ProjectProperties
options: RasterComponentOption[]
cache: Map<string, NumericTileGrid>

constructor(projectProps: ProjectProperties, name: string, category: string, options: RasterComponentOption[]) {
super(name)
this.category = category
this.projectProps = projectProps
this.options = options
this.cache = new Map()
}

async builder(node: Node) {
this.options.forEach((option) => {
node.addOutput(new Output(option.name, option.name, numericDataSocket))
})
}

async worker(node: NodeData, inputs: WorkerInputs, outputs: WorkerOutputs, ...args: unknown[]) {
const editorNode = this.editor?.nodes.find(n => n.id === node.id)
if (editorNode === undefined) { return }

const mask = await maskFromExtentAndShape(
this.projectProps.extent,
this.projectProps.zoom,
this.projectProps.maskLayer,
this.projectProps.maskCQL,
this.projectProps.mask
)


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

const promises = this.options.filter(
opt => node.outputs[opt.name].connections.length > 0
).map(
async opt => {
if (this.cache.has(opt.name)) {

outputs[opt.name] = this.cache.get(opt.name)

}else{

const geotiff = await retrieveModelDataWCS(this.projectProps.extent, opt.source, outputTileRange)

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

const out = new NumericTileGrid(this.projectProps.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()))

out.set(x, y, mask.get(x, y) === true ? (rasters[0][i]) : NaN)
}

outputs[opt.name] = out
this.cache.set(opt.name, out)

}

}
)

await Promise.all(promises)
}

}

Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,18 @@ export class SegmentComponent extends BaseComponent {
+" please note: setting this to 0 will disable this function), and the number of repeats is the number of times you want to repeat the segmentation process. (must be a value higher than 0)."

// Retrieve default values from the server
const defaultData = await fetch ("https://landscapes.wearepal.ai/api/settings").then((res) => res.json())
const defaultData = await fetch ("https://landscapes.wearepal.ai/api/settings").then((res) => res.json()).catch((e) => {
// If the server is down, use the default values

node.meta.errorMessage = "Failed to connect to the segmentation API. Component may be unavailable."
node.update()

return {
det_conf_default: 5.0,
clf_conf_default: 0,
n_repeats_default: 1
}
})

if (!('det_conf' in node.data)) {
node.data.det_conf = defaultData.det_conf_default ? defaultData.det_conf_default.toString() : "5.0"
Expand Down

0 comments on commit 4f9a815

Please sign in to comment.