★ New · feat/form-builder

Point a layer at it. Get a form.

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.

PostGISGeoPackageJSON Schema HTML · React · Vue · AngularValidationDuckDB-WASMPWA · offline
How it works

Schema-first, not hand-drawn

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.

  • Two live sources — PostGIS tables (/generate/postgis) and GPKG vector layers (/generate/gpkg); schema preview for both before you commit.
  • 21 inferred field typesText, Number, Date/DateTime, Boolean, Select, Geometry, Json, Color, Range and more — with auto-generated labels and grouping.
  • 6 layout strategies — Vertical, Horizontal, Grid, Tabs, Accordion, and multi-step Wizard; responsive by default.
  • Generation options — toggle geometry / primary-key / foreign-keys inclusion, group related fields, infer types, generate validation, and exclude columns like created_by.
POST /api/dynamicform/generate/gpkg
// 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
Generation & options

From column metadata to a usable form

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.

🔎

Introspection

Reads data_type, nullability, max_length, precision/scale, PK/FK, and is_geometry + geometry_type + srid straight from the layer.

🏷️

Auto labels & grouping

autoGenerateLabels humanizes column names; groupRelatedFields clusters them into collapsible FormGroups and FormSections.

Validation

generateValidation emits Required, MinLength/MaxLength, MinValue/MaxValue, Pattern, email/url/phone rules per field.

🎛️

Field overrides

Per-field fieldOverrides let you swap widgets, set defaults, placeholders, help text, or mark a column read-only — without touching the source table.

📐

Geometry aware

Geometry columns map to a Geometry field type; opt in via includeGeometry so the form can capture or edit the feature shape alongside attributes.

🗂️

Templates

Three starters — basic, grouped, complete — returned from GET /api/dynamicform/templates to bootstrap a new survey fast.

The API surface

Generate, preview, render, manage

CapabilityEndpointNotes
Generate (generic)POST /api/dynamicform/generateSingle request body, picks source by DataSourceType
Generate from PostGISPOST /api/dynamicform/generate/postgisConnection string + table + schema + geom/PK columns
Generate from GPKGPOST /api/dynamicform/generate/gpkgFile path + layer name + geom/PK columns
PreviewPOST /api/dynamicform/previewEstimated field count & layout, no full generate
SchemaPOST /api/dynamicform/schema/{postgis,gpkg}Raw column metadata before building a form
RenderPOST /api/dynamicform/{id}/renderReturns html + css + javascript
Standalone HTMLGET /api/dynamicform/{id}/html?theme=Full <!DOCTYPE> page, ready to host
ManageGET · DELETE /api/dynamicform/{id}Fetch or remove a stored form
TemplatesGET /api/dynamicform/templatesbasic · 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.

Render targets

Five output formats, one schema

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.

🌐

HTML

Standalone, themed HTML with inline CSS + JS — host it anywhere or embed it in the desktop preview.

⚛️

React

React-renderable output for Next.js / SPA integration.

💚

Vue

Vue-renderable output for Vue 3 apps.

🅰️

Angular

Angular-renderable output for enterprise Angular shells.

{ }

JSON

Portable JSON Schema for any RJSF-compatible renderer or downstream pipeline.

🎨

Themes

Pass ?theme=default on the HTML route to swap the look without regenerating the form.

Field collection PWA · form_builder/

Take it to the field — offline

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.

  • Four design on-ramps — import an Appsmith JSON design, reverse-engineer a schema from an existing DB (SQLite / GPKG / Spatialite / ODBC / MySQL / Postgres), prompt an in-browser LLM for a starter form, or import your own JSON.
  • Spatial field components — capture Lat/Long (DD or DMS), MGRS, USNG, and GARS, with map click-to-select and geometry editing.
  • Related tables, attachments & conditional logic — inline child records, photo/video/document blobs, and rules that show/hide fields based on other answers.
  • Map-backed attribute grid — sync the table to the map bbox, or query the nearest records to a point; click a row to zoom and open its form.
  • Export — GeoJSON, CSV, Shapefile, or GeoPackage, straight from DuckDB-WASM.
form_builder/ · package.json
// 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
PWA field components

Purpose-built for geospatial collection

The FormBuilder, FormDesigner, and specialized fields below are the building blocks the PWA composes — each driven by a JSON Schema + UI Schema pair.

🛠️

FormBuilder

Render a schema-driven form bound to a DuckDB table; onSubmit/onChange/onError hooks, readonly & disabled modes, reset button.

🎨

FormDesigner

Visual schema editor with live preview — edit JSON Schema + UI Schema side by side and save the result.

📍

SpatialFormField

Capture Lat/Long (DD & DMS), MGRS, USNG, GARS — with optional map click-to-select.

📎

AttachmentField

Drag-and-drop photos, video, and documents; preview, type/size validation, stored as blobs.

🔗

RelatedTableField

Inline add / edit / remove child records with a sortable, filterable, paginated table view.

🔀

Conditional & Chained

ConditionalField shows fields on rules; ChainedSelectField loads dependent options async.

🎙️

Voice-enabled

VoiceEnabledForm + VoiceFormControl fill fields by voice command (Qwen3-backed).

🌐

Translation & TTS

Inline translate between languages and read-back text-to-speech for accessibility.

🔁

Re-survey

Update previously collected records in place — versioned edits, not duplicate rows.

From schema to survey in minutes

Generate validated forms from the layers you already have, render them anywhere, and collect offline — all from one desktop.