一、关于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. Android 使用MediaStore.Images和 Cursor查询本地图片和图片缩略图

    先看一个实例: String[] projection = { MediaStore.Images.Thumbnails._ID ,MediaStore.Images.Thumbnails.DATA} ...

  2. ptr_fun学习笔记

    ptr_fun是将一个普通的函数适配成一个functor,添加上argument type和result type等类型, 其实现如下(例子里面是binary_function,unary_funct ...

  3. 【原创】FPGA开发手记(三) PS/2键盘

    以下内容均以Xilinx的Nexys3作为开发板   1.PS/2键盘简介 虽然Nexys3开发板是利用USB接口搭载键盘,但是其原理与PS/2键盘完全相同,现在就仅以PS/2键盘为例讲解如何将键盘搭 ...

  4. Android开发之异步消息处理机制AsyncTask

    转自:Android AsyncTask完全解析,带你从源码的角度彻底理解 另外一篇比较详细的博文:http://blog.csdn.net/liuhe688/article/details/6532 ...

  5. 纯CSS实现侧边栏/分栏高度自动相等

    by zhangxinxu from http://www.zhangxinxu.com本文地址:http://www.zhangxinxu.com/wordpress/?p=694 一.为何要分栏高 ...

  6. android WebViewClient的方法解释

    1.在点击请求的是链接是才会调用,重写此方法返回true表明点击网页里面的链接还是在当前的webview里跳转,不跳到浏览器那边. public boolean shouldOverrideUrlLo ...

  7. 在DDMS中查看网络使用详情

    在Android 4.0设置中的“流量使用情况”允许长期统计每个App如何使用网络资源.从4.0.3开始,配合最新发布的DDMS r17(在ADT r17 插件中有集成),您可以实时的在DDMS中查看 ...

  8. HDU-4515 小Q系列故事——世界上最遥远的距离

    小Q系列故事——世界上最遥远的距离 Time Limit: 500/200 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others) ...

  9. Git提交引用和引用日志

    转载自:https://github.com/geeeeeeeeek/git-recipes/wiki/5.5-Git%E6%8F%90%E4%BA%A4%E5%BC%95%E7%94%A8%E5%9 ...

  10. POJ3107--Godfather(树的重心)

    vector建图被卡了..改为链式前向星500ms过的..差了四倍多?... 表示不太会用链表建图啊..自己试着写的,没看模板..嗯..果然错了..落了一句话orz 树的重心就是找到一个树中一个点,其 ...