Этот пост представляет собой быстрое и простое введение в геопространственную функцию 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 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» и ищет в коллекции аэропортов все аэропорты, которые находятся в этих многоугольниках. Это похоже на следующее изображение на карте:
Вы можете использовать любые другие функции или критерии запроса, например, вы можете ограничить запрос международным аэропортом, отсортированным только по имени:
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 . |