1. 概要

このチュートリアルでは、MongoDBでの地理空間サポートについて説明します。

地理空間データ、地理インデックス、および地理空間検索を保存する方法について説明します。 また、 near geoWithin geoIntersectsなどの複数の地理空間検索クエリを使用します。

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

まず、MongoDBに地理空間データを保存する方法を見てみましょう。

MongoDBは、地理空間データを格納するために複数のGeoJSONタイプをサポートしています。この例では、主にPointおよびPolygonタイプを使用します。

2.1. ポイント

これは最も基本的で一般的なGeoJSONタイプであり、グリッド上の1つの特定のポイントを表すために使用されます

ここでは、 Places コレクションに、フィールドlocationPointとして持つ単純なオブジェクトがあります。

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

経度の値が最初に来て、次に緯度が来ることに注意してください。

2.2. ポリゴン

Polygonはもう少し複雑なGeoJSONタイプです。

ポリゴンを使用して、外側の境界線と、必要に応じて内側の穴がある領域を定義できます。

その場所が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フィールドに地理空間インデックスを作成する必要があります。

基本的に、2d2dsphereの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クエリを使用して、指定された距離内の場所を検索できます。

クエリに近いは、2d2dsphereインデックスの両方で機能します。

次の例では、指定された位置から1km未満で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インデックスの両方でも機能します。

この例では、指定された中心位置から半径5km以内に存在する場所を探しています。

@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]
      ]
    }
  }
})

外部境界を持つポリゴンのみを定義しましたが、それに穴を追加することもできます。 各穴は、ポイントリストになります。

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

4.3. 交差クエリ

最後に、geoIntersectsクエリを見てみましょう。

geoIntersectsクエリは、指定されたGeometryと少なくとも交差するオブジェクトを検索します。比較すると、 geoWithin は、指定されたGeometry内に完全に存在するオブジェクトを検索します。

このクエリは、2dsphereインデックスでのみ機能します。

ポリゴンと交差する場所を探す例を使って、これを実際に見てみましょう。

@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に保存する方法を学び、2d2dsphereの地理空間インデックスの違いを確認しました。 また、地理空間クエリを使用してMongoDBで検索する方法も学びました。

いつものように、例の完全なソースコードは、GitHubから入手できます。