MongoDBの地理空間サポート

1. 概要

このチュートリアルでは、MongoDBでの地理空間サポートについて調べます。
地理空間データ、地理インデックス、地理空間検索の保存方法について説明します。 また、_near _、_ geoWithin _、_ geoIntersects_などの複数の地理空間検索クエリも使用します。

2. 地理空間データの保存

まず、MongoDBに地理空間データを保存する方法を見てみましょう。
  • MongoDBは、地理空間データを格納するために複数の_GeoJSON_型をサポートしています。*この例では、主に_Point_および_Polygon_型を使用します。

* 2.1。 ポイント*

これは最も基本的で一般的な_GeoJSON_タイプであり、*グリッド上の特定の1つのポイントを表すために使用されます*。
ここでは、_location_フィールドが_Point_である_places_ collection __、__に単純なオブジェクトがあります。
{
  "name": "Big Ben",
  "location": {
    "coordinates": [-0.1268194, 51.5007292],
    "type": "Point"
  }
}
経度の値が最初になり、次に緯度が来ることに注意してください。

* 2.2。 ポリゴン*

_Polygon_はもう少し複雑な_GeoJSON_型です。
*必要に応じて、_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"
  }
}
この例では、外部境界を表すポイントの配列を定義しました。 また、最後の点が最初の点と等しくなるように境界を閉じる必要があります。
外側の境界点を反時計回りに、穴の境界を時計回りに定義する必要があることに注意してください。
これらのタイプに加えて、_LineString、_ _MultiPoint、_ _MultiPolygon、_ _MultiLineString、_および_GeometryCollection._のような他の多くのタイプもあります。

3. 地理空間インデックス

*保存した地理空間データに対して検索クエリを実行するには、_location_フィールドに地理空間インデックスを作成する必要があります。*
基本的に、_2d_と_2dsphere_の2つのオプションがあります。
しかし最初に、場所c__ollection__を定義しましょう:
MongoClient mongoClient = new MongoClient();
MongoDatabase db = mongoClient.getDatabase("myMongoDb");
collection = db.getCollection("places");

* 3.1。 _2d_地理空間インデックス*

_2d_インデックスを使用すると、2D平面計算に基づいて機能する検索クエリを実行できます。
次のように、Javaアプリケーションの_location_フィールドに_2d_インデックスを作成できます。
collection.createIndex(Indexes.geo2d("location"));
もちろん、_mongo_シェルでも同じことができます。
db.places.createIndex({location:"2d"})

* 3.2。 _2dsphere_地理空間インデックス*

_2dsphere_インデックスは、球の計算に基づいて機能するクエリをサポートします。
同様に、上記と同じ_Indexes_クラスを使用して、Javaで_2dsphere_インデックスを作成できます。
collection.createIndex(Indexes.geo2dsphere("location"));
または、_mongo_シェルで:
db.places.createIndex({location:"2dsphere"})

4. 地理空間クエリを使用した検索

さて、エキサイティングな部分として、地理空間クエリを使用して、位置に基づいてオブジェクトを検索しましょう。

* 4.1。 ニアクエリ*

__nearから始めましょう。 __ ** _ near_クエリを使用して、特定の距離内の場所を検索できます。**
_near_クエリは、_2d_と_2dsphere_の両方のインデックスで動作します__.__
次の例では、指定された位置から1 km未満で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"));
}
_mongo_シェルの対応するクエリ:
db.places.find({
  location: {
    $near: {
      $geometry: {
        type: "Point",
        coordinates: [-0.126821, 51.495885]
      },
      $maxDistance: 1000,
      $minDistance: 10
    }
  }
})
結果は、最も近いものから最も遠いものにソートされていることに注意してください。
同様に、非常に離れた場所を使用する場合、近くの場所は見つかりません。
@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());
}
また、_nearSphere_メソッドもあります。これは、_near、_とまったく同じように機能しますが、球面ジオメトリを使用して距離を計算します。

* 4.2。 クエリ内*

次に、_geoWithin_クエリを調べます。
  • geoWithin_クエリを使用すると、特定の_Geometry *内に完全に存在する場所(円、ボックス、多角形など)を検索できます。 これは、_2d_と_2dsphere_の両方のインデックスでも機能します。

    この例では、指定された中心位置から半径5 km以内に存在する場所を探しています。
@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"));
}
距離をkmからラジアンに変換する必要があることに注意してください(地球の半径で除算するだけです)。
そして、結果のクエリ:
db.places.find({
  location: {
    $geoWithin: {
      $centerSphere: [
        [-0.1435083, 51.4990956],
        0.0007848061528802386
      ]
    }
  }
})
次に、長方形の「ボックス」内に存在するすべての場所を検索します。 ボックスをその左下位置と右上位置で定義する必要があります。
@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"));
}
_mongo_シェルの対応するクエリは次のとおりです。
db.places.find({
  location: {
    $geoWithin: {
      $box: [
        [-0.1427638, 51.4991288],
        [-0.1256209, 51.5030272]
      ]
    }
  }
})
最後に、*検索する領域が長方形でも円形でもない場合、ポリゴンを使用してより具体的な領域を定義できます*:
@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"));
}
そして、対応するクエリは次のとおりです。
db.places.find({
  location: {
    $geoWithin: {
      $polygon: [
        [-0.1439, 51.4952],
        [-0.1121, 51.4989],
        [-0.13, 51.5163],
        [-0.1439, 51.4952]
      ]
    }
  }
})
外側の境界を持つポリゴンのみを定義しましたが、穴を追加することもできます。 各穴は、__ Point__sの_List_になります。
geoWithinPolygon("location", points, hole1, hole2, ...)

* 4.3。 交差クエリ*

最後に、_geoIntersects_クエリを見てみましょう。
  • _geoIntersects_クエリは、少なくとも特定の_Geometryと交差するオブジェクトを検出します。

    このクエリは、2dsphere_インデックスのみで機能します。
    _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"));
}
結果のクエリ:
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. 結論

この記事では、地理空間データをMongoDBに保存する方法を学び、_2d_と_2dsphere_の地理空間インデックスの違いを調べました。 また、地理空間クエリを使用してMongoDBで検索する方法も学びました。
いつものように、例の完全なソースコードはhttps://github.com/eugenp/tutorials/tree/master/persistence-modules/java-mongodb[GitHubで]から入手できます。