Cypher has built-in support for handling spatial values (points), and the underlying database supports storing these point values as properties on nodes and relationships.
|
Refer to Section 3.4.10, “Spatial functions” for information regarding spatial functions allowing for the creation and manipulation of spatial values. Refer to Section 3.2.7.12, “Ordering and comparison of values” for information regarding the comparison and ordering of spatial values. |
Neo4j supports only one type of spatial geometry, the Point with the following characteristics:
Four Coordinate Reference Systems (CRS) are supported, each of which falls within one of two types: geographic coordinates modeling points on the earth, or cartesian coordinates modeling points in euclidean space:
Geographic coordinate reference systems
Cartesian coordinate reference systems
Data within different coordinate systems are entirely incomparable, and cannot be implicitly converted from one to the other. This is true even if they are both cartesian or both geographic. For example, if you search for 3D points using a 2D range, you will get no results. However, they can be ordered, as discussed in more detail in the section on Cypher ordering.
Two Geographic Coordinate Reference Systems (CRS) are supported, modeling points on the earth:
A 2D geographic point in the WGS 84 CRS is specified in one of two ways:
longitude and latitude (if these are specified, and the crs is not, then the crs is assumed to be WGS-84)
x and y (in this case the crs must be specified, or will be assumed to be Cartesian)
A 3D geographic point in the WGS 84 CRS is specified one of in two ways:
longitude, latitude and either height or z (if these are specified, and the crs is not, then the crs is assumed to be WGS-84-3D)
x, y and z (in this case the crs must be specified, or will be assumed to be Cartesian-3D)
The units of the latitude and longitude fields are in decimal degrees, and need to be specified as floating point numbers using Cypher literals.
It is not possible to use any other format, like 'degrees, minutes, seconds'. The units of the height field are in meters. When geographic points
are passed to the distance function, the result will always be in meters. If the coordinates are in any other format or unit than supported, it
is necessary to explicitly convert them.
For example, if the incoming $height is a string field in kilometers, you would need to type height: toFloat($height) * 1000. Likewise if the
results of the distance function are expected to be returned in kilometers, an explicit conversion is required.
For example: RETURN distance(a,b) / 1000 AS km. An example demonstrating conversion on incoming and outgoing values is:
Query.
WITH point({ latitude:toFloat('13.43'), longitude:toFloat('56.21')}) AS p1, point({ latitude:toFloat('13.10'), longitude:toFloat('56.41')}) AS p2
RETURN toInteger(distance(p1,p2)/1000) AS km
| km |
|---|
|
1 row |
|
|
Try this query live. none WITH point({latitude:toFloat('13.43'), longitude:toFloat('56.21')}) AS p1, point({latitude:toFloat('13.10'), longitude:toFloat('56.41')}) as p2 RETURN toInteger(distance(p1,p2)/1000) as km
Two Cartesian Coordinate Reference Systems (CRS) are supported, modeling points in euclidean space:
x and y coordinate values
x, y and z coordinate values
The units of the x, y and z fields are unspecified and can mean anything the user intends them to mean. This also means that when two cartesian
points are passed to the distance function, the resulting value will be in the same units as the original coordinates. This is true for both 2D and 3D
points, as the pythagoras equation used is generalized to any number of dimensions. However, just as you cannot compare geographic points to cartesian
points, you cannot calculate the distance between a 2D point and a 3D point. If you need to do that, explicitly transform
the one type into the other.
For example:
Query.
WITH point({ x:3, y:0 }) AS p2d, point({ x:0, y:4, z:1 }) AS p3d
RETURN distance(p2d,p3d) AS bad, distance(p2d,point({ x:p3d.x, y:p3d.y })) AS good
| bad | good |
|---|---|
|
1 row |
|
|
|
|
Try this query live. none WITH point({x:3, y:0}) AS p2d, point({x:0, y:4, z:1}) as p3d RETURN distance(p2d,p3d) as bad, distance(p2d,point({x:p3d.x, y:p3d.y})) as good
All point types are created from two components:
For most use cases it is not necessary to specify the CRS explicitly as it will be deduced from the keys used to specify the coordinate. Two rules are applied to deduce the CRS from the coordinate:
Choice of keys:
latitude and longitude the CRS will be assumed to be Geographic and therefor either WGS-84 or WGS-84-3D.
x and y are used, then the default CRS would be Cartesian or Cartesian-3DNumber of dimensions:
x & y or longitude & latitude the CRS will be a 2D CRS
z or height the CRS will be a 3D CRS
All fields are provided to the point function in the form of a map of explicitly named arguments. We specifically do not support an ordered list
of coordinate fields because of the contradictory conventions between geographic and cartesian coordinates, where geographic
coordinates normally
list y before x (latitude before longitude).
See for example the following query which returns points created in each of the four supported CRS. Take particular note of
the order and keys
of the coordinates in the original point function calls, and how those values are displayed in the results:
Query.
RETURN point({ x:3, y:0 }) AS cartesian_2d, point({ x:0, y:4, z:1 }) AS cartesian_3d, point({ latitude: 12, longitude: 56 }) AS geo_2d, point({ latitude: 12, longitude: 56, height: 1000 }) AS geo_3d
| cartesian_2d | cartesian_3d | geo_2d | geo_3d |
|---|---|---|---|
|
1 row |
|||
|
|
|
|
|
Try this query live. none RETURN point({x:3, y:0}) AS cartesian_2d, point({x:0, y:4, z:1}) as cartesian_3d, point({latitude: 12, longitude: 56}) AS geo_2d, point({latitude: 12, longitude: 56, height: 1000}) as geo_3d
Just as we construct points using a map syntax, we can also access components as properties of the instance.
| Component | Description | Type | Range/Format | WGS-84 | WGS-84-3D | Cartesian | Cartesian-3D |
|---|---|---|---|---|---|---|---|
|
|
The first element of the Coordinate |
Float |
Number literal, range depends on CRS |
X |
X |
X |
X |
|
|
The second element of the Coordinate |
Float |
Number literal, range depends on CRS |
X |
X |
X |
X |
|
|
The third element of the Coordinate |
Float |
Number literal, range depends on CRS |
X |
X |
||
|
|
The second element of the Coordinate for geographic CRS, degrees North of the equator |
Float |
Number literal, |
X |
X |
||
|
|
The first element of the Coordinate for geographic CRS, degrees East of the prime meridian |
Float |
Number literal, |
X |
X |
||
|
|
The third element of the Coordinate for geographic CRS, meters above the ellipsoid defined by the datum (WGS-84) |
Float |
Number literal, range limited only by the underlying 64-bit floating point type |
X |
|||
|
|
The name of the CRS |
String |
One of |
X |
X |
X |
X |
|
|
The internal Neo4j ID for the CRS |
Integer |
One of |
X |
X |
X |
X |
The following query shows how to extract the components of a Cartesian 2D point value:
Query.
WITH point({ x:3, y:4 }) AS p
RETURN p.x, p.y, p.crs, p.srid
| p.x | p.y | p.crs | p.srid |
|---|---|---|---|
|
1 row |
|||
|
|
|
|
|
Try this query live. none WITH point({x:3, y:4}) AS p RETURN p.x, p.y, p.crs, p.srid
The following query shows how to extract the components of a WGS-84 3D point value:
Query.
WITH point({ latitude:3, longitude:4, height: 4321 }) AS p
RETURN p.latitude, p.longitude, p.height, p.x, p.y, p.z, p.crs, p.srid
| p.latitude | p.longitude | p.height | p.x | p.y | p.z | p.crs | p.srid |
|---|---|---|---|---|---|---|---|
|
1 row |
|||||||
|
|
|
|
|
|
|
|
|
Try this query live. none WITH point({latitude:3, longitude:4, height: 4321}) AS p RETURN p.latitude, p.longitude, p.height, p.x, p.y, p.z, p.crs, p.srid
If there is a schema index on a particular :Label(property) combination, and a spatial point
is assigned to that property on a node with that label, the node will be indexed in a spatial index. For spatial indexing,
Neo4j uses
space filling curves in 2D or 3D over an underlying generalized B+Tree. Points will be stored in up to four different trees,
one for each of the
four coordinate reference systems.
This allows for both equality
and range queries using exactly the same syntax and behaviour as for other property types.
If two range predicates are used, which define minimum and maximum points, this will effectively result in a
bounding box query.
In addition, queries using the distance function can, under the right conditions, also use the index, as described in the section
'Spatial distance searches'.
Points with different CRS are not comparable.
This means that any function operating on two points of different types will return null.
This is true of the distance function as well as inequality comparisons.
If these are used in a predicate, they will cause the associated MATCH to return no results.
Query.
WITH point({ x:3, y:0 }) AS p2d, point({ x:0, y:4, z:1 }) AS p3d
RETURN distance(p2d,p3d), p2d < p3d, p2d = p3d, p2d <> p3d, distance(p2d,point({ x:p3d.x, y:p3d.y }))
| distance(p2d,p3d) | p2d < p3d | p2d = p3d | p2d <> p3d | distance(p2d,point({ x:p3d.x, y:p3d.y })) |
|---|---|---|---|---|
|
1 row |
||||
|
|
|
|
|
|
Try this query live. none WITH point({x:3, y:0}) AS p2d, point({x:0, y:4, z:1}) AS p3d RETURN distance(p2d,p3d), p2d < p3d, p2d = p3d, p2d <> p3d, distance(p2d,point({x:p3d.x, y:p3d.y}))
However, all types are orderable. The Point types will be ordered after Numbers and before Temporal types. Points with different CRS with be ordered by their SRID numbers. For the current set of four CRS, this means the order is WGS84, WGS84-3D, Cartesian, Cartesian-3D.
Query.
UNWIND [point({ x:3, y:0 }), point({ x:0, y:4, z:1 }), point({ srid:4326, x:12, y:56 }), point({ srid:4979, x:12, y:56, z:1000 })] AS point
RETURN point
ORDER BY point
| point |
|---|
|
4 rows |
|
|
|
|
|
|
|
|
Try this query live. none UNWIND [point({x:3, y:0}), point({x:0, y:4, z:1}), point({srid:4326, x:12, y:56}), point({srid:4979, x:12, y:56, z:1000})] AS point RETURN point ORDER BY point