diff --git a/site/content/3.10/about-arangodb/features/community-edition.md b/site/content/3.10/about-arangodb/features/community-edition.md index ed534dd77e..b480cb802b 100644 --- a/site/content/3.10/about-arangodb/features/community-edition.md +++ b/site/content/3.10/about-arangodb/features/community-edition.md @@ -141,7 +141,7 @@ see [arangodb.com/community-server/](https://www.arangodb.com/community-server/) Flexible data field pre-processing with custom queries and the ability to chain built-in and custom Analyzers. Language-agnostic tokenization of text. -- [**GeoJSON Support**](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson): +- [**GeoJSON Support**](../../aql/functions/geo.md#geojson): Geographic data encoded in the popular GeoJSON format can be stored and used for geo-spatial queries. diff --git a/site/content/3.10/aql/functions/arangosearch.md b/site/content/3.10/aql/functions/arangosearch.md index d8475f52ae..ba44a4b36e 100644 --- a/site/content/3.10/aql/functions/arangosearch.md +++ b/site/content/3.10/aql/functions/arangosearch.md @@ -1073,7 +1073,7 @@ be used in conjunction with ArangoSearch. `GEO_CONTAINS(geoJsonA, geoJsonB) → bool` -Checks whether the [GeoJSON object](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson) `geoJsonA` +Checks whether the [GeoJSON object](geo.md#geojson) `geoJsonA` fully contains `geoJsonB` (every point in B is also in A). - **geoJsonA** (object\|array): first GeoJSON object or coordinate array @@ -1089,7 +1089,7 @@ fully contains `geoJsonB` (every point in B is also in A). `GEO_DISTANCE(geoJsonA, geoJsonB) → distance` -Return the distance between two [GeoJSON objects](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson), +Return the distance between two [GeoJSON objects](geo.md#geojson), measured from the `centroid` of each shape. - **geoJsonA** (object\|array): first GeoJSON object or coordinate array @@ -1105,7 +1105,7 @@ measured from the `centroid` of each shape. `GEO_IN_RANGE(geoJsonA, geoJsonB, low, high, includeLow, includeHigh) → bool` -Checks whether the distance between two [GeoJSON objects](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson) +Checks whether the distance between two [GeoJSON objects](geo.md#geojson) lies within a given interval. The distance is measured from the `centroid` of each shape. @@ -1129,7 +1129,7 @@ each shape. `GEO_INTERSECTS(geoJsonA, geoJsonB) → bool` -Checks whether the [GeoJSON object](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson) `geoJsonA` +Checks whether the [GeoJSON object](geo.md#geojson) `geoJsonA` intersects with `geoJsonB` (i.e. at least one point of B is in A or vice versa). - **geoJsonA** (object\|array): first GeoJSON object or coordinate array diff --git a/site/content/3.10/aql/functions/date.md b/site/content/3.10/aql/functions/date.md index d4e9471047..df8a5bd665 100644 --- a/site/content/3.10/aql/functions/date.md +++ b/site/content/3.10/aql/functions/date.md @@ -7,6 +7,8 @@ description: >- ISO 8601 date time strings archetype: default --- +## Date and time representations + AQL offers functionality to work with dates, but it does not have a special data type for dates (neither does JSON, which is usually used as format to ship data into and out of ArangoDB). Instead, dates in AQL are represented by either numbers or strings. diff --git a/site/content/3.10/aql/functions/geo.md b/site/content/3.10/aql/functions/geo.md index 27248fdc28..d3e70f1e60 100644 --- a/site/content/3.10/aql/functions/geo.md +++ b/site/content/3.10/aql/functions/geo.md @@ -7,6 +7,384 @@ description: >- accelerated by geo-spatial indexes archetype: default --- +## Geo-spatial data representations + +You can model geo-spatial information in different ways using the data types +available in ArangoDB. The recommended way is to use objects with **GeoJSON** +geometry but you can also use **longitude and latitude coordinate pairs** +for points. Both models are supported by +[Geo-Spatial Indexes](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md). + +### Coordinate pairs + +Longitude and latitude coordinates are numeric values and can be stored in the +following ways: + +- Coordinates using an array with two numbers in `[longitude, latitude]` order, + for example, in a user-chosen attribute called `location`: + + ```json + { + "location": [ -73.983, 40.764 ] + } + ``` + +- Coordinates using an array with two numbers in `[latitude, longitude]` order, + for example, in a user-chosen attribute called `location`: + + ```json + { + "location": [ 40.764, -73.983 ] + } + ``` + +- Coordinates using two separate numeric attributes, for example, in two + user-chosen attributes called `lat` and `lng` as sub-attributes of a `location` + attribute: + + ```json + { + "location": { + "lat": 40.764, + "lng": -73.983 + } + } + ``` + +### GeoJSON + +GeoJSON is a geospatial data format based on JSON. It defines several different +types of JSON objects and the way in which they can be combined to represent +data about geographic shapes on the Earth surface. + +Example of a document with a GeoJSON Point stored in a user-chosen attribute +called `location` (with coordinates in `[longitude, latitude]` order): + +```json +{ + "location": { + "type": "Point", + "coordinates": [ -73.983, 40.764 ] + } +} +``` + +GeoJSON uses a geographic coordinate reference system, +World Geodetic System 1984 (WGS 84), and units of decimal degrees. + +Internally ArangoDB maps all coordinate pairs onto a unit sphere. Distances are +projected onto a sphere with the Earth's *Volumetric mean radius* of *6371 +km*. ArangoDB implements a useful subset of the GeoJSON format +[(RFC 7946)](https://tools.ietf.org/html/rfc7946). +Feature Objects and the GeometryCollection type are not supported. +Supported geometry object types are: + +- Point +- MultiPoint +- LineString +- MultiLineString +- Polygon +- MultiPolygon + +#### Point + +A [GeoJSON Point](https://tools.ietf.org/html/rfc7946#section-3.1.2) is a +[position](https://tools.ietf.org/html/rfc7946#section-3.1.1) comprised of +a longitude and a latitude: + +```json +{ + "type": "Point", + "coordinates": [100.0, 0.0] +} +``` + +#### MultiPoint + +A [GeoJSON MultiPoint](https://tools.ietf.org/html/rfc7946#section-3.1.7) is +an array of positions: + +```json +{ + "type": "MultiPoint", + "coordinates": [ + [100.0, 0.0], + [101.0, 1.0] + ] +} +``` + +#### LineString + +A [GeoJSON LineString](https://tools.ietf.org/html/rfc7946#section-3.1.4) is +an array of two or more positions: + +```json +{ + "type": "LineString", + "coordinates": [ + [100.0, 0.0], + [101.0, 1.0] + ] +} +``` + +#### MultiLineString + +A [GeoJSON MultiLineString](https://tools.ietf.org/html/rfc7946#section-3.1.5) is +an array of LineString coordinate arrays: + +```json +{ + "type": "MultiLineString", + "coordinates": [ + [ + [100.0, 0.0], + [101.0, 1.0] + ], + [ + [102.0, 2.0], + [103.0, 3.0] + ] + ] +} +``` + +#### Polygon + +A [GeoJSON Polygon](https://tools.ietf.org/html/rfc7946#section-3.1.6) consists +of a series of closed `LineString` objects (ring-like). These *Linear Ring* +objects consist of four or more coordinate pairs with the first and last +coordinate pair being equal. Coordinate pairs of a Polygon are an array of +linear ring coordinate arrays. The first element in the array represents +the exterior ring. Any subsequent elements represent interior rings +(holes within the surface). + +The orientation of the first linear ring is crucial: the right-hand-rule +is applied, so that the area to the left of the path of the linear ring +(when walking on the surface of the Earth) is considered to be the +"interior" of the polygon. All other linear rings must be contained +within this interior. According to the GeoJSON standard, the subsequent +linear rings must be oriented following the right-hand-rule, too, +that is, they must run **clockwise** around the hole (viewed from +above). However, ArangoDB is tolerant here (as suggested by the +[GeoJSON standard](https://datatracker.ietf.org/doc/html/rfc7946#section-3.1.6)), +all but the first linear ring are inverted if the orientation is wrong. + +In the end, a point is considered to be in the interior of the polygon, +if and only if one has to cross an odd number of linear rings to reach the +exterior of the polygon prescribed by the first linear ring. + +A number of additional rules apply (and are enforced by the GeoJSON +parser): + +- A polygon must contain at least one linear ring, i.e., it must not be + empty. +- A linear ring may not be empty, it needs at least three _distinct_ + coordinate pairs, that is, at least 4 coordinate pairs (since the first and + last must be the same). +- No two edges of linear rings in the polygon must intersect, in + particular, no linear ring may be self-intersecting. +- Within the same linear ring, consecutive coordinate pairs may be the same, + otherwise all coordinate pairs need to be distinct (except the first and last one). +- Linear rings of a polygon must not share edges, but they may share coordinate pairs. +- A linear ring defines two regions on the sphere. ArangoDB always + interprets the region that lies to the left of the boundary ring (in + the direction of its travel on the surface of the Earth) as the + interior of the ring. This is in contrast to earlier versions of + ArangoDB before 3.10, which always took the **smaller** of the two + regions as the interior. Therefore, from 3.10 on one can now have + polygons whose outer ring encloses more than half the Earth's surface. +- The interior rings must be contained in the (interior) of the outer ring. +- Interior rings should follow the above rule for orientation + (counterclockwise external rings, clockwise internal rings, interior + always to the left of the line). + +Here is an example with no holes: + +```json +{ + "type": "Polygon", + "coordinates": [ + [ + [100.0, 0.0], + [101.0, 0.0], + [101.0, 1.0], + [100.0, 1.0], + [100.0, 0.0] + ] + ] +} +``` + +Here is an example with a hole: + +```json +{ + "type": "Polygon", + "coordinates": [ + [ + [100.0, 0.0], + [101.0, 0.0], + [101.0, 1.0], + [100.0, 1.0], + [100.0, 0.0] + ], + [ + [100.8, 0.8], + [100.8, 0.2], + [100.2, 0.2], + [100.2, 0.8], + [100.8, 0.8] + ] + ] +} +``` + +#### MultiPolygon + +A [GeoJSON MultiPolygon](https://tools.ietf.org/html/rfc7946#section-3.1.6) consists +of multiple polygons. The "coordinates" member is an array of +_Polygon_ coordinate arrays. See [above](#polygon) for the rules and +the meaning of polygons. + +If the polygons in a MultiPolygon are disjoint, then a point is in the +interior of the MultiPolygon if and only if it is +contained in one of the polygons. If some polygon P2 in a MultiPolygon +is contained in another polygon P1, then P2 is treated like a hole +in P1 and containment of points is defined with the even-odd-crossings rule +(see [Polygon](#polygon)). + +Additionally, the following rules apply and are enforced for +MultiPolygons: + +- No two edges in the linear rings of the polygons of a MultiPolygon + may intersect. +- Polygons in the same MultiPolygon may not share edges, but they may share + coordinate pairs. + +Example with two polygons, the second one with a hole: + +```json +{ + "type": "MultiPolygon", + "coordinates": [ + [ + [ + [102.0, 2.0], + [103.0, 2.0], + [103.0, 3.0], + [102.0, 3.0], + [102.0, 2.0] + ] + ], + [ + [ + [100.0, 0.0], + [101.0, 0.0], + [101.0, 1.0], + [100.0, 1.0], + [100.0, 0.0] + ], + [ + [100.2, 0.2], + [100.2, 0.8], + [100.8, 0.8], + [100.8, 0.2], + [100.2, 0.2] + ] + ] + ] +} +``` + +## GeoJSON interpretation + +Note the following technical detail about GeoJSON: The +[GeoJSON standard, Section 3.1.1 Position](https://datatracker.ietf.org/doc/html/rfc7946#section-3.1.1) +prescribes that lines are **cartesian lines in cylindrical coordinates** +(longitude/latitude). However, this definition is inconvenient in practice, +since such lines are not geodesic on the surface of the Earth. +Furthermore, the best available algorithms for geospatial computations on Earth +typically use geodesic lines as the boundaries of polygons on Earth. + +Therefore, ArangoDB uses the **syntax of the GeoJSON** standard, +but then interprets lines (and boundaries of polygons) as +**geodesic lines (pieces of great circles) on Earth**. This is a +violation of the GeoJSON standard, but serving a practical purpose. + +Note in particular that this can sometimes lead to unexpected results. +Consider the following polygon (remember that GeoJSON has +**longitude before latitude** in coordinate pairs): + +```json +{ "type": "Polygon", "coordinates": [[ + [4, 54], [4, 47], [16, 47], [16, 54], [4, 54] +]] } +``` + +![GeoJSON Polygon Geodesic](../../../images/geojson-polygon-geodesic.webp) + +It does not contain the point `[10, 47]` since the shortest path (geodesic) +from `[4, 47]` to `[16, 47]` lies North relative to the parallel of latitude at +47 degrees. On the contrary, the polygon does contain the point `[10, 54]` as it +lies South of the parallel of latitude at 54 degrees. + +{{< info >}} +ArangoDB version before 3.10 did an inconsistent special detection of "rectangle" +polygons that later versions from 3.10 onward no longer do, see +[Legacy Polygons](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#legacy-polygons). +{{< /info >}} + +Furthermore, there is an issue with the interpretation of linear rings +(boundaries of polygons) according to +[GeoJSON standard, Section 3.1.6 Polygon](https://datatracker.ietf.org/doc/html/rfc7946#section-3.1.6). +This section states explicitly: + +> A linear ring MUST follow the right-hand rule with respect to the +> area it bounds, i.e., exterior rings are counter-clockwise, and +> holes are clockwise. + +This rather misleading phrase means that when a linear ring is used as +the boundary of a polygon, the "interior" of the polygon lies **to the left** +of the boundary when one travels on the surface of the Earth and +along the linear ring. For +example, the polygon below travels **counter-clockwise** around the point +`[10, 50]`, and thus the interior of the polygon contains this point and +its surroundings, but not, for example, the North Pole and the South +Pole. + +```json +{ "type": "Polygon", "coordinates": [[ + [4, 54], [4, 47], [16, 47], [16, 54], [4, 54] +]] } +``` + +![GeoJSON Polygon Counter-clockwise](../../../images/geojson-polygon-ccw.webp) + +On the other hand, the following polygon travels **clockwise** around the point +`[10, 50]`, and thus its "interior" does not contain `[10, 50]`, but does +contain the North Pole and the South Pole: + +```json +{ "type": "Polygon", "coordinates": [[ + [4, 54], [16, 54], [16, 47], [4, 47], [4, 54] +]] } +``` + +![GeoJSON Polygon Clockwise](../../../images/geojson-polygon-cw.webp) + +Remember that the "interior" is to the left of the given +linear ring, so this second polygon is basically the complement on Earth +of the previous polygon! + +ArangoDB versions before 3.10 did not follow this rule and always took the +"smaller" connected component of the surface as the "interior" of the polygon. +This made it impossible to specify polygons which covered more than half of the +sphere. From version 3.10 onward, ArangoDB recognizes this correctly. +See [Legacy Polygons](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#legacy-polygons) +for how to deal with this issue. + ## Geo utility functions The following helper functions **can** use geo indexes, but do not have to in @@ -43,7 +421,7 @@ FOR doc IN coll // e.g. documents returned by a traversal `GEO_CONTAINS(geoJsonA, geoJsonB) → bool` -Checks whether the [GeoJSON object](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson) `geoJsonA` +Checks whether the [GeoJSON object](#geojson) `geoJsonA` fully contains `geoJsonB` (every point in B is also in A). The object `geoJsonA` has to be of type _Polygon_ or _MultiPolygon_. For other types containment is not well-defined because of numerical stability problems. @@ -89,7 +467,7 @@ second argument. Passing it as the first argument, like Return the distance between two GeoJSON objects in meters, measured from the **centroid** of each shape. For a list of supported types see the -[geo index page](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson). +[geo index page](#geojson). - **geoJsonA** (object): first GeoJSON object, or a coordinate array in `[longitude, latitude]` order @@ -144,9 +522,8 @@ functions. `GEO_AREA(geoJson, ellipsoid) → area` -Return the area for a polygon or multi-polygon on a sphere with the -average Earth radius, or an ellipsoid. For a list of supported types -see the [geo index page](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson). +Return the area for a [Polygon](#polygon) or [MultiPolygon](#multipolygon) +on a sphere with the average Earth radius, or an ellipsoid. - **geoJson** (object): a GeoJSON object - **ellipsoid** (string, *optional*): reference ellipsoid to use. @@ -165,8 +542,7 @@ RETURN GEO_AREA(polygon, "wgs84") `GEO_EQUALS(geoJsonA, geoJsonB) → bool` -Checks whether two GeoJSON objects are equal or not. For a list of supported -types see the [geo index page](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson). +Checks whether two [GeoJSON objects](#geojson) are equal or not. - **geoJsonA** (object): first GeoJSON object. - **geoJsonB** (object): second GeoJSON object. @@ -196,7 +572,7 @@ RETURN GEO_EQUALS(polygonA, polygonB) // false `GEO_INTERSECTS(geoJsonA, geoJsonB) → bool` -Checks whether the [GeoJSON object](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson) `geoJsonA` +Checks whether the [GeoJSON object](#geojson) `geoJsonA` intersects with `geoJsonB` (i.e. at least one point in B is also in A or vice-versa). - **geoJsonA** (object): first GeoJSON object @@ -227,7 +603,7 @@ second argument. Passing it as the first argument, like `GEO_IN_RANGE(geoJsonA, geoJsonB, low, high, includeLow, includeHigh) → bool` -Checks whether the distance between two [GeoJSON objects](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson) +Checks whether the distance between two [GeoJSON objects](#geojson) lies within a given interval. The distance is measured from the **centroid** of each shape. @@ -394,7 +770,7 @@ a linear ring. Each linear ring consists of an array with at least four longitude/latitude pairs. The first linear ring must be the outermost, while any subsequent linear ring will be interpreted as holes. -For details about the rules, see [GeoJSON polygons](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#polygon). +For details about the rules, see [GeoJSON Polygon](#polygon). - **points** (array): an array of (arrays of) `[longitude, latitude]` pairs - returns **geoJson** (object\|null): a valid GeoJSON Polygon @@ -433,7 +809,8 @@ RETURN GEO_POLYGON([ `GEO_MULTIPOLYGON(polygons) → geoJson` Construct a GeoJSON MultiPolygon. Needs at least two Polygons inside. -See [GEO_POLYGON()](#geo_polygon) and [GeoJSON MultiPolygons](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#multipolygon) for the rules of Polygon and MultiPolygon construction. +See [GEO_POLYGON()](#geo_polygon) and [GeoJSON MultiPolygon](#multipolygon) +for the rules of Polygon and MultiPolygon construction. - **polygons** (array): an array of arrays of arrays of `[longitude, latitude]` pairs - returns **geoJson** (object\|null): a valid GeoJSON MultiPolygon @@ -557,7 +934,7 @@ value in an attribute of that name. `WITHIN_RECTANGLE()` is a deprecated AQL function from version 3.4.0 on. Use [`GEO_CONTAINS()`](#geo_contains) and a GeoJSON polygon instead - but note that this uses geodesic lines from version 3.10.0 onward -(see [GeoJSON interpretation](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson-interpretation)): +(see [GeoJSON interpretation](#geojson-interpretation)): ```aql LET rect = GEO_POLYGON([ [ diff --git a/site/content/3.10/arangograph/notebooks.md b/site/content/3.10/arangograph/notebooks.md index 138ab79819..15972607d4 100644 --- a/site/content/3.10/arangograph/notebooks.md +++ b/site/content/3.10/arangograph/notebooks.md @@ -20,7 +20,7 @@ passwords, and endpoint URLs. ![ArangoGraph Notebooks Architecture](../../images/arangograph-notebooks-architecture.png) -The ArangoGraph Notebook has built-in [ArangoGraph Magic Commands](.#arangograph-magic-commands) +The ArangoGraph Notebook has built-in [ArangoGraph Magic Commands](#arangograph-magic-commands) that answer questions like: - What ArangoDB database am I connected to at the moment? - What data does the ArangoDB instance contain? diff --git a/site/content/3.10/develop/http-api/general-request-handling.md b/site/content/3.10/develop/http-api/general-request-handling.md index 93f067ebc2..81fc20a86b 100644 --- a/site/content/3.10/develop/http-api/general-request-handling.md +++ b/site/content/3.10/develop/http-api/general-request-handling.md @@ -139,7 +139,7 @@ HTTP request. The server executes the jobs from the queue asynchronously as fast as possible, while clients can continue to do other work. If the server queue is full (i.e. contains as many jobs as specified by the -[`--server.maximal-queue-size`](../../components/arangodb-server/options.md#arangodb-server-options) +[`--server.maximal-queue-size`](../../components/arangodb-server/options.md#--servermaximal-queue-size) startup option), then the request is rejected instantly with an HTTP `503 Service Unavailable` status code. diff --git a/site/content/3.10/develop/http-api/replication/write-ahead-log.md b/site/content/3.10/develop/http-api/replication/write-ahead-log.md index c1508cc090..03e7b3f69f 100644 --- a/site/content/3.10/develop/http-api/replication/write-ahead-log.md +++ b/site/content/3.10/develop/http-api/replication/write-ahead-log.md @@ -179,8 +179,8 @@ paths: - `data`: the original document data - A more detailed description of the individual replication event types and their - data structures can be found in [Operation Types](#operation-types). + For a more detailed description of the individual replication event types + and their data structures, see the Operation Types. The response also contains the following HTTP headers: diff --git a/site/content/3.10/index-and-search/analyzers.md b/site/content/3.10/index-and-search/analyzers.md index 4b933edc12..9c41c4dc4b 100644 --- a/site/content/3.10/index-and-search/analyzers.md +++ b/site/content/3.10/index-and-search/analyzers.md @@ -1254,7 +1254,7 @@ attributes: - `legacy` (boolean, _optional_): This option controls how GeoJSON Polygons are interpreted (introduced in v3.10.5). Also see [Legacy Polygons](indexing/working-with-indexes/geo-spatial-indexes.md#legacy-polygons) and - [GeoJSON interpretation](indexing/working-with-indexes/geo-spatial-indexes.md#geojson-interpretation). + [GeoJSON interpretation](../aql/functions/geo.md#geojson-interpretation). - If `legacy` is `true`, the smaller of the two regions defined by a linear ring is interpreted as the interior of the ring and a ring can at most diff --git a/site/content/3.10/index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md b/site/content/3.10/index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md index bb456cccc9..0919bc6e4b 100644 --- a/site/content/3.10/index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md +++ b/site/content/3.10/index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md @@ -8,8 +8,8 @@ description: >- archetype: default --- The geo-spatial index type in ArangoDB is based on [Google S2](http://s2geometry.io/). -Indexing is supported for a subset of the [**GeoJSON**](#geojson) geometry types -as well as simple latitude/longitude pairs. +Indexing is supported for a subset of the [**GeoJSON**](../../../aql/functions/geo.md#geojson) +geometry types as well as simple latitude/longitude pairs. AQL's geospatial functions and GeoJSON constructors are described in [Geo functions](../../../aql/functions/geo.md). @@ -34,7 +34,7 @@ documents which do not fulfill these requirements. To create an index in GeoJSON mode execute: ```js -collection.ensureIndex({ type: "geo", fields: [ "geometry" ], geoJson:true }) +collection.ensureIndex({ type: "geo", fields: [ "geometry" ], geoJson: true }) ``` This creates the index on all documents and uses _geometry_ as the attributed @@ -53,10 +53,10 @@ sparsity. In case that the index was successfully created, an object with the index details, including the index-identifier, is returned. -See [GeoJSON Interpretation](#geojson-interpretation) for technical details on -how ArangoDB interprets GeoJSON objects. In short: the **syntax** of GeoJSON is -used, but polygon boundaries and lines between points are interpreted as -geodesics (pieces of great circles on Earth). +For technical details on how ArangoDB interprets GeoJSON objects, see +[GeoJSON Interpretation](../../../aql/functions/geo.md#geojson-interpretation). +In short: the **syntax** of GeoJSON is used, but polygon boundaries and lines +between points are interpreted as geodesics (pieces of great circles on Earth). ### Non-GeoJSON mode @@ -119,14 +119,16 @@ same query is executed with the geo-spatial index, it doesn't find the documents ### Legacy Polygons -See [GeoJSON Interpretation](#geojson-interpretation) for details of the changes -between ArangoDB 3.10 and earlier versions. Two things have changed: +Between ArangoDB 3.10 and earlier versions, two things have changed: - boundaries of polygons are now geodesics and there is no special and inconsistent treatment of "rectangles" any more - linear rings are interpreted according to the rules and no longer "normalized" +See [GeoJSON Interpretation](../../../aql/functions/geo.md#geojson-interpretation) +for details. + For backward compatibility, a `legacyPolygons` option has been introduced for geo indexes. It is relevant for those that have `geoJson` set to `true` only. Geo indexes created in versions before 3.10 always @@ -161,6 +163,121 @@ intentionally or not cannot be determined automatically. Please test the new behavior manually. {{< /warning >}} +### *arangosh* Examples + +Ensures that a geo index exists: + +`collection.ensureIndex({ type: "geo", fields: [ "location" ] })` + +Creates a geospatial index on all documents using `location` as the path to the +coordinates. The value of the attribute has to be an array with at least two +numeric values. The array must contain the latitude (first value) and the +longitude (second value). + +All documents, which do not have the attribute path or have a non-conforming +value in it, are excluded from the index. + +A geo index is implicitly sparse, and there is no way to control its +sparsity. + +The index does not provide a `unique` option because of its limited usability. +It would prevent identical coordinate pairs from being inserted only, but even a +slightly different location (like 1 inch or 1 cm off) would be unique again and +not considered a duplicate, although it probably should. The desired threshold +for detecting duplicates may vary for every project (including how to calculate +the distance even) and needs to be implemented on the application layer as +needed. You can write a [Foxx service](../../../develop/foxx-microservices/_index.md) for this purpose and +make use of the [Geo-spatial functions in AQL](../../../aql/functions/geo.md) to find nearby +locations supported by a geo index. + +In case that the index was successfully created, an object with the index +details, including the index-identifier, is returned. + +--- + +To create a geo index on an array attribute that contains longitude first, set +the `geoJson` attribute to `true`. This corresponds to the format described in +[RFC 7946 Position](https://tools.ietf.org/html/rfc7946#section-3.1.1) + +`collection.ensureIndex({ type: "geo", fields: [ "location" ], geoJson: true })` + +--- + +To create a geo-spatial index on all documents using `latitude` and `longitude` +as separate attribute paths, two paths need to be specified in the `fields` +array: + +`collection.ensureIndex({ type: "geo", fields: [ "latitude", "longitude" ] })` + +In case that the index was successfully created, an object with the index +details, including the index-identifier, is returned. + +**Examples** + +Create a geo index for an array attribute: + +```js +--- +name: geoIndexCreateForArrayAttribute1 +description: '' +--- +~db._create("geo"); +db.geo.ensureIndex({ type: "geo", fields: [ "loc" ] }); +~db._drop("geo"); +``` + +Create a geo index for an array attribute: + +```js +--- +name: geoIndexCreateForArrayAttribute2 +description: '' +--- +~db._create("geo2"); +db.geo2.ensureIndex({ type: "geo", fields: [ "location.latitude", "location.longitude" ] }); +~db._drop("geo2"); +``` + +Use a geo index with the AQL `SORT` operation: + +```js +--- +name: geoIndexSortOptimization +description: '' +--- +~db._create("geoSort"); +var idx = db.geoSort.ensureIndex({ type: "geo", fields: [ "latitude", "longitude" ] }); +for (i = -90; i <= 90; i += 10) { + for (j = -180; j <= 180; j += 10) { + db.geoSort.save({ name : "Name/" + i + "/" + j, latitude : i, longitude : j }); + } +} +var query = "FOR doc in geoSort SORT DISTANCE(doc.latitude, doc.longitude, 0, 0) LIMIT 5 RETURN doc" +db._explain(query, {}, {colors: false}); +db._query(query).toArray(); +~db._drop("geoSort"); +``` + +Use a geo index with the AQL `FILTER` operation: + +```js +--- +name: geoIndexFilterOptimization +description: '' +--- +~db._create("geoFilter"); +var idx = db.geoFilter.ensureIndex({ type: "geo", fields: [ "latitude", "longitude" ] }); +for (i = -90; i <= 90; i += 10) { + for (j = -180; j <= 180; j += 10) { + db.geoFilter.save({ name : "Name/" + i + "/" + j, latitude : i, longitude : j }); + } +} +var query = "FOR doc in geoFilter FILTER DISTANCE(doc.latitude, doc.longitude, 0, 0) < 2000 RETURN doc" +db._explain(query, {}, {colors: false}); +db._query(query).toArray(); +~db._drop("geoFilter"); +``` + ## Indexed GeoSpatial Queries The geospatial index supports a variety of AQL queries, which can be built with the help @@ -316,438 +433,3 @@ The second parameter must contain the document field on that the index was created. This `FILTER` clause can be combined with a `SORT` clause using `GEO_DISTANCE()`. - -## GeoJSON - -GeoJSON is a geospatial data format based on JSON. It defines several different -types of JSON objects and the way in which they can be combined to represent -data about geographic shapes on the Earth surface. GeoJSON uses a geographic -coordinate reference system, World Geodetic System 1984 (WGS 84), and units of decimal -degrees. - -Internally ArangoDB maps all coordinate pairs onto a unit sphere. Distances are -projected onto a sphere with the Earth's *Volumetric mean radius* of *6371 -km*. ArangoDB implements a useful subset of the GeoJSON format -[(RFC 7946)](https://tools.ietf.org/html/rfc7946). -Feature Objects and the GeometryCollection type are not supported. -Supported geometry object types are: - -- Point -- MultiPoint -- LineString -- MultiLineString -- Polygon -- MultiPolygon - -### GeoJSON interpretation - -Note the following technical detail about GeoJSON: The -[GeoJSON standard, Section 3.1.1 Position](https://datatracker.ietf.org/doc/html/rfc7946#section-3.1.1) -prescribes that lines are **cartesian lines in cylindrical coordinates** -(longitude/latitude). However, this definition is inconvenient in practice, -since such lines are not geodesic on the surface of the Earth. -Furthermore, the best available algorithms for geospatial computations on Earth -typically use geodesic lines as the boundaries of polygons on Earth. - -Therefore, ArangoDB uses the **syntax of the GeoJSON** standard, -but then interprets lines (and boundaries of polygons) as -**geodesic lines (pieces of great circles) on Earth**. This is a -violation of the GeoJSON standard, but serving a practical purpose. - -Note in particular that this can sometimes lead to unexpected results. -Consider the following polygon (remember that GeoJSON has -**longitude before latitude** in coordinate pairs): - -```json -{ "type": "Polygon", "coordinates": [[ - [4, 54], [4, 47], [16, 47], [16, 54], [4, 54] -]] } -``` - -![GeoJSON Polygon Geodesic](../../../../images/geojson-polygon-geodesic.webp) - -It does not contain the point `[10, 47]` since the shortest path (geodesic) -from `[4, 47]` to `[16, 47]` lies North relative to the parallel of latitude at -47 degrees. On the contrary, the polygon does contain the point `[10, 54]` as it -lies South of the parallel of latitude at 54 degrees. - -{{< info >}} -ArangoDB version before 3.10 did an inconsistent special detection of "rectangle" -polygons that later versions from 3.10 onward no longer do, see -[Legacy Polygons](#legacy-polygons). -{{< /info >}} - -Furthermore, there is an issue with the interpretation of linear rings -(boundaries of polygons) according to -[GeoJSON standard, Section 3.1.6 Polygon](https://datatracker.ietf.org/doc/html/rfc7946#section-3.1.6). -This section states explicitly: - -> A linear ring MUST follow the right-hand rule with respect to the -> area it bounds, i.e., exterior rings are counter-clockwise, and -> holes are clockwise. - -This rather misleading phrase means that when a linear ring is used as -the boundary of a polygon, the "interior" of the polygon lies **to the left** -of the boundary when one travels on the surface of the Earth and -along the linear ring. For -example, the polygon below travels **counter-clockwise** around the point -`[10, 50]`, and thus the interior of the polygon contains this point and -its surroundings, but not, for example, the North Pole and the South -Pole. - -```json -{ "type": "Polygon", "coordinates": [[ - [4, 54], [4, 47], [16, 47], [16, 54], [4, 54] -]] } -``` - -![GeoJSON Polygon Counter-clockwise](../../../../images/geojson-polygon-ccw.webp) - -On the other hand, the following polygon travels **clockwise** around the point -`[10, 50]`, and thus its "interior" does not contain `[10, 50]`, but does -contain the North Pole and the South Pole: - -```json -{ "type": "Polygon", "coordinates": [[ - [4, 54], [16, 54], [16, 47], [4, 47], [4, 54] -]] } -``` - -![GeoJSON Polygon Clockwise](../../../../images/geojson-polygon-cw.webp) - -Remember that the "interior" is to the left of the given -linear ring, so this second polygon is basically the complement on Earth -of the previous polygon! - -ArangoDB versions before 3.10 did not follow this rule and always took the -"smaller" connected component of the surface as the "interior" of the polygon. -This made it impossible to specify polygons which covered more than half of the -sphere. From version 3.10 onward, ArangoDB recognizes this correctly. -See [Legacy Polygons](#legacy-polygons) for how to deal with this issue. - -### Point - -A [GeoJSON Point](https://tools.ietf.org/html/rfc7946#section-3.1.2) is a -[position](https://tools.ietf.org/html/rfc7946#section-3.1.1) comprised of -a longitude and a latitude: - -```json -{ - "type": "Point", - "coordinates": [100.0, 0.0] -} -``` - -### MultiPoint - -A [GeoJSON MultiPoint](https://tools.ietf.org/html/rfc7946#section-3.1.7) is -an array of positions: - -```json -{ - "type": "MultiPoint", - "coordinates": [ - [100.0, 0.0], - [101.0, 1.0] - ] -} -``` - -### LineString - -A [GeoJSON LineString](https://tools.ietf.org/html/rfc7946#section-3.1.4) is -an array of two or more positions: - -```json -{ - "type": "LineString", - "coordinates": [ - [100.0, 0.0], - [101.0, 1.0] - ] -} -``` - -### MultiLineString - -A [GeoJSON MultiLineString](https://tools.ietf.org/html/rfc7946#section-3.1.5) is -an array of LineString coordinate arrays: - -```json -{ - "type": "MultiLineString", - "coordinates": [ - [ - [100.0, 0.0], - [101.0, 1.0] - ], - [ - [102.0, 2.0], - [103.0, 3.0] - ] - ] -} -``` - -### Polygon - -A [GeoJSON Polygon](https://tools.ietf.org/html/rfc7946#section-3.1.6) consists -of a series of closed `LineString` objects (ring-like). These *Linear Ring* -objects consist of four or more coordinate pairs with the first and last -coordinate pair being equal. Coordinate pairs of a Polygon are an array of -linear ring coordinate arrays. The first element in the array represents -the exterior ring. Any subsequent elements represent interior rings -(holes within the surface). - -The orientation of the first linear ring is crucial: the right-hand-rule -is applied, so that the area to the left of the path of the linear ring -(when walking on the surface of the Earth) is considered to be the -"interior" of the polygon. All other linear rings must be contained -within this interior. According to the GeoJSON standard, the subsequent -linear rings must be oriented following the right-hand-rule, too, -that is, they must run **clockwise** around the hole (viewed from -above). However, ArangoDB is tolerant here (as suggested by the -[GeoJSON standard](https://datatracker.ietf.org/doc/html/rfc7946#section-3.1.6)), -all but the first linear ring are inverted if the orientation is wrong. - -In the end, a point is considered to be in the interior of the polygon, -if and only if one has to cross an odd number of linear rings to reach the -exterior of the polygon prescribed by the first linear ring. - -A number of additional rules apply (and are enforced by the GeoJSON -parser): - -- A polygon must contain at least one linear ring, i.e., it must not be - empty. -- A linear ring may not be empty, it needs at least three _distinct_ - coordinate pairs, that is, at least 4 coordinate pairs (since the first and - last must be the same). -- No two edges of linear rings in the polygon must intersect, in - particular, no linear ring may be self-intersecting. -- Within the same linear ring, consecutive coordinate pairs may be the same, - otherwise all coordinate pairs need to be distinct (except the first and last one). -- Linear rings of a polygon must not share edges, but they may share coordinate pairs. -- A linear ring defines two regions on the sphere. ArangoDB always - interprets the region that lies to the left of the boundary ring (in - the direction of its travel on the surface of the Earth) as the - interior of the ring. This is in contrast to earlier versions of - ArangoDB before 3.10, which always took the **smaller** of the two - regions as the interior. Therefore, from 3.10 on one can now have - polygons whose outer ring encloses more than half the Earth's surface. -- The interior rings must be contained in the (interior) of the outer ring. -- Interior rings should follow the above rule for orientation - (counterclockwise external rings, clockwise internal rings, interior - always to the left of the line). - -Here is an example with no holes: - -```json -{ - "type": "Polygon", - "coordinates": [ - [ - [100.0, 0.0], - [101.0, 0.0], - [101.0, 1.0], - [100.0, 1.0], - [100.0, 0.0] - ] - ] -} -``` - -Here is an example with a hole: - -```json -{ - "type": "Polygon", - "coordinates": [ - [ - [100.0, 0.0], - [101.0, 0.0], - [101.0, 1.0], - [100.0, 1.0], - [100.0, 0.0] - ], - [ - [100.8, 0.8], - [100.8, 0.2], - [100.2, 0.2], - [100.2, 0.8], - [100.8, 0.8] - ] - ] -} -``` - -### MultiPolygon - -A [GeoJSON MultiPolygon](https://tools.ietf.org/html/rfc7946#section-3.1.6) consists -of multiple polygons. The "coordinates" member is an array of -_Polygon_ coordinate arrays. See [above](#polygon) for the rules and -the meaning of polygons. - -If the polygons in a MultiPolygon are disjoint, then a point is in the -interior of the MultiPolygon if and only if it is -contained in one of the polygons. If some polygon P2 in a MultiPolygon -is contained in another polygon P1, then P2 is treated like a hole -in P1 and containment of points is defined with the even-odd-crossings rule -(see [Polygon](#polygon)). - -Additionally, the following rules apply and are enforced for -MultiPolygons: - -- No two edges in the linear rings of the polygons of a MultiPolygon - may intersect. -- Polygons in the same MultiPolygon may not share edges, but they may share - coordinate pairs. - -Example with two polygons, the second one with a hole: - -```json -{ - "type": "MultiPolygon", - "coordinates": [ - [ - [ - [102.0, 2.0], - [103.0, 2.0], - [103.0, 3.0], - [102.0, 3.0], - [102.0, 2.0] - ] - ], - [ - [ - [100.0, 0.0], - [101.0, 0.0], - [101.0, 1.0], - [100.0, 1.0], - [100.0, 0.0] - ], - [ - [100.2, 0.2], - [100.2, 0.8], - [100.8, 0.8], - [100.8, 0.2], - [100.2, 0.2] - ] - ] - ] -} -``` - -## *arangosh* Examples - -Ensures that a geo index exists: - -`collection.ensureIndex({ type: "geo", fields: [ "location" ] })` - -Creates a geospatial index on all documents using `location` as the path to the -coordinates. The value of the attribute has to be an array with at least two -numeric values. The array must contain the latitude (first value) and the -longitude (second value). - -All documents, which do not have the attribute path or have a non-conforming -value in it, are excluded from the index. - -A geo index is implicitly sparse, and there is no way to control its -sparsity. - -The index does not provide a `unique` option because of its limited usability. -It would prevent identical coordinate pairs from being inserted only, but even a -slightly different location (like 1 inch or 1 cm off) would be unique again and -not considered a duplicate, although it probably should. The desired threshold -for detecting duplicates may vary for every project (including how to calculate -the distance even) and needs to be implemented on the application layer as -needed. You can write a [Foxx service](../../../develop/foxx-microservices/_index.md) for this purpose and -make use of the AQL [geo functions](../../../aql/functions/geo.md) to find nearby -locations supported by a geo index. - -In case that the index was successfully created, an object with the index -details, including the index-identifier, is returned. - ---- - -To create a geo index on an array attribute that contains longitude first, set -the `geoJson` attribute to `true`. This corresponds to the format described in -[RFC 7946 Position](https://tools.ietf.org/html/rfc7946#section-3.1.1) - -`collection.ensureIndex({ type: "geo", fields: [ "location" ], geoJson: true })` - ---- - -To create a geo-spatial index on all documents using `latitude` and `longitude` -as separate attribute paths, two paths need to be specified in the `fields` -array: - -`collection.ensureIndex({ type: "geo", fields: [ "latitude", "longitude" ] })` - -In case that the index was successfully created, an object with the index -details, including the index-identifier, is returned. - -**Examples** - -Create a geo index for an array attribute: - -```js ---- -name: geoIndexCreateForArrayAttribute1 -description: '' ---- -~db._create("geo"); -db.geo.ensureIndex({ type: "geo", fields: [ "loc" ] }); -~db._drop("geo"); -``` - -Create a geo index for an array attribute: - -```js ---- -name: geoIndexCreateForArrayAttribute2 -description: '' ---- -~db._create("geo2"); -db.geo2.ensureIndex({ type: "geo", fields: [ "location.latitude", "location.longitude" ] }); -~db._drop("geo2"); -``` - -Use a geo index with the AQL `SORT` operation: - -```js ---- -name: geoIndexSortOptimization -description: '' ---- -~db._create("geoSort"); -var idx = db.geoSort.ensureIndex({ type: "geo", fields: [ "latitude", "longitude" ] }); -for (i = -90; i <= 90; i += 10) { - for (j = -180; j <= 180; j += 10) { - db.geoSort.save({ name : "Name/" + i + "/" + j, latitude : i, longitude : j }); - } -} -var query = "FOR doc in geoSort SORT DISTANCE(doc.latitude, doc.longitude, 0, 0) LIMIT 5 RETURN doc" -db._explain(query, {}, {colors: false}); -db._query(query).toArray(); -~db._drop("geoSort"); -``` - -Use a geo index with the AQL `FILTER` operation: - -```js ---- -name: geoIndexFilterOptimization -description: '' ---- -~db._create("geoFilter"); -var idx = db.geoFilter.ensureIndex({ type: "geo", fields: [ "latitude", "longitude" ] }); -for (i = -90; i <= 90; i += 10) { - for (j = -180; j <= 180; j += 10) { - db.geoFilter.save({ name : "Name/" + i + "/" + j, latitude : i, longitude : j }); - } -} -var query = "FOR doc in geoFilter FILTER DISTANCE(doc.latitude, doc.longitude, 0, 0) < 2000 RETURN doc" -db._explain(query, {}, {colors: false}); -db._query(query).toArray(); -~db._drop("geoFilter"); -``` diff --git a/site/content/3.10/release-notes/version-3.10/incompatible-changes-in-3-10.md b/site/content/3.10/release-notes/version-3.10/incompatible-changes-in-3-10.md index c12ec0c2e9..72186005a9 100644 --- a/site/content/3.10/release-notes/version-3.10/incompatible-changes-in-3-10.md +++ b/site/content/3.10/release-notes/version-3.10/incompatible-changes-in-3-10.md @@ -109,8 +109,8 @@ interpretation of GeoJSON polygons in version 3.9 and older: This can mean that old polygon GeoJSON data in the database is suddenly interpreted in a different way. See [Legacy Polygons](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#legacy-polygons) for details. -Also see the definition of [Polygons](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#polygon) -and [GeoJSON interpretation](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson-interpretation). +Also see the definition of [Polygons](../../aql/functions/geo.md#polygon) +and [GeoJSON interpretation](../../aql/functions/geo.md#geojson-interpretation). ## `geojson` Analyzers @@ -148,7 +148,7 @@ the new Analyzers. | The smaller of the two regions defined by a linear ring is interpreted as the interior of the ring. | The area to the left of the boundary ring's path is considered to be the interior. | | A ring can at most enclose half the Earth's surface | A ring can enclose the entire surface of the Earth | -Also see the definition of [Polygons](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#polygon) and the +Also see the definition of [Polygons](../../aql/functions/geo.md#polygon) and the [`geojson` Analyzer](../../index-and-search/analyzers.md#geojson) documentation. ## Maximum Array / Object Nesting diff --git a/site/content/3.10/release-notes/version-3.10/whats-new-in-3-10.md b/site/content/3.10/release-notes/version-3.10/whats-new-in-3-10.md index f0e47cf365..4c00b7dacf 100644 --- a/site/content/3.10/release-notes/version-3.10/whats-new-in-3-10.md +++ b/site/content/3.10/release-notes/version-3.10/whats-new-in-3-10.md @@ -581,7 +581,7 @@ This diverges from the previous implementation in two fundamental ways: that the "smaller" of the two connected components are the interior. This allows specifying polygons that cover more than half of the surface of the Earth and conforms to the GeoJSON standard. - See [GeoJSON interpretation](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson-interpretation) + See [GeoJSON interpretation](../../aql/functions/geo.md#geojson-interpretation) for examples. Additionally, the reported issues, which occasionally produced diff --git a/site/content/3.11/about-arangodb/features/community-edition.md b/site/content/3.11/about-arangodb/features/community-edition.md index e46761ac2b..6066f7a9eb 100644 --- a/site/content/3.11/about-arangodb/features/community-edition.md +++ b/site/content/3.11/about-arangodb/features/community-edition.md @@ -141,7 +141,7 @@ see [arangodb.com/community-server/](https://www.arangodb.com/community-server/) Flexible data field pre-processing with custom queries and the ability to chain built-in and custom Analyzers. Language-agnostic tokenization of text. -- [**GeoJSON Support**](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson): +- [**GeoJSON Support**](../../aql/functions/geo.md#geojson): Geographic data encoded in the popular GeoJSON format can be stored and used for geo-spatial queries. diff --git a/site/content/3.11/aql/functions/arangosearch.md b/site/content/3.11/aql/functions/arangosearch.md index d64b050320..71d1cdb4ce 100644 --- a/site/content/3.11/aql/functions/arangosearch.md +++ b/site/content/3.11/aql/functions/arangosearch.md @@ -1063,7 +1063,7 @@ be used in conjunction with ArangoSearch. `GEO_CONTAINS(geoJsonA, geoJsonB) → bool` -Checks whether the [GeoJSON object](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson) `geoJsonA` +Checks whether the [GeoJSON object](geo.md#geojson) `geoJsonA` fully contains `geoJsonB` (every point in B is also in A). - **geoJsonA** (object\|array): first GeoJSON object or coordinate array @@ -1079,7 +1079,7 @@ fully contains `geoJsonB` (every point in B is also in A). `GEO_DISTANCE(geoJsonA, geoJsonB) → distance` -Return the distance between two [GeoJSON objects](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson), +Return the distance between two [GeoJSON objects](geo.md#geojson), measured from the `centroid` of each shape. - **geoJsonA** (object\|array): first GeoJSON object or coordinate array @@ -1095,7 +1095,7 @@ measured from the `centroid` of each shape. `GEO_IN_RANGE(geoJsonA, geoJsonB, low, high, includeLow, includeHigh) → bool` -Checks whether the distance between two [GeoJSON objects](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson) +Checks whether the distance between two [GeoJSON objects](geo.md#geojson) lies within a given interval. The distance is measured from the `centroid` of each shape. @@ -1119,7 +1119,7 @@ each shape. `GEO_INTERSECTS(geoJsonA, geoJsonB) → bool` -Checks whether the [GeoJSON object](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson) `geoJsonA` +Checks whether the [GeoJSON object](geo.md#geojson) `geoJsonA` intersects with `geoJsonB` (i.e. at least one point of B is in A or vice versa). - **geoJsonA** (object\|array): first GeoJSON object or coordinate array diff --git a/site/content/3.11/aql/functions/date.md b/site/content/3.11/aql/functions/date.md index bc7a8bad54..6415589d77 100644 --- a/site/content/3.11/aql/functions/date.md +++ b/site/content/3.11/aql/functions/date.md @@ -7,6 +7,8 @@ description: >- ISO 8601 date time strings archetype: default --- +## Date and time representations + AQL offers functionality to work with dates, but it does not have a special data type for dates (neither does JSON, which is usually used as format to ship data into and out of ArangoDB). Instead, dates in AQL are represented by either numbers or strings. diff --git a/site/content/3.11/aql/functions/geo.md b/site/content/3.11/aql/functions/geo.md index d78d03154c..652d8b3c00 100644 --- a/site/content/3.11/aql/functions/geo.md +++ b/site/content/3.11/aql/functions/geo.md @@ -7,6 +7,384 @@ description: >- accelerated by geo-spatial indexes archetype: default --- +## Geo-spatial data representations + +You can model geo-spatial information in different ways using the data types +available in ArangoDB. The recommended way is to use objects with **GeoJSON** +geometry but you can also use **longitude and latitude coordinate pairs** +for points. Both models are supported by +[Geo-Spatial Indexes](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md). + +### Coordinate pairs + +Longitude and latitude coordinates are numeric values and can be stored in the +following ways: + +- Coordinates using an array with two numbers in `[longitude, latitude]` order, + for example, in a user-chosen attribute called `location`: + + ```json + { + "location": [ -73.983, 40.764 ] + } + ``` + +- Coordinates using an array with two numbers in `[latitude, longitude]` order, + for example, in a user-chosen attribute called `location`: + + ```json + { + "location": [ 40.764, -73.983 ] + } + ``` + +- Coordinates using two separate numeric attributes, for example, in two + user-chosen attributes called `lat` and `lng` as sub-attributes of a `location` + attribute: + + ```json + { + "location": { + "lat": 40.764, + "lng": -73.983 + } + } + ``` + +### GeoJSON + +GeoJSON is a geospatial data format based on JSON. It defines several different +types of JSON objects and the way in which they can be combined to represent +data about geographic shapes on the Earth surface. + +Example of a document with a GeoJSON Point stored in a user-chosen attribute +called `location` (with coordinates in `[longitude, latitude]` order): + +```json +{ + "location": { + "type": "Point", + "coordinates": [ -73.983, 40.764 ] + } +} +``` + +GeoJSON uses a geographic coordinate reference system, +World Geodetic System 1984 (WGS 84), and units of decimal degrees. + +Internally ArangoDB maps all coordinate pairs onto a unit sphere. Distances are +projected onto a sphere with the Earth's *Volumetric mean radius* of *6371 +km*. ArangoDB implements a useful subset of the GeoJSON format +[(RFC 7946)](https://tools.ietf.org/html/rfc7946). +Feature Objects and the GeometryCollection type are not supported. +Supported geometry object types are: + +- Point +- MultiPoint +- LineString +- MultiLineString +- Polygon +- MultiPolygon + +#### Point + +A [GeoJSON Point](https://tools.ietf.org/html/rfc7946#section-3.1.2) is a +[position](https://tools.ietf.org/html/rfc7946#section-3.1.1) comprised of +a longitude and a latitude: + +```json +{ + "type": "Point", + "coordinates": [100.0, 0.0] +} +``` + +#### MultiPoint + +A [GeoJSON MultiPoint](https://tools.ietf.org/html/rfc7946#section-3.1.7) is +an array of positions: + +```json +{ + "type": "MultiPoint", + "coordinates": [ + [100.0, 0.0], + [101.0, 1.0] + ] +} +``` + +#### LineString + +A [GeoJSON LineString](https://tools.ietf.org/html/rfc7946#section-3.1.4) is +an array of two or more positions: + +```json +{ + "type": "LineString", + "coordinates": [ + [100.0, 0.0], + [101.0, 1.0] + ] +} +``` + +#### MultiLineString + +A [GeoJSON MultiLineString](https://tools.ietf.org/html/rfc7946#section-3.1.5) is +an array of LineString coordinate arrays: + +```json +{ + "type": "MultiLineString", + "coordinates": [ + [ + [100.0, 0.0], + [101.0, 1.0] + ], + [ + [102.0, 2.0], + [103.0, 3.0] + ] + ] +} +``` + +#### Polygon + +A [GeoJSON Polygon](https://tools.ietf.org/html/rfc7946#section-3.1.6) consists +of a series of closed `LineString` objects (ring-like). These *Linear Ring* +objects consist of four or more coordinate pairs with the first and last +coordinate pair being equal. Coordinate pairs of a Polygon are an array of +linear ring coordinate arrays. The first element in the array represents +the exterior ring. Any subsequent elements represent interior rings +(holes within the surface). + +The orientation of the first linear ring is crucial: the right-hand-rule +is applied, so that the area to the left of the path of the linear ring +(when walking on the surface of the Earth) is considered to be the +"interior" of the polygon. All other linear rings must be contained +within this interior. According to the GeoJSON standard, the subsequent +linear rings must be oriented following the right-hand-rule, too, +that is, they must run **clockwise** around the hole (viewed from +above). However, ArangoDB is tolerant here (as suggested by the +[GeoJSON standard](https://datatracker.ietf.org/doc/html/rfc7946#section-3.1.6)), +all but the first linear ring are inverted if the orientation is wrong. + +In the end, a point is considered to be in the interior of the polygon, +if and only if one has to cross an odd number of linear rings to reach the +exterior of the polygon prescribed by the first linear ring. + +A number of additional rules apply (and are enforced by the GeoJSON +parser): + +- A polygon must contain at least one linear ring, i.e., it must not be + empty. +- A linear ring may not be empty, it needs at least three _distinct_ + coordinate pairs, that is, at least 4 coordinate pairs (since the first and + last must be the same). +- No two edges of linear rings in the polygon must intersect, in + particular, no linear ring may be self-intersecting. +- Within the same linear ring, consecutive coordinate pairs may be the same, + otherwise all coordinate pairs need to be distinct (except the first and last one). +- Linear rings of a polygon must not share edges, but they may share coordinate pairs. +- A linear ring defines two regions on the sphere. ArangoDB always + interprets the region that lies to the left of the boundary ring (in + the direction of its travel on the surface of the Earth) as the + interior of the ring. This is in contrast to earlier versions of + ArangoDB before 3.10, which always took the **smaller** of the two + regions as the interior. Therefore, from 3.10 on one can now have + polygons whose outer ring encloses more than half the Earth's surface. +- The interior rings must be contained in the (interior) of the outer ring. +- Interior rings should follow the above rule for orientation + (counterclockwise external rings, clockwise internal rings, interior + always to the left of the line). + +Here is an example with no holes: + +```json +{ + "type": "Polygon", + "coordinates": [ + [ + [100.0, 0.0], + [101.0, 0.0], + [101.0, 1.0], + [100.0, 1.0], + [100.0, 0.0] + ] + ] +} +``` + +Here is an example with a hole: + +```json +{ + "type": "Polygon", + "coordinates": [ + [ + [100.0, 0.0], + [101.0, 0.0], + [101.0, 1.0], + [100.0, 1.0], + [100.0, 0.0] + ], + [ + [100.8, 0.8], + [100.8, 0.2], + [100.2, 0.2], + [100.2, 0.8], + [100.8, 0.8] + ] + ] +} +``` + +#### MultiPolygon + +A [GeoJSON MultiPolygon](https://tools.ietf.org/html/rfc7946#section-3.1.6) consists +of multiple polygons. The "coordinates" member is an array of +_Polygon_ coordinate arrays. See [above](#polygon) for the rules and +the meaning of polygons. + +If the polygons in a MultiPolygon are disjoint, then a point is in the +interior of the MultiPolygon if and only if it is +contained in one of the polygons. If some polygon P2 in a MultiPolygon +is contained in another polygon P1, then P2 is treated like a hole +in P1 and containment of points is defined with the even-odd-crossings rule +(see [Polygon](#polygon)). + +Additionally, the following rules apply and are enforced for +MultiPolygons: + +- No two edges in the linear rings of the polygons of a MultiPolygon + may intersect. +- Polygons in the same MultiPolygon may not share edges, but they may share + coordinate pairs. + +Example with two polygons, the second one with a hole: + +```json +{ + "type": "MultiPolygon", + "coordinates": [ + [ + [ + [102.0, 2.0], + [103.0, 2.0], + [103.0, 3.0], + [102.0, 3.0], + [102.0, 2.0] + ] + ], + [ + [ + [100.0, 0.0], + [101.0, 0.0], + [101.0, 1.0], + [100.0, 1.0], + [100.0, 0.0] + ], + [ + [100.2, 0.2], + [100.2, 0.8], + [100.8, 0.8], + [100.8, 0.2], + [100.2, 0.2] + ] + ] + ] +} +``` + +## GeoJSON interpretation + +Note the following technical detail about GeoJSON: The +[GeoJSON standard, Section 3.1.1 Position](https://datatracker.ietf.org/doc/html/rfc7946#section-3.1.1) +prescribes that lines are **cartesian lines in cylindrical coordinates** +(longitude/latitude). However, this definition is inconvenient in practice, +since such lines are not geodesic on the surface of the Earth. +Furthermore, the best available algorithms for geospatial computations on Earth +typically use geodesic lines as the boundaries of polygons on Earth. + +Therefore, ArangoDB uses the **syntax of the GeoJSON** standard, +but then interprets lines (and boundaries of polygons) as +**geodesic lines (pieces of great circles) on Earth**. This is a +violation of the GeoJSON standard, but serving a practical purpose. + +Note in particular that this can sometimes lead to unexpected results. +Consider the following polygon (remember that GeoJSON has +**longitude before latitude** in coordinate pairs): + +```json +{ "type": "Polygon", "coordinates": [[ + [4, 54], [4, 47], [16, 47], [16, 54], [4, 54] +]] } +``` + +![GeoJSON Polygon Geodesic](../../../images/geojson-polygon-geodesic.webp) + +It does not contain the point `[10, 47]` since the shortest path (geodesic) +from `[4, 47]` to `[16, 47]` lies North relative to the parallel of latitude at +47 degrees. On the contrary, the polygon does contain the point `[10, 54]` as it +lies South of the parallel of latitude at 54 degrees. + +{{< info >}} +ArangoDB version before 3.10 did an inconsistent special detection of "rectangle" +polygons that later versions from 3.10 onward no longer do, see +[Legacy Polygons](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#legacy-polygons). +{{< /info >}} + +Furthermore, there is an issue with the interpretation of linear rings +(boundaries of polygons) according to +[GeoJSON standard, Section 3.1.6 Polygon](https://datatracker.ietf.org/doc/html/rfc7946#section-3.1.6). +This section states explicitly: + +> A linear ring MUST follow the right-hand rule with respect to the +> area it bounds, i.e., exterior rings are counter-clockwise, and +> holes are clockwise. + +This rather misleading phrase means that when a linear ring is used as +the boundary of a polygon, the "interior" of the polygon lies **to the left** +of the boundary when one travels on the surface of the Earth and +along the linear ring. For +example, the polygon below travels **counter-clockwise** around the point +`[10, 50]`, and thus the interior of the polygon contains this point and +its surroundings, but not, for example, the North Pole and the South +Pole. + +```json +{ "type": "Polygon", "coordinates": [[ + [4, 54], [4, 47], [16, 47], [16, 54], [4, 54] +]] } +``` + +![GeoJSON Polygon Counter-clockwise](../../../images/geojson-polygon-ccw.webp) + +On the other hand, the following polygon travels **clockwise** around the point +`[10, 50]`, and thus its "interior" does not contain `[10, 50]`, but does +contain the North Pole and the South Pole: + +```json +{ "type": "Polygon", "coordinates": [[ + [4, 54], [16, 54], [16, 47], [4, 47], [4, 54] +]] } +``` + +![GeoJSON Polygon Clockwise](../../../images/geojson-polygon-cw.webp) + +Remember that the "interior" is to the left of the given +linear ring, so this second polygon is basically the complement on Earth +of the previous polygon! + +ArangoDB versions before 3.10 did not follow this rule and always took the +"smaller" connected component of the surface as the "interior" of the polygon. +This made it impossible to specify polygons which covered more than half of the +sphere. From version 3.10 onward, ArangoDB recognizes this correctly. +See [Legacy Polygons](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#legacy-polygons) +for how to deal with this issue. + ## Geo utility functions The following helper functions **can** use geo indexes, but do not have to in @@ -43,7 +421,7 @@ FOR doc IN coll // e.g. documents returned by a traversal `GEO_CONTAINS(geoJsonA, geoJsonB) → bool` -Checks whether the [GeoJSON object](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson) `geoJsonA` +Checks whether the [GeoJSON object](#geojson) `geoJsonA` fully contains `geoJsonB` (every point in B is also in A). The object `geoJsonA` has to be of type _Polygon_ or _MultiPolygon_. For other types containment is not well-defined because of numerical stability problems. @@ -89,7 +467,7 @@ second argument. Passing it as the first argument, like Return the distance between two GeoJSON objects in meters, measured from the **centroid** of each shape. For a list of supported types see the -[geo index page](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson). +[geo index page](#geojson). - **geoJsonA** (object): first GeoJSON object, or a coordinate array in `[longitude, latitude]` order @@ -142,9 +520,8 @@ functions. `GEO_AREA(geoJson, ellipsoid) → area` -Return the area for a polygon or multi-polygon on a sphere with the -average Earth radius, or an ellipsoid. For a list of supported types -see the [geo index page](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson). +Return the area for a [Polygon](#polygon) or [MultiPolygon](#multipolygon) +on a sphere with the average Earth radius, or an ellipsoid. - **geoJson** (object): a GeoJSON object - **ellipsoid** (string, *optional*): reference ellipsoid to use. @@ -163,8 +540,7 @@ RETURN GEO_AREA(polygon, "wgs84") `GEO_EQUALS(geoJsonA, geoJsonB) → bool` -Checks whether two GeoJSON objects are equal or not. For a list of supported -types see the [geo index page](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson). +Checks whether two [GeoJSON objects](#geojson) are equal or not. - **geoJsonA** (object): first GeoJSON object. - **geoJsonB** (object): second GeoJSON object. @@ -194,7 +570,7 @@ RETURN GEO_EQUALS(polygonA, polygonB) // false `GEO_INTERSECTS(geoJsonA, geoJsonB) → bool` -Checks whether the [GeoJSON object](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson) `geoJsonA` +Checks whether the [GeoJSON object](#geojson) `geoJsonA` intersects with `geoJsonB` (i.e. at least one point in B is also in A or vice-versa). - **geoJsonA** (object): first GeoJSON object @@ -225,7 +601,7 @@ second argument. Passing it as the first argument, like `GEO_IN_RANGE(geoJsonA, geoJsonB, low, high, includeLow, includeHigh) → bool` -Checks whether the distance between two [GeoJSON objects](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson) +Checks whether the distance between two [GeoJSON objects](#geojson) lies within a given interval. The distance is measured from the **centroid** of each shape. @@ -392,7 +768,7 @@ a linear ring. Each linear ring consists of an array with at least four longitude/latitude pairs. The first linear ring must be the outermost, while any subsequent linear ring will be interpreted as holes. -For details about the rules, see [GeoJSON polygons](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#polygon). +For details about the rules, see [GeoJSON Polygon](#polygon). - **points** (array): an array of (arrays of) `[longitude, latitude]` pairs - returns **geoJson** (object\|null): a valid GeoJSON Polygon @@ -431,7 +807,8 @@ RETURN GEO_POLYGON([ `GEO_MULTIPOLYGON(polygons) → geoJson` Construct a GeoJSON MultiPolygon. Needs at least two Polygons inside. -See [GEO_POLYGON()](#geo_polygon) and [GeoJSON MultiPolygons](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#multipolygon) for the rules of Polygon and MultiPolygon construction. +See [GEO_POLYGON()](#geo_polygon) and [GeoJSON MultiPolygon](#multipolygon) +for the rules of Polygon and MultiPolygon construction. - **polygons** (array): an array of arrays of arrays of `[longitude, latitude]` pairs - returns **geoJson** (object\|null): a valid GeoJSON MultiPolygon @@ -555,7 +932,7 @@ value in an attribute of that name. `WITHIN_RECTANGLE()` is a deprecated AQL function from version 3.4.0 on. Use [`GEO_CONTAINS()`](#geo_contains) and a GeoJSON polygon instead - but note that this uses geodesic lines from version 3.10.0 onward -(see [GeoJSON interpretation](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson-interpretation)): +(see [GeoJSON interpretation](#geojson-interpretation)): ```aql LET rect = GEO_POLYGON([ [ diff --git a/site/content/3.11/arangograph/notebooks.md b/site/content/3.11/arangograph/notebooks.md index 138ab79819..15972607d4 100644 --- a/site/content/3.11/arangograph/notebooks.md +++ b/site/content/3.11/arangograph/notebooks.md @@ -20,7 +20,7 @@ passwords, and endpoint URLs. ![ArangoGraph Notebooks Architecture](../../images/arangograph-notebooks-architecture.png) -The ArangoGraph Notebook has built-in [ArangoGraph Magic Commands](.#arangograph-magic-commands) +The ArangoGraph Notebook has built-in [ArangoGraph Magic Commands](#arangograph-magic-commands) that answer questions like: - What ArangoDB database am I connected to at the moment? - What data does the ArangoDB instance contain? diff --git a/site/content/3.11/develop/http-api/general-request-handling.md b/site/content/3.11/develop/http-api/general-request-handling.md index 93f067ebc2..81fc20a86b 100644 --- a/site/content/3.11/develop/http-api/general-request-handling.md +++ b/site/content/3.11/develop/http-api/general-request-handling.md @@ -139,7 +139,7 @@ HTTP request. The server executes the jobs from the queue asynchronously as fast as possible, while clients can continue to do other work. If the server queue is full (i.e. contains as many jobs as specified by the -[`--server.maximal-queue-size`](../../components/arangodb-server/options.md#arangodb-server-options) +[`--server.maximal-queue-size`](../../components/arangodb-server/options.md#--servermaximal-queue-size) startup option), then the request is rejected instantly with an HTTP `503 Service Unavailable` status code. diff --git a/site/content/3.11/develop/http-api/replication/write-ahead-log.md b/site/content/3.11/develop/http-api/replication/write-ahead-log.md index c1508cc090..03e7b3f69f 100644 --- a/site/content/3.11/develop/http-api/replication/write-ahead-log.md +++ b/site/content/3.11/develop/http-api/replication/write-ahead-log.md @@ -179,8 +179,8 @@ paths: - `data`: the original document data - A more detailed description of the individual replication event types and their - data structures can be found in [Operation Types](#operation-types). + For a more detailed description of the individual replication event types + and their data structures, see the Operation Types. The response also contains the following HTTP headers: diff --git a/site/content/3.11/index-and-search/analyzers.md b/site/content/3.11/index-and-search/analyzers.md index fed4b770c7..0dcec2f5a5 100644 --- a/site/content/3.11/index-and-search/analyzers.md +++ b/site/content/3.11/index-and-search/analyzers.md @@ -1254,7 +1254,7 @@ attributes: - `legacy` (boolean, _optional_): This option controls how GeoJSON Polygons are interpreted (introduced in v3.10.5). Also see [Legacy Polygons](indexing/working-with-indexes/geo-spatial-indexes.md#legacy-polygons) and - [GeoJSON interpretation](indexing/working-with-indexes/geo-spatial-indexes.md#geojson-interpretation). + [GeoJSON interpretation](../aql/functions/geo.md#geojson-interpretation). - If `legacy` is `true`, the smaller of the two regions defined by a linear ring is interpreted as the interior of the ring and a ring can at most diff --git a/site/content/3.11/index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md b/site/content/3.11/index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md index bb456cccc9..0919bc6e4b 100644 --- a/site/content/3.11/index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md +++ b/site/content/3.11/index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md @@ -8,8 +8,8 @@ description: >- archetype: default --- The geo-spatial index type in ArangoDB is based on [Google S2](http://s2geometry.io/). -Indexing is supported for a subset of the [**GeoJSON**](#geojson) geometry types -as well as simple latitude/longitude pairs. +Indexing is supported for a subset of the [**GeoJSON**](../../../aql/functions/geo.md#geojson) +geometry types as well as simple latitude/longitude pairs. AQL's geospatial functions and GeoJSON constructors are described in [Geo functions](../../../aql/functions/geo.md). @@ -34,7 +34,7 @@ documents which do not fulfill these requirements. To create an index in GeoJSON mode execute: ```js -collection.ensureIndex({ type: "geo", fields: [ "geometry" ], geoJson:true }) +collection.ensureIndex({ type: "geo", fields: [ "geometry" ], geoJson: true }) ``` This creates the index on all documents and uses _geometry_ as the attributed @@ -53,10 +53,10 @@ sparsity. In case that the index was successfully created, an object with the index details, including the index-identifier, is returned. -See [GeoJSON Interpretation](#geojson-interpretation) for technical details on -how ArangoDB interprets GeoJSON objects. In short: the **syntax** of GeoJSON is -used, but polygon boundaries and lines between points are interpreted as -geodesics (pieces of great circles on Earth). +For technical details on how ArangoDB interprets GeoJSON objects, see +[GeoJSON Interpretation](../../../aql/functions/geo.md#geojson-interpretation). +In short: the **syntax** of GeoJSON is used, but polygon boundaries and lines +between points are interpreted as geodesics (pieces of great circles on Earth). ### Non-GeoJSON mode @@ -119,14 +119,16 @@ same query is executed with the geo-spatial index, it doesn't find the documents ### Legacy Polygons -See [GeoJSON Interpretation](#geojson-interpretation) for details of the changes -between ArangoDB 3.10 and earlier versions. Two things have changed: +Between ArangoDB 3.10 and earlier versions, two things have changed: - boundaries of polygons are now geodesics and there is no special and inconsistent treatment of "rectangles" any more - linear rings are interpreted according to the rules and no longer "normalized" +See [GeoJSON Interpretation](../../../aql/functions/geo.md#geojson-interpretation) +for details. + For backward compatibility, a `legacyPolygons` option has been introduced for geo indexes. It is relevant for those that have `geoJson` set to `true` only. Geo indexes created in versions before 3.10 always @@ -161,6 +163,121 @@ intentionally or not cannot be determined automatically. Please test the new behavior manually. {{< /warning >}} +### *arangosh* Examples + +Ensures that a geo index exists: + +`collection.ensureIndex({ type: "geo", fields: [ "location" ] })` + +Creates a geospatial index on all documents using `location` as the path to the +coordinates. The value of the attribute has to be an array with at least two +numeric values. The array must contain the latitude (first value) and the +longitude (second value). + +All documents, which do not have the attribute path or have a non-conforming +value in it, are excluded from the index. + +A geo index is implicitly sparse, and there is no way to control its +sparsity. + +The index does not provide a `unique` option because of its limited usability. +It would prevent identical coordinate pairs from being inserted only, but even a +slightly different location (like 1 inch or 1 cm off) would be unique again and +not considered a duplicate, although it probably should. The desired threshold +for detecting duplicates may vary for every project (including how to calculate +the distance even) and needs to be implemented on the application layer as +needed. You can write a [Foxx service](../../../develop/foxx-microservices/_index.md) for this purpose and +make use of the [Geo-spatial functions in AQL](../../../aql/functions/geo.md) to find nearby +locations supported by a geo index. + +In case that the index was successfully created, an object with the index +details, including the index-identifier, is returned. + +--- + +To create a geo index on an array attribute that contains longitude first, set +the `geoJson` attribute to `true`. This corresponds to the format described in +[RFC 7946 Position](https://tools.ietf.org/html/rfc7946#section-3.1.1) + +`collection.ensureIndex({ type: "geo", fields: [ "location" ], geoJson: true })` + +--- + +To create a geo-spatial index on all documents using `latitude` and `longitude` +as separate attribute paths, two paths need to be specified in the `fields` +array: + +`collection.ensureIndex({ type: "geo", fields: [ "latitude", "longitude" ] })` + +In case that the index was successfully created, an object with the index +details, including the index-identifier, is returned. + +**Examples** + +Create a geo index for an array attribute: + +```js +--- +name: geoIndexCreateForArrayAttribute1 +description: '' +--- +~db._create("geo"); +db.geo.ensureIndex({ type: "geo", fields: [ "loc" ] }); +~db._drop("geo"); +``` + +Create a geo index for an array attribute: + +```js +--- +name: geoIndexCreateForArrayAttribute2 +description: '' +--- +~db._create("geo2"); +db.geo2.ensureIndex({ type: "geo", fields: [ "location.latitude", "location.longitude" ] }); +~db._drop("geo2"); +``` + +Use a geo index with the AQL `SORT` operation: + +```js +--- +name: geoIndexSortOptimization +description: '' +--- +~db._create("geoSort"); +var idx = db.geoSort.ensureIndex({ type: "geo", fields: [ "latitude", "longitude" ] }); +for (i = -90; i <= 90; i += 10) { + for (j = -180; j <= 180; j += 10) { + db.geoSort.save({ name : "Name/" + i + "/" + j, latitude : i, longitude : j }); + } +} +var query = "FOR doc in geoSort SORT DISTANCE(doc.latitude, doc.longitude, 0, 0) LIMIT 5 RETURN doc" +db._explain(query, {}, {colors: false}); +db._query(query).toArray(); +~db._drop("geoSort"); +``` + +Use a geo index with the AQL `FILTER` operation: + +```js +--- +name: geoIndexFilterOptimization +description: '' +--- +~db._create("geoFilter"); +var idx = db.geoFilter.ensureIndex({ type: "geo", fields: [ "latitude", "longitude" ] }); +for (i = -90; i <= 90; i += 10) { + for (j = -180; j <= 180; j += 10) { + db.geoFilter.save({ name : "Name/" + i + "/" + j, latitude : i, longitude : j }); + } +} +var query = "FOR doc in geoFilter FILTER DISTANCE(doc.latitude, doc.longitude, 0, 0) < 2000 RETURN doc" +db._explain(query, {}, {colors: false}); +db._query(query).toArray(); +~db._drop("geoFilter"); +``` + ## Indexed GeoSpatial Queries The geospatial index supports a variety of AQL queries, which can be built with the help @@ -316,438 +433,3 @@ The second parameter must contain the document field on that the index was created. This `FILTER` clause can be combined with a `SORT` clause using `GEO_DISTANCE()`. - -## GeoJSON - -GeoJSON is a geospatial data format based on JSON. It defines several different -types of JSON objects and the way in which they can be combined to represent -data about geographic shapes on the Earth surface. GeoJSON uses a geographic -coordinate reference system, World Geodetic System 1984 (WGS 84), and units of decimal -degrees. - -Internally ArangoDB maps all coordinate pairs onto a unit sphere. Distances are -projected onto a sphere with the Earth's *Volumetric mean radius* of *6371 -km*. ArangoDB implements a useful subset of the GeoJSON format -[(RFC 7946)](https://tools.ietf.org/html/rfc7946). -Feature Objects and the GeometryCollection type are not supported. -Supported geometry object types are: - -- Point -- MultiPoint -- LineString -- MultiLineString -- Polygon -- MultiPolygon - -### GeoJSON interpretation - -Note the following technical detail about GeoJSON: The -[GeoJSON standard, Section 3.1.1 Position](https://datatracker.ietf.org/doc/html/rfc7946#section-3.1.1) -prescribes that lines are **cartesian lines in cylindrical coordinates** -(longitude/latitude). However, this definition is inconvenient in practice, -since such lines are not geodesic on the surface of the Earth. -Furthermore, the best available algorithms for geospatial computations on Earth -typically use geodesic lines as the boundaries of polygons on Earth. - -Therefore, ArangoDB uses the **syntax of the GeoJSON** standard, -but then interprets lines (and boundaries of polygons) as -**geodesic lines (pieces of great circles) on Earth**. This is a -violation of the GeoJSON standard, but serving a practical purpose. - -Note in particular that this can sometimes lead to unexpected results. -Consider the following polygon (remember that GeoJSON has -**longitude before latitude** in coordinate pairs): - -```json -{ "type": "Polygon", "coordinates": [[ - [4, 54], [4, 47], [16, 47], [16, 54], [4, 54] -]] } -``` - -![GeoJSON Polygon Geodesic](../../../../images/geojson-polygon-geodesic.webp) - -It does not contain the point `[10, 47]` since the shortest path (geodesic) -from `[4, 47]` to `[16, 47]` lies North relative to the parallel of latitude at -47 degrees. On the contrary, the polygon does contain the point `[10, 54]` as it -lies South of the parallel of latitude at 54 degrees. - -{{< info >}} -ArangoDB version before 3.10 did an inconsistent special detection of "rectangle" -polygons that later versions from 3.10 onward no longer do, see -[Legacy Polygons](#legacy-polygons). -{{< /info >}} - -Furthermore, there is an issue with the interpretation of linear rings -(boundaries of polygons) according to -[GeoJSON standard, Section 3.1.6 Polygon](https://datatracker.ietf.org/doc/html/rfc7946#section-3.1.6). -This section states explicitly: - -> A linear ring MUST follow the right-hand rule with respect to the -> area it bounds, i.e., exterior rings are counter-clockwise, and -> holes are clockwise. - -This rather misleading phrase means that when a linear ring is used as -the boundary of a polygon, the "interior" of the polygon lies **to the left** -of the boundary when one travels on the surface of the Earth and -along the linear ring. For -example, the polygon below travels **counter-clockwise** around the point -`[10, 50]`, and thus the interior of the polygon contains this point and -its surroundings, but not, for example, the North Pole and the South -Pole. - -```json -{ "type": "Polygon", "coordinates": [[ - [4, 54], [4, 47], [16, 47], [16, 54], [4, 54] -]] } -``` - -![GeoJSON Polygon Counter-clockwise](../../../../images/geojson-polygon-ccw.webp) - -On the other hand, the following polygon travels **clockwise** around the point -`[10, 50]`, and thus its "interior" does not contain `[10, 50]`, but does -contain the North Pole and the South Pole: - -```json -{ "type": "Polygon", "coordinates": [[ - [4, 54], [16, 54], [16, 47], [4, 47], [4, 54] -]] } -``` - -![GeoJSON Polygon Clockwise](../../../../images/geojson-polygon-cw.webp) - -Remember that the "interior" is to the left of the given -linear ring, so this second polygon is basically the complement on Earth -of the previous polygon! - -ArangoDB versions before 3.10 did not follow this rule and always took the -"smaller" connected component of the surface as the "interior" of the polygon. -This made it impossible to specify polygons which covered more than half of the -sphere. From version 3.10 onward, ArangoDB recognizes this correctly. -See [Legacy Polygons](#legacy-polygons) for how to deal with this issue. - -### Point - -A [GeoJSON Point](https://tools.ietf.org/html/rfc7946#section-3.1.2) is a -[position](https://tools.ietf.org/html/rfc7946#section-3.1.1) comprised of -a longitude and a latitude: - -```json -{ - "type": "Point", - "coordinates": [100.0, 0.0] -} -``` - -### MultiPoint - -A [GeoJSON MultiPoint](https://tools.ietf.org/html/rfc7946#section-3.1.7) is -an array of positions: - -```json -{ - "type": "MultiPoint", - "coordinates": [ - [100.0, 0.0], - [101.0, 1.0] - ] -} -``` - -### LineString - -A [GeoJSON LineString](https://tools.ietf.org/html/rfc7946#section-3.1.4) is -an array of two or more positions: - -```json -{ - "type": "LineString", - "coordinates": [ - [100.0, 0.0], - [101.0, 1.0] - ] -} -``` - -### MultiLineString - -A [GeoJSON MultiLineString](https://tools.ietf.org/html/rfc7946#section-3.1.5) is -an array of LineString coordinate arrays: - -```json -{ - "type": "MultiLineString", - "coordinates": [ - [ - [100.0, 0.0], - [101.0, 1.0] - ], - [ - [102.0, 2.0], - [103.0, 3.0] - ] - ] -} -``` - -### Polygon - -A [GeoJSON Polygon](https://tools.ietf.org/html/rfc7946#section-3.1.6) consists -of a series of closed `LineString` objects (ring-like). These *Linear Ring* -objects consist of four or more coordinate pairs with the first and last -coordinate pair being equal. Coordinate pairs of a Polygon are an array of -linear ring coordinate arrays. The first element in the array represents -the exterior ring. Any subsequent elements represent interior rings -(holes within the surface). - -The orientation of the first linear ring is crucial: the right-hand-rule -is applied, so that the area to the left of the path of the linear ring -(when walking on the surface of the Earth) is considered to be the -"interior" of the polygon. All other linear rings must be contained -within this interior. According to the GeoJSON standard, the subsequent -linear rings must be oriented following the right-hand-rule, too, -that is, they must run **clockwise** around the hole (viewed from -above). However, ArangoDB is tolerant here (as suggested by the -[GeoJSON standard](https://datatracker.ietf.org/doc/html/rfc7946#section-3.1.6)), -all but the first linear ring are inverted if the orientation is wrong. - -In the end, a point is considered to be in the interior of the polygon, -if and only if one has to cross an odd number of linear rings to reach the -exterior of the polygon prescribed by the first linear ring. - -A number of additional rules apply (and are enforced by the GeoJSON -parser): - -- A polygon must contain at least one linear ring, i.e., it must not be - empty. -- A linear ring may not be empty, it needs at least three _distinct_ - coordinate pairs, that is, at least 4 coordinate pairs (since the first and - last must be the same). -- No two edges of linear rings in the polygon must intersect, in - particular, no linear ring may be self-intersecting. -- Within the same linear ring, consecutive coordinate pairs may be the same, - otherwise all coordinate pairs need to be distinct (except the first and last one). -- Linear rings of a polygon must not share edges, but they may share coordinate pairs. -- A linear ring defines two regions on the sphere. ArangoDB always - interprets the region that lies to the left of the boundary ring (in - the direction of its travel on the surface of the Earth) as the - interior of the ring. This is in contrast to earlier versions of - ArangoDB before 3.10, which always took the **smaller** of the two - regions as the interior. Therefore, from 3.10 on one can now have - polygons whose outer ring encloses more than half the Earth's surface. -- The interior rings must be contained in the (interior) of the outer ring. -- Interior rings should follow the above rule for orientation - (counterclockwise external rings, clockwise internal rings, interior - always to the left of the line). - -Here is an example with no holes: - -```json -{ - "type": "Polygon", - "coordinates": [ - [ - [100.0, 0.0], - [101.0, 0.0], - [101.0, 1.0], - [100.0, 1.0], - [100.0, 0.0] - ] - ] -} -``` - -Here is an example with a hole: - -```json -{ - "type": "Polygon", - "coordinates": [ - [ - [100.0, 0.0], - [101.0, 0.0], - [101.0, 1.0], - [100.0, 1.0], - [100.0, 0.0] - ], - [ - [100.8, 0.8], - [100.8, 0.2], - [100.2, 0.2], - [100.2, 0.8], - [100.8, 0.8] - ] - ] -} -``` - -### MultiPolygon - -A [GeoJSON MultiPolygon](https://tools.ietf.org/html/rfc7946#section-3.1.6) consists -of multiple polygons. The "coordinates" member is an array of -_Polygon_ coordinate arrays. See [above](#polygon) for the rules and -the meaning of polygons. - -If the polygons in a MultiPolygon are disjoint, then a point is in the -interior of the MultiPolygon if and only if it is -contained in one of the polygons. If some polygon P2 in a MultiPolygon -is contained in another polygon P1, then P2 is treated like a hole -in P1 and containment of points is defined with the even-odd-crossings rule -(see [Polygon](#polygon)). - -Additionally, the following rules apply and are enforced for -MultiPolygons: - -- No two edges in the linear rings of the polygons of a MultiPolygon - may intersect. -- Polygons in the same MultiPolygon may not share edges, but they may share - coordinate pairs. - -Example with two polygons, the second one with a hole: - -```json -{ - "type": "MultiPolygon", - "coordinates": [ - [ - [ - [102.0, 2.0], - [103.0, 2.0], - [103.0, 3.0], - [102.0, 3.0], - [102.0, 2.0] - ] - ], - [ - [ - [100.0, 0.0], - [101.0, 0.0], - [101.0, 1.0], - [100.0, 1.0], - [100.0, 0.0] - ], - [ - [100.2, 0.2], - [100.2, 0.8], - [100.8, 0.8], - [100.8, 0.2], - [100.2, 0.2] - ] - ] - ] -} -``` - -## *arangosh* Examples - -Ensures that a geo index exists: - -`collection.ensureIndex({ type: "geo", fields: [ "location" ] })` - -Creates a geospatial index on all documents using `location` as the path to the -coordinates. The value of the attribute has to be an array with at least two -numeric values. The array must contain the latitude (first value) and the -longitude (second value). - -All documents, which do not have the attribute path or have a non-conforming -value in it, are excluded from the index. - -A geo index is implicitly sparse, and there is no way to control its -sparsity. - -The index does not provide a `unique` option because of its limited usability. -It would prevent identical coordinate pairs from being inserted only, but even a -slightly different location (like 1 inch or 1 cm off) would be unique again and -not considered a duplicate, although it probably should. The desired threshold -for detecting duplicates may vary for every project (including how to calculate -the distance even) and needs to be implemented on the application layer as -needed. You can write a [Foxx service](../../../develop/foxx-microservices/_index.md) for this purpose and -make use of the AQL [geo functions](../../../aql/functions/geo.md) to find nearby -locations supported by a geo index. - -In case that the index was successfully created, an object with the index -details, including the index-identifier, is returned. - ---- - -To create a geo index on an array attribute that contains longitude first, set -the `geoJson` attribute to `true`. This corresponds to the format described in -[RFC 7946 Position](https://tools.ietf.org/html/rfc7946#section-3.1.1) - -`collection.ensureIndex({ type: "geo", fields: [ "location" ], geoJson: true })` - ---- - -To create a geo-spatial index on all documents using `latitude` and `longitude` -as separate attribute paths, two paths need to be specified in the `fields` -array: - -`collection.ensureIndex({ type: "geo", fields: [ "latitude", "longitude" ] })` - -In case that the index was successfully created, an object with the index -details, including the index-identifier, is returned. - -**Examples** - -Create a geo index for an array attribute: - -```js ---- -name: geoIndexCreateForArrayAttribute1 -description: '' ---- -~db._create("geo"); -db.geo.ensureIndex({ type: "geo", fields: [ "loc" ] }); -~db._drop("geo"); -``` - -Create a geo index for an array attribute: - -```js ---- -name: geoIndexCreateForArrayAttribute2 -description: '' ---- -~db._create("geo2"); -db.geo2.ensureIndex({ type: "geo", fields: [ "location.latitude", "location.longitude" ] }); -~db._drop("geo2"); -``` - -Use a geo index with the AQL `SORT` operation: - -```js ---- -name: geoIndexSortOptimization -description: '' ---- -~db._create("geoSort"); -var idx = db.geoSort.ensureIndex({ type: "geo", fields: [ "latitude", "longitude" ] }); -for (i = -90; i <= 90; i += 10) { - for (j = -180; j <= 180; j += 10) { - db.geoSort.save({ name : "Name/" + i + "/" + j, latitude : i, longitude : j }); - } -} -var query = "FOR doc in geoSort SORT DISTANCE(doc.latitude, doc.longitude, 0, 0) LIMIT 5 RETURN doc" -db._explain(query, {}, {colors: false}); -db._query(query).toArray(); -~db._drop("geoSort"); -``` - -Use a geo index with the AQL `FILTER` operation: - -```js ---- -name: geoIndexFilterOptimization -description: '' ---- -~db._create("geoFilter"); -var idx = db.geoFilter.ensureIndex({ type: "geo", fields: [ "latitude", "longitude" ] }); -for (i = -90; i <= 90; i += 10) { - for (j = -180; j <= 180; j += 10) { - db.geoFilter.save({ name : "Name/" + i + "/" + j, latitude : i, longitude : j }); - } -} -var query = "FOR doc in geoFilter FILTER DISTANCE(doc.latitude, doc.longitude, 0, 0) < 2000 RETURN doc" -db._explain(query, {}, {colors: false}); -db._query(query).toArray(); -~db._drop("geoFilter"); -``` diff --git a/site/content/3.11/release-notes/version-3.10/incompatible-changes-in-3-10.md b/site/content/3.11/release-notes/version-3.10/incompatible-changes-in-3-10.md index c12ec0c2e9..72186005a9 100644 --- a/site/content/3.11/release-notes/version-3.10/incompatible-changes-in-3-10.md +++ b/site/content/3.11/release-notes/version-3.10/incompatible-changes-in-3-10.md @@ -109,8 +109,8 @@ interpretation of GeoJSON polygons in version 3.9 and older: This can mean that old polygon GeoJSON data in the database is suddenly interpreted in a different way. See [Legacy Polygons](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#legacy-polygons) for details. -Also see the definition of [Polygons](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#polygon) -and [GeoJSON interpretation](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson-interpretation). +Also see the definition of [Polygons](../../aql/functions/geo.md#polygon) +and [GeoJSON interpretation](../../aql/functions/geo.md#geojson-interpretation). ## `geojson` Analyzers @@ -148,7 +148,7 @@ the new Analyzers. | The smaller of the two regions defined by a linear ring is interpreted as the interior of the ring. | The area to the left of the boundary ring's path is considered to be the interior. | | A ring can at most enclose half the Earth's surface | A ring can enclose the entire surface of the Earth | -Also see the definition of [Polygons](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#polygon) and the +Also see the definition of [Polygons](../../aql/functions/geo.md#polygon) and the [`geojson` Analyzer](../../index-and-search/analyzers.md#geojson) documentation. ## Maximum Array / Object Nesting diff --git a/site/content/3.11/release-notes/version-3.10/whats-new-in-3-10.md b/site/content/3.11/release-notes/version-3.10/whats-new-in-3-10.md index f0e47cf365..4c00b7dacf 100644 --- a/site/content/3.11/release-notes/version-3.10/whats-new-in-3-10.md +++ b/site/content/3.11/release-notes/version-3.10/whats-new-in-3-10.md @@ -581,7 +581,7 @@ This diverges from the previous implementation in two fundamental ways: that the "smaller" of the two connected components are the interior. This allows specifying polygons that cover more than half of the surface of the Earth and conforms to the GeoJSON standard. - See [GeoJSON interpretation](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson-interpretation) + See [GeoJSON interpretation](../../aql/functions/geo.md#geojson-interpretation) for examples. Additionally, the reported issues, which occasionally produced diff --git a/site/content/3.11/release-notes/version-3.11/whats-new-in-3-11.md b/site/content/3.11/release-notes/version-3.11/whats-new-in-3-11.md index 3a687fe207..acaa0cfa8c 100644 --- a/site/content/3.11/release-notes/version-3.11/whats-new-in-3-11.md +++ b/site/content/3.11/release-notes/version-3.11/whats-new-in-3-11.md @@ -1072,7 +1072,7 @@ To enable tracing for traversals and path searches at startup, you can set `--log.level graphs=trace`. To enable or disable it at runtime, you can call the -[`PUT /_admin/log/level`](../../develop/http-api/monitoring/logs.md#set-the-sersver-log-levels) +[`PUT /_admin/log/level`](../../develop/http-api/monitoring/logs.md#set-the-server-log-levels) endpoint of the HTTP API and set the log level using a request body like `{"graphs":"TRACE"}`. diff --git a/site/content/3.12/about-arangodb/features/community-edition.md b/site/content/3.12/about-arangodb/features/community-edition.md index 1f8e6dd2ab..1f7cc854e6 100644 --- a/site/content/3.12/about-arangodb/features/community-edition.md +++ b/site/content/3.12/about-arangodb/features/community-edition.md @@ -137,7 +137,7 @@ see [arangodb.com/community-server/](https://www.arangodb.com/community-server/) Flexible data field pre-processing with custom queries and the ability to chain built-in and custom Analyzers. Language-agnostic tokenization of text. -- [**GeoJSON Support**](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson): +- [**GeoJSON Support**](../../aql/functions/geo.md#geojson): Geographic data encoded in the popular GeoJSON format can be stored and used for geo-spatial queries. diff --git a/site/content/3.12/aql/functions/arangosearch.md b/site/content/3.12/aql/functions/arangosearch.md index da4b8a08f2..673c6b7731 100644 --- a/site/content/3.12/aql/functions/arangosearch.md +++ b/site/content/3.12/aql/functions/arangosearch.md @@ -1063,7 +1063,7 @@ be used in conjunction with ArangoSearch. `GEO_CONTAINS(geoJsonA, geoJsonB) → bool` -Checks whether the [GeoJSON object](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson) `geoJsonA` +Checks whether the [GeoJSON object](geo.md#geojson) `geoJsonA` fully contains `geoJsonB` (every point in B is also in A). - **geoJsonA** (object\|array): first GeoJSON object or coordinate array @@ -1079,7 +1079,7 @@ fully contains `geoJsonB` (every point in B is also in A). `GEO_DISTANCE(geoJsonA, geoJsonB) → distance` -Return the distance between two [GeoJSON objects](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson), +Return the distance between two [GeoJSON objects](geo.md#geojson), measured from the `centroid` of each shape. - **geoJsonA** (object\|array): first GeoJSON object or coordinate array @@ -1095,7 +1095,7 @@ measured from the `centroid` of each shape. `GEO_IN_RANGE(geoJsonA, geoJsonB, low, high, includeLow, includeHigh) → bool` -Checks whether the distance between two [GeoJSON objects](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson) +Checks whether the distance between two [GeoJSON objects](geo.md#geojson) lies within a given interval. The distance is measured from the `centroid` of each shape. @@ -1119,7 +1119,7 @@ each shape. `GEO_INTERSECTS(geoJsonA, geoJsonB) → bool` -Checks whether the [GeoJSON object](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson) `geoJsonA` +Checks whether the [GeoJSON object](geo.md#geojson) `geoJsonA` intersects with `geoJsonB` (i.e. at least one point of B is in A or vice versa). - **geoJsonA** (object\|array): first GeoJSON object or coordinate array diff --git a/site/content/3.12/aql/functions/date.md b/site/content/3.12/aql/functions/date.md index bc7a8bad54..6415589d77 100644 --- a/site/content/3.12/aql/functions/date.md +++ b/site/content/3.12/aql/functions/date.md @@ -7,6 +7,8 @@ description: >- ISO 8601 date time strings archetype: default --- +## Date and time representations + AQL offers functionality to work with dates, but it does not have a special data type for dates (neither does JSON, which is usually used as format to ship data into and out of ArangoDB). Instead, dates in AQL are represented by either numbers or strings. diff --git a/site/content/3.12/aql/functions/geo.md b/site/content/3.12/aql/functions/geo.md index d78d03154c..6c7e17d00f 100644 --- a/site/content/3.12/aql/functions/geo.md +++ b/site/content/3.12/aql/functions/geo.md @@ -7,6 +7,384 @@ description: >- accelerated by geo-spatial indexes archetype: default --- +## Geo-spatial data representations + +You can model geo-spatial information in different ways using the data types +available in ArangoDB. The recommended way is to use objects with **GeoJSON** +geometry but you can also use **longitude and latitude coordinate pairs** +for points. Both models are supported by +[Geo-Spatial Indexes](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md). + +### Coordinate pairs + +Longitude and latitude coordinates are numeric values and can be stored in the +following ways: + +- Coordinates using an array with two numbers in `[longitude, latitude]` order, + for example, in a user-chosen attribute called `location`: + + ```json + { + "location": [ -73.983, 40.764 ] + } + ``` + +- Coordinates using an array with two numbers in `[latitude, longitude]` order, + for example, in a user-chosen attribute called `location`: + + ```json + { + "location": [ 40.764, -73.983 ] + } + ``` + +- Coordinates using two separate numeric attributes, for example, in two + user-chosen attributes called `lat` and `lng` as sub-attributes of a `location` + attribute: + + ```json + { + "location": { + "lat": 40.764, + "lng": -73.983 + } + } + ``` + +### GeoJSON + +GeoJSON is a geospatial data format based on JSON. It defines several different +types of JSON objects and the way in which they can be combined to represent +data about geographic shapes on the Earth surface. + +Example of a document with a GeoJSON Point stored in a user-chosen attribute +called `location` (with coordinates in `[longitude, latitude]` order): + +```json +{ + "location": { + "type": "Point", + "coordinates": [ -73.983, 40.764 ] + } +} +``` + +GeoJSON uses a geographic coordinate reference system, +World Geodetic System 1984 (WGS 84), and units of decimal degrees. + +Internally ArangoDB maps all coordinate pairs onto a unit sphere. Distances are +projected onto a sphere with the Earth's *Volumetric mean radius* of *6371 +km*. ArangoDB implements a useful subset of the GeoJSON format +[(RFC 7946)](https://tools.ietf.org/html/rfc7946). +Feature Objects and the GeometryCollection type are not supported. +Supported geometry object types are: + +- Point +- MultiPoint +- LineString +- MultiLineString +- Polygon +- MultiPolygon + +#### Point + +A [GeoJSON Point](https://tools.ietf.org/html/rfc7946#section-3.1.2) is a +[position](https://tools.ietf.org/html/rfc7946#section-3.1.1) comprised of +a longitude and a latitude: + +```json +{ + "type": "Point", + "coordinates": [100.0, 0.0] +} +``` + +#### MultiPoint + +A [GeoJSON MultiPoint](https://tools.ietf.org/html/rfc7946#section-3.1.7) is +an array of positions: + +```json +{ + "type": "MultiPoint", + "coordinates": [ + [100.0, 0.0], + [101.0, 1.0] + ] +} +``` + +#### LineString + +A [GeoJSON LineString](https://tools.ietf.org/html/rfc7946#section-3.1.4) is +an array of two or more positions: + +```json +{ + "type": "LineString", + "coordinates": [ + [100.0, 0.0], + [101.0, 1.0] + ] +} +``` + +#### MultiLineString + +A [GeoJSON MultiLineString](https://tools.ietf.org/html/rfc7946#section-3.1.5) is +an array of LineString coordinate arrays: + +```json +{ + "type": "MultiLineString", + "coordinates": [ + [ + [100.0, 0.0], + [101.0, 1.0] + ], + [ + [102.0, 2.0], + [103.0, 3.0] + ] + ] +} +``` + +#### Polygon + +A [GeoJSON Polygon](https://tools.ietf.org/html/rfc7946#section-3.1.6) consists +of a series of closed `LineString` objects (ring-like). These *Linear Ring* +objects consist of four or more coordinate pairs with the first and last +coordinate pair being equal. Coordinate pairs of a Polygon are an array of +linear ring coordinate arrays. The first element in the array represents +the exterior ring. Any subsequent elements represent interior rings +(holes within the surface). + +The orientation of the first linear ring is crucial: the right-hand-rule +is applied, so that the area to the left of the path of the linear ring +(when walking on the surface of the Earth) is considered to be the +"interior" of the polygon. All other linear rings must be contained +within this interior. According to the GeoJSON standard, the subsequent +linear rings must be oriented following the right-hand-rule, too, +that is, they must run **clockwise** around the hole (viewed from +above). However, ArangoDB is tolerant here (as suggested by the +[GeoJSON standard](https://datatracker.ietf.org/doc/html/rfc7946#section-3.1.6)), +all but the first linear ring are inverted if the orientation is wrong. + +In the end, a point is considered to be in the interior of the polygon, +if and only if one has to cross an odd number of linear rings to reach the +exterior of the polygon prescribed by the first linear ring. + +A number of additional rules apply (and are enforced by the GeoJSON +parser): + +- A polygon must contain at least one linear ring, i.e., it must not be + empty. +- A linear ring may not be empty, it needs at least three _distinct_ + coordinate pairs, that is, at least 4 coordinate pairs (since the first and + last must be the same). +- No two edges of linear rings in the polygon must intersect, in + particular, no linear ring may be self-intersecting. +- Within the same linear ring, consecutive coordinate pairs may be the same, + otherwise all coordinate pairs need to be distinct (except the first and last one). +- Linear rings of a polygon must not share edges, but they may share coordinate pairs. +- A linear ring defines two regions on the sphere. ArangoDB always + interprets the region that lies to the left of the boundary ring (in + the direction of its travel on the surface of the Earth) as the + interior of the ring. This is in contrast to earlier versions of + ArangoDB before 3.10, which always took the **smaller** of the two + regions as the interior. Therefore, from 3.10 on one can now have + polygons whose outer ring encloses more than half the Earth's surface. +- The interior rings must be contained in the (interior) of the outer ring. +- Interior rings should follow the above rule for orientation + (counterclockwise external rings, clockwise internal rings, interior + always to the left of the line). + +Here is an example with no holes: + +```json +{ + "type": "Polygon", + "coordinates": [ + [ + [100.0, 0.0], + [101.0, 0.0], + [101.0, 1.0], + [100.0, 1.0], + [100.0, 0.0] + ] + ] +} +``` + +Here is an example with a hole: + +```json +{ + "type": "Polygon", + "coordinates": [ + [ + [100.0, 0.0], + [101.0, 0.0], + [101.0, 1.0], + [100.0, 1.0], + [100.0, 0.0] + ], + [ + [100.8, 0.8], + [100.8, 0.2], + [100.2, 0.2], + [100.2, 0.8], + [100.8, 0.8] + ] + ] +} +``` + +#### MultiPolygon + +A [GeoJSON MultiPolygon](https://tools.ietf.org/html/rfc7946#section-3.1.6) consists +of multiple polygons. The "coordinates" member is an array of +_Polygon_ coordinate arrays. See [above](#polygon) for the rules and +the meaning of polygons. + +If the polygons in a MultiPolygon are disjoint, then a point is in the +interior of the MultiPolygon if and only if it is +contained in one of the polygons. If some polygon P2 in a MultiPolygon +is contained in another polygon P1, then P2 is treated like a hole +in P1 and containment of points is defined with the even-odd-crossings rule +(see [Polygon](#polygon)). + +Additionally, the following rules apply and are enforced for +MultiPolygons: + +- No two edges in the linear rings of the polygons of a MultiPolygon + may intersect. +- Polygons in the same MultiPolygon may not share edges, but they may share + coordinate pairs. + +Example with two polygons, the second one with a hole: + +```json +{ + "type": "MultiPolygon", + "coordinates": [ + [ + [ + [102.0, 2.0], + [103.0, 2.0], + [103.0, 3.0], + [102.0, 3.0], + [102.0, 2.0] + ] + ], + [ + [ + [100.0, 0.0], + [101.0, 0.0], + [101.0, 1.0], + [100.0, 1.0], + [100.0, 0.0] + ], + [ + [100.2, 0.2], + [100.2, 0.8], + [100.8, 0.8], + [100.8, 0.2], + [100.2, 0.2] + ] + ] + ] +} +``` + +## GeoJSON interpretation + +Note the following technical detail about GeoJSON: The +[GeoJSON standard, Section 3.1.1 Position](https://datatracker.ietf.org/doc/html/rfc7946#section-3.1.1) +prescribes that lines are **cartesian lines in cylindrical coordinates** +(longitude/latitude). However, this definition is inconvenient in practice, +since such lines are not geodesic on the surface of the Earth. +Furthermore, the best available algorithms for geospatial computations on Earth +typically use geodesic lines as the boundaries of polygons on Earth. + +Therefore, ArangoDB uses the **syntax of the GeoJSON** standard, +but then interprets lines (and boundaries of polygons) as +**geodesic lines (pieces of great circles) on Earth**. This is a +violation of the GeoJSON standard, but serving a practical purpose. + +Note in particular that this can sometimes lead to unexpected results. +Consider the following polygon (remember that GeoJSON has +**longitude before latitude** in coordinate pairs): + +```json +{ "type": "Polygon", "coordinates": [[ + [4, 54], [4, 47], [16, 47], [16, 54], [4, 54] +]] } +``` + +![GeoJSON Polygon Geodesic](../../../images/geojson-polygon-geodesic.webp) + +It does not contain the point `[10, 47]` since the shortest path (geodesic) +from `[4, 47]` to `[16, 47]` lies North relative to the parallel of latitude at +47 degrees. On the contrary, the polygon does contain the point `[10, 54]` as it +lies South of the parallel of latitude at 54 degrees. + +{{< info >}} +ArangoDB version before 3.10 did an inconsistent special detection of "rectangle" +polygons that later versions from 3.10 onward no longer do, see +[Legacy Polygons](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#legacy-polygons). +{{< /info >}} + +Furthermore, there is an issue with the interpretation of linear rings +(boundaries of polygons) according to +[GeoJSON standard, Section 3.1.6 Polygon](https://datatracker.ietf.org/doc/html/rfc7946#section-3.1.6). +This section states explicitly: + +> A linear ring MUST follow the right-hand rule with respect to the +> area it bounds, i.e., exterior rings are counter-clockwise, and +> holes are clockwise. + +This rather misleading phrase means that when a linear ring is used as +the boundary of a polygon, the "interior" of the polygon lies **to the left** +of the boundary when one travels on the surface of the Earth and +along the linear ring. For +example, the polygon below travels **counter-clockwise** around the point +`[10, 50]`, and thus the interior of the polygon contains this point and +its surroundings, but not, for example, the North Pole and the South +Pole. + +```json +{ "type": "Polygon", "coordinates": [[ + [4, 54], [4, 47], [16, 47], [16, 54], [4, 54] +]] } +``` + +![GeoJSON Polygon Counter-clockwise](../../../images/geojson-polygon-ccw.webp) + +On the other hand, the following polygon travels **clockwise** around the point +`[10, 50]`, and thus its "interior" does not contain `[10, 50]`, but does +contain the North Pole and the South Pole: + +```json +{ "type": "Polygon", "coordinates": [[ + [4, 54], [16, 54], [16, 47], [4, 47], [4, 54] +]] } +``` + +![GeoJSON Polygon Clockwise](../../../images/geojson-polygon-cw.webp) + +Remember that the "interior" is to the left of the given +linear ring, so this second polygon is basically the complement on Earth +of the previous polygon! + +ArangoDB versions before 3.10 did not follow this rule and always took the +"smaller" connected component of the surface as the "interior" of the polygon. +This made it impossible to specify polygons which covered more than half of the +sphere. From version 3.10 onward, ArangoDB recognizes this correctly. +See [Legacy Polygons](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#legacy-polygons) +for how to deal with this issue. + ## Geo utility functions The following helper functions **can** use geo indexes, but do not have to in @@ -43,7 +421,7 @@ FOR doc IN coll // e.g. documents returned by a traversal `GEO_CONTAINS(geoJsonA, geoJsonB) → bool` -Checks whether the [GeoJSON object](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson) `geoJsonA` +Checks whether the [GeoJSON object](#geojson) `geoJsonA` fully contains `geoJsonB` (every point in B is also in A). The object `geoJsonA` has to be of type _Polygon_ or _MultiPolygon_. For other types containment is not well-defined because of numerical stability problems. @@ -89,7 +467,7 @@ second argument. Passing it as the first argument, like Return the distance between two GeoJSON objects in meters, measured from the **centroid** of each shape. For a list of supported types see the -[geo index page](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson). +[geo index page](#geojson). - **geoJsonA** (object): first GeoJSON object, or a coordinate array in `[longitude, latitude]` order @@ -142,9 +520,8 @@ functions. `GEO_AREA(geoJson, ellipsoid) → area` -Return the area for a polygon or multi-polygon on a sphere with the -average Earth radius, or an ellipsoid. For a list of supported types -see the [geo index page](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson). +Return the area for a [Polygon](#polygon) or [MultiPolygon](#multipolygon) +on a sphere with the average Earth radius, or an ellipsoid. - **geoJson** (object): a GeoJSON object - **ellipsoid** (string, *optional*): reference ellipsoid to use. @@ -163,8 +540,7 @@ RETURN GEO_AREA(polygon, "wgs84") `GEO_EQUALS(geoJsonA, geoJsonB) → bool` -Checks whether two GeoJSON objects are equal or not. For a list of supported -types see the [geo index page](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson). +Checks whether two [GeoJSON objects](#geojson) are equal or not. - **geoJsonA** (object): first GeoJSON object. - **geoJsonB** (object): second GeoJSON object. @@ -194,7 +570,7 @@ RETURN GEO_EQUALS(polygonA, polygonB) // false `GEO_INTERSECTS(geoJsonA, geoJsonB) → bool` -Checks whether the [GeoJSON object](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson) `geoJsonA` +Checks whether the [GeoJSON object](#geojson) `geoJsonA` intersects with `geoJsonB` (i.e. at least one point in B is also in A or vice-versa). - **geoJsonA** (object): first GeoJSON object @@ -225,7 +601,7 @@ second argument. Passing it as the first argument, like `GEO_IN_RANGE(geoJsonA, geoJsonB, low, high, includeLow, includeHigh) → bool` -Checks whether the distance between two [GeoJSON objects](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson) +Checks whether the distance between two [GeoJSON objects](#geojson) lies within a given interval. The distance is measured from the **centroid** of each shape. @@ -392,7 +768,7 @@ a linear ring. Each linear ring consists of an array with at least four longitude/latitude pairs. The first linear ring must be the outermost, while any subsequent linear ring will be interpreted as holes. -For details about the rules, see [GeoJSON polygons](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#polygon). +For details about the rules, see [GeoJSON polygons](#polygon). - **points** (array): an array of (arrays of) `[longitude, latitude]` pairs - returns **geoJson** (object\|null): a valid GeoJSON Polygon @@ -431,7 +807,8 @@ RETURN GEO_POLYGON([ `GEO_MULTIPOLYGON(polygons) → geoJson` Construct a GeoJSON MultiPolygon. Needs at least two Polygons inside. -See [GEO_POLYGON()](#geo_polygon) and [GeoJSON MultiPolygons](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#multipolygon) for the rules of Polygon and MultiPolygon construction. +See [GEO_POLYGON()](#geo_polygon) and [GeoJSON MultiPolygon](#multipolygon) +for the rules of Polygon and MultiPolygon construction. - **polygons** (array): an array of arrays of arrays of `[longitude, latitude]` pairs - returns **geoJson** (object\|null): a valid GeoJSON MultiPolygon @@ -555,7 +932,7 @@ value in an attribute of that name. `WITHIN_RECTANGLE()` is a deprecated AQL function from version 3.4.0 on. Use [`GEO_CONTAINS()`](#geo_contains) and a GeoJSON polygon instead - but note that this uses geodesic lines from version 3.10.0 onward -(see [GeoJSON interpretation](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson-interpretation)): +(see [GeoJSON interpretation](#geojson-interpretation)): ```aql LET rect = GEO_POLYGON([ [ diff --git a/site/content/3.12/arangograph/notebooks.md b/site/content/3.12/arangograph/notebooks.md index 138ab79819..15972607d4 100644 --- a/site/content/3.12/arangograph/notebooks.md +++ b/site/content/3.12/arangograph/notebooks.md @@ -20,7 +20,7 @@ passwords, and endpoint URLs. ![ArangoGraph Notebooks Architecture](../../images/arangograph-notebooks-architecture.png) -The ArangoGraph Notebook has built-in [ArangoGraph Magic Commands](.#arangograph-magic-commands) +The ArangoGraph Notebook has built-in [ArangoGraph Magic Commands](#arangograph-magic-commands) that answer questions like: - What ArangoDB database am I connected to at the moment? - What data does the ArangoDB instance contain? diff --git a/site/content/3.12/develop/http-api/general-request-handling.md b/site/content/3.12/develop/http-api/general-request-handling.md index bdae809f02..a5b747efbb 100644 --- a/site/content/3.12/develop/http-api/general-request-handling.md +++ b/site/content/3.12/develop/http-api/general-request-handling.md @@ -139,7 +139,7 @@ HTTP request. The server executes the jobs from the queue asynchronously as fast as possible, while clients can continue to do other work. If the server queue is full (i.e. contains as many jobs as specified by the -[`--server.maximal-queue-size`](../../components/arangodb-server/options.md#arangodb-server-options) +[`--server.maximal-queue-size`](../../components/arangodb-server/options.md#--servermaximal-queue-size) startup option), then the request is rejected instantly with an HTTP `503 Service Unavailable` status code. diff --git a/site/content/3.12/develop/http-api/replication/write-ahead-log.md b/site/content/3.12/develop/http-api/replication/write-ahead-log.md index d9add73b78..737b8b9d8c 100644 --- a/site/content/3.12/develop/http-api/replication/write-ahead-log.md +++ b/site/content/3.12/develop/http-api/replication/write-ahead-log.md @@ -179,8 +179,8 @@ paths: - `data`: the original document data - A more detailed description of the individual replication event types and their - data structures can be found in [Operation Types](#operation-types). + For a more detailed description of the individual replication event types + and their data structures, see the Operation Types. The response also contains the following HTTP headers: diff --git a/site/content/3.12/index-and-search/analyzers.md b/site/content/3.12/index-and-search/analyzers.md index d03d498b52..d07cbd9677 100644 --- a/site/content/3.12/index-and-search/analyzers.md +++ b/site/content/3.12/index-and-search/analyzers.md @@ -1254,7 +1254,7 @@ attributes: - `legacy` (boolean, _optional_): This option controls how GeoJSON Polygons are interpreted (introduced in v3.10.5). Also see [Legacy Polygons](indexing/working-with-indexes/geo-spatial-indexes.md#legacy-polygons) and - [GeoJSON interpretation](indexing/working-with-indexes/geo-spatial-indexes.md#geojson-interpretation). + [GeoJSON interpretation](../aql/functions/geo.md#geojson-interpretation). - If `legacy` is `true`, the smaller of the two regions defined by a linear ring is interpreted as the interior of the ring and a ring can at most diff --git a/site/content/3.12/index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md b/site/content/3.12/index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md index bb456cccc9..0919bc6e4b 100644 --- a/site/content/3.12/index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md +++ b/site/content/3.12/index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md @@ -8,8 +8,8 @@ description: >- archetype: default --- The geo-spatial index type in ArangoDB is based on [Google S2](http://s2geometry.io/). -Indexing is supported for a subset of the [**GeoJSON**](#geojson) geometry types -as well as simple latitude/longitude pairs. +Indexing is supported for a subset of the [**GeoJSON**](../../../aql/functions/geo.md#geojson) +geometry types as well as simple latitude/longitude pairs. AQL's geospatial functions and GeoJSON constructors are described in [Geo functions](../../../aql/functions/geo.md). @@ -34,7 +34,7 @@ documents which do not fulfill these requirements. To create an index in GeoJSON mode execute: ```js -collection.ensureIndex({ type: "geo", fields: [ "geometry" ], geoJson:true }) +collection.ensureIndex({ type: "geo", fields: [ "geometry" ], geoJson: true }) ``` This creates the index on all documents and uses _geometry_ as the attributed @@ -53,10 +53,10 @@ sparsity. In case that the index was successfully created, an object with the index details, including the index-identifier, is returned. -See [GeoJSON Interpretation](#geojson-interpretation) for technical details on -how ArangoDB interprets GeoJSON objects. In short: the **syntax** of GeoJSON is -used, but polygon boundaries and lines between points are interpreted as -geodesics (pieces of great circles on Earth). +For technical details on how ArangoDB interprets GeoJSON objects, see +[GeoJSON Interpretation](../../../aql/functions/geo.md#geojson-interpretation). +In short: the **syntax** of GeoJSON is used, but polygon boundaries and lines +between points are interpreted as geodesics (pieces of great circles on Earth). ### Non-GeoJSON mode @@ -119,14 +119,16 @@ same query is executed with the geo-spatial index, it doesn't find the documents ### Legacy Polygons -See [GeoJSON Interpretation](#geojson-interpretation) for details of the changes -between ArangoDB 3.10 and earlier versions. Two things have changed: +Between ArangoDB 3.10 and earlier versions, two things have changed: - boundaries of polygons are now geodesics and there is no special and inconsistent treatment of "rectangles" any more - linear rings are interpreted according to the rules and no longer "normalized" +See [GeoJSON Interpretation](../../../aql/functions/geo.md#geojson-interpretation) +for details. + For backward compatibility, a `legacyPolygons` option has been introduced for geo indexes. It is relevant for those that have `geoJson` set to `true` only. Geo indexes created in versions before 3.10 always @@ -161,6 +163,121 @@ intentionally or not cannot be determined automatically. Please test the new behavior manually. {{< /warning >}} +### *arangosh* Examples + +Ensures that a geo index exists: + +`collection.ensureIndex({ type: "geo", fields: [ "location" ] })` + +Creates a geospatial index on all documents using `location` as the path to the +coordinates. The value of the attribute has to be an array with at least two +numeric values. The array must contain the latitude (first value) and the +longitude (second value). + +All documents, which do not have the attribute path or have a non-conforming +value in it, are excluded from the index. + +A geo index is implicitly sparse, and there is no way to control its +sparsity. + +The index does not provide a `unique` option because of its limited usability. +It would prevent identical coordinate pairs from being inserted only, but even a +slightly different location (like 1 inch or 1 cm off) would be unique again and +not considered a duplicate, although it probably should. The desired threshold +for detecting duplicates may vary for every project (including how to calculate +the distance even) and needs to be implemented on the application layer as +needed. You can write a [Foxx service](../../../develop/foxx-microservices/_index.md) for this purpose and +make use of the [Geo-spatial functions in AQL](../../../aql/functions/geo.md) to find nearby +locations supported by a geo index. + +In case that the index was successfully created, an object with the index +details, including the index-identifier, is returned. + +--- + +To create a geo index on an array attribute that contains longitude first, set +the `geoJson` attribute to `true`. This corresponds to the format described in +[RFC 7946 Position](https://tools.ietf.org/html/rfc7946#section-3.1.1) + +`collection.ensureIndex({ type: "geo", fields: [ "location" ], geoJson: true })` + +--- + +To create a geo-spatial index on all documents using `latitude` and `longitude` +as separate attribute paths, two paths need to be specified in the `fields` +array: + +`collection.ensureIndex({ type: "geo", fields: [ "latitude", "longitude" ] })` + +In case that the index was successfully created, an object with the index +details, including the index-identifier, is returned. + +**Examples** + +Create a geo index for an array attribute: + +```js +--- +name: geoIndexCreateForArrayAttribute1 +description: '' +--- +~db._create("geo"); +db.geo.ensureIndex({ type: "geo", fields: [ "loc" ] }); +~db._drop("geo"); +``` + +Create a geo index for an array attribute: + +```js +--- +name: geoIndexCreateForArrayAttribute2 +description: '' +--- +~db._create("geo2"); +db.geo2.ensureIndex({ type: "geo", fields: [ "location.latitude", "location.longitude" ] }); +~db._drop("geo2"); +``` + +Use a geo index with the AQL `SORT` operation: + +```js +--- +name: geoIndexSortOptimization +description: '' +--- +~db._create("geoSort"); +var idx = db.geoSort.ensureIndex({ type: "geo", fields: [ "latitude", "longitude" ] }); +for (i = -90; i <= 90; i += 10) { + for (j = -180; j <= 180; j += 10) { + db.geoSort.save({ name : "Name/" + i + "/" + j, latitude : i, longitude : j }); + } +} +var query = "FOR doc in geoSort SORT DISTANCE(doc.latitude, doc.longitude, 0, 0) LIMIT 5 RETURN doc" +db._explain(query, {}, {colors: false}); +db._query(query).toArray(); +~db._drop("geoSort"); +``` + +Use a geo index with the AQL `FILTER` operation: + +```js +--- +name: geoIndexFilterOptimization +description: '' +--- +~db._create("geoFilter"); +var idx = db.geoFilter.ensureIndex({ type: "geo", fields: [ "latitude", "longitude" ] }); +for (i = -90; i <= 90; i += 10) { + for (j = -180; j <= 180; j += 10) { + db.geoFilter.save({ name : "Name/" + i + "/" + j, latitude : i, longitude : j }); + } +} +var query = "FOR doc in geoFilter FILTER DISTANCE(doc.latitude, doc.longitude, 0, 0) < 2000 RETURN doc" +db._explain(query, {}, {colors: false}); +db._query(query).toArray(); +~db._drop("geoFilter"); +``` + ## Indexed GeoSpatial Queries The geospatial index supports a variety of AQL queries, which can be built with the help @@ -316,438 +433,3 @@ The second parameter must contain the document field on that the index was created. This `FILTER` clause can be combined with a `SORT` clause using `GEO_DISTANCE()`. - -## GeoJSON - -GeoJSON is a geospatial data format based on JSON. It defines several different -types of JSON objects and the way in which they can be combined to represent -data about geographic shapes on the Earth surface. GeoJSON uses a geographic -coordinate reference system, World Geodetic System 1984 (WGS 84), and units of decimal -degrees. - -Internally ArangoDB maps all coordinate pairs onto a unit sphere. Distances are -projected onto a sphere with the Earth's *Volumetric mean radius* of *6371 -km*. ArangoDB implements a useful subset of the GeoJSON format -[(RFC 7946)](https://tools.ietf.org/html/rfc7946). -Feature Objects and the GeometryCollection type are not supported. -Supported geometry object types are: - -- Point -- MultiPoint -- LineString -- MultiLineString -- Polygon -- MultiPolygon - -### GeoJSON interpretation - -Note the following technical detail about GeoJSON: The -[GeoJSON standard, Section 3.1.1 Position](https://datatracker.ietf.org/doc/html/rfc7946#section-3.1.1) -prescribes that lines are **cartesian lines in cylindrical coordinates** -(longitude/latitude). However, this definition is inconvenient in practice, -since such lines are not geodesic on the surface of the Earth. -Furthermore, the best available algorithms for geospatial computations on Earth -typically use geodesic lines as the boundaries of polygons on Earth. - -Therefore, ArangoDB uses the **syntax of the GeoJSON** standard, -but then interprets lines (and boundaries of polygons) as -**geodesic lines (pieces of great circles) on Earth**. This is a -violation of the GeoJSON standard, but serving a practical purpose. - -Note in particular that this can sometimes lead to unexpected results. -Consider the following polygon (remember that GeoJSON has -**longitude before latitude** in coordinate pairs): - -```json -{ "type": "Polygon", "coordinates": [[ - [4, 54], [4, 47], [16, 47], [16, 54], [4, 54] -]] } -``` - -![GeoJSON Polygon Geodesic](../../../../images/geojson-polygon-geodesic.webp) - -It does not contain the point `[10, 47]` since the shortest path (geodesic) -from `[4, 47]` to `[16, 47]` lies North relative to the parallel of latitude at -47 degrees. On the contrary, the polygon does contain the point `[10, 54]` as it -lies South of the parallel of latitude at 54 degrees. - -{{< info >}} -ArangoDB version before 3.10 did an inconsistent special detection of "rectangle" -polygons that later versions from 3.10 onward no longer do, see -[Legacy Polygons](#legacy-polygons). -{{< /info >}} - -Furthermore, there is an issue with the interpretation of linear rings -(boundaries of polygons) according to -[GeoJSON standard, Section 3.1.6 Polygon](https://datatracker.ietf.org/doc/html/rfc7946#section-3.1.6). -This section states explicitly: - -> A linear ring MUST follow the right-hand rule with respect to the -> area it bounds, i.e., exterior rings are counter-clockwise, and -> holes are clockwise. - -This rather misleading phrase means that when a linear ring is used as -the boundary of a polygon, the "interior" of the polygon lies **to the left** -of the boundary when one travels on the surface of the Earth and -along the linear ring. For -example, the polygon below travels **counter-clockwise** around the point -`[10, 50]`, and thus the interior of the polygon contains this point and -its surroundings, but not, for example, the North Pole and the South -Pole. - -```json -{ "type": "Polygon", "coordinates": [[ - [4, 54], [4, 47], [16, 47], [16, 54], [4, 54] -]] } -``` - -![GeoJSON Polygon Counter-clockwise](../../../../images/geojson-polygon-ccw.webp) - -On the other hand, the following polygon travels **clockwise** around the point -`[10, 50]`, and thus its "interior" does not contain `[10, 50]`, but does -contain the North Pole and the South Pole: - -```json -{ "type": "Polygon", "coordinates": [[ - [4, 54], [16, 54], [16, 47], [4, 47], [4, 54] -]] } -``` - -![GeoJSON Polygon Clockwise](../../../../images/geojson-polygon-cw.webp) - -Remember that the "interior" is to the left of the given -linear ring, so this second polygon is basically the complement on Earth -of the previous polygon! - -ArangoDB versions before 3.10 did not follow this rule and always took the -"smaller" connected component of the surface as the "interior" of the polygon. -This made it impossible to specify polygons which covered more than half of the -sphere. From version 3.10 onward, ArangoDB recognizes this correctly. -See [Legacy Polygons](#legacy-polygons) for how to deal with this issue. - -### Point - -A [GeoJSON Point](https://tools.ietf.org/html/rfc7946#section-3.1.2) is a -[position](https://tools.ietf.org/html/rfc7946#section-3.1.1) comprised of -a longitude and a latitude: - -```json -{ - "type": "Point", - "coordinates": [100.0, 0.0] -} -``` - -### MultiPoint - -A [GeoJSON MultiPoint](https://tools.ietf.org/html/rfc7946#section-3.1.7) is -an array of positions: - -```json -{ - "type": "MultiPoint", - "coordinates": [ - [100.0, 0.0], - [101.0, 1.0] - ] -} -``` - -### LineString - -A [GeoJSON LineString](https://tools.ietf.org/html/rfc7946#section-3.1.4) is -an array of two or more positions: - -```json -{ - "type": "LineString", - "coordinates": [ - [100.0, 0.0], - [101.0, 1.0] - ] -} -``` - -### MultiLineString - -A [GeoJSON MultiLineString](https://tools.ietf.org/html/rfc7946#section-3.1.5) is -an array of LineString coordinate arrays: - -```json -{ - "type": "MultiLineString", - "coordinates": [ - [ - [100.0, 0.0], - [101.0, 1.0] - ], - [ - [102.0, 2.0], - [103.0, 3.0] - ] - ] -} -``` - -### Polygon - -A [GeoJSON Polygon](https://tools.ietf.org/html/rfc7946#section-3.1.6) consists -of a series of closed `LineString` objects (ring-like). These *Linear Ring* -objects consist of four or more coordinate pairs with the first and last -coordinate pair being equal. Coordinate pairs of a Polygon are an array of -linear ring coordinate arrays. The first element in the array represents -the exterior ring. Any subsequent elements represent interior rings -(holes within the surface). - -The orientation of the first linear ring is crucial: the right-hand-rule -is applied, so that the area to the left of the path of the linear ring -(when walking on the surface of the Earth) is considered to be the -"interior" of the polygon. All other linear rings must be contained -within this interior. According to the GeoJSON standard, the subsequent -linear rings must be oriented following the right-hand-rule, too, -that is, they must run **clockwise** around the hole (viewed from -above). However, ArangoDB is tolerant here (as suggested by the -[GeoJSON standard](https://datatracker.ietf.org/doc/html/rfc7946#section-3.1.6)), -all but the first linear ring are inverted if the orientation is wrong. - -In the end, a point is considered to be in the interior of the polygon, -if and only if one has to cross an odd number of linear rings to reach the -exterior of the polygon prescribed by the first linear ring. - -A number of additional rules apply (and are enforced by the GeoJSON -parser): - -- A polygon must contain at least one linear ring, i.e., it must not be - empty. -- A linear ring may not be empty, it needs at least three _distinct_ - coordinate pairs, that is, at least 4 coordinate pairs (since the first and - last must be the same). -- No two edges of linear rings in the polygon must intersect, in - particular, no linear ring may be self-intersecting. -- Within the same linear ring, consecutive coordinate pairs may be the same, - otherwise all coordinate pairs need to be distinct (except the first and last one). -- Linear rings of a polygon must not share edges, but they may share coordinate pairs. -- A linear ring defines two regions on the sphere. ArangoDB always - interprets the region that lies to the left of the boundary ring (in - the direction of its travel on the surface of the Earth) as the - interior of the ring. This is in contrast to earlier versions of - ArangoDB before 3.10, which always took the **smaller** of the two - regions as the interior. Therefore, from 3.10 on one can now have - polygons whose outer ring encloses more than half the Earth's surface. -- The interior rings must be contained in the (interior) of the outer ring. -- Interior rings should follow the above rule for orientation - (counterclockwise external rings, clockwise internal rings, interior - always to the left of the line). - -Here is an example with no holes: - -```json -{ - "type": "Polygon", - "coordinates": [ - [ - [100.0, 0.0], - [101.0, 0.0], - [101.0, 1.0], - [100.0, 1.0], - [100.0, 0.0] - ] - ] -} -``` - -Here is an example with a hole: - -```json -{ - "type": "Polygon", - "coordinates": [ - [ - [100.0, 0.0], - [101.0, 0.0], - [101.0, 1.0], - [100.0, 1.0], - [100.0, 0.0] - ], - [ - [100.8, 0.8], - [100.8, 0.2], - [100.2, 0.2], - [100.2, 0.8], - [100.8, 0.8] - ] - ] -} -``` - -### MultiPolygon - -A [GeoJSON MultiPolygon](https://tools.ietf.org/html/rfc7946#section-3.1.6) consists -of multiple polygons. The "coordinates" member is an array of -_Polygon_ coordinate arrays. See [above](#polygon) for the rules and -the meaning of polygons. - -If the polygons in a MultiPolygon are disjoint, then a point is in the -interior of the MultiPolygon if and only if it is -contained in one of the polygons. If some polygon P2 in a MultiPolygon -is contained in another polygon P1, then P2 is treated like a hole -in P1 and containment of points is defined with the even-odd-crossings rule -(see [Polygon](#polygon)). - -Additionally, the following rules apply and are enforced for -MultiPolygons: - -- No two edges in the linear rings of the polygons of a MultiPolygon - may intersect. -- Polygons in the same MultiPolygon may not share edges, but they may share - coordinate pairs. - -Example with two polygons, the second one with a hole: - -```json -{ - "type": "MultiPolygon", - "coordinates": [ - [ - [ - [102.0, 2.0], - [103.0, 2.0], - [103.0, 3.0], - [102.0, 3.0], - [102.0, 2.0] - ] - ], - [ - [ - [100.0, 0.0], - [101.0, 0.0], - [101.0, 1.0], - [100.0, 1.0], - [100.0, 0.0] - ], - [ - [100.2, 0.2], - [100.2, 0.8], - [100.8, 0.8], - [100.8, 0.2], - [100.2, 0.2] - ] - ] - ] -} -``` - -## *arangosh* Examples - -Ensures that a geo index exists: - -`collection.ensureIndex({ type: "geo", fields: [ "location" ] })` - -Creates a geospatial index on all documents using `location` as the path to the -coordinates. The value of the attribute has to be an array with at least two -numeric values. The array must contain the latitude (first value) and the -longitude (second value). - -All documents, which do not have the attribute path or have a non-conforming -value in it, are excluded from the index. - -A geo index is implicitly sparse, and there is no way to control its -sparsity. - -The index does not provide a `unique` option because of its limited usability. -It would prevent identical coordinate pairs from being inserted only, but even a -slightly different location (like 1 inch or 1 cm off) would be unique again and -not considered a duplicate, although it probably should. The desired threshold -for detecting duplicates may vary for every project (including how to calculate -the distance even) and needs to be implemented on the application layer as -needed. You can write a [Foxx service](../../../develop/foxx-microservices/_index.md) for this purpose and -make use of the AQL [geo functions](../../../aql/functions/geo.md) to find nearby -locations supported by a geo index. - -In case that the index was successfully created, an object with the index -details, including the index-identifier, is returned. - ---- - -To create a geo index on an array attribute that contains longitude first, set -the `geoJson` attribute to `true`. This corresponds to the format described in -[RFC 7946 Position](https://tools.ietf.org/html/rfc7946#section-3.1.1) - -`collection.ensureIndex({ type: "geo", fields: [ "location" ], geoJson: true })` - ---- - -To create a geo-spatial index on all documents using `latitude` and `longitude` -as separate attribute paths, two paths need to be specified in the `fields` -array: - -`collection.ensureIndex({ type: "geo", fields: [ "latitude", "longitude" ] })` - -In case that the index was successfully created, an object with the index -details, including the index-identifier, is returned. - -**Examples** - -Create a geo index for an array attribute: - -```js ---- -name: geoIndexCreateForArrayAttribute1 -description: '' ---- -~db._create("geo"); -db.geo.ensureIndex({ type: "geo", fields: [ "loc" ] }); -~db._drop("geo"); -``` - -Create a geo index for an array attribute: - -```js ---- -name: geoIndexCreateForArrayAttribute2 -description: '' ---- -~db._create("geo2"); -db.geo2.ensureIndex({ type: "geo", fields: [ "location.latitude", "location.longitude" ] }); -~db._drop("geo2"); -``` - -Use a geo index with the AQL `SORT` operation: - -```js ---- -name: geoIndexSortOptimization -description: '' ---- -~db._create("geoSort"); -var idx = db.geoSort.ensureIndex({ type: "geo", fields: [ "latitude", "longitude" ] }); -for (i = -90; i <= 90; i += 10) { - for (j = -180; j <= 180; j += 10) { - db.geoSort.save({ name : "Name/" + i + "/" + j, latitude : i, longitude : j }); - } -} -var query = "FOR doc in geoSort SORT DISTANCE(doc.latitude, doc.longitude, 0, 0) LIMIT 5 RETURN doc" -db._explain(query, {}, {colors: false}); -db._query(query).toArray(); -~db._drop("geoSort"); -``` - -Use a geo index with the AQL `FILTER` operation: - -```js ---- -name: geoIndexFilterOptimization -description: '' ---- -~db._create("geoFilter"); -var idx = db.geoFilter.ensureIndex({ type: "geo", fields: [ "latitude", "longitude" ] }); -for (i = -90; i <= 90; i += 10) { - for (j = -180; j <= 180; j += 10) { - db.geoFilter.save({ name : "Name/" + i + "/" + j, latitude : i, longitude : j }); - } -} -var query = "FOR doc in geoFilter FILTER DISTANCE(doc.latitude, doc.longitude, 0, 0) < 2000 RETURN doc" -db._explain(query, {}, {colors: false}); -db._query(query).toArray(); -~db._drop("geoFilter"); -``` diff --git a/site/content/3.12/release-notes/version-3.10/incompatible-changes-in-3-10.md b/site/content/3.12/release-notes/version-3.10/incompatible-changes-in-3-10.md index c12ec0c2e9..72186005a9 100644 --- a/site/content/3.12/release-notes/version-3.10/incompatible-changes-in-3-10.md +++ b/site/content/3.12/release-notes/version-3.10/incompatible-changes-in-3-10.md @@ -109,8 +109,8 @@ interpretation of GeoJSON polygons in version 3.9 and older: This can mean that old polygon GeoJSON data in the database is suddenly interpreted in a different way. See [Legacy Polygons](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#legacy-polygons) for details. -Also see the definition of [Polygons](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#polygon) -and [GeoJSON interpretation](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson-interpretation). +Also see the definition of [Polygons](../../aql/functions/geo.md#polygon) +and [GeoJSON interpretation](../../aql/functions/geo.md#geojson-interpretation). ## `geojson` Analyzers @@ -148,7 +148,7 @@ the new Analyzers. | The smaller of the two regions defined by a linear ring is interpreted as the interior of the ring. | The area to the left of the boundary ring's path is considered to be the interior. | | A ring can at most enclose half the Earth's surface | A ring can enclose the entire surface of the Earth | -Also see the definition of [Polygons](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#polygon) and the +Also see the definition of [Polygons](../../aql/functions/geo.md#polygon) and the [`geojson` Analyzer](../../index-and-search/analyzers.md#geojson) documentation. ## Maximum Array / Object Nesting diff --git a/site/content/3.12/release-notes/version-3.10/whats-new-in-3-10.md b/site/content/3.12/release-notes/version-3.10/whats-new-in-3-10.md index f0e47cf365..4c00b7dacf 100644 --- a/site/content/3.12/release-notes/version-3.10/whats-new-in-3-10.md +++ b/site/content/3.12/release-notes/version-3.10/whats-new-in-3-10.md @@ -581,7 +581,7 @@ This diverges from the previous implementation in two fundamental ways: that the "smaller" of the two connected components are the interior. This allows specifying polygons that cover more than half of the surface of the Earth and conforms to the GeoJSON standard. - See [GeoJSON interpretation](../../index-and-search/indexing/working-with-indexes/geo-spatial-indexes.md#geojson-interpretation) + See [GeoJSON interpretation](../../aql/functions/geo.md#geojson-interpretation) for examples. Additionally, the reported issues, which occasionally produced diff --git a/site/content/3.12/release-notes/version-3.11/whats-new-in-3-11.md b/site/content/3.12/release-notes/version-3.11/whats-new-in-3-11.md index 3a687fe207..acaa0cfa8c 100644 --- a/site/content/3.12/release-notes/version-3.11/whats-new-in-3-11.md +++ b/site/content/3.12/release-notes/version-3.11/whats-new-in-3-11.md @@ -1072,7 +1072,7 @@ To enable tracing for traversals and path searches at startup, you can set `--log.level graphs=trace`. To enable or disable it at runtime, you can call the -[`PUT /_admin/log/level`](../../develop/http-api/monitoring/logs.md#set-the-sersver-log-levels) +[`PUT /_admin/log/level`](../../develop/http-api/monitoring/logs.md#set-the-server-log-levels) endpoint of the HTTP API and set the log level using a request body like `{"graphs":"TRACE"}`. diff --git a/site/content/3.12/release-notes/version-3.12/incompatible-changes-in-3-12.md b/site/content/3.12/release-notes/version-3.12/incompatible-changes-in-3-12.md index 36f8470dc1..b54a9b155a 100644 --- a/site/content/3.12/release-notes/version-3.12/incompatible-changes-in-3-12.md +++ b/site/content/3.12/release-notes/version-3.12/incompatible-changes-in-3-12.md @@ -93,7 +93,7 @@ memory limit and not just 56% of it. ## Higher reported memory usage for AQL queries -Due to the [improved memory accounting in v3.12](whats-new-in-3-12.md#improved-memory-accounting), +Due to the [improved memory accounting in v3.12](whats-new-in-3-12.md#improved-memory-accounting-and-usage), certain AQL queries may now get aborted because they exceed the defined memory limit but didn't get killed in previous versions. This is because of the more accurate memory tracking that reports a higher (actual) usage now. It allows diff --git a/site/themes/arangodb-docs-theme/layouts/_default/_markup/render-link.html b/site/themes/arangodb-docs-theme/layouts/_default/_markup/render-link.html index b5416383d7..74b4be1301 100644 --- a/site/themes/arangodb-docs-theme/layouts/_default/_markup/render-link.html +++ b/site/themes/arangodb-docs-theme/layouts/_default/_markup/render-link.html @@ -18,16 +18,30 @@ {{- $permalink = .Destination }} {{- end }} {{- else }} + {{- $currentFile := .Page.File.Path }} {{- $isOnlyFragment := findRE `(?m)^#` $link }} {{- if $isOnlyFragment }} + {{- if not (.Page.Fragments.Identifiers.Contains $url.Fragment) }} + {{- if site.Params.failOnBrokenLinks }} + {{- errorf " Broken anchor link '%v' found in %s, available anchors: %v
" $fragment $currentFile (delimit .Page.Fragments.Identifiers ", ") }} + {{- else }} + {{- warnf "Broken anchor link '%v' found in %s, available anchors: %v" $fragment $currentPage (delimit .Page.Fragments.Identifiers ", ") }} + {{- end }} + {{- end }} {{- $permalink = $fragment }} {{- else }} {{- $page := .Page.GetPage $path }} - {{- with $page }} + {{- if $page }} + {{- if and $fragment (not (strings.HasSuffix $path "arangodb-server/options.md")) (not (strings.HasSuffix $path "query-optimization.md")) (not ($page.Fragments.Identifiers.Contains $url.Fragment)) }} + {{- if site.Params.failOnBrokenLinks }} + {{- errorf " Broken anchor link '%v%v' found in %s, available anchors: %v
" $path $fragment $currentFile (delimit $page.Fragments.Identifiers ", ") }} + {{- else }} + {{- warnf "Broken anchor link '%v%v' found in %s, available anchors: %v" $path $fragment $currentPage (delimit $page.Fragments.Identifiers ", ") }} + {{- end }} + {{- end }} {{- $permalink = printf "%s%s" $page.RelPermalink $fragment }} {{- else }} {{- if ne $currentPage "/" }} - {{- $currentFile := .Page.File.Path }} {{- if site.Params.failOnBrokenLinks }} {{- errorf " Broken link '%v' found in %s
" $link $currentFile }} {{- else }} diff --git a/site/themes/arangodb-docs-theme/layouts/partials/content-footer.html b/site/themes/arangodb-docs-theme/layouts/partials/content-footer.html index 608ec25a06..810c99d93f 100644 --- a/site/themes/arangodb-docs-theme/layouts/partials/content-footer.html +++ b/site/themes/arangodb-docs-theme/layouts/partials/content-footer.html @@ -60,21 +60,17 @@ - {{ with $prev }} - {{- if eq (len .Ancestors) 1 }} - - - {{ else }} - -

{{.Params.menuTitle | markdownify }}

- {{- end}} - {{ end }} - +{{- with $prev }} + {{- if ne (len .Ancestors) 1 }} + +

{{ .Params.menuTitle | markdownify }}

+ {{- end}} +{{- end }}
- {{ with $next }} - -

{{.Params.menuTitle | markdownify }}

- {{- end }} +{{- with $next }} + +

{{ .Params.menuTitle | markdownify }}

+{{- end }}