Geo fields

Store points, lines, polygons and their multi-part forms in collections as GeoJSON, and import or export geographic data.

#Geo fields

A geo field stores one GeoJSON geometry per record. There are seven geo field types:

Type Stores Use for
point A single [lng, lat] coordinate Shop locations, waypoints, addresses
linestring An ordered sequence of coordinates Routes, trails, pipelines
polygon A closed ring of coordinates (with optional holes) Boundaries, zones, catchment areas
multipoint Several coordinates as one value A chain of shops, every entrance to a park
multilinestring Several disconnected lines as one value A river and its tributaries
multipolygon Several separate areas as one value Island groups, a council split by a river
geometry Any of the above — the editor picks the type at input time Mixed datasets

Each of the first six accepts only its own GeoJSON type. A multipolygon field refuses a bare Polygon exactly as a polygon field refuses a MultiPolygon; if a collection needs both, the field is a geometry. Being strict is the point — anything reading the column back can rely on the shape.

GeometryCollection is not supported by any of them. It mixes geometry types inside one value, and every per-part rule in the spatial index assumes one type per value. Store one in a json field instead; it will not be spatially indexed.

#Parts

A multi geometry is a list of parts — the members of a MultiPolygon are whole polygons, the members of a MultiLineString whole lines. The point of storing them as one value rather than as several records is that they are one thing with one set of attributes: an island group has a single name, a single owner, a single set of dates.

Not every operator sees the parts, though, and Spatial queries says which do. _geo_within and _geo_dwithin resolve a record to a single stored point instead of testing its parts — a permanent property of those two rather than a gap waiting to be filled.

A one-part multi geometry is perfectly valid, and common — many exporters emit MultiPoint for a single dropped pin. The dashboard's map editor draws one part at a time and wraps it for you; several parts are entered through its GeoJSON editor.

#How storage works

Each geo field named {name} expands into twelve physical columns in the collection's D1 table:

Column Type Purpose
{name}_json TEXT Canonical GeoJSON geometry
{name}_lat REAL Centroid latitude
{name}_lng REAL Centroid longitude
{name}_rep_lat REAL Representative point latitude — a point guaranteed inside the geometry
{name}_rep_lng REAL Representative point longitude
{name}_s2_13 INTEGER Spatial index cell ID, coarse level
{name}_s2_17 INTEGER Spatial index cell ID, fine level
{name}_bbox TEXT Bounding box [west, south, east, north] (non-point geometries only)
{name}_bbox_w REAL The bounding box's west edge, indexed
{name}_bbox_s REAL The bounding box's south edge, indexed
{name}_bbox_e REAL The bounding box's east edge, indexed
{name}_bbox_n REAL The bounding box's north edge, indexed

The bounding box is stored twice on purpose. {name}_bbox is the readable canonical value; the four _bbox_* columns are the same four numbers in a shape a database index can search, which is what keeps a viewport query off a full table scan. Both are written from one computation, so they cannot disagree.

Everything except points also stores covering cell ranges in an _s2_coverings table, so spatial queries can find a record by its extent rather than only by a single point. The covering is computed per part, so an island group is indexed as its islands rather than as one box around the sea between them.

#The two stored points

A geometry that is not a point stores two points standing in for it, and they answer different questions:

  • Centroid (_lat / _lng) — the balance point. It is a good "where is this, roughly" for sorting and for placing a label, and it is what distance calculations use. It is not guaranteed to lie inside the geometry: the centroid of a horseshoe is in the gap, and the centroid of an archipelago is in open water.
  • Representative Point (_rep_lat / _rep_lng) — a point guaranteed to lie inside the geometry, in its largest part. This is the one to use when "inside" is what matters.

Both are computed on write; there is nothing to configure. The reasoning is recorded in ADR 0021.

#Accepted input formats

When you write a record, the geo field accepts:

  • A GeoJSON geometry object: { "type": "Point", "coordinates": [151.2093, -33.8688] }
  • A { lat, lng } shorthand object (points only), converted to a GeoJSON Point
  • Either of the above as a JSON string

Coordinates in GeoJSON are always [longitude, latitude] — the reverse of the { lat, lng } shorthand.

Important

A single part whose consecutive coordinates jump the antimeridian (180°) is refused on write, for every geo type — within one part, longitude is a flat number line. Split such data at 180° into several parts, as RFC 7946 §3.1.9 directs; that IS one of the things multi geometries are for, and a split geometry is stored and queried correctly, its bounding box taken on the shortest arc of longitude containing every part. See ADR 0022.

#HTTP

POST /api/v1/collections/campsites/records
Authorization: Bearer sk-your-api-key
Content-Type: application/json

{
  "name": "Acacia Flat",
  "location": { "type": "Point", "coordinates": [150.3542, -33.6412] }
}

#SDK

import { EmuView } from '@emuview/sdk';

const sdk = new EmuView({ url: 'https://your-api.example.com', apiKey: 'sk-your-api-key' });

const campsite = await sdk.collection('campsites').create({
	name: 'Acacia Flat',
	location: { lat: -33.6412, lng: 150.3542 } // shorthand — stored as a GeoJSON Point
});

#Editing with the map input

In the record form, a geo field renders as an interactive map:

  • Point placement — click the map or drag the marker
  • Line drawing — click to add vertices, double-click to finish
  • Polygon drawing — click to add vertices, click the first vertex to close the ring
  • Manual coordinates — lat/lng number inputs (point mode)
  • GPS location — a "use my location" button backed by browser geolocation (point mode)
  • GeoJSON editor — a raw JSON text editor, for pasting geometries. It applies the same validation the API does, so a geometry the field cannot hold — a MultiPolygon pasted into a point field, say — is rejected in the form rather than at save time
  • Address search — geocoding via OpenStreetMap Nominatim, showing up to five results
  • Geometry mode switcher — Point / Line / Polygon buttons, shown for geometry fields

Existing lines and polygons can be edited vertex by vertex: drag a vertex to move it, click + to insert one, Shift+click to delete one.

#When the map is read-only

The vertex editor models a shape as one flat list of positions, so there are shapes it cannot change without losing part of them. Rather than change them anyway, it shows the geometry read-only, says why, and leaves the GeoJSON editor available — which can edit any of them.

That happens in two cases:

  • A shape the vertex list cannot rebuild. A polygon with interior rings (holes), and the three multi-part types — MultiPoint, MultiLineString and MultiPolygon. The rule is the round trip itself, not a list of types: if reading the geometry into vertices and rebuilding it does not return the same geometry, the map will not open it.
  • More than 500 vertices, whatever the type. The editor creates about two map markers and two DOM nodes per vertex and rebuilds all of them after every change. A single imported administrative boundary is far above this.

Multi-part geometries still display normally, both here and in map views — only editing them on the map is refused. Drawing a multi-part geometry with several parts by hand is not supported; import one, or paste it into the GeoJSON editor. Drawing ONE part is: click a multipoint field and you get a one-member MultiPoint, draw on a multipolygon field and you get a one-member MultiPolygon. There is no gesture for "start a second island".

#Map input options

Configure the input per field via interfaceOptions:

Option Type Default Description
mapInitialCenter [lng, lat] [0, 20] Map centre when the field is empty
mapZoom number 2 Initial zoom level when the field is empty
mapStyle string System basemap MapLibre style URL for the editor basemap
geocodingEnabled boolean true for point and multipoint fields Show the address search bar
gpsLocation boolean true Show the GPS location button in point mode

#Importing geo data

Import records from a file with POST /api/v1/collections/:name/records/import. Four formats are supported, detected from the file extension or content when format isn't given:

Format Detection Notes
GeoJSON .geojson, .json, or JSON content Accepts a FeatureCollection, a Feature array, or a single Feature. Feature properties map to fields by name
CSV .csv Latitude/longitude columns map to a point field; other columns map to fields by header name
KML .kml or XML content Placemarks convert to GeoJSON; ExtendedData becomes properties
GPX .gpx or XML content Waypoints, tracks, and routes convert to GeoJSON

In the dashboard, the import dialog walks through four steps: Upload → Mapping (CSV only) → Importing → Complete.

const result = await sdk.collection('campsites').geoImport(file, {
	format: 'geojson', // or 'csv' — omit to auto-detect
	geoField: 'location', // target geo field; defaults to the first geo field
	latColumn: 'lat', // CSV only
	lngColumn: 'lng' // CSV only
});

// { imported: 120, failed: 2, total: 122, errors: [...] }

#Exporting geo data

Export records with GET /api/v1/collections/:name/records/export:

Format Content type
csv text/csv
json application/json
geojson application/geo+json

GeoJSON export produces a FeatureCollection. Use geo_field to choose which geo field supplies the geometry when a collection has more than one; other fields become feature properties.

const blob = await sdk.collection('campsites').export({
	format: 'geojson',
	geoField: 'location'
});
Note

Exports respect row-level access rules — users only export records they can read.

  • Spatial queries — filter records by location using the stored index
  • Field types — all collection field types, including geo types
  • Map views — browse and style geo records on a map