The Dynamic Form Builder introspects a PostGIS table or a GeoPackage vector layer and generates a complete, validated data-entry form - labels, types, options, geometry handling, and layout - then renders it to live HTML. Pair it with the offline-capable field-collection PWA to collect, edit, and export spatial records with zero infrastructure.
Form Builder walkthrough - navigating between forms
No more re-keying column names into form fields by hand. The service reads the real column metadata - data type, nullable, max-length, precision, primary/foreign keys, geometry type and SRID - and turns each column into a typed, validated FormField. Exclude the noise, keep the structure.
/generate/postgis) and GPKG vector layers (/generate/gpkg); schema preview for both before you commit.Text, Number, Date/DateTime, Boolean, Select, Geometry, Json, Color, Range and more - with auto-generated labels and grouping.created_by.// GPKG layer โ typed form (shared by PostGIS path) { "formName": "Building Survey", "title": "Building Survey Data Entry", "filePath": "Data/GPKG/survey_points.gpkg", "layerName": "survey_points", "geometryColumn": "geom", "primaryKey": "fid", "options": { "includeGeometry": true, "groupRelatedFields": true, "generateValidation": true, "excludedColumns": ["created_by", "updated_at"] } } โ DynamicForm with N typed FormFields โ render to HTML / React / Vue / Angular
Every FormField carries its own type, constraints, options, validation rules, and the original ColumnInfo it was derived from - so the form stays truthful to the underlying table even after you override individual fields.
Reads data_type, nullability, max_length, precision/scale, PK/FK, and is_geometry + geometry_type + srid straight from the layer.
autoGenerateLabels humanizes column names; groupRelatedFields clusters them into collapsible FormGroups and FormSections.
generateValidation emits Required, MinLength/MaxLength, MinValue/MaxValue, Pattern, email/url/phone rules per field.
Per-field fieldOverrides let you swap widgets, set defaults, placeholders, help text, or mark a column read-only - without touching the source table.
Geometry columns map to a Geometry field type; opt in via includeGeometry so the form can capture or edit the feature shape alongside attributes.
Three starters - basic, grouped, complete - returned from GET /api/dynamicform/templates to bootstrap a new survey fast.
| Capability | Endpoint | Notes |
|---|---|---|
| Generate (generic) | POST /api/dynamicform/generate | Single request body, picks source by DataSourceType |
| Generate from PostGIS | POST /api/dynamicform/generate/postgis | Connection string + table + schema + geom/PK columns |
| Generate from GPKG | POST /api/dynamicform/generate/gpkg | File path + layer name + geom/PK columns |
| Preview | POST /api/dynamicform/preview | Estimated field count & layout, no full generate |
| Schema | POST /api/dynamicform/schema/{postgis,gpkg} | Raw column metadata before building a form |
| Render | POST /api/dynamicform/{id}/render | Returns html + css + javascript |
| Standalone HTML | GET /api/dynamicform/{id}/html?theme= | Full <!DOCTYPE> page, ready to host |
| Manage | GET ยท DELETE /api/dynamicform/{id} | Fetch or remove a stored form |
| Templates | GET /api/dynamicform/templates | basic ยท grouped ยท complete presets |
In-app designer ships at /form-builder.html with PostGIS / GPKG tabs, options checkboxes, and a live Form Preview panel. Source: Controllers/DynamicFormController.cs, Services/DynamicFormService.cs, Services/FormRenderingService.cs, Models/FormModels.cs.
The FormRenderFormat enum emits to the framework you already use - so a generated survey can drop straight into an existing app, not just the in-app preview.
Standalone, themed HTML with inline CSS + JS - host it anywhere or embed it in the desktop preview.
React-renderable output for Next.js / SPA integration.
Vue-renderable output for Vue 3 apps.
Angular-renderable output for enterprise Angular shells.
Portable JSON Schema for any RJSF-compatible renderer or downstream pipeline.
Pass ?theme=default on the HTML route to swap the look without regenerating the form.
A bundled Next.js PWA (geospatial-form-builder) turns generated designs into a real data-collection app. Built on MapLibre GL JS and DuckDB-WASM, it runs fully offline with no CDN dependencies, stores records in-browser, and exports to the formats your GIS expects.
// offline PWA stack (no CDN, no internet required) { "name": "geospatial-form-builder", "dependencies": { "next": "^16.2.10", "react": "^19.2.7", "maplibre-gl": "^5.24.0", "react-hook-form": "^7.81.0", "lets-form": "^0.13.1" }, "devDependencies": { "@ducanh2912/next-pwa": "^10.2.9", "tailwindcss": "^4.3.3" } } โ DuckDB-WASM via POST /api/duckdb/{id}/query โ installable, air-gap-ready
The FormBuilder, FormDesigner, and specialized fields below are the building blocks the PWA composes - each driven by a JSON Schema + UI Schema pair.
Render a schema-driven form bound to a DuckDB table; onSubmit/onChange/onError hooks, readonly & disabled modes, reset button.
Visual schema editor with live preview - edit JSON Schema + UI Schema side by side and save the result.
Capture Lat/Long (DD & DMS), MGRS, USNG, GARS - with optional map click-to-select.
Drag-and-drop photos, video, and documents; preview, type/size validation, stored as blobs.
Inline add / edit / remove child records with a sortable, filterable, paginated table view.
ConditionalField shows fields on rules; ChainedSelectField loads dependent options async.
VoiceEnabledForm + VoiceFormControl fill fields by voice command (Qwen3-backed).
Inline translate between languages and read-back text-to-speech for accessibility.
Update previously collected records in place - versioned edits, not duplicate rows.
Generate validated forms from the layers you already have, render them anywhere, and collect offline - all from one desktop.