SuperAPI AI Directory

maproulette-tagfix

General & other

View repository

Updated 10-15 21:42

About this skill

MapRoulette Tag Fix challenges

Example task: examples/tag-fix-task.ndjson. Docs: Tag Fix, external IDs, rebuild, line-by-line GeoJSON.

1. Emit line-by-line GeoJSON (because Tag Fix needs it)

Do: one task = one line = one FeatureCollection, each line prefixed with RFC 7464 RS (\x1E).

Because: Tag Fix cooperativeWork is ignored on traditional multi-feature GeoJSON; you get normal tasks instead.

const rs = String.fromCharCode(0x1e)
stream.write(`${rs}${JSON.stringify(taskFeatureCollection)}\n`)

2. Use one stable OSM type/id everywhere (because rebuild + editors need it)

Do: build osmId = "${type}/${id}" as a string (way/123, node/456, relation/789). Put the same value in:

Place Why
features[0].properties.id External task ID; visible in task props; Mustache {{id}}; API lookup …/challenge/{id}/task/{type%2Fid}
features[0].id Same ID on the feature (rebuild matching)
cooperativeWork.operations[].data.id Tag Fix target element

Because: MapRoulette matches rebuilds and editor preselect on that external ID. Wrong or mismatched IDs → duplicates or broken Tag Fixes.

Do not: use a bare numeric OSM id, a different string in cooperativeWork than on the feature, or {{osmIdentifier}} in instructions — use properties.id and {{id}} only (osmIdentifier is a legacy name in some older feeds).

3. Build Tag Fix cooperativeWork (because that is the proposed change)

Do:

"cooperativeWork": {
  "meta": { "version": 2, "type": 1 },
  "operations": [{
    "operationType": "modifyElement",
    "data": {
      "id": "way/123",
      "operations": [
        { "operation": "setTags", "data": { "cycleway:both": "no", "source": "official data" } },
        { "operation": "unsetTags", "data": ["old_tag"] }
      ]
    }
  }]
}
  • meta.type: 1 = Tag Fix (approve/reject in MR; MapRoulette writes to OSM). type: 2 = OSC cooperative (JOSM).
  • Only encode pending tag changes — not tags already on OSM. The Tag Fix UI may list current tags; cooperativeWork must still be delta-only.
  • One setTags can set several keys on one element. Use unsetTags when removing tags (with tag names in the array) — omit empty unsetTags: [].
  • Geometry is for the map pin only (Point or LineString); it is not sent as the edit — only cooperativeWork is.
  • When Tag Fix auto-writes to OSM from official or third-party data, filter tasks by licence/compatibility before export — do not propose tags the data licence does not allow.

4. Wire task_markdown + challenge instruction (because body text is Mustache-templated)

Do — per task properties:

properties: {
  id: osmId, // also used as {{id}} in the challenge instruction
  task_markdown: markdownBody.replaceAll('\n', ' \n'), // space before \\n keeps MR markdown line breaks
  task_updated_at: new Date().toISOString(),
}

Do — challenge instruction template (set once on create/update):

## Kontext {{id}}

{{task_markdown}}

(Letzte Aktualisierung der Aufgabe: {{task_updated_at}})

Because: Mustache in the challenge instruction pulls feature properties. Putting {{id}} at the start gives mappers an immediate OSM reference. Keep long per-task copy in task_markdown; keep shared framing (title, update stamps) in the challenge instruction.

5. Point the challenge at remote GeoJSON, then rebuild to update

Do — create/update challenge: set remoteGeoJson to your data URL, or upload line-by-line GeoJSON. A static file (e.g. GitHub Pages, public/… regenerated in CI) works the same as a live API — MapRoulette fetches the URL on rebuild.

Do — Tag Fix layout: include TagDiffWidget in taskWidgetLayout so mappers get approve/reject tag diffs in the task UI.

Do — refresh tasks after source data changes (shell / GitHub Actions step):

CHALLENGE_ID=12345
MAPROULETTE_API_KEY=your-api-key   # or ${{ secrets.MAPROULETTE_API_KEY }} in Actions

curl --fail -sS -X PUT \
  "https://maproulette.org/api/v2/challenge/${CHALLENGE_ID}/rebuild?removeUnmatched=true&skipSnapshot=true" \
  -H "apiKey: ${MAPROULETTE_API_KEY}" \
  -H "accept: */*"

Or UI: Challenge → Rebuild (optionally “remove incomplete first”).

Because: create/update only changes challenge metadata; tasks are (re)built from the GeoJSON source. The feed can refresh more often than you rebuild — schedule rebuilds when MR should pick up new tasks. Stable type/id IDs let MapRoulette match existing tasks instead of duplicating. removeUnmatched=true drops incomplete tasks no longer in the feed.

Optional — mixed priority: set properties.priority (e.g. prio1, prio2) and matching challenge priority rules when one challenge mixes high- and low-value fixes.

Checklist

  • Line-by-line FeatureCollections with \x1E prefix
  • Same string type/id on properties.id, cooperativeWork…data.id, and ideally feature.id
  • cooperativeWork.meta = { version: 2, type: 1 }
  • cooperativeWork is delta-only; no empty unsetTags
  • Challenge instruction starts with ## Kontext {{id}} then {{task_markdown}} (not {{osmIdentifier}})
  • Newlines in task_markdown normalized with .replaceAll('\n', ' \n')
  • After data change: rebuild (API or UI), not only challenge PUT

Reference