You are currently browsing legacy 4.1 version of documentation. Click here to switch to the newest 4.2 version.

We can help you with migration to the latest RavenDB

Contact Us Now
see on GitHub

Indexes: Indexing Spatial Data

To support the ability to retrieve the data based on spatial coordinates, the spatial search has been introduced.

Information

This article describes how to setup a spatial field in static index. If you are interested in an automatic approach, please visit relevant spatial querying article that can be found here.

Creating Indexes

To take an advantage of the spatial search, first we need to create an index with a spatial field. To mark field as the spatial field, we need to use the CreateSpatialField method:

object CreateSpatialField(double? lat, double? lng);

object CreateSpatialField(string shapeWkt);

Where:

  • lat/lng are latitude/longitude coordinates
  • shapeWKT is a shape in the WKT format

Example

public static class Event {
    private String id;
    private String name;
    private double latitude;
    private double longitude;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getLatitude() {
        return latitude;
    }

    public void setLatitude(double latitude) {
        this.latitude = latitude;
    }

    public double getLongitude() {
        return longitude;
    }

    public void setLongitude(double longitude) {
        this.longitude = longitude;
    }
}

public static class Events_ByNameAndCoordinates extends AbstractIndexCreationTask {
    public Events_ByNameAndCoordinates() {
        map = "docs.Events.Select(e => new { " +
            "    name = e.name, " +
            "    coordinates = this.CreateSpatialField(((double ? ) e.latitude), ((double ? ) e.longitude)) " +
            "})";
    }
}
public static class EventWithWKT {
    private String id;
    private String name;
    private String wkt;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getWkt() {
        return wkt;
    }

    public void setWkt(String wkt) {
        this.wkt = wkt;
    }
}

public static class EventsWithWKT_ByNameAndWKT extends AbstractIndexCreationTask {
    public EventsWithWKT_ByNameAndWKT() {
        map = "docs.EventWithWKTs.Select(e => new { " +
            "    name = e.name, " +
            "    wkt = this.CreateSpatialField(e.wkt) " +
            "})";
    }
}
public static class Events_ByNameAndCoordinates extends AbstractJavaScriptIndexCreationTask {
    public Events_ByNameAndCoordinates() {
        setMaps(Sets.newHashSet("map('events', function (e){\n" +
            "    return {\n" +
            "        name: e.name  ,\n" +
            "        coordinates: createSpatialField(e.latitude, e.longitude)\n" +
            "    };\n" +
            "})"));
    }
}

Options

RavenDB supports both the Geography and Cartesian systems and multiple strategies for each one of them.

public static class SpatialOptionsFactory {
    public GeographySpatialOptionsFactory geography()

    public CartesianSpatialOptionsFactory cartesian()
}

public SpatialOptions defaultOptions()

public SpatialOptions defaultOptions(SpatialUnits circleRadiusUnits)

public SpatialOptions boundingBoxIndex()

public SpatialOptions boundingBoxIndex(SpatialUnits circleRadiusUnits)

public SpatialOptions geohashPrefixTreeIndex(int maxTreeLevel)

public SpatialOptions geohashPrefixTreeIndex(int maxTreeLevel, SpatialUnits circleRadiusUnits)

public SpatialOptions quadPrefixTreeIndex(int maxTreeLevel)

public SpatialOptions quadPrefixTreeIndex(int maxTreeLevel, SpatialUnits circleRadiusUnits)
public SpatialOptions boundingBoxIndex()

public SpatialOptions quadPrefixTreeIndex(int maxTreeLevel, SpatialBounds bounds)

Changing Default Behavior

By default, if no action is taken, the GeohashPrefixTree strategy is used with GeohashLevel set to 9. This behavior can be changed by using the spatial() method from AbstractIndexCreationTask

public static class Events_ByNameAndCoordinates_Custom extends AbstractIndexCreationTask {
    public Events_ByNameAndCoordinates_Custom() {
        map = "docs.Events.Select(e => new { " +
            "    name = e.name, " +
            "    coordinates = this.CreateSpatialField(((double ? ) e.latitude), ((double ? ) e.longitude)) " +
            "})";

        spatial("coordinates", f -> f.cartesian().boundingBoxIndex());
    }
}

Spatial search strategies

GeohashPrefixTree

Geohash is a latitude/longitude representation system that describes earth as a grid with 32 cells, assigning an alphanumeric character to each grid cell. Each grid cell is further divided into 32 smaller chunks, and each chunk has an alphanumeric character assigned as well, and so on.

E.g. The location of 'New York' in the United States is represented by the following geohash: DR5REGY6R and it represents the 40.7144 -74.0060 coordinates. Removing characters from the end of geohash will decrease the precision level.

More information about geohash uses, decoding algorithm and limitations can be found here.

QuadPrefixTree

QuadTree represents the earth as a grid with exactly four cells and similarly to geohash, each grid cell (sometimes called a bucket) has a letter assigned, and is divided further into 4 more cells and so on.

More information about QuadTree can be found here.

BoundingBox

More information about BoundingBox can be found here.

Warning

GeohashPrefixTree is a default SpatialSearchStrategy. Doing any changes to the strategy after an index has been created will trigger the re-indexation process.

Precision

By default, the precision level (maxTreeLevel) for GeohashPrefixTree is set to 9 and for QuadPrefixTree the value is 23. This means that the coordinates are represented by a 9 or 23 character string. The difference exists because the QuadTree representation would be much less precise if the level would be the same.

Geohash precision values

Source: http://unterbahn.com

Level E-W Distance at Equator N-S Distance at Equator
12 ~3.7cm ~1.8cm
11 ~14.9cm ~14.9cm
10 ~1.19m ~0.60m
9 ~4.78m ~4.78m
8 ~38.2m ~19.1m
7 ~152.8m ~152.8m
6 ~1.2km ~0.61km
5 ~4.9km ~4.9km
4 ~39km ~19.6km
3 ~157km ~157km
2 ~1252km ~626km
1 ~5018km ~5018km

Quadtree precision values

Level Distance at Equator
30 ~4cm
29 ~7cm
28 ~15cm
27 ~30cm
26 ~60cm
25 ~1.19m
24 ~2.39m
23 ~4.78m
22 ~9.56m
21 ~19.11m
20 ~38.23m
19 ~76.23m
18 ~152.92m
17 ~305.84m
16 ~611.67m
15 ~1.22km
14 ~2.45km
13 ~4.89km
12 ~9.79km
11 ~19.57km
10 ~39.15km
9 ~78.29km
8 ~156.58km
7 ~313.12km
6 ~625.85km
5 ~1249km
4 ~2473km
3 ~4755km
2 ~7996km
1 ~15992km

Remarks

Information

You can read more about spatial search in a dedicated querying article available here.

Warning

Distance by default is measured in kilometers.