Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[sensorthings] Handle array results from Observations #57583

Merged
merged 1 commit into from
May 30, 2024

Conversation

nyalldawson
Copy link
Collaborator

Fixes #57577

Copy link

🪟 Windows builds ready!

Windows builds of this PR are available for testing here. Debug symbols for this build are available here.

(Built from commit be68074)

@rduivenvoorde
Copy link
Contributor

@nyalldawson and what about the 'json' case as a result? Not sure from the code if this is handled too.

(could be a last resort to just create a string from it?)

@nyalldawson
Copy link
Collaborator Author

@rduivenvoorde

It's messy -- we can only have "json" fields which contain map or list values, not single objects. So we'd have to use an array type for this field always.

Ultimately it's a limitation in qgis vector data provider API -- we'd ideally have a "any" field type which means the field can contain data of any type.

@rduivenvoorde
Copy link
Contributor

@nyalldawson Thanks! I build this PR and did some test. Below some observations (sorry for the length/messiness):

  1. this fixes the json exception \o/ :-) I can now retrieve FeaturesOfInterest from the Air Quality service

Some issues/ideas around use when I tried to retrieve FeaturesOfInterest from the service (main goal: retrieve points with values of a given type of measurement, to be able to use the Temporal Filter to 'see' the values on the map in time change):

  • default (at least for Observations) the Order By on 'PhenomenonTime' (always available) instead of a blank field and sort Order Deschending (as I think users mostly want recent values?):

image

Would become:

image

The 'limit' is set to 100 in the Expansions dialog, while (I think) for Datastreams and Observer Properties, a better default value would be lower? (not sure if this would speed up retrieval though):

image

  • In the main dialog at 'Expansions'-line you only see the Expansion itself, I think it would be very usefull to also see Limit, Order By and sort,
    eg: Observation (100) Descending ordered by phenomentonTime ( or instead of Descending a arrow down?)
    Ah wait: clicking in the Layer panel on the Filter icon gives you the Data Filter dialog (looking exactly the same) and THAT one show all fields.

I'm trying to 'create' a filter for my main goal: retrieving (latest or a recent set) of measurements on my map which I can control with the Temporal Controller OR view using Plotly...

Ui thingie: I'm not able to see the (long, because of expanding) Field (names) easy even when I make the dialog huge:

image

Issue? I see the 'Expansions' in the main dialog is not cleaned when changing the (main) Entity type, while I think that should be done(?), as the Expansion is never valid anymore if you change Entity type?

Trying to fetch only Observations of NO2:
Observations/Datastream/ObservedProperty/name eq 'NO2'

but I end up with a query part in the url's with (Observation_Datastream_ObservedProperty_name eq 'NO2') not sure if this is fixable.

By crafting a good url by hand, I still get a timeout/500 though:

https://airquality-frost.k8s.ilt-dmz.iosb.fraunhofer.de/v1.1/FeaturesOfInterest?$top=200&$count=false&$filter=(feature/type eq 'Point' or feature/geometry/type eq 'Point') and (geo.intersects(feature, geography'Polygon ((4.96998038694149358 51.98031302864895054, 5.32698782307856611 51.98031302864895054, 5.32698782307856611 52.18820000000000192, 4.96998038694149358 52.18820000000000192, 4.96998038694149358 51.98031302864895054))')) and (Observations/Datastream/ObservedProperty/name eq 'NO2')&$expand=Observations($orderby=phenomenonTime desc;$top=100;$expand=Datastream($top=2;$expand=ObservedProperty($top=2)))

@nyalldawson I thought to Retrieve Things on their Location, but... I cannot 'expand' the Locations of a Thing??

Should it not be possible to Expand Locations?
Manual: https://airquality-frost.k8s.ilt-dmz.iosb.fraunhofer.de/v1.1/Things(1)?$expand=Locations
works
(which I thought is never more the one, but apparently you can have more Locations IF encodingTypes are then different??? see https://docs.ogc.org/is/18-088/18-088.html#thing Table 4)

I'm really eager to know if my 'goal' is doable @hylkevds do you have an idea? Of is the STA model really about retrieving data step by step (so first Things/Location and then one by one get data from a Thing/Datastream)?

I found an earlier question of me FraunhoferIOSB/FROST-Server#1107 in which I retrieve geojson (working, but slow) :

https://airquality-frost.k8s.ilt-dmz.iosb.fraunhofer.de/v1.1/Datastreams?$count=true&$filter=ObservedProperty/name eq 'SO2'&$expand=ObservedProperty($select=name,description,@iot.id),Observations($select=result,phenomenonTime,@iot.id;$orderby=phenomenonTime%20desc;$top=1),Thing/Locations&$resultFormat=GeoJSON&$top=2000

https://airquality-frost.k8s.ilt-dmz.iosb.fraunhofer.de/v1.1/Datastreams?$count=true&$filter=ObservedProperty/name eq 'SO2'&$expand=ObservedProperty
          ($select=name,description,@iot.id)
        ,Observations
          ($select=result,phenomenonTime,@iot.id
          ;$orderby=phenomenonTime%20desc
          ;$top=1
          )
        ,Thing/Locations&$resultFormat=GeoJSON&$top=2000

@rduivenvoorde
Copy link
Contributor

Other things I see: in the attribute table (of a STA layer) the tooltips all show 'Name \nNULL'

Screenshot from 2024-05-30 14-34-13

And another observation:

If you fetch Locations, with an expanded Thing/Datastream/Observation, instead of the 'phenemenonTime' there are properties: phenemenonTimeStart and phenemenonTimeEnd:

image

@nyalldawson nyalldawson merged commit a956041 into qgis:master May 30, 2024
31 of 32 checks passed
@nyalldawson nyalldawson deleted the sensor_things_array branch May 30, 2024 22:55
@nyalldawson
Copy link
Collaborator Author

@rduivenvoorde
Thanks for the detailed testing! I'll address your comments one-by-one:

default (at least for Observations) the Order By on 'PhenomenonTime' (always available) instead of a blank field and sort Order Deschending (as I think users mostly want recent values?):
The 'limit' is set to 100 in the Expansions dialog, while (I think) for Datastreams and Observer Properties, a better default value would be lower? (not sure if this would speed up retrieval though):

I'll put these into #57574

In the main dialog at 'Expansions'-line you only see the Expansion itself, I think it would be very usefull to also see Limit, Order

This is already addressed in #57574

Ui thingie: I'm not able to see the (long, because of expanding) Field (names) easy even when I make the dialog huge:

I'll fix in #57574

Issue? I see the 'Expansions' in the main dialog is not cleaned when changing the (main) Entity type, while I think that should be done(?), as the Expansion is never valid anymore if you change Entity type?

Already addressed in #57574

Trying to fetch only Observations of NO2:
Observations/Datastream/ObservedProperty/name eq 'NO2'
but I end up with a query part in the url's with (Observation_Datastream_ObservedProperty_name eq 'NO2') not sure if this is fixable.

This one should be possible after #57574, where per-entity filters are introduced

By crafting a good url by hand, I still get a timeout/500 though:
https://airquality-frost.k8s.ilt-dmz.iosb.fraunhofer.de/v1.1/FeaturesOfInterest?$top=200&$count=false&$filter=(feature/type eq 'Point' or feature/geometry/type eq 'Point') and (geo.intersects(feature, geography'Polygon ((4.96998038694149358 51.98031302864895054, 5.32698782307856611 51.98031302864895054, 5.32698782307856611 52.18820000000000192, 4.96998038694149358 52.18820000000000192, 4.96998038694149358 51.98031302864895054))')) and (Observations/Datastream/ObservedProperty/name eq 'NO2')&$expand=Observations($orderby=phenomenonTime desc;$top=100;$expand=Datastream($top=2;$expand=ObservedProperty($top=2)))

Same here. I suspect it's one of those rough edges in the sensor things specifications/server implementations, where we can easily craft a request which results in a non-indexed, very expensive database lookup on the server. It's something we should handle by filing bugs with the server, and see if future versions of the server software can add indexes/optimisations to cover these use cases.

I thought to Retrieve Things on their Location, but... I cannot 'expand' the Locations of a Thing??
Should it not be possible to Expand Locations?
Manual: https://airquality-frost.k8s.ilt-dmz.iosb.fraunhofer.de/v1.1/Things(1)?$expand=Locations

That's because we currently have a constraint in place that the entity with geometry must be the base level entity, not an expansion.

Other things I see: in the attribute table (of a STA layer) the tooltips all show 'Name \nNULL'

That's not specific to SensorThings, but it's improved in #57621 . It's supposed to show the field type + the default value, so the "NULL" here is the default field value. (You'll see the same with any vector layer.)

If you fetch Locations, with an expanded Thing/Datastream/Observation, instead of the 'phenemenonTime' there are properties: phenemenonTimeStart and phenemenonTimeEnd:

This is because QGIS doesn't have a field type for "date time range", so we have to split the ranges out to two separate fields

@hylkevds
Copy link

The air quality service is currently optimised for querying starting at the Thing/Location side, not at the Feature side. The reason for that is that the Datastreams are attached to Things, and not to Features. When starting at the Features side, each feature has one big bag of observations, not sorted into separate time-series per property.

The expensive part in that query:

https://airquality-frost.k8s.ilt-dmz.iosb.fraunhofer.de/v1.1/FeaturesOfInterest?
  $top=200&
  $count=false&
  $filter=(feature/type eq 'Point' or feature/geometry/type eq 'Point')
     and (geo.intersects(feature, geography'Polygon ((4.96998038694149358 51.98031302864895054, 5.32698782307856611 51.98031302864895054, 5.32698782307856611 52.18820000000000192, 4.96998038694149358 52.18820000000000192, 4.96998038694149358 51.98031302864895054))'))
     and (Observations/Datastream/ObservedProperty/name eq 'NO2')&
  $expand=Observations(
    $orderby=phenomenonTime desc;
    $top=100;
    $expand=Datastream(
      $top=2;
      $expand=ObservedProperty($top=2)))

is filtering the Features based on the ObservedProperties of the Observations. That is very inefficient, because in this service, each Feature has very many Observations. So in this case it's better to start from the Thing/Location side.

We're changing this model in Version 2, so that a Feature can be linked to Datastreams too, and you'd only get the Observations of a Feature for those Features that are Samples, and that thus don't have that many Observations.

Also note, that although the above query filters on only those Features that have NO2 Observations, in the expand, all Observations would be returned, not just those for NO2.

As for the multiple Locations of a Thing: Yes, a Thing can have multiple Locations, as long as those are different representations of the same real-world location. We also have a demo service that has all the European NUTS regions in 5 different resolutions, so each Thing has 5 Locations:

@rduivenvoorde
Copy link
Contributor

Thanks for the new PR will test it later

If you fetch Locations, with an expanded Thing/Datastream/Observation, instead of the 'phenemenonTime' there are properties: phenemenonTimeStart and phenemenonTimeEnd:

This is because QGIS doesn't have a field type for "date time range", so we have to split the ranges out to two separate fields

But an Observation does not have a date time range, but only a phenemenonTime I thought?
It is the datastream that has a Time_Period
An Observation has a phenemenonTime of type TM_Object(???) and a resultTime of type TM_instant.

image

Argh.. now I see: https://airquality-frost.k8s.ilt-dmz.iosb.fraunhofer.de/v1.1/Observations(522318087)

phenemenonTime has "2017-12-31T23:00:00Z/2018-01-01T23:00:00Z"
resultTime is None

So using such data with the Temporal Controller would mean:

  • either use phenemenonTimeStart and duration of a day
  • or (better) use both phenemenonTimeStart and phenemenonTimeEnd

@hylkevds
Copy link

Exactly, a TM_Object can be either a TM_Instant or a TM_Period.
That means an Observation can have either as a phenomenonTime. In a GUI I usually model it as if it will always have a "start" but may not have an "end".

@rduivenvoorde
Copy link
Contributor

rduivenvoorde commented May 31, 2024

We're changing this model in Version 2, so that a Feature can be linked to Datastreams too, and you'd only get the Observations of a Feature for those Features that are Samples, and that thus don't have that many Observations.

Also note, that although the above query filters on only those Features that have NO2 Observations, in the expand, all Observations would be returned, not just those for NO2.

@hylkevds thanks for the explanation!

It's just that I'm trying to fit STA on the way QGIS is able to handle 'temporal features' using the Temporal Controller:

(and this is also my use-case: show (on a map, the changes of) the hourly measurements of a certain quantity/ObservedProperty of static sensors, but ALSO of sensors attached to driving vehicles)

Actually it means that you'd want a feature build from one observation PLUS (at least) the Thing AND it's location:A

  • the Thing properties because you probably want a map with labels with names of the Things
  • the Location because... we are GIS
  • the (one type of) Observation because we want to see the change in measurements (or show them in a graph)
  • and preferably one Type of Observation per (GIS) layer, to not mix stuff

Or of the driving sensor:

  • the (one type of) Observation because we want to see the change in measurements (or show them in a graph)
  • a FeatureOfInterest attached to every Observation
  • preferably some info so we can show the unit or ObserverProperty etc

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants