Skip to content

Commit

Permalink
Merge pull request #4126 from telefonicaid/feature/4114_geojson_featu…
Browse files Browse the repository at this point in the history
…re_support_fixes

FIX Feature and FeatureCollection support to implement geometry normalization
  • Loading branch information
mapedraza authored May 18, 2022
2 parents 3cc7ff7 + d9db110 commit 169b5b1
Show file tree
Hide file tree
Showing 7 changed files with 183 additions and 134 deletions.
2 changes: 1 addition & 1 deletion CHANGES_NEXT_RELEASE
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
- Add: support for Feature and FeaturesCollection GeoJSON types in entity locations (#4114)
- Add: support for Feature and FeaturesCollection GeoJSON types (normalizing geometries) in entity locations (#4114)
- Add: support to null element in string list filters (e.g. q=A:foo,null) (#4120)
- Fix: allow limit=0 in all paginated operations in the NGSIv2 API (entities, entity types, subscriptions and registrations) (#1492)
- Add: conditions.alterationTypes subscription fuctionality (#1494)
Expand Down
23 changes: 16 additions & 7 deletions doc/manuals/user/ngsiv2_implementation_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,17 +230,26 @@ We have successfully tested the following types:
* MultiLineString
* Polygon
* MultiPolygon
* Feature (using the value of `geometry` field at the first level)

On the contrary, the following types doesn't work (you will get a "Database Error" if you try to use them):
More information on the tests conducted can be found [here](https://github.com/telefonicaid/fiware-orion/issues/3586).

* GeometryCollection
The types `Feature` and `FeatureCollection` are also supported, but in a special way. You can
use `Feature` or `FeatureCollection` to create/update `geo:json` attributes. However, when
the attribute value is retrieved (GET resposes or notifictaions) you will get only the content of:

With regards to `FeatureCollection`, it is supported only if it contains a single Feature (i.e.
the `features` field has only one element), in which case the value of `geometry` field of
such element is used.
* the `geometry` field, in the case of `Feature`
* the `geometry` field of the first item of the `features` array, in the case of `FeatureCollection`

More information on the tests conducted can be found [here](https://github.com/telefonicaid/fiware-orion/issues/3586).
Note that actually Orion stores the full value used at `Feature` or `FeatureCollection`
creation/updating time. However, from the point of view of normalization with other `geo:json` types,
it has been decided to return only the `geometry` part. In the future, maybe a flag to return
the full content would be implemented (more detail [in this issue](https://github.com/telefonicaid/fiware-orion/issues/4125)).

With regards to `FeatureCollection`, it is only accepted at creation/update time only if it contains a single
`Feature` (i.e. the `features` field has only one element). Otherwise , Orion would return an `BadRequest`error.

The only GeoJSON type not supported at all is `GeometryCollection`. You will get a "Database Error"
if you try to use them).

## Legacy attribute format in notifications

Expand Down
79 changes: 78 additions & 1 deletion src/lib/mongoBackend/location.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,79 @@ static bool stringArray2coords



/* ****************************************************************************
*
* getGeometryFromFeature -
*/
static orion::CompoundValueNode* getGeometryFromFeature(orion::CompoundValueNode* feature)
{
for (unsigned int ix = 0; ix < feature->childV.size(); ++ix)
{
orion::CompoundValueNode* childP = feature->childV[ix];
if (childP->name == "geometry")
{
return childP;
}
}

LM_E(("Runtime Error (geometry field expected in GeoJson Feature)"));
return NULL;
}



/* ****************************************************************************
*
* getGeometryFromFeatureCollection -
*/
static orion::CompoundValueNode* getGeometryFromFeatureCollection(orion::CompoundValueNode* featureCollection)
{
for (unsigned int ix = 0; ix < featureCollection->childV.size(); ++ix)
{
orion::CompoundValueNode* childP = featureCollection->childV[ix];
if (childP->name == "features")
{
return getGeometryFromFeature(featureCollection->childV[ix]->childV[0]);
}
}

LM_E(("Runtime Error (feature field expected in GeoJson FeatureCollection)"));
return NULL;
}



/* ****************************************************************************
*
* getGeometry -
*
* Get geometry compound value from attribute, taking into account special GeoJSON
* types such as Feature and FeatureCollection
*/
orion::CompoundValueNode* getGeometry(orion::CompoundValueNode* compoundValueP)
{
for (unsigned int ix = 0; ix < compoundValueP->childV.size(); ++ix)
{
orion::CompoundValueNode* childP = compoundValueP->childV[ix];
if ((childP->name == "type") && (childP->valueType == orion::ValueTypeString))
{
if (childP->stringValue == "Feature")
{
return getGeometryFromFeature(compoundValueP);
}
if (childP->stringValue == "FeatureCollection")
{
return getGeometryFromFeatureCollection(compoundValueP);
}
}
}

// Regular geo:json
return compoundValueP;
}



/* ****************************************************************************
*
* isFeatureType -
Expand Down Expand Up @@ -150,7 +223,11 @@ static bool isFeatureCollectionType(CompoundValueNode* featureCollection, orion:
}



/* ****************************************************************************
*
* isSpecialGeoJsonType -
*
*/
static bool isSpecialGeoJsonType(const ContextAttribute* caP, orion::BSONObjBuilder* geoJson, ApiVersion apiVersion)
{
if (caP->compoundValueP == NULL)
Expand Down
11 changes: 11 additions & 0 deletions src/lib/mongoBackend/location.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,15 @@ extern bool processLocationAtAppendAttribute
OrionError* oe
);



/* ****************************************************************************
*
* getGeometry -
*
* Get geometry compound value from attribute, taking into account special GeoJSON
* types such as Feature and FeatureCollection
*/
extern orion::CompoundValueNode* getGeometry(orion::CompoundValueNode* compoundValueP);

#endif // SRC_LIB_MONGOBACKEND_LOCATION_H_
14 changes: 13 additions & 1 deletion src/lib/ngsi/ContextAttribute.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
#include "rest/OrionError.h"
#include "parse/CompoundValueNode.h"

#include "mongoBackend/location.h"
#include "mongoBackend/dbConstants.h"
#include "mongoBackend/dbFieldEncoding.h"
#include "mongoBackend/compoundValueBson.h"
Expand Down Expand Up @@ -959,7 +960,18 @@ std::string ContextAttribute::toJson(const std::vector<std::string>& metadataFi
//
if (compoundValueP != NULL)
{
jh.addRaw("value", compoundValueP->toJson());
orion::CompoundValueNode* childToRenderP = compoundValueP;
if (type == GEO_JSON)
{
childToRenderP = getGeometry(compoundValueP);
}

// Some internal error conditions in getGeometryToRender() (e.g. out of band manipulation
// of DB entities) may lead to NULL, so the check is needed
if (childToRenderP != NULL)
{
jh.addRaw("value", childToRenderP->toJson());
}
}
else if (valueType == orion::ValueTypeNumber)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ brokerStart CB

#
# 01. Create entity with geo:json using Feature type
# 02. Get entity and see Feature
# 02. Get entity and see Feature geometry
# 03. Geoquery covering the location returns the entity
# 04. Update entity with a regular geo:json Point
# 05. Get entity and see Point
# 06. Geoquery covering the location returns the entity
# 07. Update entity with a Feature again
# 08. Get entity and see Feature
# 08. Get entity and see Feature geometry
# 09. Geoquery covering the location returns the entity
#

Expand Down Expand Up @@ -68,8 +68,8 @@ echo
echo


echo "02. Get entity and see Feature"
echo "=============================="
echo "02. Get entity and see Feature geometry"
echo "======================================="
orionCurl --url /v2/entities/E
echo
echo
Expand Down Expand Up @@ -140,8 +140,8 @@ echo
echo


echo "08. Get entity and see Feature"
echo "=============================="
echo "08. Get entity and see Feature geometry"
echo "======================================="
orionCurl --url /v2/entities/E
echo
echo
Expand All @@ -165,10 +165,10 @@ Date: REGEX(.*)



02. Get entity and see Feature
==============================
02. Get entity and see Feature geometry
=======================================
HTTP/1.1 200 OK
Content-Length: 227
Content-Length: 133
Content-Type: application/json
Fiware-Correlator: REGEX([0-9a-f\-]{36})
Date: REGEX(.*)
Expand All @@ -179,17 +179,11 @@ Date: REGEX(.*)
"metadata": {},
"type": "geo:json",
"value": {
"geometry": {
"coordinates": [
-3.612711914,
40.539019781
],
"type": "Point"
},
"properties": {
"label": "-3.6127119138731127, 40.53901978067972"
},
"type": "Feature"
"coordinates": [
-3.612711914,
40.539019781
],
"type": "Point"
}
},
"type": "T"
Expand All @@ -199,7 +193,7 @@ Date: REGEX(.*)
03. Geoquery covering the location returns the entity
=====================================================
HTTP/1.1 200 OK
Content-Length: 229
Content-Length: 135
Content-Type: application/json
Fiware-Correlator: REGEX([0-9a-f\-]{36})
Date: REGEX(.*)
Expand All @@ -211,17 +205,11 @@ Date: REGEX(.*)
"metadata": {},
"type": "geo:json",
"value": {
"geometry": {
"coordinates": [
-3.612711914,
40.539019781
],
"type": "Point"
},
"properties": {
"label": "-3.6127119138731127, 40.53901978067972"
},
"type": "Feature"
"coordinates": [
-3.612711914,
40.539019781
],
"type": "Point"
}
},
"type": "T"
Expand Down Expand Up @@ -297,10 +285,10 @@ Date: REGEX(.*)



08. Get entity and see Feature
==============================
08. Get entity and see Feature geometry
=======================================
HTTP/1.1 200 OK
Content-Length: 229
Content-Length: 134
Content-Type: application/json
Fiware-Correlator: REGEX([0-9a-f\-]{36})
Date: REGEX(.*)
Expand All @@ -311,17 +299,11 @@ Date: REGEX(.*)
"metadata": {},
"type": "geo:json",
"value": {
"geometry": {
"coordinates": [
-23.612711914,
50.539019781
],
"type": "Point"
},
"properties": {
"label": "-23.6127119138731127, 50.53901978067972"
},
"type": "Feature"
"coordinates": [
-23.612711914,
50.539019781
],
"type": "Point"
}
},
"type": "T"
Expand All @@ -331,7 +313,7 @@ Date: REGEX(.*)
09. Geoquery covering the location returns the entity
=====================================================
HTTP/1.1 200 OK
Content-Length: 231
Content-Length: 136
Content-Type: application/json
Fiware-Correlator: REGEX([0-9a-f\-]{36})
Date: REGEX(.*)
Expand All @@ -343,17 +325,11 @@ Date: REGEX(.*)
"metadata": {},
"type": "geo:json",
"value": {
"geometry": {
"coordinates": [
-23.612711914,
50.539019781
],
"type": "Point"
},
"properties": {
"label": "-23.6127119138731127, 50.53901978067972"
},
"type": "Feature"
"coordinates": [
-23.612711914,
50.539019781
],
"type": "Point"
}
},
"type": "T"
Expand Down
Loading

0 comments on commit 169b5b1

Please sign in to comment.