Spatial values

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 Spatial functions for information regarding spatial functions allowing for the creation and manipulation of spatial values.

Refer to Ordering and comparison of values for information regarding the comparison and ordering of spatial values.

Introduction

Neo4j supports only one type of spatial geometry, the Point with the following characteristics:

  • Each point can have either 2 or 3 dimensions. This means it contains either 2 or 3 64-bit floating point values, which together are called the Coordinate.

  • Each point will also be associated with a specific Coordinate Reference System (CRS) that determines the meaning of the values in the Coordinate.

  • Instances of Point and lists of Point can be assigned to node and relationship properties.

  • Nodes with Point or List(Point) properties can be indexed using a spatial index. This is true for all CRS (and for both 2D and 3D). There is no special syntax for creating spatial indexes, as it is supported using the existing indexes.

  • The distance function will work on points in all CRS and in both 2D and 3D but only if the two points have the same CRS (and therefore also same dimension).

Coordinate Reference Systems

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:

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 Ordering and comparison of values.

Geographic coordinate reference systems

Two Geographic Coordinate Reference Systems (CRS) are supported, modeling points on the earth:

  • WGS 84 2D

    • 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)

    • Specifying this CRS can be done using either the name 'wgs-84' or the SRID 4326 as described in Point(WGS-84)

  • WGS 84 3D

    • 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)

    • Specifying this CRS can be done using either the name 'wgs-84-3d' or the SRID 4979 as described in Point(WGS-84-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 point.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(point.distance(p1, p2)/1000) AS km
Table 1. Result
km

42

Rows: 1

Cartesian coordinate reference systems

Two Cartesian Coordinate Reference Systems (CRS) are supported, modeling points in euclidean space:

  • Cartesian 2D

    • A 2D point in the Cartesian CRS is specified with a map containing x and y coordinate values

    • Specifying this CRS can be done using either the name 'cartesian' or the SRID 7203 as described in Point(Cartesian)

  • Cartesian 3D

    • A 3D point in the Cartesian CRS is specified with a map containing x, y and z coordinate values

    • Specifying this CRS can be done using either the name 'cartesian-3d' or the SRID 9157 as described in Point(Cartesian-3D)

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
  point.distance(p2d, p3d) AS bad,
  point.distance(p2d, point({x: p3d.x, y: p3d.y})) AS good
Table 2. Result
bad good

<null>

5.0

Rows: 1

Spatial instants

Creating points

All point types are created from two components:

  • The Coordinate containing either 2 or 3 floating point values (64-bit)

  • The Coordinate Reference System (or CRS) defining the meaning (and possibly units) of the values in the Coordinate

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:

    • If the coordinate is specified using the keys latitude and longitude the CRS will be assumed to be Geographic and therefor either WGS-84 or WGS-84-3D.

    • If instead x and y are used, then the default CRS would be Cartesian or Cartesian-3D

  • Number of dimensions:

    • If there are 2 dimensions in the coordinate, x & y or longitude & latitude the CRS will be a 2D CRS

    • If there is a third dimensions in the coordinate, 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
Table 3. Result
cartesian_2d cartesian_3d geo_2d geo_3d

point({x: 3.0, y: 0.0, crs: 'cartesian'})

point({x: 0.0, y: 4.0, z: 1.0, crs: 'cartesian-3d'})

point({x: 56.0, y: 12.0, crs: 'wgs-84'})

point({x: 56.0, y: 12.0, z: 1000.0, crs: 'wgs-84-3d'})

Rows: 1

For the geographic coordinates, it is important to note that the latitude value should always lie in the interval [-90, 90] and any other value outside this range will throw an exception. The longitude value should always lie in the interval [-180, 180] and any other value outside this range will be wrapped around to fit in this range. The height value and any cartesian coordinates are not explicitly restricted, and any value within the allowed range of the signed 64-bit floating point type will be accepted.

Accessing components of points

Just as we construct points using a map syntax, we can also access components as properties of the instance.

Table 4. Components of point instances and where they are supported
Component Description Type Range/Format WGS-84 WGS-84-3D Cartesian Cartesian-3D

instant.x

The first element of the Coordinate

Float

Number literal, range depends on CRS

instant.y

The second element of the Coordinate

Float

Number literal, range depends on CRS

instant.z

The third element of the Coordinate

Float

Number literal, range depends on CRS

instant.latitude

The second element of the Coordinate for geographic CRS, degrees North of the equator

Float

Number literal, -90.0 to 90.0

instant.longitude

The first element of the Coordinate for geographic CRS, degrees East of the prime meridian

Float

Number literal, -180.0 to 180.0

instant.height

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

instant.crs

The name of the CRS

String

One of wgs-84, wgs-84-3d, cartesian, cartesian-3d

instant.srid

The internal Neo4j ID for the CRS

Integer

One of 4326, 4979, 7203, 9157

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 AS x,
  p.y AS y,
  p.crs AS crs,
  p.srid AS srid
Table 5. Result
x y crs srid

3.0

4.0

"cartesian"

7203

Rows: 1

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 AS latitude,
  p.longitude AS longitude,
  p.height AS height,
  p.x AS x,
  p.y AS y,
  p.z AS z,
  p.crs AS crs,
  p.srid AS srid
Table 6. Result
latitude longitude height x y z crs srid

3.0

4.0

4321.0

4.0

3.0

4321.0

"wgs-84-3d"

4979

Rows: 1

Spatial index

If there is a 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'.

Comparability and orderability

The comparability and orderability of spacial values are due to change in an upcoming future release. This means that queries that rely on the comparison of two points using the inequality operators, <, , >, and >=, or the specific order of an ORDER BY n.point query will need to be rewritten.

The most efficient way to do this is to explicitly specify the ordering. For example, by using point.x, point.y in cartesian coordinates, or point.longitude and point.latitude in geographic coordinates.