MongoDB的地埋空间数据存储、空间索引以及空间查询
一、关于MongoDB
在众多NoSQL数据库,MongoDB是一个优秀的产品。其官方介绍如下:
MongoDB (from "humongous") is a scalable, high-performance, open source, document-oriented database.
看起来,十分诱人!值得说明的是,MongoDB的document是以BSON(Binary JSON)格式存储的,完全支持Schema Free。这对地理空间数据是十分友好的。因为有著名的GeoJSON可供使用。另外OGR库也支持将Geometry类型导出为JSON格式。
本文将尝试使用OGR库把Shapefile导入到MongoDB存储,然后建立空间索引,进行空间查询。
著名的Foursquare使用了MongoDB数据库。
二、开发环境
MongoDB+Python+Pymongo+GDAL for Python
关于MongoDB和Python安装,本文不做介绍。
在继续本文之前,请先启动你的MongoDB服务器。本文默认采用如下服务器参数:
Server:localhost
Post:27017
Database Name:gisdb
三、将shapefile导入到MongoDB
这里我直接提供代码,代码中已经有比较详尽的注释了。代码基本源于“引文1”,只是做了些改动,将MongoDB的Geometry的存储格式由wkt改成json。你可直接复制并运行下面的代码,当然需要修改一下Shapefile路径和MongoDB服务器相关参数。
import os
import sys
import json
from pymongo import json_util
from pymongo.connection import Connection
from progressbar import ProgressBar
from osgeo import ogr
def shp2mongodb(shape_path, mongodb_server, mongodb_port, mongodb_db, mongodb_collection, append, query_filter):
"""Convert a shapefile to a mongodb collection"""
print ‘Converting a shapefile to a mongodb collection ‘
driver = ogr.GetDriverByName(‘ESRI Shapefile’)
print ‘Opening the shapefile %s…’ % shape_path
ds = driver.Open(shape_path, 0)
if ds is None:
print ‘Can not open’, ds
sys.exit(1)
lyr = ds.GetLayer()
totfeats = lyr.GetFeatureCount()
lyr.SetAttributeFilter(query_filter)
print ‘Starting to load %s of %s features in shapefile %s to MongoDB…’ % (lyr.GetFeatureCount(), totfeats, lyr.GetName())
print ‘Opening MongoDB connection to server %s:%i…’ % (mongodb_server, mongodb_port)
connection = Connection(mongodb_server, mongodb_port)
print ‘Getting database %s’ % mongodb_db
db = connection[mongodb_db]
print ‘Getting the collection %s’ % mongodb_collection
collection = db[mongodb_collection]
if append == False:
print ‘Removing features from the collection…’
collection.remove({})
print ‘Starting loading features…’
# define the progressbar
pbar = ProgressBar(maxval=lyr.GetFeatureCount()).start()
k=0
# iterate the features and access its attributes (including geometry) to store them in MongoDb
feat = lyr.GetNextFeature()
while feat:
mongofeat = {}
geom = feat.GetGeometryRef()
mongogeom = geom.ExportToJson()
# store the geometry data with json format
mongofeat['geom'] = json.loads(mongogeom,object_hook=json_util.object_hook)
# iterate the feature’s fields to get its values and store them in MongoDb
feat_defn = lyr.GetLayerDefn()
for i in range(feat_defn.GetFieldCount()):
value = feat.GetField(i)
if isinstance(value, str):
value = unicode(value, "gb2312")
field = feat.GetFieldDefnRef(i)
fieldname = field.GetName()
mongofeat[fieldname] = value
# insert the feature in the collection
collection.insert(mongofeat)
feat.Destroy()
feat = lyr.GetNextFeature()
k = k + 1
pbar.update(k)
pbar.finish()
print ‘%s features loaded in MongoDb from shapefile.’ % lyr.GetFeatureCount()
input_shape = ‘/home/evan/data/map/res4_4m/XianCh_point.shp’
mongodb_server = ‘localhost’
mongodb_port = 27017
mongodb_db = ‘gisdb’
mongodb_collection = ‘xqpoint’
filter = ”
print ‘Importing data to mongodb…’
shp2mongodb(input_shape, mongodb_server, mongodb_port, mongodb_db, mongodb_collection, False, filter)
四、MongoDB中空间数据的存储格式
在MongoDB的Shell中执行:
>db.xqpoint.findOne()
结果如下:
{
"_id" : ObjectId("4dc82e7f7de36a5ceb000000"),
"PERIMETER" : 0,
"NAME" : "漠河县",
"PYNAME" : "Mohe Xian",
"AREA" : 0,
"ADCODE93" : 232723,
"CNTYPT_ID" : 31,
"CNTYPT_" : 1,
"geom" : {
"type" : "Point",
"coordinates" : [
122.53233,
52.968872
]
},
"ID" : 1031,
"PN" : 1,
"CLASS" : "AI"
}
这便是一个document,使用JSON格式,一目了然。其中的"geom"即为Geometry类型的数据,即地理空间数据,也是采用JSON格式存储,这样后续的空间索引与空间查询将十分方便。
MongoDB原生地支持了空间索引与空间查询,这一点比PostgreSQL方便,不再需要使用PostGIS进行空间扩展了。至于性能,我还没测试,在此不敢妄加评论。
五、在MongoDB中建立空间索引
>db.xqpoint.ensureIndex({‘geom.coordinates’:’2d’})
是不是十分简单?其它参数及用法请自行查看MongoDB手册。
六、在MongoDB中进行空间查询
>db.xqpoint.find({"geom.coordinates":[122.53233,52.968872]})
即可查询到上述“莫河县”这个点。当然,像这种精确查询,实际应用并不多。实际应用的空间查询大多为范围查询。MongoDB支持邻域查询($near),和范围查询($within)。
1. 邻域查询($near)
>db.xqpoint.find({"geom.coordinates":{$near:[122,52]}})
上述查询语句查询点[122,52]附近的点,MongoDB默认返回附近的100个点,并按距离排序。你也可以用limit()指定返回的结果数量,如:>db.xqpoint.find({"geom.coordinates":{$near:[122,52]}}).limit(5)
另外,你也可以指定一个最大距离,只查询这个距离内的点。
>db.xqpoint.find({"geom.coordinates":{$near:[122,52],$maxDistance:5}}).limit(5)
MongoDB的find()方法可很方便的进行查询,同时MongoDB也提供了geoNear命令,用于邻域查询。
>db.runCommand({geoNear:"xqpoint",near:[122,56],num:2})
上述语句用于查询[122,56]点附近的点,并只返回2个点。结果如下:
{
"ns" : "gisdb.xqpoint",
"near" : "1110011000111101111100010000011000111101111100010000",
"results" : [
{
"dis" : 3.077515616588727,
"obj" : {
"_id" : ObjectId("4dc82e7f7de36a5ceb000000"),
"PERIMETER" : 0,
"NAME" : "漠河县",
"PYNAME" : "Mohe Xian",
"AREA" : 0,
"ADCODE93" : 232723,
"CNTYPT_ID" : 31,
"CNTYPT_" : 1,
"geom" : {
"type" : "Point",
"coordinates" : [
122.53233,
52.968872
]
},
"ID" : 1031,
"PN" : 1,
"CLASS" : "AI"
}
},
{
"dis" : 4.551319677334594,
"obj" : {
"_id" : ObjectId("4dc82e7f7de36a5ceb000001"),
"PERIMETER" : 0,
"NAME" : "塔河县",
"PYNAME" : "Tahe Xian",
"AREA" : 0,
"ADCODE93" : 232722,
"CNTYPT_ID" : 66,
"CNTYPT_" : 2,
"geom" : {
"type" : "Point",
"coordinates" : [
124.7058,
52.340332
]
},
"ID" : 1059,
"PN" : 1,
"CLASS" : "AI"
}
}
],
"stats" : {
"time" : 0,
"btreelocs" : 85,
"nscanned" : 85,
"objectsLoaded" : 4,
"avgDistance" : 3.814417646961661,
"maxDistance" : 4.551319677334594
},
"ok" : 1
}
当然,我们也可附加条件查询条件,如查询[122,56]附近的且"PYNAME"为"Tahe Xian"的点:
>db.runCommand({geoNear:"xqpoint",near:[122,56],num:2,query:{"PYNAME":"Tahe Xian"})
返回结果如下:
{
"ns" : "gisdb.xqpoint",
"near" : "1110011000111101111100010000011000111101111100010000",
"results" : [
{
"dis" : 4.551319677334594,
"obj" : {
"_id" : ObjectId("4dc82e7f7de36a5ceb000001"),
"PERIMETER" : 0,
"NAME" : "塔河县",
"PYNAME" : "Tahe Xian",
"AREA" : 0,
"ADCODE93" : 232722,
"CNTYPT_ID" : 66,
"CNTYPT_" : 2,
"geom" : {
"type" : "Point",
"coordinates" : [
124.7058,
52.340332
]
},
"ID" : 1059,
"PN" : 1,
"CLASS" : "AI"
}
}
],
"stats" : {
"time" : 45,
"btreelocs" : 2095,
"nscanned" : 2096,
"objectsLoaded" : 2096,
"avgDistance" : 4.551319677334594,
"maxDistance" : 4.551319677334594
},
"ok" : 1
}
2. 范围查询($within)
MongoDB的$within操作符支持的形状有$box(矩形),$center(圆形),$polygon(多边形,包括凹多边形和凸多边形)。所有的范围查询,默认是包含边界的。
查询一个矩形范围,需要指定矩形的左下角和右上角两个坐标点,如下:
> box = [[80,40],[100,50]]
> db.xqpoint.find({"geom.coordinates":{$within:{$box:box}}})
查询一个圆形范围,需要指定圆心坐标和半径,如下:
> center = [80,44]
> radius =5
> db.xqpoint.find({"geom.coordinates":{$within:{$center:[center,radius]}}})
查询一个多边形范围,需要指定多边形的各个顶点,可以通过一个顶点数组或一系列点对象指定。其中,最后一个点是默认与第一个点连接的。如下:
> polygon1 = [[75,35],[80,35],[80,45],[60,40]]
> db.xqpoint.find({"geom.coordinates":{$within:{$polygon:polygon1}}})
或者
> polygon2 = {a:{75,35},b:{80,35},c:{80,45},d:{60,40}}
> db.xqpoint.find({"geom.coordinates":{$within:{$polygon:polygon2}}})
注意:MongoDB 1.9及以上版本才支持多边形范围查询。
P.S. MongoDB还支持复合索引,球面模型(可简单理解为投影吧),多位置文档(Multi-location Documents,即一个文档中包括多个Geometry),可参见“引文2”或MongoDB手册。
七、参考资料
引文1:http://www.paolocorti.net/2009/12/06/using-mongodb-to-store-geographic-data/
引文2:http://www.mongodb.org/display/DOCS/Geospatial+Indexing
MongoDB的地埋空间数据存储、空间索引以及空间查询的更多相关文章
- MongoDB 支持地理空间数据存储
MongoDB 支持地理空间数据存储 官方文档 https://docs.mongodb.com/manual/geospatial-queries/ MongoDB 支持对于地理空间数据的查询操作. ...
- ArcGIS Engine空间查询功能的实现(QueryFilterClass+SpatialFilterClass)
地图中包含大量的信息,为了快速地了解所需信息,必须借助为空间数据专门编写的空间查询功能. 空间查询主要有两种类型: 基于属性的查询,也称为属性查询. 基于空间位置的查询,也称为空间查询. 查询类的基本 ...
- MongoDB地理空间数据存储及检索
目录 1.存入地理数据 GeoJSON数据存入 1.Ponit 点数据 2.LineString 线数据(多段线) 3. Polygon 多边形数据 4.MultiPoint多点.MultiLineS ...
- MongoDB系列五(地理空间索引与查询).
一.经纬度表示方式 MongoDB 中对经纬度的存储有着自己的一套规范(主要是为了可以在该字段上建立地理空间索引).包括两种方式,分别是 Legacy Coordinate Pairs (这个词实在不 ...
- Neo4J空间数据存储
1.Neo4j Spatial 简介 1.1Neo4j Spatial概念 Neo4j Spatial项目是图数据库Neo4j的一个插件,它通过将空间数据映射到图模型(graph model),它将对 ...
- 浅析MongoDB数据库的海量数据存储应用
[摘要]当今已进入大数据时代,特别是大规模互联网web2.0应用不断发展及云计算所需要的海量存储和海量计算发展,传统的关系型数据库已无法满足这方面的需求.随着NoSQL数据库的不断发展和成熟,可以较好 ...
- SQL Server 2008空间数据应用系列八:基于Bing Maps(Silverlight)的空间数据存储
原文:SQL Server 2008空间数据应用系列八:基于Bing Maps(Silverlight)的空间数据存储 友情提示,您阅读本篇博文的先决条件如下: 1.本文示例基于Microsoft S ...
- 利用Mongodb做地理空间查询
MongoDB 是一个基于分布式文件存储的数据库.由C++语言编写.旨在为WEB应用提供可扩展的高性能数据存储解决方案. 前言 在移动开发中,经常会用到定位的功能,例如美团.饿了么.猫眼电影等的app ...
- Oracle获取干净的建表DDL语句,不含其它存储、表空间、段属性
早上一个同事资讯怎么获取到建表语句而且是不带存储那种SQL.Oracle自己提供了一个函数DBMS_METADATA.GET_DDL,但是获取到的建表语句含有存储.表空间.以及一些其他段的属性.如图: ...
随机推荐
- Java简单文件传输 socket简单文件传输示例
服务器端代码: import java.io.*; import java.net.*; /** * Created with IntelliJ IDEA. * User: HYY * Date: 1 ...
- Uva 12361 File Retrieval 后缀数组+并查集
题意:有F个单词,1 <= F <=60 , 长度<=10^4, 每次可以输入一个字符串,所有包含该字串的单词会形成一个集合. 问最多能形成多少个不同的集合.集合不能为空. 分析:用 ...
- 运行edX Devstack
前一篇文章,我们安装完成了edX Devstack,本文将介绍edX Devstack的常用命令 6.1. 连接到 Devstack Virtual Machine 为了连接到Devstack vir ...
- Android 关于HttpClient上传中文乱码的解决办法
使用过HttpClient的人都知道可以通过addTextBody方法来添加要上传的文本信息,但是,如果要上传中文的话,或还有中文名称的文件会出现乱码的问题,解决办法其实很简单: 第一步:设置Mult ...
- 【HDOJ】1241 Oil Deposits
经典的BFS. #include <stdio.h> #include <string.h> #define MAXNUM 105 #define MAXROW 105 #de ...
- 结构体page_cur_t
/** Type of the index page */ typedef byte page_t; /** Index page cursor */ typedef struct page_cur_ ...
- PHP数组排列
一.先看最简单的情况.有两个数组: $arr1 = array(1,9,5);$arr2 = array(6,2,4); array_multisort($arr1,$arr2); print_r($ ...
- C#.NET 打印连续纸高度动态变化(基于长江支流的金质打印通)
问题是这样的,打印机使用的是卷筒的连续纸,要打印的内容因为数据行数不同,高度会有变化.这时如果能在打印时动态改变纸张大小(其实只改变高度即可)当然是最好的选择. 我使用了网上久负盛名的[长江支流]的“ ...
- LoadRunner 录制IE 8卡死
LoadRunner11录制脚本,对IE的版本是有所限制的,它只是支持IE 8 以下版本,其中包括IE 8,高出IE 8版本Loadrunner是不支持的. 我遇到LoadRunner录制脚本时IE8 ...
- object c小代码——日期篇
1.判断两个日期是否是同一天,不要求小时,分钟要一样 用法 NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier: ...