空间地理检索
2dsphere索引
尽管MySQL
能做的MongoDB也都能做,例如,它也可以创建完全固定属性的表
。
但我个人认为,MongoDB一个非常有特色的地方就是它在地理空间位置的存储和查询上的极高效率。
通俗地来解释它的原理,就是MongoDB把世界地图铺成一个平面,然后用经纬度把它划分成很多个小格子,然后通过数学公式来找需要的那些格子所在的经纬度。

比如,以紫禁城为例,MongoDB会建立一个以(0, 0)、(200, 0)、(200, 2000)和(0, 200)这四个点围起来一个大格子,那么,在这个范围内的所有的位置坐标都可以非常容易地算出来。
把这个方法扩大到全世界范围,也是一样的。

距离单位
mongodb包含三种不同的距离单位:米(meters)、平面单位(flat units,指经纬度的“一度”)、弧度(radians,指扇形角的度数)。
不同的存储方式或者查询方式使用的单位不一样。
如果数据在db中是以GeoJSON存储,则所有查询的单位都是米。
如果查询条件是
$near
,则查询单位是平面单位“度”。假定系统传进来的参数是“米”,由于$near
接受的是平面单位“度”,那么在拼接语句的时候需要将“米”转换成“度”:米/1000/111.12,意思是将“米”转换成“公里”,再转换成“度”。如果不进行转换就进行查询,例如传入100米会被认为是100度,差距有多大,可想而知。
其他情况,需要进行类似转换。
查询命令 | 距离单位说明 |
---|---|
$near | 度 |
$nearSphere | 弧度 |
$center | 度 |
$centerSphere | 弧度 |
$polygon | 度 |
$geoNear | 度或弧度,通过spherical参数决定 |
米、平面单位“度”、弧度转换方式。
1米 = 1/1000/111.12度,其中1000表示转换成公里,111.12表示公里转换成度,这是近似值。
1米 = 1/1000/6378137弧度, 其中1000表示转换成公里,6378137表示公里转换成弧度,以赤道为准,也是近似值。
MongoDB既然在空间处理上这么厉害,那时间上呢?很遗憾,它不适用于处理时间敏感的应用场景。
示例代码
下面是各种不同方式进行位置查询的示范。
城市位置点集合。
package com.mongodb;
/**
* MongoDB supports 2-dimensional geospatial indexes. You can watch MongoSF (May 2011) presentation to understand it at a basic level.
* In this article, I will help you quickly write Geospatial queries described in above presentation using Java programming language.
* I assume you have MongoDB server up and running on your machine. Source code for this article is available at this Github project.
* A repository of Gegraphical Places
* Below Java class stores major cities in California, US in the form of String array. We’ll use this array in the next program.
*
*/
public class Places {
public static final String[] cities = new String[] {
"Palos Verdes Estates", "Los Altos Hills", "Hillsborough", "Monte Sereno", "Villa Park", "Palo Alto", "Belvedere",
"Los Altos", "Rolling Hills", "Montecito", "Piedmont", "Foster City", "Yorba Linda", "Mission Canyon",
"Saratoga", "Orinda", "Manhattan Beach", "Pleasanton", "Imperial", "Goleta", "Tiburon", "Tustin Foothills",
"Rancho Palos Verdes", "Mountain View", "La Habra Heights", "Newport Beach", "Toro Canyon", "Agoura Hills",
"Redondo Beach", "Menlo Park", "Mill Valley", "Indian Wells", "Moraga", "Ross", "La Palma", "Kensington",
"Hermosa Beach", "Thousand Oaks", "Belmont", "Rolling Hills Estates", "Loyola", "Summerland",
"Santa Monica", "Rossmoor", "Irvine", "Lafayette", "Laguna Niguel", "Torrance", "Fairbanks Ranch",
"Cupertino", "Santa Barbara", "Portola Valley", "Woodside", "San Ramon", "Santa Ynez", "Emerald Lake Hills",
"Angwin", "El Segundo", "Orange", "West Menlo Park", "West Bishop", "Ladera Heights", "Huntington Beach",
"Atherton", "Coronado", "Danville", "Diamond Bar", "Rancho Santa Fe", "Chino Hills", "Clayton", "Walnut",
"San Anselmo", "Solvang", "Cerritos", "Blackhawk-Camino Tassajara", "Highlands-Baywood Park",
"Fountain Valley", "Westlake Village", "Sunnyvale", "Poway", "Del Monte Forest", "Brea", "San Carlos",
"Los Gatos", "Rancho Santa Margarita", "Camarillo", "Cypress", "Newport Coast", "San Joaquin Hills",
"Folsom", "Arroyo Grande", "Malibu", "Sausalito", "Del Rio", "Green Valley", "Mission Viejo", "Aliso Viejo",
"Stanford", "Encinitas", "Rancho Mirage"
};
}
通过多种不同方式查询城市的位置。
package com.mongodb;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DBAddress;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.Mongo;
import com.mongodb.MongoException;
/**
* Example code for Geospatial queries in MongoDB
*
* Geospatial Queries Program
* This program run following methods in that order:
* 1. addPlaces() – Adds above listed 100 California cities on a 10X10 plane. Data is stored in MongoDB collection named “places”.
* 2. findWithinCircle() – Finds all cities in a circle whose center is (5,5) and are within a radius of 1 unit (of our imaginary plane)
* 3. findWithinBox() – Finds all cities within a rectangle formed between coordinates (4,4) and (6,6)
* 4. FindWithinPolygon() – Finds all cities within a polygon (triangle in this case) formed from 3 coordinates.
* 5. findCenterSphere() – Same as 2, but query is spherical in nature.
* 6. findNear() – Finds all cities near (4,4) having a maximum distance of 2 unit.
* 7. findNearSphere() – Same as above, but query is spherical in nature.
*
* MongoDB Console (after running program)
amresh@ubuntu:/usr/local/mongodb-linux-i686-1.8.1/bin$ ./mongo
MongoDB shell version: 1.8.1
connecting to: test
> use geospatial;
switched to db geospatial
> db.places.find();
{ "_id" : ObjectId("50747ebf44ae3dfd6b8eb40a"), "name" : "Palos Verdes Estates", "loc" : [ 0, 0 ] }
{ "_id" : ObjectId("50747ebf44ae3dfd6b8eb40b"), "name" : "Los Altos Hills", "loc" : [ 1, 0 ] }
{ "_id" : ObjectId("50747ebf44ae3dfd6b8eb40c"), "name" : "Hillsborough", "loc" : [ 2, 0 ] }
{ "_id" : ObjectId("50747ebf44ae3dfd6b8eb40d"), "name" : "Monte Sereno", "loc" : [ 3, 0 ] }
{ "_id" : ObjectId("50747ebf44ae3dfd6b8eb40e"), "name" : "Villa Park", "loc" : [ 4, 0 ] }
{ "_id" : ObjectId("50747ebf44ae3dfd6b8eb40f"), "name" : "Palo Alto", "loc" : [ 5, 0 ] }
{ "_id" : ObjectId("50747ebf44ae3dfd6b8eb410"), "name" : "Belvedere", "loc" : [ 6, 0 ] }
{ "_id" : ObjectId("50747ebf44ae3dfd6b8eb411"), "name" : "Los Altos", "loc" : [ 7, 0 ] }
{ "_id" : ObjectId("50747ebf44ae3dfd6b8eb412"), "name" : "Rolling Hills", "loc" : [ 8, 0 ] }
{ "_id" : ObjectId("50747ebf44ae3dfd6b8eb413"), "name" : "Montecito", "loc" : [ 9, 0 ] }
{ "_id" : ObjectId("50747ebf44ae3dfd6b8eb414"), "name" : "Piedmont", "loc" : [ 0, 1 ] }
{ "_id" : ObjectId("50747ebf44ae3dfd6b8eb415"), "name" : "Foster City", "loc" : [ 1, 1 ] }
{ "_id" : ObjectId("50747ebf44ae3dfd6b8eb416"), "name" : "Yorba Linda", "loc" : [ 2, 1 ] }
{ "_id" : ObjectId("50747ebf44ae3dfd6b8eb417"), "name" : "Mission Canyon", "loc" : [ 3, 1 ] }
{ "_id" : ObjectId("50747ebf44ae3dfd6b8eb418"), "name" : "Saratoga", "loc" : [ 4, 1 ] }
{ "_id" : ObjectId("50747ebf44ae3dfd6b8eb419"), "name" : "Orinda", "loc" : [ 5, 1 ] }
{ "_id" : ObjectId("50747ebf44ae3dfd6b8eb41a"), "name" : "Manhattan Beach", "loc" : [ 6, 1 ] }
{ "_id" : ObjectId("50747ebf44ae3dfd6b8eb41b"), "name" : "Pleasanton", "loc" : [ 7, 1 ] }
{ "_id" : ObjectId("50747ebf44ae3dfd6b8eb41c"), "name" : "Imperial", "loc" : [ 8, 1 ] }
{ "_id" : ObjectId("50747ebf44ae3dfd6b8eb41d"), "name" : "Goleta", "loc" : [ 9, 1 ] }
has more
*
*/
public class GeospatialExample {
public static final String dbName = "geospatial";
public static final String host = "127.0.0.1";
public static final int port = 27017;
public static final String collectionName = "places";
public static final String indexName = "geospatialIdx";
Mongo mongo;
DBCollection collection;
private Mongo getMongo() throws UnknownHostException {
try {
mongo = new Mongo(new DBAddress(host, port, dbName));
} catch (MongoException e) {
e.printStackTrace();
}
return mongo;
}
public static void main(String[] args) {
new GeospatialExample().runExample();
}
private void runExample() {
try {
collection = getMongo().getDB(dbName).getCollection(collectionName);
} catch (UnknownHostException e) {
e.printStackTrace();
}
collection.createIndex(new BasicDBObject("loc", "2d"), indexName);
addPlaces();
findWithinCircle();
findWithinBox();
findWithinPolygon();
findCenterSphere();
findNear();
findNearSphere();
}
private void findWithinCircle() {
System.out.println("findWithinCircle\n----------------------\n");
List circle = new ArrayList();
circle.add(new double[] { 5, 5 }); // Centre of circle
circle.add(1); // Radius
BasicDBObject query = new BasicDBObject("loc", new BasicDBObject("$within", new BasicDBObject("$center", circle)));
printOutputs(query);
}
private void findWithinBox() {
System.out.println("findWithinBox\n----------------------\n");
List box = new ArrayList();
box.add(new double[] { 4, 4 }); // Starting coordinate
box.add(new double[] { 6, 6 }); // Ending coordinate
BasicDBObject query = new BasicDBObject("loc", new BasicDBObject("$within", new BasicDBObject("$box", box)));
printOutputs(query);
}
private void findWithinPolygon() {
System.out.println("findWithinPolygon\n----------------------\n");
List polygon = new ArrayList();
polygon.add(new double[] { 3, 3 }); // Starting coordinate
polygon.add(new double[] { 8, 3 }); // Ending coordinate
polygon.add(new double[] { 6, 7 }); // Ending coordinate
BasicDBObject query = new BasicDBObject("loc", new BasicDBObject("$within", new BasicDBObject("$polygon", polygon)));
printOutputs(query);
}
private void findNear() {
System.out.println("findNear\n----------------------\n");
BasicDBObject filter = new BasicDBObject("$near", new double[] { 4, 4 });
filter.put("$maxDistance", 2);
BasicDBObject query = new BasicDBObject("loc", filter);
printOutputs(query);
}
private void findNearSphere() {
System.out.println("findNearSphere\n----------------------\n");
BasicDBObject filter = new BasicDBObject("$nearSphere", new double[] { 5, 5 });
filter.put("$maxDistance", 0.06);
// Radius of the earth: 3959.8728
BasicDBObject query = new BasicDBObject("loc", filter);
printOutputs(query);
}
private void findCenterSphere() {
System.out.println("findCenterSphere\n----------------------\n");
List circle = new ArrayList();
circle.add(new double[] { 5, 5 }); // Centre of circle
circle.add(0.06); // Radius
BasicDBObject query = new BasicDBObject("loc", new BasicDBObject("$within", new BasicDBObject("$centerSphere", circle)));
printOutputs(query);
}
public void printOutputs(BasicDBObject query) {
DBCursor cursor = collection.find(query);
List<BasicDBList> outputs = new ArrayList<BasicDBList>();
while (cursor.hasNext()) {
DBObject result = cursor.next();
System.out.println(result.get("name") + "--->" + result.get("loc"));
outputs.add((BasicDBList) result.get("loc"));
}
for (int y = 9; y >= 0; y--) {
String s = "";
for (int x = 0; x < 10; x++) {
boolean found = false;
for (BasicDBList obj : outputs) {
double xVal = (Double) obj.get(0);
double yVal = (Double) obj.get(1);
if (yVal == y && xVal == x) {
found = true;
}
}
if (found) {
s = s + " @";
} else {
s = s + " +";
}
}
System.out.println(s);
}
}
private void addPlaces() {
System.out.println("Adding places...");
for (int i = 0; i < 100; i++) {
double x = i % 10;
double y = Math.floor(i / 10);
addPlace(collection, Places.cities[i], new double[] { x, y });
}
System.out.println("All places added");
}
private void addPlace(DBCollection collection, String name, final double[] location) {
final BasicDBObject place = new BasicDBObject();
place.put("name", name);
place.put("loc", location);
collection.insert(place);
}
}
感谢支持
更多内容,请移步《超级个体》。