一、关于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的地埋空间数据存储、空间索引以及空间查询的更多相关文章

  1. MongoDB 支持地理空间数据存储

    MongoDB 支持地理空间数据存储 官方文档 https://docs.mongodb.com/manual/geospatial-queries/ MongoDB 支持对于地理空间数据的查询操作. ...

  2. ArcGIS Engine空间查询功能的实现(QueryFilterClass+SpatialFilterClass)

    地图中包含大量的信息,为了快速地了解所需信息,必须借助为空间数据专门编写的空间查询功能. 空间查询主要有两种类型: 基于属性的查询,也称为属性查询. 基于空间位置的查询,也称为空间查询. 查询类的基本 ...

  3. MongoDB地理空间数据存储及检索

    目录 1.存入地理数据 GeoJSON数据存入 1.Ponit 点数据 2.LineString 线数据(多段线) 3. Polygon 多边形数据 4.MultiPoint多点.MultiLineS ...

  4. MongoDB系列五(地理空间索引与查询).

    一.经纬度表示方式 MongoDB 中对经纬度的存储有着自己的一套规范(主要是为了可以在该字段上建立地理空间索引).包括两种方式,分别是 Legacy Coordinate Pairs (这个词实在不 ...

  5. Neo4J空间数据存储

    1.Neo4j Spatial 简介 1.1Neo4j Spatial概念 Neo4j Spatial项目是图数据库Neo4j的一个插件,它通过将空间数据映射到图模型(graph model),它将对 ...

  6. 浅析MongoDB数据库的海量数据存储应用

    [摘要]当今已进入大数据时代,特别是大规模互联网web2.0应用不断发展及云计算所需要的海量存储和海量计算发展,传统的关系型数据库已无法满足这方面的需求.随着NoSQL数据库的不断发展和成熟,可以较好 ...

  7. SQL Server 2008空间数据应用系列八:基于Bing Maps(Silverlight)的空间数据存储

    原文:SQL Server 2008空间数据应用系列八:基于Bing Maps(Silverlight)的空间数据存储 友情提示,您阅读本篇博文的先决条件如下: 1.本文示例基于Microsoft S ...

  8. 利用Mongodb做地理空间查询

    MongoDB 是一个基于分布式文件存储的数据库.由C++语言编写.旨在为WEB应用提供可扩展的高性能数据存储解决方案. 前言 在移动开发中,经常会用到定位的功能,例如美团.饿了么.猫眼电影等的app ...

  9. Oracle获取干净的建表DDL语句,不含其它存储、表空间、段属性

    早上一个同事资讯怎么获取到建表语句而且是不带存储那种SQL.Oracle自己提供了一个函数DBMS_METADATA.GET_DDL,但是获取到的建表语句含有存储.表空间.以及一些其他段的属性.如图: ...

随机推荐

  1. c++学习之旅-Cygwin+Eclipse ide for c++

    一,cygwin下载完毕后配置系统环境片两path指向cygwin/bin 二,eclipse设置 2.1 设置工作目录的cygwin映射 cygwin/d ->d:\ 2.2设置编译 下面新建 ...

  2. UNICODE并没有提供对诸如Braille, Cherokee, Ethiopic, Khmer, Mongolian, Hmong, Tai Lu, Tai Mau文字的支持

    UNICODE支持欧洲.非洲.中东.亚洲(包括统一标准的东亚象形汉字和韩国象形文字).但是,UNICODE并没有提供对诸如Braille, Cherokee, Ethiopic, Khmer, Mon ...

  3. 坚果云创业团队访谈:我们 DIY 云存储(不要过度关注竞争对手,尤其当我们还是小公司的时候)

    坚果云(http://jianguoyun.com/)是一款用于多平台文件同步.备份和交换的云存储工具,立志于提供“便捷,安全”的服务.坚果云自去年年初启动内测,至今年三月初刚刚正式发布.近日我们拜访 ...

  4. 上海CEC大收购(包括华大九天)

    紫光收购展讯.锐迪科后,上海开始通过扶植CEC培育新势力,CEC已经收购上海澜起,即将收购amlogic.Ominivision,还在与marvell眉来眼去,此外华大九天已经移植上海,加上之前的上海 ...

  5. Android TabActivity与Activity之间的动画跳转(主要Tabhost中跳转出来的动画效果解决)

    首先,要说的是ActivityA到ActivityB的切换这个相对简单,只要overridePendingTransition(In,out). 这里不就说了.但是这里要说名的ActivityA不能T ...

  6. USACO4.13Fence Loops(无向图最小环)

    最近脑子有点乱 老是不想清楚就啪啪的敲 敲完之后一看 咦..样例都过不去 仔细一想 这样不对啊 刚开始就写了一SPFA 最后发现边跟点的关系没处理好 删了..写dfs..还是没转化好 开始搜解题方法 ...

  7. 结构体dfield_t

    /* SQL data field struct */ typedef struct dfield_struct dfield_t; /** Structure for an SQL data fie ...

  8. bzoj2756

    看到相邻格子都+1的操作一下就想到黑白染色了相邻格子都+1说明不管怎么弄,黑格子的总和和白格子总和的差总是定值这里首先要注意,最后不一定变成的是所有元素中的最大值,可能比它大比如 1 2 2     ...

  9. 提取WORD中的所有InlineShape图片并保存成文件

    InlineShape表示的类型其实不只是图片,还包括OLE, ACTIVE X等. 下面是MSDN中的定义:Represents an object in the text layer of a d ...

  10. 解决:javah 无法访问引用Android对象的问题

    无法访问android.view.View 是没有引入android.jar包 javah的参数中 有一个-bootclasspath参数 让他指向android.jar包 例如 javah -jni ...