Skip to content
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
106 changes: 106 additions & 0 deletions apps/geolibre-desktop/src/components/processing/ProcessingDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import {
subsetUrlToolKind,
} from "../../lib/subset-tool-url";
import { buildWhiteboxToolShareUrl, whiteboxToolShareBase } from "../../lib/whitebox-tool-url";
import { fieldSourceInputName, isFieldParameterName } from "../../lib/whitebox-field-params";
import { clearPrintExtent, drawPrintExtent } from "../../lib/print-extent";
import { startGeoLibreSidecar, stopGeoLibreSidecar } from "../../lib/sidecar";
import {
Expand Down Expand Up @@ -201,6 +202,16 @@ function isSubsetUrlParameter(tool: WhiteboxTool, param: WhiteboxToolParameter):
);
}

// A `*_field` / `*_attribute` string param names a column of one of the tool's
// vector inputs (points_to_line's `line_field`/`sort_field`, and ~170 other
// tools), so the dialog can offer the selected layer's attribute names instead
// of asking the user to recall a column name (GeoLibre#1459). The kind check is
// what keeps a same-named *dataset* param out (join_tables' `primary_key_field`
// is a vector input): only a scalar string names a column.
function isFieldParameter(param: WhiteboxToolParameter): boolean {
return parameterKind(param) === "string" && isFieldParameterName(param.name);
}

function isPathParameter(param: WhiteboxToolParameter): boolean {
const kind = parameterKind(param);
if (isDataInputParameter(param) || isOutputParameter(param)) return true;
Expand Down Expand Up @@ -671,6 +682,60 @@ export function ProcessingDialog({ mapControllerRef, onAddRaster }: ProcessingDi
// source filter — pointless when every tool is from Whitebox.
const hasGeolibreTools = useMemo(() => tools.some((tool) => tool.source === "geolibre"), [tools]);

// The selected tool's vector inputs, which decide both the coordinate-units
// note and where a field parameter's column names come from.
const vectorInputParams = useMemo(
() => (selectedTool?.params ?? []).filter((param) => parameterKind(param) === "vector_in"),
[selectedTool],
);

// Attribute-field names per layer, memoized on the layer set (and the dialog
// being open) so it doesn't recompute on every keystroke. GeoJSON is
// schemaless, so sample the first FIELD_SCAN_SAMPLE features rather than
// scanning a whole large layer on the React commit path.
const fieldsByLayer = useMemo(() => {
const FIELD_SCAN_SAMPLE = 1000;
const map = new Map<string, string[]>();
if (!open) return map;
for (const layer of layers) {
if (!layer.geojson) continue;
const keys = new Set<string>();
for (const feature of layer.geojson.features.slice(0, FIELD_SCAN_SAMPLE)) {
for (const key of Object.keys(feature.properties ?? {})) keys.add(key);
}
if (keys.size) map.set(layer.id, [...keys]);
}
return map;
}, [layers, open]);

// Column names to offer for a `*_field` parameter (GeoLibre#1459): those of
// the layer picked for the vector input the parameter names. With a single
// vector input that is unambiguous; with several, an unmatched name falls back
// to the union of every selected input's columns, so the right column is still
// in the list even when the naming doesn't line up. Empty when the input is a
// file path rather than a loaded layer — the field stays a plain text box.
const fieldOptions = useCallback(
(param: WhiteboxToolParameter): string[] => {
if (!vectorInputParams.length || !isFieldParameter(param)) return [];
const columnsOf = (input: WhiteboxToolParameter): string[] => {
const value = values[input.name];
if (typeof value !== "string" || !value.startsWith(LAYER_TOKEN_PREFIX)) return [];
return fieldsByLayer.get(value.slice(LAYER_TOKEN_PREFIX.length)) ?? [];
};
const sourceName =
vectorInputParams.length === 1
? vectorInputParams[0].name
: fieldSourceInputName(
param.name,
vectorInputParams.map((input) => input.name),
);
const source = vectorInputParams.find((input) => input.name === sourceName);
if (source) return columnsOf(source);
return [...new Set(vectorInputParams.flatMap(columnsOf))];
},
[fieldsByLayer, values, vectorInputParams],
);

// A shareable `?tool=` deep link for the selected tool: the tool id plus the
// parameters the user changed from their defaults. Local file paths
// (`*_in`/`*_out`) are excluded — they are machine-specific and either useless
Expand Down Expand Up @@ -1775,6 +1840,17 @@ export function ProcessingDialog({ mapControllerRef, onAddRaster }: ProcessingDi

<ScrollArea className="min-h-0">
<div className="grid gap-4 pb-2 pe-5">
{/* Local (WASM) mode hands every vector input to the runner as
GeoJSON, which RFC 7946 fixes to WGS84 — so a tool's distance,
spacing or tolerance parameter is measured in degrees, not
metres. Nothing in the tool descriptions says so, which is how
a 0.1 "spacing" (≈ 11 km) yielded a handful of points on a
city-scale line (GeoLibre#1458). */}
{runLocal && vectorInputParams.length > 0 ? (
<p className="text-xs text-muted-foreground">
{t("processing.whitebox.vectorUnitsNote")}
</p>
) : null}
{(selectedTool?.params ?? []).length === 0 ? (
<p className="text-sm text-muted-foreground">
{t("processing.whitebox.noParameters")}
Expand All @@ -1788,6 +1864,7 @@ export function ProcessingDialog({ mapControllerRef, onAddRaster }: ProcessingDi
toolId={selectedTool.id}
runLocal={runLocal}
value={values[param.name]}
fieldOptions={fieldOptions(param)}
onChange={(value) => updateValue(param.name, value)}
onPickFile={(fileName, bytes) =>
handlePickInputFile(param.name, fileName, bytes)
Expand Down Expand Up @@ -1896,6 +1973,8 @@ function JobOutputPanel({ job }: { job: WhiteboxJob }) {
interface ParameterFieldProps {
param: WhiteboxToolParameter;
layers: GeoLibreLayer[];
/** Attribute names to offer for a `*_field` parameter; empty keeps it free text. */
fieldOptions?: string[];
onChange: (value: unknown) => void;
onPickFile?: (fileName: string, bytes: Uint8Array) => void;
/** When set, renders a "Use map extent" button that fills this bbox field
Expand All @@ -1917,6 +1996,7 @@ interface ParameterFieldProps {
function ParameterField({
param,
layers,
fieldOptions,
onChange,
onPickFile,
onUseMapExtent,
Expand Down Expand Up @@ -2070,6 +2150,32 @@ function ParameterField({
</p>
)}
</div>
) : fieldOptions?.length ? (
// A `*_field` parameter with a layer chosen for its vector input: offer
// that layer's attribute names so the column need not be typed from
// memory (GeoLibre#1459). The text box stays editable alongside the
// picker, so a column the property sample missed can still be typed.
<div className="grid grid-cols-[minmax(150px,200px)_minmax(0,1fr)] gap-2">
<Select
aria-label={t("processing.whitebox.selectField")}
value={fieldOptions.includes(valueText) ? valueText : ""}
onChange={(event) => onChange(event.target.value)}
>
<option value="">{t("processing.whitebox.selectField")}</option>
{fieldOptions.map((name) => (
<option key={name} value={name}>
{name}
</option>
))}
</Select>
<Input
id={`whitebox-${param.name}`}
type="text"
value={valueText}
placeholder={t("processing.whitebox.fieldName")}
onChange={(event: ChangeEvent<HTMLInputElement>) => onChange(event.target.value)}
/>
</div>
) : isPathParameter(param) ? (
<PathPickerInput
id={`whitebox-${param.name}`}
Expand Down
5 changes: 4 additions & 1 deletion apps/geolibre-desktop/src/i18n/locales/ar.json
Original file line number Diff line number Diff line change
Expand Up @@ -3328,7 +3328,10 @@
"noParameters": "لا تحتوي هذه الأداة على معاملات.",
"noOutput": "لا يوجد ناتج بعد.",
"enabled": "مُفعّل",
"requiredSuffix": " | مطلوب"
"requiredSuffix": " | مطلوب",
"selectField": "اختر حقلاً",
"fieldName": "اسم الحقل",
"vectorUnitsNote": "تُقرأ الطبقات المتجهة بنظام WGS84، لذا تكون قيم المسافة والتباعد والتسامح بالدرجات وليس بالأمتار. تعادل الدرجة الواحدة نحو 111 كم، و0.001 درجة نحو 111 مترًا."
},
"sidecar": {
"unavailableTitle": "خادم المعالجة غير متاح.",
Expand Down
5 changes: 4 additions & 1 deletion apps/geolibre-desktop/src/i18n/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -3113,7 +3113,10 @@
"noParameters": "Dieses Werkzeug hat keine Parameter.",
"noOutput": "Noch keine Ausgabe.",
"enabled": "Aktiviert",
"requiredSuffix": " | erforderlich"
"requiredSuffix": " | erforderlich",
"selectField": "Feld auswählen",
"fieldName": "Feldname",
"vectorUnitsNote": "Vektorebenen werden als WGS84 gelesen, daher sind Entfernungs-, Abstands- und Toleranzwerte in Grad angegeben, nicht in Metern. 1° entspricht etwa 111 km und 0,001° etwa 111 m."
},
"sidecar": {
"unavailableTitle": "Der Verarbeitungsserver ist nicht verfügbar.",
Expand Down
5 changes: 4 additions & 1 deletion apps/geolibre-desktop/src/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -3267,7 +3267,10 @@
"noParameters": "This tool has no parameters.",
"noOutput": "No output yet.",
"enabled": "Enabled",
"requiredSuffix": " | required"
"requiredSuffix": " | required",
"selectField": "Select a field",
"fieldName": "Field name",
"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."
},
"sidecar": {
"unavailableTitle": "The processing server isn't available.",
Expand Down
5 changes: 4 additions & 1 deletion apps/geolibre-desktop/src/i18n/locales/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -3124,7 +3124,10 @@
"noParameters": "Esta herramienta no tiene parámetros.",
"noOutput": "Aún no hay salida.",
"enabled": "Activado",
"requiredSuffix": " | obligatorio"
"requiredSuffix": " | obligatorio",
"selectField": "Seleccione un campo",
"fieldName": "Nombre del campo",
"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."
},
"sidecar": {
"unavailableTitle": "El servidor de procesamiento no está disponible.",
Expand Down
5 changes: 4 additions & 1 deletion apps/geolibre-desktop/src/i18n/locales/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -3113,7 +3113,10 @@
"noParameters": "Cet outil n'a aucun paramètre.",
"noOutput": "Pas encore de sortie.",
"enabled": "Activé",
"requiredSuffix": " | requis"
"requiredSuffix": " | requis",
"selectField": "Sélectionner un champ",
"fieldName": "Nom du champ",
"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."
},
"sidecar": {
"unavailableTitle": "Le serveur de traitement n'est pas disponible.",
Expand Down
5 changes: 4 additions & 1 deletion apps/geolibre-desktop/src/i18n/locales/hi.json
Original file line number Diff line number Diff line change
Expand Up @@ -3113,7 +3113,10 @@
"noParameters": "इस टूल में कोई पैरामीटर नहीं है।",
"noOutput": "अभी कोई आउटपुट नहीं।",
"enabled": "सक्षम",
"requiredSuffix": " | आवश्यक"
"requiredSuffix": " | आवश्यक",
"selectField": "फ़ील्ड चुनें",
"fieldName": "फ़ील्ड नाम",
"vectorUnitsNote": "वेक्टर लेयर WGS84 के रूप में पढ़ी जाती हैं, इसलिए दूरी, अंतराल और सहनशीलता के मान मीटर में नहीं, डिग्री में होते हैं। 1° लगभग 111 किमी और 0.001° लगभग 111 मीटर के बराबर है।"
},
"sidecar": {
"unavailableTitle": "प्रोसेसिंग सर्वर उपलब्ध नहीं है।",
Expand Down
5 changes: 4 additions & 1 deletion apps/geolibre-desktop/src/i18n/locales/id.json
Original file line number Diff line number Diff line change
Expand Up @@ -3059,7 +3059,10 @@
"noParameters": "Alat ini tidak memiliki parameter.",
"noOutput": "Belum ada keluaran.",
"enabled": "Aktif",
"requiredSuffix": " | wajib"
"requiredSuffix": " | wajib",
"selectField": "Pilih bidang",
"fieldName": "Nama bidang",
"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."
},
"sidecar": {
"unavailableTitle": "Server pemrosesan tidak tersedia.",
Expand Down
5 changes: 4 additions & 1 deletion apps/geolibre-desktop/src/i18n/locales/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -3113,7 +3113,10 @@
"noParameters": "Questo strumento non ha parametri.",
"noOutput": "Nessun output ancora.",
"enabled": "Attivato",
"requiredSuffix": " | obbligatorio"
"requiredSuffix": " | obbligatorio",
"selectField": "Seleziona un campo",
"fieldName": "Nome del campo",
"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."
},
"sidecar": {
"unavailableTitle": "Il server di elaborazione non è disponibile.",
Expand Down
5 changes: 4 additions & 1 deletion apps/geolibre-desktop/src/i18n/locales/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -3059,7 +3059,10 @@
"noParameters": "このツールにはパラメータがありません。",
"noOutput": "まだ出力はありません。",
"enabled": "有効",
"requiredSuffix": " | 必須"
"requiredSuffix": " | 必須",
"selectField": "フィールドを選択",
"fieldName": "フィールド名",
"vectorUnitsNote": "ベクターレイヤーは WGS84 として読み込まれるため、距離・間隔・許容値の単位はメートルではなく度です。1° は約 111 km、0.001° は約 111 m です。"
},
"sidecar": {
"unavailableTitle": "処理サーバーを利用できません。",
Expand Down
5 changes: 4 additions & 1 deletion apps/geolibre-desktop/src/i18n/locales/ka.json
Original file line number Diff line number Diff line change
Expand Up @@ -3113,7 +3113,10 @@
"noParameters": "ამ ხელსაწყოს პარამეტრები არ აქვს.",
"noOutput": "შედეგი ჯერ არ არის.",
"enabled": "ჩართული",
"requiredSuffix": " | სავალდებულო"
"requiredSuffix": " | სავალდებულო",
"selectField": "აირჩიეთ ველი",
"fieldName": "ველის სახელი",
"vectorUnitsNote": "ვექტორული ფენები იკითხება WGS84-ში, ამიტომ მანძილის, ბიჯისა და ტოლერანტობის მნიშვნელობები გრადუსებშია და არა მეტრებში. 1° დაახლოებით 111 კმ-ია, ხოლო 0,001° — დაახლოებით 111 მ."
},
"sidecar": {
"unavailableTitle": "დამუშავების სერვერი მიუწვდომელია.",
Expand Down
5 changes: 4 additions & 1 deletion apps/geolibre-desktop/src/i18n/locales/ko.json
Original file line number Diff line number Diff line change
Expand Up @@ -3059,7 +3059,10 @@
"noParameters": "이 도구에는 매개변수가 없습니다.",
"noOutput": "아직 출력이 없습니다.",
"enabled": "사용",
"requiredSuffix": " | 필수"
"requiredSuffix": " | 필수",
"selectField": "필드 선택",
"fieldName": "필드 이름",
"vectorUnitsNote": "벡터 레이어는 WGS84로 읽으므로 거리, 간격, 허용 오차 값의 단위는 미터가 아니라 도입니다. 1°는 약 111km, 0.001°는 약 111m입니다."
},
"sidecar": {
"unavailableTitle": "처리 서버를 사용할 수 없습니다.",
Expand Down
5 changes: 4 additions & 1 deletion apps/geolibre-desktop/src/i18n/locales/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -3113,7 +3113,10 @@
"noParameters": "Dit gereedschap heeft geen parameters.",
"noOutput": "Nog geen uitvoer.",
"enabled": "Ingeschakeld",
"requiredSuffix": " | vereist"
"requiredSuffix": " | vereist",
"selectField": "Selecteer een veld",
"fieldName": "Veldnaam",
"vectorUnitsNote": "Vectorlagen worden als WGS84 gelezen, dus afstands-, tussenruimte- en tolerantiewaarden zijn in graden, niet in meters. 1° is ongeveer 111 km en 0,001° ongeveer 111 m."
},
"sidecar": {
"unavailableTitle": "De verwerkingsserver is niet beschikbaar.",
Expand Down
5 changes: 4 additions & 1 deletion apps/geolibre-desktop/src/i18n/locales/pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -3113,7 +3113,10 @@
"noParameters": "Esta ferramenta não tem parâmetros.",
"noOutput": "Ainda não há saída.",
"enabled": "Ativado",
"requiredSuffix": " | obrigatório"
"requiredSuffix": " | obrigatório",
"selectField": "Selecione um campo",
"fieldName": "Nome do campo",
"vectorUnitsNote": "As camadas vetoriais são lidas como WGS84, portanto os valores de distância, espaçamento e tolerância estão em graus, não em metros. 1° equivale a cerca de 111 km e 0,001° a cerca de 111 m."
},
"sidecar": {
"unavailableTitle": "O servidor de processamento não está disponível.",
Expand Down
5 changes: 4 additions & 1 deletion apps/geolibre-desktop/src/i18n/locales/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -3221,7 +3221,10 @@
"noParameters": "У этого инструмента нет параметров.",
"noOutput": "Вывода пока нет.",
"enabled": "Включено",
"requiredSuffix": " | обязательно"
"requiredSuffix": " | обязательно",
"selectField": "Выберите поле",
"fieldName": "Имя поля",
"vectorUnitsNote": "Векторные слои читаются в WGS84, поэтому значения расстояния, шага и допуска задаются в градусах, а не в метрах. 1° — примерно 111 км, а 0,001° — примерно 111 м."
},
"sidecar": {
"unavailableTitle": "Сервер обработки недоступен.",
Expand Down
5 changes: 4 additions & 1 deletion apps/geolibre-desktop/src/i18n/locales/tr.json
Original file line number Diff line number Diff line change
Expand Up @@ -3113,7 +3113,10 @@
"noParameters": "Bu aracın parametresi yok.",
"noOutput": "Henüz çıktı yok.",
"enabled": "Etkin",
"requiredSuffix": " | zorunlu"
"requiredSuffix": " | zorunlu",
"selectField": "Bir alan seçin",
"fieldName": "Alan adı",
"vectorUnitsNote": "Vektör katmanları WGS84 olarak okunur; bu nedenle mesafe, aralık ve tolerans değerleri metre değil derece cinsindendir. 1° yaklaşık 111 km, 0,001° ise yaklaşık 111 m'dir."
},
"sidecar": {
"unavailableTitle": "İşleme sunucusu kullanılamıyor.",
Expand Down
5 changes: 4 additions & 1 deletion apps/geolibre-desktop/src/i18n/locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -3059,7 +3059,10 @@
"noParameters": "此工具没有参数。",
"noOutput": "暂无输出。",
"enabled": "启用",
"requiredSuffix": " | 必填"
"requiredSuffix": " | 必填",
"selectField": "选择字段",
"fieldName": "字段名称",
"vectorUnitsNote": "矢量图层以 WGS84 读取,因此距离、间距和容差值的单位是度,而不是米。1° 约为 111 公里,0.001° 约为 111 米。"
},
"sidecar": {
"unavailableTitle": "处理服务器不可用。",
Expand Down
Loading
Loading