Skip to content

feat(thing): expose observedArea on Thing for STAC and DCAT-AP spatial extent#124

Open
Vishmayraj wants to merge 1 commit into
istSOS:mainfrom
Vishmayraj:feat/thing-observed-area
Open

feat(thing): expose observedArea on Thing for STAC and DCAT-AP spatial extent#124
Vishmayraj wants to merge 1 commit into
istSOS:mainfrom
Vishmayraj:feat/thing-observed-area

Conversation

@Vishmayraj
Copy link
Copy Markdown
Contributor

@Vishmayraj Vishmayraj commented Mar 23, 2026

What this does

Adds observedArea as a computed derived field on the Thing entity, returning the aggregated spatial footprint of each Thing in a single GET /Things response.

{
  "@iot.id": 1,
  "name": "thing_name_1",
  "observedArea": {
    "type": "Polygon",
    "coordinates": [[[97.72, 25.12], [-165.38, 37.54], "..."]]
  }
}

Spec note

observedArea is defined by OGC STA 1.1 on Datastream, not on Thing. Noticing the already made extensions from the standard entity Thing by adding Commit, this PR adds observedArea as a deliberate extension at the Thing level, consistent with istSOS4's existing pattern.

Why this matters

In the STA to DCAT-AP 3.0 mapping, Thing is best modelled as a dcat:DatasetSeries a device-level grouping container for all its Datastreams. Connectors and harvesters that build a device-level catalog index need the spatial footprint of each Thing without having to expand into its Datastreams.

Without this field, that requires N requests:

GET /Things
GET /Things(1)/Datastreams   # to get observedArea
GET /Things(2)/Datastreams   # ...
GET /Things(N)/Datastreams   # ...

With this field, a single GET /Things gives the full device index including spatial extent in one call. This is paired with the phenomenonTime PR which does the same for temporal extent, together enabling complete dcat:DatasetSeries metadata from single request.

Note: per the DCAT-AP mapping spec, dct:spatial on dcat:Dataset draws from
Datastream.observedArea directly. This field targets the dcat:DatasetSeries
(Thing) level, not the Dataset (Datastream) level.

How it works

The PostgreSQL function observedArea(sensorthings."Thing") already existed in the schema. It computes ST_ConvexHull(ST_Collect(...)) across all Datastreams belonging to the Thing, returning NULL if no Datastreams have geometry yet. This PR wires it up to the API layer with minimal changes:

  • models/thing.py -> declares observed_area = Column("observedArea", Geometry) on the Thing ORM model so SQLAlchemy can select it
  • sta2rest.py -> adds observed_area to Thing's DEFAULT_SELECT so it appears in all Thing responses without needing $select

The existing get_select_attr in visitors.py already handles Geometry columns by wrapping them in ST_AsGeoJSON, so the output is clean GeoJSON with no additional changes needed.

Testing

GET /Things                              -> each Thing includes observedArea as GeoJSON or null
GET /Things(1)                           -> single Thing includes observedArea
GET /Things?$select=id,name,observedArea -> $select works correctly
Screenshot 2026-03-23 182446 Screenshot 2026-03-23 172229

@ClaudioPrimerano
Copy link
Copy Markdown
Collaborator

Hi @Vishmayraj I think this should go into a separate branch/PR, since it looks like a distinct feature (Thing.observedArea exposure for STAC/DCAT-AP) rather than part of the current scope. Keeping it separate would make the change easier to review and track.

@Vishmayraj
Copy link
Copy Markdown
Contributor Author

Hi @Vishmayraj I think this should go into a separate branch/PR, since it looks like a distinct feature (Thing.observedArea exposure for STAC/DCAT-AP) rather than part of the current scope. Keeping it separate would make the change easier to review and track.

Thanks for the feedback. Just to clarify - these are already separate PRs on independent branches (feat/thing-observed-area for #124 and feat/thing-phenomenonTime for #125), each with a single commit. The cross-referencing in the descriptions was just for context on the DCAT-AP use case, not an indication that they're coupled. Happy to trim the descriptions if that framing made them read as a bundled submission.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants