Skip to content

Commit d96f8c7

Browse files
committed
fix(processing): honor WASM tool defaults and offer attribute pickers for field parameters
Fixes #1458 Fixes #1459 Points Along Lines dropped every line's endpoint and gave no hint that its spacing is measured in degrees, and Points To Line made the user type a column name from memory. Both are Processing toolbox issues in local (WASM) mode. Honor the WASM manifest's defaults. `manifestToWhiteboxTool` discarded the manifest's `defaults` map, so the dialog fell back to `false` for every bool. `points_along_lines` documents "Include line endpoints (default true)" but rendered as an unchecked box and sent `--include_end=false`: on the reporter's 0.36-degree line, spacing 0.1 produced 3 points instead of 4, and 0.2 produced a single point, which reads as "nothing happens". 1028 optional parameters across the catalog gain their documented default, 103 of them bools documented as true. The map doubles as the tool's example invocation, so dataset and required entries are dropped: `points_along_lines` lists `input: "lines.shp"` and `spacing: 50` next to the one real default, and prefilling those would offer a path that does not exist and an arbitrary value for a required distance. A parameter the manifest leaves undefaulted now falls back to the catalog's default, so WASM mode opens a tool with the same values the sidecar would. State the coordinate units. Vector inputs reach the WASM runner as GeoJSON, which RFC 7946 fixes to WGS84, so a distance, spacing or tolerance parameter is in degrees. Nothing said so, which is how a spacing of 0.1 (about 11 km) yielded a handful of points on a city-scale line. The parameter list now carries a note with the degree-to-metre scale. Offer the layer's columns for a field parameter. A `*_field` / `*_attribute` string parameter names a column of one of the tool's vector inputs, matched on the name suffix so this covers ~170 tools rather than a hard-coded list. The picker is filled from the layer chosen for the vector input the parameter names, resolved by longest leading name segment for a multi-input tool and falling back to the union of every selected input's columns when the names do not line up. The text box stays editable alongside it, so a file-path input or a column the property sample missed still works. Verified in the browser against the reporter's coordinates, in both themes: Points To Line now lists Lat/Lon/Label/DT for both attribute parameters, and Points Along Lines opens with "Include line endpoints" checked and returns 4 points at spacing 0.1.
1 parent 011bdaa commit d96f8c7

22 files changed

Lines changed: 426 additions & 22 deletions

File tree

apps/geolibre-desktop/src/components/processing/ProcessingDialog.tsx

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ import {
6060
subsetUrlToolKind,
6161
} from "../../lib/subset-tool-url";
6262
import { buildWhiteboxToolShareUrl, whiteboxToolShareBase } from "../../lib/whitebox-tool-url";
63+
import { fieldSourceInputName, isFieldParameterName } from "../../lib/whitebox-field-params";
6364
import { clearPrintExtent, drawPrintExtent } from "../../lib/print-extent";
6465
import { startGeoLibreSidecar, stopGeoLibreSidecar } from "../../lib/sidecar";
6566
import {
@@ -201,6 +202,16 @@ function isSubsetUrlParameter(tool: WhiteboxTool, param: WhiteboxToolParameter):
201202
);
202203
}
203204

205+
// A `*_field` / `*_attribute` string param names a column of one of the tool's
206+
// vector inputs (points_to_line's `line_field`/`sort_field`, and ~170 other
207+
// tools), so the dialog can offer the selected layer's attribute names instead
208+
// of asking the user to recall a column name (GeoLibre#1459). The kind check is
209+
// what keeps a same-named *dataset* param out (join_tables' `primary_key_field`
210+
// is a vector input): only a scalar string names a column.
211+
function isFieldParameter(param: WhiteboxToolParameter): boolean {
212+
return parameterKind(param) === "string" && isFieldParameterName(param.name);
213+
}
214+
204215
function isPathParameter(param: WhiteboxToolParameter): boolean {
205216
const kind = parameterKind(param);
206217
if (isDataInputParameter(param) || isOutputParameter(param)) return true;
@@ -671,6 +682,60 @@ export function ProcessingDialog({ mapControllerRef, onAddRaster }: ProcessingDi
671682
// source filter — pointless when every tool is from Whitebox.
672683
const hasGeolibreTools = useMemo(() => tools.some((tool) => tool.source === "geolibre"), [tools]);
673684

685+
// The selected tool's vector inputs, which decide both the coordinate-units
686+
// note and where a field parameter's column names come from.
687+
const vectorInputParams = useMemo(
688+
() => (selectedTool?.params ?? []).filter((param) => parameterKind(param) === "vector_in"),
689+
[selectedTool],
690+
);
691+
692+
// Attribute-field names per layer, memoized on the layer set (and the dialog
693+
// being open) so it doesn't recompute on every keystroke. GeoJSON is
694+
// schemaless, so sample the first FIELD_SCAN_SAMPLE features rather than
695+
// scanning a whole large layer on the React commit path.
696+
const fieldsByLayer = useMemo(() => {
697+
const FIELD_SCAN_SAMPLE = 1000;
698+
const map = new Map<string, string[]>();
699+
if (!open) return map;
700+
for (const layer of layers) {
701+
if (!layer.geojson) continue;
702+
const keys = new Set<string>();
703+
for (const feature of layer.geojson.features.slice(0, FIELD_SCAN_SAMPLE)) {
704+
for (const key of Object.keys(feature.properties ?? {})) keys.add(key);
705+
}
706+
if (keys.size) map.set(layer.id, [...keys]);
707+
}
708+
return map;
709+
}, [layers, open]);
710+
711+
// Column names to offer for a `*_field` parameter (GeoLibre#1459): those of
712+
// the layer picked for the vector input the parameter names. With a single
713+
// vector input that is unambiguous; with several, an unmatched name falls back
714+
// to the union of every selected input's columns, so the right column is still
715+
// in the list even when the naming doesn't line up. Empty when the input is a
716+
// file path rather than a loaded layer — the field stays a plain text box.
717+
const fieldOptions = useCallback(
718+
(param: WhiteboxToolParameter): string[] => {
719+
if (!vectorInputParams.length || !isFieldParameter(param)) return [];
720+
const columnsOf = (input: WhiteboxToolParameter): string[] => {
721+
const value = values[input.name];
722+
if (typeof value !== "string" || !value.startsWith(LAYER_TOKEN_PREFIX)) return [];
723+
return fieldsByLayer.get(value.slice(LAYER_TOKEN_PREFIX.length)) ?? [];
724+
};
725+
const sourceName =
726+
vectorInputParams.length === 1
727+
? vectorInputParams[0].name
728+
: fieldSourceInputName(
729+
param.name,
730+
vectorInputParams.map((input) => input.name),
731+
);
732+
const source = vectorInputParams.find((input) => input.name === sourceName);
733+
if (source) return columnsOf(source);
734+
return [...new Set(vectorInputParams.flatMap(columnsOf))];
735+
},
736+
[fieldsByLayer, values, vectorInputParams],
737+
);
738+
674739
// A shareable `?tool=` deep link for the selected tool: the tool id plus the
675740
// parameters the user changed from their defaults. Local file paths
676741
// (`*_in`/`*_out`) are excluded — they are machine-specific and either useless
@@ -1775,6 +1840,17 @@ export function ProcessingDialog({ mapControllerRef, onAddRaster }: ProcessingDi
17751840

17761841
<ScrollArea className="min-h-0">
17771842
<div className="grid gap-4 pb-2 pe-5">
1843+
{/* Local (WASM) mode hands every vector input to the runner as
1844+
GeoJSON, which RFC 7946 fixes to WGS84 — so a tool's distance,
1845+
spacing or tolerance parameter is measured in degrees, not
1846+
metres. Nothing in the tool descriptions says so, which is how
1847+
a 0.1 "spacing" (≈ 11 km) yielded a handful of points on a
1848+
city-scale line (GeoLibre#1458). */}
1849+
{runLocal && vectorInputParams.length > 0 ? (
1850+
<p className="text-xs text-muted-foreground">
1851+
{t("processing.whitebox.vectorUnitsNote")}
1852+
</p>
1853+
) : null}
17781854
{(selectedTool?.params ?? []).length === 0 ? (
17791855
<p className="text-sm text-muted-foreground">
17801856
{t("processing.whitebox.noParameters")}
@@ -1788,6 +1864,7 @@ export function ProcessingDialog({ mapControllerRef, onAddRaster }: ProcessingDi
17881864
toolId={selectedTool.id}
17891865
runLocal={runLocal}
17901866
value={values[param.name]}
1867+
fieldOptions={fieldOptions(param)}
17911868
onChange={(value) => updateValue(param.name, value)}
17921869
onPickFile={(fileName, bytes) =>
17931870
handlePickInputFile(param.name, fileName, bytes)
@@ -1896,6 +1973,8 @@ function JobOutputPanel({ job }: { job: WhiteboxJob }) {
18961973
interface ParameterFieldProps {
18971974
param: WhiteboxToolParameter;
18981975
layers: GeoLibreLayer[];
1976+
/** Attribute names to offer for a `*_field` parameter; empty keeps it free text. */
1977+
fieldOptions?: string[];
18991978
onChange: (value: unknown) => void;
19001979
onPickFile?: (fileName: string, bytes: Uint8Array) => void;
19011980
/** When set, renders a "Use map extent" button that fills this bbox field
@@ -1917,6 +1996,7 @@ interface ParameterFieldProps {
19171996
function ParameterField({
19181997
param,
19191998
layers,
1999+
fieldOptions,
19202000
onChange,
19212001
onPickFile,
19222002
onUseMapExtent,
@@ -2070,6 +2150,32 @@ function ParameterField({
20702150
</p>
20712151
)}
20722152
</div>
2153+
) : fieldOptions?.length ? (
2154+
// A `*_field` parameter with a layer chosen for its vector input: offer
2155+
// that layer's attribute names so the column need not be typed from
2156+
// memory (GeoLibre#1459). The text box stays editable alongside the
2157+
// picker, so a column the property sample missed can still be typed.
2158+
<div className="grid grid-cols-[minmax(150px,200px)_minmax(0,1fr)] gap-2">
2159+
<Select
2160+
aria-label={t("processing.whitebox.selectField")}
2161+
value={fieldOptions.includes(valueText) ? valueText : ""}
2162+
onChange={(event) => onChange(event.target.value)}
2163+
>
2164+
<option value="">{t("processing.whitebox.selectField")}</option>
2165+
{fieldOptions.map((name) => (
2166+
<option key={name} value={name}>
2167+
{name}
2168+
</option>
2169+
))}
2170+
</Select>
2171+
<Input
2172+
id={`whitebox-${param.name}`}
2173+
type="text"
2174+
value={valueText}
2175+
placeholder={t("processing.whitebox.fieldName")}
2176+
onChange={(event: ChangeEvent<HTMLInputElement>) => onChange(event.target.value)}
2177+
/>
2178+
</div>
20732179
) : isPathParameter(param) ? (
20742180
<PathPickerInput
20752181
id={`whitebox-${param.name}`}

apps/geolibre-desktop/src/i18n/locales/ar.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3328,7 +3328,10 @@
33283328
"noParameters": "لا تحتوي هذه الأداة على معاملات.",
33293329
"noOutput": "لا يوجد ناتج بعد.",
33303330
"enabled": "مُفعّل",
3331-
"requiredSuffix": " | مطلوب"
3331+
"requiredSuffix": " | مطلوب",
3332+
"selectField": "اختر حقلاً",
3333+
"fieldName": "اسم الحقل",
3334+
"vectorUnitsNote": "تُقرأ الطبقات الشعاعية بنظام WGS84، لذا تكون قيم المسافة والتباعد والتسامح بالدرجات وليس بالأمتار. تعادل الدرجة الواحدة نحو 111 كم، و0.001 درجة نحو 111 مترًا."
33323335
},
33333336
"sidecar": {
33343337
"unavailableTitle": "خادم المعالجة غير متاح.",

apps/geolibre-desktop/src/i18n/locales/de.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3113,7 +3113,10 @@
31133113
"noParameters": "Dieses Werkzeug hat keine Parameter.",
31143114
"noOutput": "Noch keine Ausgabe.",
31153115
"enabled": "Aktiviert",
3116-
"requiredSuffix": " | erforderlich"
3116+
"requiredSuffix": " | erforderlich",
3117+
"selectField": "Feld auswählen",
3118+
"fieldName": "Feldname",
3119+
"vectorUnitsNote": "Vektorebenen werden als WGS84 gelesen, daher sind Abstands-, Raster- und Toleranzwerte in Grad angegeben, nicht in Metern. 1° entspricht etwa 111 km und 0,001° etwa 111 m."
31173120
},
31183121
"sidecar": {
31193122
"unavailableTitle": "Der Verarbeitungsserver ist nicht verfügbar.",

apps/geolibre-desktop/src/i18n/locales/en.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3267,7 +3267,10 @@
32673267
"noParameters": "This tool has no parameters.",
32683268
"noOutput": "No output yet.",
32693269
"enabled": "Enabled",
3270-
"requiredSuffix": " | required"
3270+
"requiredSuffix": " | required",
3271+
"selectField": "Select a field",
3272+
"fieldName": "Field name",
3273+
"vectorUnitsNote": "Vector layers are read as WGS84, so distance, spacing and tolerance values are in degrees, not meters. 1° is about 111 km, and 0.001° is about 111 m."
32713274
},
32723275
"sidecar": {
32733276
"unavailableTitle": "The processing server isn't available.",

apps/geolibre-desktop/src/i18n/locales/es.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3124,7 +3124,10 @@
31243124
"noParameters": "Esta herramienta no tiene parámetros.",
31253125
"noOutput": "Aún no hay salida.",
31263126
"enabled": "Activado",
3127-
"requiredSuffix": " | obligatorio"
3127+
"requiredSuffix": " | obligatorio",
3128+
"selectField": "Seleccione un campo",
3129+
"fieldName": "Nombre del campo",
3130+
"vectorUnitsNote": "Las capas vectoriales se leen como WGS84, por lo que los valores de distancia, espaciado y tolerancia están en grados, no en metros. 1° equivale a unos 111 km y 0,001° a unos 111 m."
31283131
},
31293132
"sidecar": {
31303133
"unavailableTitle": "El servidor de procesamiento no está disponible.",

apps/geolibre-desktop/src/i18n/locales/fr.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3113,7 +3113,10 @@
31133113
"noParameters": "Cet outil n'a aucun paramètre.",
31143114
"noOutput": "Pas encore de sortie.",
31153115
"enabled": "Activé",
3116-
"requiredSuffix": " | requis"
3116+
"requiredSuffix": " | requis",
3117+
"selectField": "Sélectionner un champ",
3118+
"fieldName": "Nom du champ",
3119+
"vectorUnitsNote": "Les couches vectorielles sont lues en WGS84 : les valeurs de distance, d'espacement et de tolérance sont donc exprimées en degrés, et non en mètres. 1° vaut environ 111 km et 0,001° environ 111 m."
31173120
},
31183121
"sidecar": {
31193122
"unavailableTitle": "Le serveur de traitement n'est pas disponible.",

apps/geolibre-desktop/src/i18n/locales/hi.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3113,7 +3113,10 @@
31133113
"noParameters": "इस टूल में कोई पैरामीटर नहीं है।",
31143114
"noOutput": "अभी कोई आउटपुट नहीं।",
31153115
"enabled": "सक्षम",
3116-
"requiredSuffix": " | आवश्यक"
3116+
"requiredSuffix": " | आवश्यक",
3117+
"selectField": "फ़ील्ड चुनें",
3118+
"fieldName": "फ़ील्ड नाम",
3119+
"vectorUnitsNote": "वेक्टर लेयर WGS84 के रूप में पढ़ी जाती हैं, इसलिए दूरी, अंतराल और सहनशीलता के मान मीटर में नहीं, डिग्री में होते हैं। 1° लगभग 111 किमी और 0.001° लगभग 111 मीटर के बराबर है।"
31173120
},
31183121
"sidecar": {
31193122
"unavailableTitle": "प्रोसेसिंग सर्वर उपलब्ध नहीं है।",

apps/geolibre-desktop/src/i18n/locales/id.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3059,7 +3059,10 @@
30593059
"noParameters": "Alat ini tidak memiliki parameter.",
30603060
"noOutput": "Belum ada keluaran.",
30613061
"enabled": "Aktif",
3062-
"requiredSuffix": " | wajib"
3062+
"requiredSuffix": " | wajib",
3063+
"selectField": "Pilih bidang",
3064+
"fieldName": "Nama bidang",
3065+
"vectorUnitsNote": "Lapisan vektor dibaca sebagai WGS84, sehingga nilai jarak, spasi, dan toleransi dinyatakan dalam derajat, bukan meter. 1° kira-kira 111 km dan 0,001° kira-kira 111 m."
30633066
},
30643067
"sidecar": {
30653068
"unavailableTitle": "Server pemrosesan tidak tersedia.",

apps/geolibre-desktop/src/i18n/locales/it.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3113,7 +3113,10 @@
31133113
"noParameters": "Questo strumento non ha parametri.",
31143114
"noOutput": "Nessun output ancora.",
31153115
"enabled": "Attivato",
3116-
"requiredSuffix": " | obbligatorio"
3116+
"requiredSuffix": " | obbligatorio",
3117+
"selectField": "Seleziona un campo",
3118+
"fieldName": "Nome del campo",
3119+
"vectorUnitsNote": "I livelli vettoriali vengono letti come WGS84, quindi i valori di distanza, spaziatura e tolleranza sono in gradi, non in metri. 1° corrisponde a circa 111 km e 0,001° a circa 111 m."
31173120
},
31183121
"sidecar": {
31193122
"unavailableTitle": "Il server di elaborazione non è disponibile.",

apps/geolibre-desktop/src/i18n/locales/ja.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3059,7 +3059,10 @@
30593059
"noParameters": "このツールにはパラメータがありません。",
30603060
"noOutput": "まだ出力はありません。",
30613061
"enabled": "有効",
3062-
"requiredSuffix": " | 必須"
3062+
"requiredSuffix": " | 必須",
3063+
"selectField": "フィールドを選択",
3064+
"fieldName": "フィールド名",
3065+
"vectorUnitsNote": "ベクターレイヤーは WGS84 として読み込まれるため、距離・間隔・許容値の単位はメートルではなく度です。1° は約 111 km、0.001° は約 111 m です。"
30633066
},
30643067
"sidecar": {
30653068
"unavailableTitle": "処理サーバーを利用できません。",

0 commit comments

Comments
 (0)