Skip to content

Maps: Geographic Annotation with Points, Lines, Polygons and Regions

Draw labelled points, lines and polygons on a map in Potato, pin places or label regions, over a GeoJSON outline, georeferenced image, GeoTIFF or your own tiles.

New in v2.12.0

geo_annotation puts a map on the annotation page. Annotators draw labelled points, lines and polygons on it, pin places, or give labels to the regions of an outline map. The map is OpenLayers 10.9.0, which Potato serves itself, so no map library loads from the internet.

yaml
annotation_schemes:
  - annotation_type: geo_annotation
    name: land_cover
    description: "Outline the land cover"
    task: draw
    tools: [polygon, freehand, point]
    labels:
      - {name: Water, color: "#0072B2"}
      - {name: Forest, color: "#009E73"}
    basemap:
      geojson: maps/regions.geojson

The Potato repository has two runnable examples. examples/advanced/dialect-map/ is a draw-a-map dialect survey on a fictional country that uses all three tasks (Perceptual Dialectology), and examples/image/geo-annotation/ outlines land cover over georeferenced images, one image per item, with a suggested polygon on the first item.

Tasks

taskWhat the annotator doesWhat is stored
draw (default)Draws points, lines, polygons and freehand polygons, and gives each a label, plus a typed name with feature_textOne GeoJSON feature per drawing
pinPlaces one point for each label. Each label is a question, such as "Where were you born?"One point per label
classify_regionPicks a label, then clicks regions of the basemap outline to give them that label. Clicking a region again with the same label clears itOne feature per labelled region, with geometry: null and the region ID

The basemap

Every scheme needs a basemap, in basemap or per item through basemap_field, and without one potato validate and the server refuse the config. Potato does not fall back to an internet map, so a study loads no map from the internet unless its config names one.

SourceConfigNotes
GeoJSON outlinebasemap: {geojson: maps/regions.geojson}A file under task_dir, embedded in the page. Keep it simplified, a few hundred KB at most, because it is part of every annotation page
Georeferenced imagebasemap: {image: /media/area.png, extent: [min_lon, min_lat, max_lon, max_lat]}A PNG or JPEG in plate carrée (equirectangular) projection covering extent
GeoTIFF or Cloud Optimized GeoTIFFbasemap: {geotiff: /media/area.tif}Read with geotiff.js 3.0.5, which ships with Potato. The file must be in EPSG:4326 or EPSG:3857, and the browser needs WebGL
XYZ tilesbasemap: {tiles: "https://tiles.example.org/{z}/{x}/{y}.png", attribution: "..."}Your own tile server or a provider you have an agreement with. attribution is required for an external URL, and {-y} is accepted for TMS numbering

A GeoJSON outline can be combined with one raster source, and the outline is drawn on top.

The OpenStreetMap Foundation's tile servers run on donated resources, and their tile usage policy forbids heavy use. A study with many annotators panning a map is heavy use, so Potato accepts the URL with a warning in the log. Host your own tiles or use a commercial provider.

basemap_field names an item field that holds the item's basemap: a GeoJSON FeatureCollection, a string URL (.tif and .tiff are read as GeoTIFF, anything else as GeoJSON), or an object with the same keys as basemap. The server checks each item's basemap against the same rules before sending the item, and an item may name an external server only when the scheme's own basemap names a URL on that host.

For classify_region, each outline feature is a region. Its ID is the region_id_property property (default id), then the GeoJSON feature ID, then r<index>, and its name is the region_name_property property (default name).

Options

KeyDefaultMeaning
taskdrawdraw, pin or classify_region
labelsrequiredStrings or {name, color, description}. Colours default to an Okabe-Ito palette. Quote Yes, No and On, which YAML otherwise loads as booleans
tools[point, line, polygon, freehand]draw only. A select-and-move tool is always added
feature_textofftrue, or {prompt, required, placeholder}: a text box per feature, stored as properties.text
basemapnonegeojson, image, extent, geotiff, tiles, attribution, max_zoom, region_id_property, region_name_property
basemap_fieldnoneItem field with a per-item basemap
preannotation_fieldnoneItem field with GeoJSON features to start from (not for classify_region)
coordinate_orderlonlatlatlon if the preannotation data or the outline writes latitude first. Output is always [lon, lat]
projectionEPSG:3857Display projection, EPSG:3857 (Web Mercator) or EPSG:4326. Storage is always EPSG:4326
center, zoomfit the basemapInitial view, with center: [lon, lat]
height440Map height in pixels, from 200 to 1200
snappingfalseSnaps new vertices to existing features and to the outline
min_features, max_features1 if required, nonedraw only. Drawing tools are disabled at the maximum
require_all_regionsfalseclassify_region: every region must get a label before Next
label_requirementnone{required: true} blocks Next until the task's own condition holds: min_features features for draw, a pin for every label for pin, at least one region (or all) for classify_region

Using the map

To draw, choose a label, then a tool. A point is one click. A line is a click per point and a double-click on the last. A polygon is a click per corner, finished by clicking the first corner, double-clicking or pressing Enter, with Backspace removing the last corner and Esc cancelling. Freehand is press and drag. The select tool reshapes a feature by its outline or a vertex, and Delete removes it. Every feature appears in the list under the map with a label menu, the name box, Show and Delete.

For pins, the list shows one row per label, and clicking the map places the active one. For regions, pick a label button and click regions.

From the keyboard, Tab to the map, then the arrow keys pan and + and - zoom. Add point at map centre and Use map centre place points without a mouse, and every feature's label, name and deletion are reachable through the list.

Features from preannotation_field are marked "(suggested)". They are not saved as the annotator's answer until the annotator edits one or presses Accept suggestions, so an item the annotator only opened does not count as labelled.

Stored data

The answer is a GeoJSON FeatureCollection in EPSG:4326 following RFC 7946: positions are [longitude, latitude], exterior rings run counter-clockwise and holes clockwise, coordinates have 7 decimals (about 1 cm), and longitudes are wrapped into -180 to 180.

json
{"type": "FeatureCollection", "features": [
  {"type": "Feature", "id": "f1",
   "geometry": {"type": "Polygon", "coordinates": [[[-29.0, -38.3], [-28.2, -38.3], [-28.6, -37.5], [-29.0, -38.3]]]},
   "properties": {"label": "Very different", "text": "Coast talk", "tool": "polygon"}}
]}

The scheme also works on survey pages, where there is no item, so basemap_field and preannotation_field do nothing there.

Export

bash
# Every feature as GeoJSON, one file per item (geojson/<item>.geojson)
python -m potato.export --config config.yaml --format geojson --output out/
# ... or one file for the whole project
python -m potato.export --config config.yaml --format geojson --output out/ --option layout=single
 
# Heat maps of overlapping polygons, plus CSVs of names and region labels
python -m potato.export --config config.yaml --format geo_overlap --output out/

The GeoJSON export normalises every feature again and adds item_id, user_id, schema and feature_id to its properties. Features that cannot be normalised, such as a position out of range or a ring with no area, are left out and listed in the export warnings. classify_region answers get their region geometry back from the outline. geo_overlap is described in Perceptual Dialectology.

Limits

  • A polygon that crosses the 180° meridian is not split, so draw two polygons.
  • A GeoTIFF in a projection other than EPSG:4326 or EPSG:3857 does not show, and neither does any GeoTIFF without WebGL. The page says so, and the outline still works.
  • A GeoTIFF is drawn without interpolation, so zooming in shows the real pixel boundaries.
  • For pixel coordinates on an ordinary image rather than geographic ones, use image annotation.

Further Reading

For implementation details, see the source documentation.