Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Units in model view #467

Merged
merged 4 commits into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,6 @@ yarn-debug.log*
/coverage

# Ignore local vs code properties
.vscode
.vscode

.DS_Store
10 changes: 10 additions & 0 deletions app/assets/stylesheets/model-editor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@
background: steelblue;
}

.socket.property {
background: purple;
}

.socket--number,
.socket.number {
background: #46b44c;
Expand Down Expand Up @@ -83,6 +87,12 @@
}
}

.connection.socket-output-property {
.main-path {
stroke: purple;
}
}

.connection.socket-output-number,
.connection.socket-output-number {
.main-path {
Expand Down
18 changes: 13 additions & 5 deletions app/javascript/projects/analysis_panel.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,33 @@
import { Extent } from 'ol/extent'
import * as React from 'react'
import { DatasetLayer, Layer, ModelOutputLayer } from './state'
import { BooleanTileGrid, CategoricalTileGrid, NumericTileGrid } from './modelling/tile_grid'
import { BooleanTileGrid, CategoricalTileGrid, NumericTileGrid, TileGridProps } from './modelling/tile_grid'
import { ChartData, extentToChartDataCached } from './analysis_panel_tools/subsection'
import { GenerateChart } from './analysis_panel_tools/charts'
import './analysis_panel.css'
import { getArea } from 'ol/sphere'
import { fromExtent } from 'ol/geom/Polygon'
import { TeamExtentData } from './project_editor'
import { getMedianCellSize } from './modelling/components/cell_area_component'

export type ChartType = "pie" | "hist" | "bar" | "kde"

interface ChartProps {
chartType: ChartType | undefined
chartData: ChartData | undefined

props: TileGridProps | undefined
cellArea: number
}

const Chart = ({ chartType, chartData }: ChartProps) => {
const Chart = ({ chartType, chartData, props, cellArea }: ChartProps) => {

if (!chartType || !chartData) return <></>

return <GenerateChart
chartData={chartData}
chartType={chartType}
props={props}
cellArea={cellArea}
/>
}

Expand Down Expand Up @@ -90,9 +94,10 @@ const ChartSelection = ({ SourceType, ChartTypeSelected, SetChartType }: ChartSe
interface ChartLegendProps {
chartData: ChartData | undefined
sourceType: string
props: TileGridProps | undefined
}

const ChartLegend = ({ chartData, sourceType }: ChartLegendProps) => {
const ChartLegend = ({ chartData, sourceType, props }: ChartLegendProps) => {
if (!chartData) {
return null
}
Expand Down Expand Up @@ -122,7 +127,7 @@ const ChartLegend = ({ chartData, sourceType }: ChartLegendProps) => {
<input
disabled
type="text"
value={NumStats[key]}
value={(key === "sum" && props && props.unit) ? `${NumStats[key]} ${props.unit}` : NumStats[key]}
/>
</div>
))
Expand Down Expand Up @@ -264,6 +269,8 @@ export const AnalysisPanel = ({ selectedArea, setSelectedArea, setShowAP, select
<Chart
chartType={chartType}
chartData={chartData}
props={data instanceof NumericTileGrid ? data.properties : undefined}
cellArea={data ? getMedianCellSize(data).area : 0}
/>
</div>
<div style={{ textAlign: 'center' }}>
Expand All @@ -283,6 +290,7 @@ export const AnalysisPanel = ({ selectedArea, setSelectedArea, setShowAP, select
<ChartLegend
chartData={chartData}
sourceType={dataSourceType}
props={data instanceof NumericTileGrid ? data.properties : undefined}
/>
</div>
</>
Expand Down
27 changes: 25 additions & 2 deletions app/javascript/projects/analysis_panel_tools/charts/histogram.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import * as React from "react"
import * as d3 from 'd3'
import { ChartData } from "../subsection"
import { TileGridProps } from "../../modelling/tile_grid"

interface HistogramProps {
chartData: ChartData
props: TileGridProps | undefined
cellArea: number
}
export const GenerateHistogram = ({ chartData }: HistogramProps) => {

export const GenerateHistogram = ({ chartData, props, cellArea }: HistogramProps) => {


const axesRef = React.useRef(null)
Expand Down Expand Up @@ -62,7 +66,7 @@ export const GenerateHistogram = ({ chartData }: HistogramProps) => {


return (
<svg id="hist" width={width} height={height}>
<svg id="hist" width={width} height={height} style={{marginBottom: 15, overflow: "auto"}}>
<g
width={boundsWidth}
height={boundsHeight}
Expand All @@ -76,6 +80,25 @@ export const GenerateHistogram = ({ chartData }: HistogramProps) => {
ref={axesRef}
transform={`translate(${[MARGIN.left, MARGIN.top].join(",")})`}
/>
<text
x={width / 2}
y={height - 5}
textAnchor="middle"
fontSize="14px"
fill="black"
>
{props?.area && props.unit ? `${props.unit}/${props.area}` : `value`}
</text>
<text
x={-height / 2}
y={15}
transform="rotate(-90)"
textAnchor="middle"
fontSize="14px"
fill="black"
>
km²
</text>
</svg>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@ import { ChartType } from "../../analysis_panel"
import { GeneratePieChart } from "./pie"
import { GenerateHistogram } from "./histogram"
import { GenerateBarChart } from "./bar"
import { TileGridProps } from "../../modelling/tile_grid"

interface ChartProps {
chartData: ChartData
chartType: ChartType
props: TileGridProps | undefined
cellArea: number
}
export const GenerateChart = ({ chartData, chartType }: ChartProps) => {
export const GenerateChart = ({ chartData, chartType, props, cellArea }: ChartProps) => {
switch (chartType) {
case "pie":
return <GeneratePieChart
Expand All @@ -19,6 +22,8 @@ export const GenerateChart = ({ chartData, chartType }: ChartProps) => {
case "hist":
return <GenerateHistogram
chartData={chartData}
props={props}
cellArea={cellArea}
/>
break;
case "bar":
Expand Down
18 changes: 13 additions & 5 deletions app/javascript/projects/analysis_panel_tools/subsection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { getArea } from "ol/sphere"
import { fromExtent } from "ol/geom/Polygon"
import { getColorStops } from "../reify_layer/model_output"
import { re, sum } from "mathjs"
import { getMedianCellSize } from "../modelling/components/cell_area_component"

type Color = [number, number, number, number]

Expand Down Expand Up @@ -35,6 +36,14 @@ export function findColor(value: number, colorArray: any[]): Color {
return alpha
}

function unitsAdjustmentFactor(unit: string | undefined, grid: NumericTileGrid): number {
const area = getMedianCellSize(grid).area
if (unit === "m²") return area / 1
if (unit === "ha") return area / 10000
if (unit === "km²") return area / (1000 ** 2)
return 1
}

function medianFromMap(arr: [number, number][], total: number): number | undefined {
let idx = total / 2;

Expand Down Expand Up @@ -162,17 +171,17 @@ export function extentToChartData(colors: Color[] | undefined, model: BooleanTil
let counts = new Map<any, number>()
let color = new Map<any, [number, number, number, number]>()
let numeric_stats: NumericStats | undefined
const cellSize = getMedianCellSize(model).area / 1000000

for (let x = outputTileRange.minX; x <= outputTileRange.maxX; x++) {
for (let y = outputTileRange.minY; y <= outputTileRange.maxY; y++) {

if (model instanceof CategoricalTileGrid) {

const area = getArea(fromExtent(tileGrid.getTileCoordExtent([model.zoom, x, y]))) / 1000000

const value = model.labels.get(model.get(x, y)) ? model.labels.get(model.get(x, y)) : "No Data"
const count = counts.get(value) || 0
counts.set(value, count + area)
counts.set(value, count + cellSize)


if (colors) {
Expand All @@ -182,12 +191,11 @@ export function extentToChartData(colors: Color[] | undefined, model: BooleanTil

} else {

const area = model instanceof NumericTileGrid ? 1 : getArea(fromExtent(tileGrid.getTileCoordExtent([model.zoom, x, y]))) / 1000000
const value = model.get(x, y)

const count = counts.get(value) || 0

counts.set(value, count + area)
counts.set(value, count + cellSize)

if (colors && model instanceof BooleanTileGrid) {
const col_value = colors[value ? 1 : 0]
Expand Down Expand Up @@ -219,7 +227,7 @@ export function extentToChartData(colors: Color[] | undefined, model: BooleanTil
const range = max - min
const step = range / bins

const _sum = sum(mapEntries.map((x) => x[1] * x[0]))
const _sum = sum(mapEntries.map((x) => x[1] * x[0])) * unitsAdjustmentFactor(model.properties.area, model)
const total_entries = mapEntries.reduce((acc, cur) => acc + cur[1], 0)

const _mean = _sum / total_entries
Expand Down
6 changes: 6 additions & 0 deletions app/javascript/projects/modelling/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ import { SegmentComponent } from "./segment_component"
import { KewSamplesComponent } from "./kew_samples_component"
import { InterpolationComponent } from "./interpolation_component"
import { NatmapSoilComponent } from "./natmap_soil_component"
import { UnitComponent } from "./units_component"
import { areas, units } from "../tile_grid"

export interface ProjectProperties {
extent: Extent
Expand Down Expand Up @@ -87,6 +89,10 @@ export function createDefaultComponents(saveMapLayer: SaveMapLayer, saveModel: S
new MapLayerComponent(saveMapLayer),
new SaveModelOutputComponent(saveModel),

// Properties
new UnitComponent('Unit', projectProps, units.map((unit, idx) => ({ name: unit, id: idx }))),
new UnitComponent('Area', projectProps, areas.map((unit, idx) => ({ name: unit, id: idx }))),

// Conversions
new NumberToNumericDatasetComponent(),
new NumericDatasetToNumberComponent(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Input, Node } from "rete"
import { NodeData, WorkerInputs, WorkerOutputs } from "rete/types/core/data"
import { dataSocket } from "../socket_types"
import { dataSocket, propertySocket } from "../socket_types"
import { BooleanTileGrid, CategoricalTileGrid, NumericTileGrid } from "../tile_grid"
import { BaseComponent } from "./base_component"

Expand All @@ -18,6 +18,7 @@ export class MapLayerComponent extends BaseComponent {
async builder(node: Node) {
node.meta.toolTip = "Output a model to the map view."
node.addInput(new Input("in", "Output", dataSocket))
node.addInput(new Input("props", "Properties (optional)", propertySocket, true))
}

async worker(node: NodeData, inputs: WorkerInputs, outputs: WorkerOutputs, ...args: unknown[]) {
Expand All @@ -32,7 +33,18 @@ export class MapLayerComponent extends BaseComponent {

const name = editorNode.data.name as string

if (inputs["in"][0]) this.callback(node.id, name ? (name !== "" ? name.trim() : undefined) : undefined, inputs["in"][0] as BooleanTileGrid | NumericTileGrid | CategoricalTileGrid)
let out = inputs["in"][0] as BooleanTileGrid | NumericTileGrid | CategoricalTileGrid
const props = inputs["props"]

out = (out instanceof NumericTileGrid && props.length > 0) ? out.clone() : out

props.forEach((prop: any) => {
if (out instanceof NumericTileGrid){
out.properties[(prop.type as string).toLowerCase()] = prop.unit
}
})

if (out) this.callback(node.id, name ? (name !== "" ? name.trim() : undefined) : undefined, out)
else editorNode.meta.errorMessage = 'No input'

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,11 @@ export class SegmentComponent extends BaseComponent {
}

if (!('cls_conf' in node.data)) {
node.data.cls_conf = "90"
node.data.cls_conf = "0"
}

if (!('n_repeats' in node.data)) {
node.data.n_repeats = "5"
node.data.n_repeats = "1"
}

if (!('prompt' in node.data)) {
Expand Down
37 changes: 37 additions & 0 deletions app/javascript/projects/modelling/components/units_component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Node, Output } from "rete";
import { NodeData, WorkerInputs, WorkerOutputs } from "rete/types/core/data";
import { BaseComponent } from "./base_component";
import { ProjectProperties } from ".";
import { propertySocket } from "../socket_types";
import { SelectControl, SelectControlOptions } from "../controls/select";

export class UnitComponent extends BaseComponent {
unitArray: SelectControlOptions[]
unitType: string
unit: string

constructor(unit: string, projectProps : ProjectProperties, unitArray: SelectControlOptions[]) {
super(`${unit} Property`)
this.category = "Properties"
this.unitArray = unitArray
this.unit = unit
}

async builder(node: Node) {
node.addControl(new SelectControl(
this.editor,
this.unit,
() => this.unitArray,
() => []
))
node.addOutput(new Output(this.unit, this.unit, propertySocket))
}

worker(node: NodeData, inputs: WorkerInputs, outputs: WorkerOutputs, ...args: unknown[]): void {
const idx = (node.data[this.unit] ?? 0 )as number
outputs[this.unit] = {
type: this.unit,
unit: this.unitArray[idx].name
}
}
}
2 changes: 2 additions & 0 deletions app/javascript/projects/modelling/socket_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export const numericDataSocket = new Socket('Numeric dataset')

export const categoricalDataSocket = new Socket('Categorical dataset')

export const propertySocket = new Socket('Property')

export const dataSocket = new Socket('Dataset')
booleanDataSocket.combineWith(dataSocket)
numericDataSocket.combineWith(dataSocket)
Expand Down
Loading
Loading