Skip to main content

Geometry and geoqueries

Geometries are GeoJSON in WGS 84 longitude and latitude. Andromeda keeps altitude when you send it, but geoqueries only look at the footprint on the ground, and near measures real distances on the earth's surface.

Geometries​

{
"location": {
"type": "GeoProperty",
"value": {
"type": "Point",
"coordinates": [
-3.7038,
40.4168,
667
]
}
}
}

A GeoProperty value is a GeoJSON geometry, and every position is longitude then latitude in WGS 84. location, observationSpace, operationSpace and any GeoProperty you define all follow the same rules.

GeometryCollection is refused, both as a value and as the reference geometry of a query.

Coordinates are held as double-precision floats (see Precision).

A Property whose value happens to be GeoJSON is not a GeoProperty. {"type": "Property", "value": {"type": "GeometryCollection", "geometries": [...]}} is stored and returned as written, numbers digit for digit, and no geoquery looks at it. Only an attribute that states no type and whose value is a GeoJSON geometry is read as a GeoProperty.

Precision​

Every ordinate of a geometry, and every number of its bbox, is held as the double-precision float nearest to the number you wrote. That one value is what Andromeda indexes, matches in a geoquery, measures for near and orders by distance, and it is what comes back in the Core API, in the Temporal API history, in registrations and in notifications. It is written as the shortest decimal that reads back as the same double, in the one spelling Andromeda gives every number (see Numbers).

A response, a notification and the stored entity therefore spell an ordinate alike. 13.0 becomes 13, 14.550000000000000001 becomes 14.55, 52.50 becomes 52.5, and 13.123456789012345678 becomes 13.123456789012346. An ordinate already written in that spelling comes back byte for byte.

The digits a double can't hold are far below any physical meaning. Neighbouring doubles near a longitude of 180 degrees are 2.8e-14 degrees apart, about 3 nanometres on the ground, and closer together for smaller ordinates. Altitudes in metres are held finer still. For comparison, the eighth decimal place of a degree is about a millimetre, and survey-grade satellite positioning reaches about a centimetre.

The CRS84 ranges are checked on the double. An ordinate such as 180.00000000000000001, whose nearest double is 180, is held as 180 and accepted.

A number in a Property value is held differently, and keeps its exact decimal value (see Numbers). A girth of 13.123456789012345678 comes back with all its digits, even where the same number as a longitude comes back as 13.123456789012346.

Altitude​

A position has two ordinates (longitude, latitude) or three (longitude, latitude, altitude). Fewer than two isn't a position, and is refused. Four or more are refused too. GeoJSON says positions shouldn't go beyond three elements, since nobody defines what the extra ones mean.

A geometry is either flat throughout or elevated throughout. If some positions have an altitude and others don't, the geometry is refused, and the error names the first position that differs. A partly elevated shape can't be compared by height, and filling in or dropping altitudes would make up or throw away data.

The refusal is 400 BadRequestData, naming the GeoProperty. These rules apply wherever a geometry is read, which means entity and temporal writes, batch operations, registrations, subscription geoQ and the coordinates query parameter.

Andromeda never adds an altitude and never removes one. A geometry you write with altitudes comes back with them, in every representation (normalized, concise, keyValues, GeoJSON), in notifications, in the temporal history and in registrations.

Geoqueries​

GET /ngsi-ld/v1/entities?type=Building&georel=near;maxDistance==500&geometry=Point&coordinates=[-3.7038,40.4168]

geoproperty names the GeoProperty the geoquery reads, location when it's absent. It takes a term of your @context, a prefixed name or an absolute IRI, so geoproperty=entrance and geoproperty=https://example.org/entrance select the same entities when your @context maps entrance to that IRI. The same goes for the geoproperty member of a geoQ.

Only the footprint counts​

All seven georel relations (near, within, contains, overlaps, intersects, equals, disjoint) and distance ordering work on the horizontal footprint of a geometry. Altitude is kept, but it never changes whether something matches. The relations are defined on the projection onto the horizontal plane (clause 4.10 with OGC 06-103r4), so a broker that let altitude change a within result would disagree with every other conformant broker on the same data.

A real three-dimensional relation would need a new relation name. ETSI GS CIM 050 proposes some (inside, covers, coveredBy), and Andromeda implements none of them.

What near measures​

near;maxDistance==d matches an entity whose geometry comes within d metres of the reference geometry. near;minDistance==d matches one that stays at least d metres away.

  • The distance is the shortest one between the two geometries, never between their centres. A building whose nearest wall is 100 m from the reference point is 100 m away, wherever its middle is.
  • Two geometries that touch or overlap are zero apart. A point inside a polygon is near it at every distance, and beyond it at none.
  • An entity without the GeoProperty being queried, or with an empty geometry, matches neither form.
  • A near takes exactly one of maxDistance and minDistance. Using both as a distance band needs the near_distance_band extension.

Distances are measured on the WGS 84 ellipsoid, as PostGIS does for its geography type. A sphere would be faster, but it can be off by up to half a percent. A distance of 5,000.17 m due north of the equator on the ellipsoid measures 5,028.24 m on a sphere.

near treats each edge between two positions as a great-circle arc, the shortest path over the earth's surface, not as a straight line in longitude and latitude. On short edges this makes no real difference. On long ones it does. An edge along the 60th parallel spanning 1 degree of longitude bows about 105 m towards the pole. If your geometries have long edges, like region outlines drawn with a handful of points, cut them into shorter segments. An edge spanning more than 180 degrees of longitude goes the short way round across the antimeridian, so cut such geometries at the antimeridian.

The six other relations treat edges as straight lines in longitude and latitude.

Sorting by distance is a separate measure. It uses the spherical haversine distance. Since a sort never shows you a distance, it can't visibly contradict a near filter in the same answer.

What gets refused​

ResponseWhenWhat to do
400 BadRequestDataa geometry mixes altitudes, has four or more ordinates, or fewer than twoSend every position with two ordinates, or every position with three
400 BadRequestDataa GeometryCollection, as a value or as a query's reference geometrySplit it into separate GeoProperties, or use a Multi* geometry

The rest of the geometry rules (closed rings, coordinate ranges and so on) are under Geometry in Strictness.

Where Andromeda interprets the specification​

A GeoProperty value is a GeoJSON geometry, and GeometryCollection is excluded. WGS 84 positions come from GeoJSON. Two or three ordinates are allowed, and positions should not be extended beyond three elements. Coordinate precision is left to the implementation.

A Property may hold any JSON value, and its type is what was stated. A GeoProperty is inferred from a GeoJSON value only when no type is stated, so a stated Property keeps GeoJSON as plain data. In concise output such a Property keeps "type": "Property", since without it the value would read back as a GeoProperty.

Every relation, near included, is defined by reference to OGC 06-103r4. That standard says spatial operations "will therefore not reflect z or m values in calculations (e.g., Equals, Length) or in generation of new geometry values (e.g., Buffer, ConvexHull, Intersection)", and defines the relational operators on the projection onto the horizontal plane.

The specification asks for geoproperty as a short-hand name, while the GeoQuery data type describes it as an "Attribute name as short hand string or URI". Andromeda accepts both, on the URL and in a geoQ.

near distances follow the same definition, which names no earth model. Cutting at the antimeridian follows GeoJSON. Distance ordering is by haversine.