Статьи

Введение в геопространственную функцию MongoDB

Этот пост представляет собой быстрое и простое введение в геопространственную функцию MongoDB 2.6 с использованием простого набора данных и запросов.

Хранение геопространственной информации

Как вы знаете, вы можете хранить данные любого типа, но если вы хотите запросить их, вам нужно использовать некоторые координаты и создать индекс для них. MongoDB поддерживает три типа индексов для запросов GeoSpatial:

  • 2d индекс : использует простую координату (долгота, широта). Как указано в документации: 2d-индекс предназначен для устаревших пар координат, используемых в MongoDB 2.2 и более ранних версиях . По этой причине я не буду подробно рассказывать об этом в этом посте. Просто для записи 2d Index используются для запроса данных, хранящихся в виде точек на двухмерной плоскости
  • 2d Sphere Index : поддержка запросов любой геометрии на земноподобной сфере, данные могут быть сохранены в виде GeoJSON и устаревших координатных пар (долгота, широта). В оставшейся части статьи я буду использовать этот тип индекса и сосредоточиться на GeoJSON.
  • Geo Haystack : используются для запроса на очень маленькой площади. Сегодня он менее используется приложениями, и я не буду описывать его в этом посте.

Таким образом, эта статья теперь будет сосредоточена на 2d индексе Sphere с форматом GeoJSON для хранения и запроса документов.

Так что же такое GeoJSON?

Вы можете посмотреть на сайте http://geojson.org/ , давайте сделаем очень краткое объяснение. GeoJSON — это формат для кодирования в JSON различных географических структур данных, который поддерживает следующие типы: Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygon и Geometry.

Формат GeoJSON довольно прост, для простых геометрий основан на двух атрибутах: тип и координаты. Давайте возьмем несколько примеров:

Город, где я провожу все свое детство, Пленёф Валь-Андре, Франция, имеет следующие координаты (из Википедии)

48 ° 35 ′ 30,12 ″ с.ш., 2 ° 32 ′ 48,84 ″ з.д.

Это обозначение является точкой, основанной на широте и долготе с использованием системы WGS 84 (градусы, минуты, секунды). Не очень прост в использовании приложением / кодом, поэтому также возможно представить одну и ту же точку, используя следующие значения широты и долготы:

48,5917, -2,5469

Этот использует систему WGS 84 (десятичные градусы). Это координаты, которые вы видите в большинстве приложений / API, которые вы используете в качестве разработчика (например, Google Maps / Earth).

По умолчанию GeoJSON и MongoDB используют эти значения, но координаты должны храниться в широте, долготе , поэтому эта точка в GeoJSON будет выглядеть следующим образом:

1
2
3
4
5
6
7
{
  "type": "Point",
  "coordinates": [
    -2.5469
    48.5917
  ]
}

01-GeoJSON-точка

Это простая «точка», давайте теперь, например, посмотрим на линию, очень приятную прогулку по пляжу:

01
02
03
04
05
06
07
08
09
10
{
  "type": "LineString",
  "coordinates": [
    [-2.551082,48.5955632],
    [-2.551229,48.594312],
    [-2.551550,48.593312],
    [-2.552400,48.592312],
    [-2.553677, 48.590898]
  ]
}

02-GeoJSON-LINESTRING

Таким образом, используя тот же подход, вы сможете создавать MultiPoint, MultiLineString, Polygon, MultiPolygon. Также возможно смешать все это в одном документе, используя GeometryCollection. В следующем примере представлена ​​коллекция геометрии MultiLineString и Polygon над Центральным парком:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
  "type" : "GeometryCollection",
  "geometries" : [
    {
      "type" : "Polygon",
      "coordinates" : [
         [
      [ -73.9580, 40.8003 ],
          [ -73.9498, 40.7968 ],
      [ -73.9737, 40.7648 ],
      [ -73.9814, 40.7681 ],
      [ -73.9580, 40.8003  ]
     ]
       ]
    },
    {
      "type" : "MultiLineString",
      "coordinates" : [
         [ [ -73.96943, 40.78519 ], [ -73.96082, 40.78095 ] ],
     [ [ -73.96415, 40.79229 ], [ -73.95544, 40.78854 ] ],
         [ [ -73.97162, 40.78205 ], [ -73.96374, 40.77715 ] ],
         [ [ -73.97880, 40.77247 ], [ -73.97036, 40.76811 ] ]
       ]
     }
  ]
}

03-gejson-коллекция

Примечание. Вы можете, если хотите, протестировать / визуализировать эти документы JSON с помощью службы http://geojsonlint.com/ .

Что теперь? Давайте хранить данные!

Если у вас есть документ GeoJSON, вам просто нужно сохранить его в своем документе. Например, если вы хотите сохранить документ об аэропорте JFK с указанием его местоположения, вы можете выполнить следующую команду:

01
02
03
04
05
06
07
08
09
10
db.airports.insert(
  {
    "name" : "John F Kennedy Intl",
    "type" : "International",
    "code" : "JFK",
    "loc" : {
      "type" : "Point",
      "coordinates" : [ -73.778889, 40.639722 ]
    }
}

Да, это так просто! Вы просто сохраняете GeoJSON как один из атрибутов документа (в этом примере loc).

Запросы геопространственной информации

Теперь, когда у нас есть данные, хранящиеся в MongoDB, теперь можно использовать геопространственную информацию для выполнения некоторых интересных запросов.

Для этого нам нужен образец набора данных. Я создал один, используя некоторые открытые данные, найденные в разных местах. Этот набор данных содержит следующую информацию:

  • коллекция аэропортов со списком аэропортов США (Point)
  • коллекция штатов со списком штатов США (MultiPolygon)

Я создал этот набор данных из различных источников OpenData ( http://geocommons.com/ , http://catalog.data.gov/dataset ) и использую toGeoJSON, чтобы преобразовать их в нужный формат.

Давайте установим набор данных:

  1. Загрузите его отсюда
  2. Распакуйте файл geo.zip
  3. Восстановите данные в вашем экземпляре mongoDB, используя следующую команду mongorestore geo.zip

MongoDB позволяет приложениям выполнять следующие типы запросов к геопространственным данным:

  • включение
  • пересечение
  • близость

Очевидно, вы сможете использовать все остальные операторы в дополнение к геопространственным. Давайте теперь посмотрим на некоторые конкретные примеры.

включение

Найти все аэропорты в Калифорнии. Для этого вам нужно получить Калифорнийское местоположение (Polygon) и использовать команду $ geoWithin в запросе. Из оболочки это будет выглядеть так:

01
02
03
04
05
06
07
08
09
10
use geo
 
var cal = db.states.findOne(  {code : "CA"}  );
 
db.airports.find( 
  
    loc : { $geoWithin : { $geometry : cal.loc } } 
  },
  { name : 1 , type : 1, code : 1, _id: 0
);

Результат:

1
2
3
4
5
{ "name" : "Modesto City - County", "type" : "", "code" : "MOD" }
...
{ "name" : "San Francisco Intl", "type" : "International", "code" : "SFO" }
{ "name" : "San Jose International", "type" : "International", "code" : "SJC" }
...

Таким образом, запрос использует «California MultiPolygon» и ищет в коллекции аэропортов все аэропорты, которые находятся в этих многоугольниках. Это похоже на следующее изображение на карте:

04-GeoJSON-кал-аэропорт

Вы можете использовать любые другие функции или критерии запроса, например, вы можете ограничить запрос международным аэропортом, отсортированным только по имени:

1
2
3
4
5
6
7
db.airports.find( 
  
    loc : { $geoWithin : { $geometry : cal.loc } },
    type : "International" 
  },
  { name : 1 , type : 1, code : 1, _id: 0
).sort({ name : 1 });

Результат:

1
2
3
4
5
6
7
{ "name" : "Los Angeles Intl", "type" : "International", "code" : "LAX" }
{ "name" : "Metropolitan Oakland Intl", "type" : "International", "code" : "OAK" }
{ "name" : "Ontario Intl", "type" : "International", "code" : "ONT" }
{ "name" : "San Diego Intl", "type" : "International", "code" : "SAN" }
{ "name" : "San Francisco Intl", "type" : "International", "code" : "SFO" }
{ "name" : "San Jose International", "type" : "International", "code" : "SJC" }
{ "name" : "Southern California International", "type" : "International", "code" : "VCV" }

Я не знаю, смотрели ли вы подробно, но мы запрашиваем эти документы без индекса. Вы можете выполнить запрос с помощью объяснения (), чтобы увидеть, что происходит. Оператору $ geoWithin не нужен индекс, но ваши запросы будут более эффективными, поэтому давайте создадим индекс:

1
db.airports.ensureIndex( { "loc" : "2dsphere" } );

Запустите объяснение, и вы увидите разницу.

пересечение

Предположим, что вы хотите знать, что представляют собой все соседние штаты с Калифорнией, для этого нам просто нужно найти все штаты, координаты которых «пересекаются» с Калифорнией. Это делается с помощью следующего запроса:

1
2
3
4
5
6
7
8
var cal = db.states.findOne(  {code : "CA"}  );
db.states.find(
  {
    loc : { $geoIntersects : { $geometry : cal.loc  }  } ,
    code : { $ne : "CA" 
  },
  { name : 1, code : 1 , _id : 0 }
);

Результат:

1
2
3
{ "name" : "Oregon", "code" : "OR" }
{ "name" : "Nevada", "code" : "NV" }
{ "name" : "Arizona", "code" : "AZ" }

05-GeoJSON-пересекаются

Как и раньше, оператору $ geoIntersect не нужен индекс для работы, но он будет более эффективным со следующим индексом:

1
db.states.ensureIndex( { loc : "2dsphere" } );

близость

Последняя особенность, которую я хочу выделить в этом посте, связана с запросом с критериями близости. Давайте найдем все международные аэропорты, которые расположены менее чем в 20 км от водохранилища в центральном парке Нью-Йорка. Для этого вы будете использовать оператор $ near.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
db.airports.find(
  {
    loc : {
      $near : {
        $geometry : {
          type : "Point" ,
          coordinates : [-73.965355,40.782865
        },
        $maxDistance : 20000
      }
    },
    type : "International"
  },
  {
    name : 1,
    code : 1,
    _id : 0
  }
);

Полученные результаты:

1
2
{ "name" : "La Guardia", "code" : "LGA" }
{ "name" : "Newark Intl", "code" : "EWR"

Таким образом, этот запрос возвращает 2 аэропорта, самым близким из которых является La Guardia, поскольку оператор $ near сортирует результаты по расстоянию. Также важно подчеркнуть, что для оператора $ near требуется индекс.

Вывод

В этом первом посте о геопространственных объектах вы узнали:

  • основы GeoJSON
  • как запрашивать документы с критериями включения, пересечения и близости.

Теперь вы можете больше поиграть с этим, например, интегрировать это в приложение, которое предоставляет данные в некоторый пользовательский интерфейс, или посмотреть, как вы можете использовать геопространственные операторы в конвейере агрегации.

Ссылка: Введение в геопространственную функцию MongoDB от нашего партнера JCG Тугдуала Граля в блоге Tug’s Blog .