一、关于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. 处理MySQL数据库出现大量Locked的一个案例 (转)

    本文转自:http://blog.itpub.net/7607759/viewspace-696781/ 做为一款轻量级数据库软件,MySQL在使用过程中遇到访问速度慢,或者无法响应这类的问题,解决方 ...

  2. easyui源码翻译1.32--Draggable(拖动)

    前言 使用$.fn.draggable.defaults重写默认值对象.下载该插件翻译源码 源码 /** * jQuery EasyUI 1.3.2 * *翻译:qq 1364386878 --拖动 ...

  3. vcastr2.0插件超级简单使用

                Vcastr2.0简单使用 友情提示:1.蓝色文字为必修改内容.2.#字符后面是解释该代码段的主要内容 1. 引用swfobject.js文件 #public/videoplu ...

  4. MSSQL版本

    (1)661是sql2008 R2的版本号     Microsoft SQL Server 2008 R2 (RTM) - 10.50.1600.1 (Intel X86)   Apr  2 201 ...

  5. Mysql表的七种引擎类型,InnoDB和MyISAM引擎对比区别总结

    InnoDB和MyISAM区别总结 我用MySQL的时候用的是Navicat for MySQL(Navicat for mysql v9.0.15注册码生成器)操作库.表操作的,默认的表就是Inno ...

  6. android & Linux uevent机制

    Linux uevent机制 Uevent是内核通知android有状态变化的一种方法,比如USB线插入.拔出,电池电量变化等等.其本质是内核发送(可以通过socket)一个字符串,应用层(andro ...

  7. 阿里云数加平台——BI报表使用概述和总结

    先声明一点,本人写此文章初衷只为对前段时间的工作做些总结,并做个记录,以备日后查用,此外也顺便与他人分享一下.当然间接上也为阿里云的大数据平台做了个免费广告.以下开始正文. 首先进入数加服务的控制面板 ...

  8. BZOJ_1626_[Usaco2007_Dec]_Building_Roads_修建道路_(Kruskal)

    描述 http://www.lydsy.com/JudgeOnline/problem.php?id=1626 给出\(n\)个点的坐标,其中一些点已经连通,现在要把所有点连通,求修路的最小长度. 分 ...

  9. JAVA与.NET的相互调用——通过Web服务实现相互调用

    JAVA与.NET是现今世界竞争激烈的两大开发媒体,两者语言有很多相似的地方.而在很多大型的开发项目里面,往往需要使用两种语言进行集成开发.而很多的开发人员都会偏向于其中一种语言,在使用集成开发的时候 ...

  10. lihgtoj 1006

    记忆化搜索下即可. #include<cstdio> #include<string> #include<cstring> #include<iostream ...