[openstreetmap/openstreetmap-website] Implement JSON support for the changeset download API (PR #5973)

Jake Low notifications at github.com
Thu May 15 20:06:47 UTC 2025


jake-low left a comment (openstreetmap/openstreetmap-website#5973)

> Edit: I've just noticed that https://github.com/OSMCha/osmchange-parser has already implemented the re-grouping. Maybe @jake-low has some insights to share on the JSON format.

Just dropping in to respond to this ping, sorry it took a while.

I extracted osmchange-parser from the backend of OSMCha last year while doing some refactoring. I published it as its own package on the off chance it would be useful somewhere else, but to my knowledge nobody else is currently using it. The JSON structure emitted by that parser isn't something I gave a lot of thought to; it mirrors the input XML structure pretty closely simply because that was easy to accomplish with a SAX-style parser.

I didn't see anyone respond above to @joto's point, but I'd like to reiterate it: I don't think osmChange files need to categorize edits into `create`/`modify`/`delete` groups explicitly. These groups are implied by other properties that are also included in the file (the element's `version` number and `visible` boolean). Implementations that deal with osmChange files need to handle cases where the stated type of the edit doesn't match its semantics ([example](https://github.com/bdon/OSMExpress/blob/main/python/examples/augmented_diff.py#L126-L134)), which means they might as well just ignore the stated type all the time, and instead infer the type from the `version` and `visible` attributes.

Given this, I think a candidate JSON schema that's worth considering is one like this:

```js
{
  "version": "0.6",
  "generator": "OpenStreetMap server",
  "copyright": "OpenStreetMap and contributors",
  "attribution": "http://www.openstreetmap.org/copyright",
  "license": "http://opendatacommons.org/licenses/odbl/1-0/",
  "elements": [
    // this is an array of elements in normal OSM order
    // (sorted by type, then ID, then version number).
    {
      "type": "node",
      "id": 349018659,
      "lat": 47.4624576,
      "lon": -121.4782337,
      "timestamp": "2024-08-19T15:53:56Z",
      "version": 7,
      "changeset": 155467928,
      "user": "Mateusz Konieczny - bot account",
      "uid": 3199858,
      "tags": {
        "ele": "1907.7",
        "gnis:feature_id": "1521553",
        "name": "Kaleetan Peak",
        "natural": "peak",
        "source": "USGS",
        "wikidata": "Q49040648",
        "wikipedia": "en:Kaleetan Peak"
      }
    },
    // ... followed by more elements (omitted in this example) ...
  ]
}
```

This has the advantage that the response is the _exact_ same schema as if you fetched an element individually from the API (e.g. from `/node/:id` or `/node/:id/history`). For comparison:

```
λ curl -s 'https://www.openstreetmap.org/api/0.6/node/349018659' -H 'Accept: application/json' | jq
{
  "version": "0.6",
  "generator": "openstreetmap-cgimap 2.0.1 (764192 spike-08.openstreetmap.org)",
  "copyright": "OpenStreetMap and contributors",
  "attribution": "http://www.openstreetmap.org/copyright",
  "license": "http://opendatacommons.org/licenses/odbl/1-0/",
  "elements": [
    {
      "type": "node",
      "id": 349018659,
      "lat": 47.4624576,
      "lon": -121.4782337,
      "timestamp": "2024-08-19T15:53:56Z",
      "version": 7,
      "changeset": 155467928,
      "user": "Mateusz Konieczny - bot account",
      "uid": 3199858,
      "tags": {
        "ele": "1907.7",
        "gnis:feature_id": "1521553",
        "name": "Kaleetan Peak",
        "natural": "peak",
        "source": "USGS",
        "wikidata": "Q49040648",
        "wikipedia": "en:Kaleetan Peak"
      }
    }
  ]
}
```

This is nice especially in strongly typed languages: if you have a type definition for an `Element` as returned by the OSM API, you can reuse that definition when fetching data from any of these endpoints. (I'm ignoring the `if-unused` attribute used for uploading in these examples for simplicity)

```typescript
// this type definition works for responses from several different endpoints
// (/node/:id, /node/:id/history, /changeset/:id/download, etc)
type OSMResponse = {
  version: string;
  generator: string;
  copyright: string;
  attribution: string;
  license: string;
  elements: Element[];
}

type Element = Node | Way | Relation;

type Node = {
  type: 'node';
  id: number;
  lat: number;
  lon: number;
  timestamp: string;
  version: number;
  changeset: number;
  user: string;
  uid: number;
  tags: Record<string, string>;
}

type Way = ...
```

-- 
Reply to this email directly or view it on GitHub:
https://github.com/openstreetmap/openstreetmap-website/pull/5973#issuecomment-2884935872
You are receiving this because you are subscribed to this thread.

Message ID: <openstreetmap/openstreetmap-website/pull/5973/c2884935872 at github.com>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.openstreetmap.org/pipermail/rails-dev/attachments/20250515/54cd311c/attachment-0001.htm>


More information about the rails-dev mailing list