Если у меня есть документ в форме:
{
pos: { lat: 0, lon: 30 }
}
в коллекции пользователей (пожалуйста, притворяйтесь, что это настоящие lat/lon:-))
Каков правильный способ получить все значения в пределах определенного радиуса: 50 миль?
Если у меня есть документ в форме:
{
pos: { lat: 0, lon: 30 }
}
в коллекции пользователей (пожалуйста, притворяйтесь, что это настоящие lat/lon:-))
Каков правильный способ получить все значения в пределах определенного радиуса: 50 миль?
Это трехэтапный процесс.
2dsphere
.$geoWithin
и $centerSphere
.Чтобы выполнить геопространственные запросы, вам необходимо изменить структуру документа, чтобы она соответствовала точке GeoJSON. Это выглядит так.
loc : {
type : "Point",
coordinates : [lng, lat]
}
Пример кода для перевода вашей коллекции в формат точки.
// sample setup code.
// use test;
// db.positions.drop();
// db.positions.insert({
// pos : {
// lat : 0,
// lon : 30
// }
// });
db.positions.find().forEach(function (doc) {
var point = {
_id : doc._id,
loc : {
type : "Point",
coordinates : [doc.pos.lon, doc.pos.lat]
}
};
db.positions.update(doc, point);
});
db.positions.find().pretty();
Впоследствии вы можете использовать операторы $geoWithin
и $near
в ваших запросах, как показано ниже.
var createLandmarkDoc = function (name, lng, lat) {
return {
name : name,
loc : {
type : "Point",
coordinates : [lng, lat]
}
};
};
var addNewLandmark = function(name, lng, lat){
db.landmarks.insert(createLandmarkDoc(name, lng, lat));
};
db.landmarks.drop();
// Step 1: Add points.
addNewLandmark("Washington DC", 38.8993487, -77.0145665);
addNewLandmark("White House", 38.9024593, -77.0388266);
addNewLandmark("Library of Congress", 38.888684, -77.0047189);
addNewLandmark("Patuxent Research Refuge", 39.0391718, -76.8216182);
addNewLandmark("The Pentagon", 38.871857, -77.056267);
addNewLandmark("Massachusetts Institute of Technology", 42.360091, -71.09416);
// Step 2: Create index
db.landmarks.ensureIndex({
loc : "2dsphere"
});
var milesToRadian = function(miles){
var earthRadiusInMiles = 3959;
return miles / earthRadiusInMiles;
};
var landmark = db.landmarks.findOne({name: "Washington DC"});
var query = {
"loc" : {
$geoWithin : {
$centerSphere : [landmark.loc.coordinates, milesToRadian(5) ]
}
}
};
// Step 3: Query points.
db.landmarks.find(query).pretty();
{
"_id" : ObjectId("540e70c96033ed0d2d9694fa"),
"name" : "Washington DC",
"loc" : {
"type" : "Point",
"coordinates" : [
38.8993487,
-77.0145665
]
}
}
{
"_id" : ObjectId("540e70c96033ed0d2d9694fc"),
"name" : "Library of Congress",
"loc" : {
"type" : "Point",
"coordinates" : [
38.888684,
-77.0047189
]
}
}
{
"_id" : ObjectId("540e70c96033ed0d2d9694fb"),
"name" : "White House",
"loc" : {
"type" : "Point",
"coordinates" : [
38.9024593,
-77.0388266
]
}
}
{
"_id" : ObjectId("540e70c96033ed0d2d9694fe"),
"name" : "The Pentagon",
"loc" : {
"type" : "Point",
"coordinates" : [
38.871857,
-77.056267
]
}
}
Дополнительная информация:
В основном вы используете $geoWithin:
с $centerSphere:
для коллекции с индексом 2dsphere. Но давайте пойдем шаг за шагом.
Сценарий: давайте представим, что пользователь просматривает отели в своем районе... Он хочет отобразить отели, которые находятся в 50-мильном диапазоне от его текущего местоположения, а ваше приложение базы данных заполнено данными об отелях. Возможно, вы бы использовали какой-нибудь MAP API для отображения их на карте (если вы хотите проверить ссылки внизу), но для этого примера мы просто хотим получить данные об отелях в радиусе 50 миль от местоположения отелей. в вашей базе данных MongoDB.
* Возможно, вы захотите отредактировать
coordinates
в соответствии с вашими поблизости, если вы хотите, чтобы этот код работал без каких-либо изменений в радиус расстояние.
// Your 'hotels' collection could look something like this:
db.hotels.insertMany([
{
'name': 'Golebiewski',
'address': 'Poland, Mikolajki',
'location': { type: "Point", coordinates: [ 40, 5.000001 ] },
'tel': '000 000 000'
},
{
'name': 'Baltazar',
'address': 'Poland, Pultusk 36',
'location': { type: "Point", coordinates: [ 40, 5.000002 ] },
'tel': '000 000 000'
},
{
'name': 'Zamek',
'address': 'Poland, Pultusk',
'location': { type: "Point", coordinates: [ 40, 5.000003 ] },
'tel': '000 000 000'
},
])
Затем индексируйте location
с помощью 2dsphere
db.places.createIndex( { location : "2dsphere" } )
Сначала подготовьте свои инструменты:
// custom function - convert miles to radian
let milesToRadian = function(miles){
var earthRadiusInMiles = 3963;
return miles / earthRadiusInMiles;
};
// or custom function - convert km to radian
let kmToRadian = function(miles){
var earthRadiusInMiles = 6378;
return miles / earthRadiusInMiles;
};
// custom function - returns array with current user geolocation points - [latitude, longitude]
function getUserLocation() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function (position) {
let geoPoints = [position.coords.latitude, position.coords.longitude];
console.log(geoPoints);
return geoPoints;
});
} else {
console.log("Geolocation is not supported by this browser.");
}
}
Теперь используйте новые инструменты для создания запроса к геопространственным данным MongoDB.
var query = {'location' : {$geoWithin: { $centerSphere: getUserLocation(), milesToRadian(50) ]}}}
// MongoDB example query could look something like:
// {'location': {$geoWithin: { $centerSphere: [ [ 40.000000001, 5.000000001 ], 0.012616704516780217 ]}}}
db.hotels.find(query);
MongoDB возвращает список документов (отелей) в радиусе 50 миль от местоположения пользователя. Просто помните, что в вашем документе должна быть реализована структура GeoJSON для хранения местоположения, и он должен быть индексом 2dsphere. Результат будет отсортирован по расстоянию от пользователя - ASC.
{ "_id" : ObjectId("5d3130f810050424d26836d6"), "name" : "Golebiewski", "address" : "Poland, Mikolajki", "location" : { "type" : "Point", "coordinates" : [ 40, 5.000001 ] }, "tel" : "000 000 000" }
{ "_id" : ObjectId("5d3130f810050424d26836d7"), "name" : "Baltazar", "address" : "Poland, Pultusk 36", "location" : { "type" : "Point", "coordinates" : [ 40, 5.000002 ] }, "tel" : "000 000 000" }
{ "_id" : ObjectId("5d3130f810050424d26836d8"), "name" : "Zamek", "address" : "Poland, Pultusk", "location" : { "type" : "Point", "coordinates" : [ 40, 5.000003 ] }, "tel" : "000 000 000" }
MongoDB - геопространственные запросы: https://docs.mongodb.com/manual/geospatial-queries/ https://www.youtube.com/watch?v=3xP-gzh2ck0
Получите ваши данные на карту. Здесь у вас есть несколько действительно интересных учебников для этого.
Откройте карту улиц с помощью MapBox API: https://docs.mapbox.com/help/tutorials/building-a-store-locator/
Карты Google с Google JavaScript API: https://developers.google.com/maps/documentation/javascript/marker-clustering
Если вы хотите преобразовать адрес в точки геокодирования, вы можете использовать Nominatim графический интерфейс или Nominatim API
Например, найдите "Poland Pultusk" в браузере или запросите ответ json, добавив к строковому параметру &format=json
https://nominatim.openstreetmap.org/search.php?q=Poland+Pultusk https://nominatim.openstreetmap.org/search.php?q=Poland+Pultusk&format=json
Ps. MapBox бесплатен до 50 тыс. Загрузок в месяц, затем $ 5/1000 API-вызовов. Google дает вам 200 долларов ежемесячного бесплатного использования кредита, что составляет 28 тысяч бесплатные вызовы API, затем 7 $/1000 вызовов API. Подберите свое оружие и получайте удовольствие:>.