Geospatial Support in MongoDB – MongoDB中的地理空间支持

最后修改: 2019年 6月 1日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’ll explore the Geospatial support in MongoDB.

在本教程中,我们将探讨MongoDB中的地理空间支持。

We’ll discuss how to store geospatial data, geo indexing, and geospatial search. We’ll also use multiple geospatial search queries like near, geoWithin, and geoIntersects.

我们将讨论如何存储地理空间数据、地理索引和地理空间搜索。我们还将使用多种地理空间搜索查询,如near, geoWithin, 和geoIntersects

2. Storing Geospatial Data

2.存储地理空间数据

First, let’s see how to store geospatial data in MongoDB.

首先,让我们看看如何在MongoDB中存储地理空间数据。

MongoDB supports multiple GeoJSON types to store geospatial data. Throughout our examples, we’ll mainly use the Point and Polygon types.

MongoDB支持多种GeoJSON类型来存储地理空间数据。在整个示例中,我们将主要使用PointPolygon类型。

2.1. Point

2.1.

This is the most basic and common GeoJSON type, and it’s used to represent one specific point on the grid.

这是最基本和最常见的GeoJSON类型,它被用来表示网格上的一个特定点

Here, we have a simple object, in our places collection, that has field location as a Point:

在这里,我们有一个简单的对象,在我们的places集合中,它的字段location是一个Point

{
  "name": "Big Ben",
  "location": {
    "coordinates": [-0.1268194, 51.5007292],
    "type": "Point"
  }
}

Note that the longitude value comes first, then the latitude.

请注意,经度值在前,然后是纬度。

2.2. Polygon

2.2.多边形

Polygon is a bit more complex GeoJSON type.

Polygon是一个有点复杂的GeoJSON类型。

We can use Polygon to define an area with its exterior borders and also interior holes if needed.

我们可以使用Polygon来定义一个区域的外部边界,如果需要,也可以定义内部的孔。

Let’s see another object that has its location defined as a Polygon:

让我们看看另一个对象,它的位置被定义为一个Polygon

{
  "name": "Hyde Park",
  "location": {
    "coordinates": [
      [
        [-0.159381, 51.513126],
        [-0.189615, 51.509928],
        [-0.187373, 51.502442],
        [-0.153019, 51.503464],
        [-0.159381, 51.513126]
      ]
    ],
    "type": "Polygon"
  }
}

In this example, we defined an array of points that represent exterior bounds. We also have to close the bound so that the last point equals the first point.

在这个例子中,我们定义了一个代表外部边界的点阵列。我们还必须关闭边界,使最后一个点等于第一个点。

Note that we need to define the exterior bounds points in counterclockwise direction and hole bounds in a clockwise direction.

注意,我们需要在逆时针方向上定义外部边界点,在顺时针方向上定义洞口边界。

In addition to these types, there are also many other types like LineString, MultiPoint, MultiPolygon, MultiLineString, and GeometryCollection.

除了这些类型,还有许多其他类型,如LineString、MultiPoint、MultiPolygon、MultiLineString、GeometryCollection。

3. Geospatial Indexing

3.地理空间索引

To perform search queries on the geospatial data we stored, we need to create a geospatial index on our location field.

为了对我们存储的地理空间数据进行搜索查询,我们需要在我们的location字段上创建一个地理空间索引。

We basically have two options: 2d and 2dsphere.

我们基本上有两个选择。2d2dsphere

But first, let’s define our places collection:

但首先,让我们定义我们的地方c集合

MongoClient mongoClient = new MongoClient();
MongoDatabase db = mongoClient.getDatabase("myMongoDb");
collection = db.getCollection("places");

3.1. 2d Geospatial Index

3.1.2d地理空间索引

The 2d index enables us to perform search queries that work based on 2d plane calculations.

2d索引使我们能够进行基于2d平面计算的搜索查询工作。

We can create a 2d index on the location field in our Java application as follows:

我们可以在我们的Java应用程序中对location字段创建一个2d索引,如下所示。

collection.createIndex(Indexes.geo2d("location"));

Of course, we can do the same in the mongo shell:

当然,我们可以在mongo shell中做同样的事情。

db.places.createIndex({location:"2d"})

3.2. 2dsphere Geospatial Index

3.2.2dsphere地理空间索引

The 2dsphere index supports queries that work based on sphere calculations.

2dsphere索引支持基于球体计算的查询工作。

Similarly, we can create a 2dsphere index in Java using the same Indexes class as above:

同样,我们可以在 Java 中使用与上述相同的 Indexes 类来创建 2dsphere 索引。

collection.createIndex(Indexes.geo2dsphere("location"));

Or in the mongo shell:

或者在mongo壳中。

db.places.createIndex({location:"2dsphere"})

4. Searching Using Geospatial Queries

4.使用地理空间查询进行搜索

Now, for the exciting part, let’s search for objects based on their location using geospatial queries.

现在,令人兴奋的部分是,让我们使用地理空间查询来搜索基于其位置的对象。

4.1. Near Query

4.1.近距离查询

Let’s start with near. We can use the near query to search for places within a given distance.

让我们从near>开始。我们可以使用near查询来搜索指定距离内的地方。

The near query works with both 2d and 2dsphere indices.

附近查询对2d2dsphereindices都有效。

In the next example, we’ll search for places that are less than 1 km and more than 10 meters away from the given position:

在下一个例子中,我们将搜索距离给定位置小于1公里和大于10米的地方。

@Test
public void givenNearbyLocation_whenSearchNearby_thenFound() {
    Point currentLoc = new Point(new Position(-0.126821, 51.495885));
 
    FindIterable<Document> result = collection.find(
      Filters.near("location", currentLoc, 1000.0, 10.0));

    assertNotNull(result.first());
    assertEquals("Big Ben", result.first().get("name"));
}

And the corresponding query in the mongo shell:

以及mongo shell中的相应查询。

db.places.find({
  location: {
    $near: {
      $geometry: {
        type: "Point",
        coordinates: [-0.126821, 51.495885]
      },
      $maxDistance: 1000,
      $minDistance: 10
    }
  }
})

Note that the results are sorted from nearest to farthest.

请注意,结果是从最近的到最远的进行排序的。

Similarly, if we use a very far away location, we won’t find any nearby places:

同样地,如果我们使用一个非常远的位置,我们不会找到任何附近的地方。

@Test
public void givenFarLocation_whenSearchNearby_thenNotFound() {
    Point currentLoc = new Point(new Position(-0.5243333, 51.4700223));
 
    FindIterable<Document> result = collection.find(
      Filters.near("location", currentLoc, 5000.0, 10.0));

    assertNull(result.first());
}

We also have the nearSphere method, which acts exactly like near, except it calculates the distance using spherical geometry.

我们还有nearSphere方法,它的作用与near完全一样,只是它使用球面几何来计算距离。

4.2. Within Query

4.2.内部查询

Next, we’ll explore the geoWithin query.

接下来,我们将探讨geoWithin查询。

The geoWithin query enables us to search for places that fully exist within a given Geometry, like a circle, box, or polygon. This also works with both 2d and 2dsphere indices.

geoWithin查询使我们能够搜索完全存在于给定Geometry内的地方,比如一个圆、盒子或多边形。这也适用于2d2dsphere指数。

In this example, we’re looking for places that exist within a 5 km radius from the given center position:

在这个例子中,我们要寻找的是存在于给定中心位置5公里半径范围内的地方。

@Test
public void givenNearbyLocation_whenSearchWithinCircleSphere_thenFound() {
    double distanceInRad = 5.0 / 6371;
 
    FindIterable<Document> result = collection.find(
      Filters.geoWithinCenterSphere("location", -0.1435083, 51.4990956, distanceInRad));

    assertNotNull(result.first());
    assertEquals("Big Ben", result.first().get("name"));
}

Note that we need to transform the distance from km to radian (just divide by Earth’s radius).

请注意,我们需要将距离从公里转化为弧度(除以地球的半径即可)。

And the resulting query:

以及由此产生的查询。

db.places.find({
  location: {
    $geoWithin: {
      $centerSphere: [
        [-0.1435083, 51.4990956],
        0.0007848061528802386
      ]
    }
  }
})

Next, we’ll search for all places that exist within a rectangle “box”. We need to define the box by its lower left position and upper right position:

接下来,我们将搜索存在于一个矩形 “盒子 “内的所有地方。我们需要通过左下角位置和右上角位置来定义这个盒子。

@Test
public void givenNearbyLocation_whenSearchWithinBox_thenFound() {
    double lowerLeftX = -0.1427638;
    double lowerLeftY = 51.4991288;
    double upperRightX = -0.1256209;
    double upperRightY = 51.5030272;

    FindIterable<Document> result = collection.find(
      Filters.geoWithinBox("location", lowerLeftX, lowerLeftY, upperRightX, upperRightY));

    assertNotNull(result.first());
    assertEquals("Big Ben", result.first().get("name"));
}

Here’s the corresponding query in mongo shell:

下面是mongo shell中的相应查询。

db.places.find({
  location: {
    $geoWithin: {
      $box: [
        [-0.1427638, 51.4991288],
        [-0.1256209, 51.5030272]
      ]
    }
  }
})

Finally, if the area we want to search within isn’t a rectangle or a circle, we can use a polygon to define a more specific area:

最后,如果我们要搜索的区域不是矩形或圆形,我们可以使用多边形来定义一个更具体的区域

@Test
public void givenNearbyLocation_whenSearchWithinPolygon_thenFound() {
    ArrayList<List<Double>> points = new ArrayList<List<Double>>();
    points.add(Arrays.asList(-0.1439, 51.4952));
    points.add(Arrays.asList(-0.1121, 51.4989));
    points.add(Arrays.asList(-0.13, 51.5163));
    points.add(Arrays.asList(-0.1439, 51.4952));
 
    FindIterable<Document> result = collection.find(
      Filters.geoWithinPolygon("location", points));

    assertNotNull(result.first());
    assertEquals("Big Ben", result.first().get("name"));
}

And here’s the corresponding query:

而这里是相应的查询。

db.places.find({
  location: {
    $geoWithin: {
      $polygon: [
        [-0.1439, 51.4952],
        [-0.1121, 51.4989],
        [-0.13, 51.5163],
        [-0.1439, 51.4952]
      ]
    }
  }
})

We only defined a polygon with its exterior bounds, but we can also add holes to it. Each hole will be a List of Points:

我们只定义了一个具有外部边界的多边形,但我们也可以给它添加孔。每个洞将是一个ListPoints。

geoWithinPolygon("location", points, hole1, hole2, ...)

4.3. Intersect Query

4.3.交叉查询

Finally, let’s look at the geoIntersects query.

最后,让我们看看geoIntersects查询。

The geoIntersects query finds objects that at least intersect with a given Geometry. By comparison, geoWithin finds objects that fully exist within a given Geometry.

geoIntersects查询找到至少与给定的Geometry>相交的对象。相比之下,geoWithin找到完全存在于给定的Geometry内的对象。

This query works with the 2dsphere index only.

该查询仅适用于2dsphere索引。

Let’s see this in practice, with an example of looking for any place that intersects with a Polygon:

让我们在实践中看看,以寻找与Polygon相交的任何地方为例。

@Test
public void givenNearbyLocation_whenSearchUsingIntersect_thenFound() {
    ArrayList<Position> positions = new ArrayList<Position>();
    positions.add(new Position(-0.1439, 51.4952));
    positions.add(new Position(-0.1346, 51.4978));
    positions.add(new Position(-0.2177, 51.5135));
    positions.add(new Position(-0.1439, 51.4952));
    Polygon geometry = new Polygon(positions);
 
    FindIterable<Document> result = collection.find(
      Filters.geoIntersects("location", geometry));

    assertNotNull(result.first());
    assertEquals("Hyde Park", result.first().get("name"));
}

The resulting query:

由此产生的查询。

db.places.find({
  location:{
    $geoIntersects:{
      $geometry:{
        type:"Polygon",
          coordinates:[
          [
            [-0.1439, 51.4952],
            [-0.1346, 51.4978],
            [-0.2177, 51.5135],
            [-0.1439, 51.4952]
          ]
        ]
      }
    }
  }
})

5. Conclusion

5.结论

In this article, we learned how to store geospatial data in MongoDB and looked at the difference between 2d and 2dsphere geospatial indices. We also learned how to search in MongoDB using geospatial queries.

在这篇文章中,我们学习了如何在MongoDB中存储地理空间数据,并了解了2d2dsphere地理空间索引之间的区别。我们还学习了如何使用地理空间查询在MongoDB中进行搜索。

As usual, the full source code for the examples is available over on GitHub.

像往常一样,这些例子的完整源代码可以在GitHub上找到