Этот пост представляет собой быстрое и простое введение в геопространственную функцию 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
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] ]} |
Таким образом, используя тот же подход, вы сможете создавать 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 ] ] ] } ]} |
Примечание. Вы можете, если хотите, протестировать / визуализировать эти документы 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, чтобы преобразовать их в нужный формат.
Давайте установим набор данных:
- Загрузите его отсюда
- Распакуйте файл geo.zip
- Восстановите данные в вашем экземпляре mongoDB, используя следующую команду mongorestore geo.zip
MongoDB позволяет приложениям выполнять следующие типы запросов к геопространственным данным:
- включение
- пересечение
- близость
Очевидно, вы сможете использовать все остальные операторы в дополнение к геопространственным. Давайте теперь посмотрим на некоторые конкретные примеры.
включение
Найти все аэропорты в Калифорнии. Для этого вам нужно получить Калифорнийское местоположение (Polygon) и использовать команду $ geoWithin в запросе. Из оболочки это будет выглядеть так:
|
01
02
03
04
05
06
07
08
09
10
|
use geovar 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» и ищет в коллекции аэропортов все аэропорты, которые находятся в этих многоугольниках. Это похоже на следующее изображение на карте:
Вы можете использовать любые другие функции или критерии запроса, например, вы можете ограничить запрос международным аэропортом, отсортированным только по имени:
|
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" } |
Как и раньше, оператору $ 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 . |




