MongoDB中空间数据的存储和操作

本文使用官方C# Driver,实现在MongoDB中存储,查询空间数据(矢量)

空间数据的存储

本例中,从一个矢量文件(shapefile格式)中读取矢量要素空间信息以及属性表,并写入到MongoDB中去,其中读取shapefile文件以及将空间信息转成json的功能通过Ogr库实现

 //打开MongoDB的Collection
            MongoDatabase db = server.GetDatabase("aa");
            MongoCollection colSheng = db.GetCollection("sheng");
            //使用Ogr库打开Shapefile文件
            DataSource ds = Ogr.Open(@"c:\temp\sheng.shp", 0);
            Layer lyr = ds.GetLayerByIndex(0);
            //读取要素数量和字段数量
            int feaCount = lyr.GetFeatureCount(0);
            int fieldCount = lyr.GetLayerDefn().GetFieldCount();
            //读取所有字段名
            List<string> fieldNames  =new List<string>();
            for (int i = 0; i < fieldCount; i++)
            {
                fieldNames.Add(lyr.GetLayerDefn().GetFieldDefn(i).GetName());
            }
            //循环将所有要素添加到MongoDB中
            for (int i = 0; i < feaCount; i++)
            {
                //使用Ogr库将矢量要素的空间信息转成Json格式
                Feature fea = lyr.GetFeature(i);
                Geometry geo = fea.GetGeometryRef();
                string json = geo.ExportToJson(null);

                BsonDocument doc = new BsonDocument();

                //将Json格式的空间信息存到Collection中
                //BsonValue bs = BsonValue.Create(json);                  //这种方法是不可以的,添加到库里之后无法使用空间查询语句查询
                BsonValue bs2 = BsonDocument.Parse(json);               //这种方法才是正确的
                //doc.Add(new BsonElement("geom", bs));
                doc.Add(new BsonElement("geo",bs2));
                //通过循环将所有字段的属性信息存入Collection中
                for (int j = 0; j < fieldCount; j++)
                {
                    string tmpFieldVal = fea.GetFieldAsString(j);
                    doc.Add(new BsonElement(fieldNames[j],tmpFieldVal));
                }
                var res  = colSheng.Insert<BsonDocument>(doc);
            }

然后,可以查看一下存储到MongoDB中的矢量数据是什么样的

在命令行中输入:

> db.sheng.find().limit(1)

结果为

{ "_id" : ObjectId("5371bf4e1dbba31914224563"), "geo" : { "type" : "Polygon", "coordinates" : [ [ [ 89.8496, 14.093 ], [ 90.3933, 14.004 ], [ 90.2708, 13.4708 ], [ 89.7284, 13.5597 ], [ 89.8496, 14.093 ] ] ] }, "pyname" : "sx", "boxtype" : "inter", "date" : "2012/6/5 12:41:42" }

可以看到名称为geo的这个Field,里边存的就是矢量要素的坐标信息

空间查询与空间索引

可用的空间操作包括geointersect,geowithin,near等,参考http://docs.mongodb.org/manual/reference/operator/query-geospatial/

这里使用geointersect为例说明一下:

 //获取Collection
            MongoDatabase db = server.GetDatabase("aa");
            MongoCollection colSheng = db.GetCollection("sheng");

            //定义一个查询框或查询多边形
            var poly = GeoJson.Polygon<GeoJson2DCoordinates>(
                GeoJson.Position(100, 20),
                GeoJson.Position(110, 20),
                GeoJson.Position(110, 40),
                GeoJson.Position(100, 40),
                GeoJson.Position(100, 20));
            //以这个查询多边形为条件定义一条查询语句
            var queryFilter2 = Query.GeoIntersects("geo", poly);
            //进行查询,输出MongoCursor
            cur = colSheng.FindAs<BsonDocument>(queryFilter2).SetFields( "pyname", "date");
            //获取结果
            var res = cur.ToArray();
            for (int i = 0; i < res.Count(); i++)
            {
                BsonDocument tmpDoc = res.ElementAt(i);
                //do something you want
            }

关于空间索引,可参考http://docs.mongodb.org/manual/applications/geospatial-indexes/

这里不详细说了

空间查询运算的问题:

在使用GeoIntersect进行空间查询时,遇到了查询结果与ArcGIS不一致的情况,详细看了一下,像是MongoDB的一个BUG(目前使用的是2.6.0版本)

具体信息如下(在命令行中操作):

Collection中的坐标

> db.test.find()
{ "_id" : ObjectId("535884771dbba31858ad2101"), "geo" : { "type" : "Polygon", "coordinates" : [ [ [ 96.722, 38.755 ], [ 97.3482, 38.6922 ], [ 97.1674, 38.0752 ], [ 96.5474, 38.1383 ], [ 96.722, 38.755 ] ] ] } }

使用的查询语句

> db.test.find({ "geo" : { "$geoIntersects" : { "$geometry" : { "type" : "Polygon", "coordinates" : [[[91.0, 33.0], [102.0, 33.0], [102.0, 38.0], [91.0, 38.0], [91.0, 33.0]]] } } } })

查询结果:

{ "_id" : ObjectId("535884771dbba31858ad2101"), "geo" : { "type" : "Polygon", "coordinates" : [ [ [ 96.722, 38.755 ], [ 97.3482, 38.6922 ], [ 97.1674, 38.0752 ], [ 96.5474, 38.1383 ], [ 96.722, 38.755 ] ] ] } }

但可以看到,collection中只有一条记录,且该记录所有点的Y坐标均大于38.0,为什么查询结果里,这条记录与语句中的Box相交呢。。。很奇怪

因为有这样的问题,所以还不放心直接将空间查询用于实际应用,而是通过一种变通的方法进行简单的空间查询,测试后发现,可能是由于空间索引的问题,这种方式查询比自带的GeoIntersects方法要快

大致思路为:为每一条记录均生成一个最小外接矩形,得到其xmax,xmin,ymax,ymin四个边界值,用数值的形式保存至Collection中,每次进行空间查询时,首先通过最小外接矩形进行一次筛选,判断这些最小外接矩形与查询语句中多边形的最小外接矩形之间的关系,如果相交,那么进行第二步判断,通过Ogr组件判断实际的多边形是否相交,返回最后结果

首先是生成最小外接矩形的代码:

//获取Collection
            MongoDatabase db = server.GetDatabase("aa");
            MongoCollection colsheng= db.GetCollection("sheng");
            //查询所有记录
            var cur = colsheng.FindAllAs<BsonDocument>();
            long totalCount = cur.Count();
            //遍历所有记录
            for (int i = 0; i <= totalCount/1000; i++)
            {
                if (i * 1000 >= totalCount) continue;
                int skip = i * 1000;
                var cur2 = cur.Clone<BsonDocument>().SetSkip(skip).SetLimit(1000);
                var lst = cur2.ToArray();
                for (int j = 0; j < lst.Count(); j++)
                {
                    //获取一条记录对应的BsonDocument
                    BsonDocument doc = lst[j];
                        var id = doc["_id"];                    //该记录对应的ID
                        BsonDocument geo = doc["geo"].ToBsonDocument();
                        string geostr = geo[1].ToString();        //该记录对应空间信息的Json字符串
                        List<double> coords = GetCoordLstFromString(geostr);        //解析Json串,获得所有点的坐标(这里子函数就省略了)
                        double xmin = 181, xmax = -181, ymin = 91, ymax = -91;        //四个边界值,由于图层为经纬度,所以初值设为这些值
                        //计算最大最小值
                        for (int k = 0; k < coords.Count; k++)
                        {
                            if (k % 2 == 0)
                            {
                                if (coords[k] < xmin) xmin = coords[k];
                                if (coords[k] > xmax) xmax = coords[k];
                            }
                            else
                            {
                                if (coords[k] < ymin) ymin = coords[k];
                                if (coords[k] > ymax) ymax = coords[k];
                            }
                        }
                        //将最大最小值写入Collection
                        var tmpQuery = Query.EQ("_id", id);
                        var tmpUpdate = MongoDB.Driver.Builders.Update.Set("xmax", xmax);
                        var tmpres = col02c.Update(tmpQuery, tmpUpdate);
                        tmpUpdate = MongoDB.Driver.Builders.Update.Set("xmin", xmin);
                        tmpres = col02c.Update(tmpQuery, tmpUpdate);
                        tmpUpdate = MongoDB.Driver.Builders.Update.Set("ymax", ymax);
                        tmpres = col02c.Update(tmpQuery, tmpUpdate);
                        tmpUpdate = MongoDB.Driver.Builders.Update.Set("ymin", ymin);
                        tmpres = col02c.Update(tmpQuery, tmpUpdate);
                    }
                }

然后是查询的代码:

//获取Collection
            MongoDatabase db = server.GetDatabase("aa");
            MongoCollection colSheng = db.GetCollection("zy02c");
            //第一步,通过四边界筛选,
            var query = Query.And(Query.GT("xmax", 91.0), Query.LT("xmin", 102.0), Query.GT("ymax", 33.0), Query.LT("ymin", 38.0));
            var cur = colSheng.FindAs<BsonDocument>(query);
            //定义第二空间运算时的条件多边形(Ogr格式的定义)
            Geometry queryGeoLR = new Geometry(wkbGeometryType.wkbLinearRing);
            queryGeoLR.AddPoint(91.0, 33.0,0);
            queryGeoLR.AddPoint(102.0, 33.0,0);
            queryGeoLR.AddPoint(102.0, 38.0,0);
            queryGeoLR.AddPoint(91.0, 38.0,0);
            queryGeoLR.AddPoint(91.0, 33.0,0);
            Geometry queryGeo = new Geometry(wkbGeometryType.wkbPolygon);
            queryGeo.AddGeometry(queryGeoLR);
            //循环查询到的结果
            var lst = cur.ToArray();
            for (int i = lst.Length-1; i >=0; i--)
            {
                //获取当前记录对应的BsonDocument
                BsonDocument doc = lst[i];
                var id = doc["_id"];            //当前记录的ID
                BsonDocument geo = doc["geo"].ToBsonDocument();
                string geostr = geo[1].ToString();        //当前记录对应空间信息的Json字符串

                //通过Json串获取坐标值,并生成对应的Geometry对象
                List<double> coords = GetCoordLstFromString(geostr);
                Geometry resGeoLR = new Geometry(wkbGeometryType.wkbLinearRing);
                for (int j = 0; j < coords.Count / 2; j++)
                {
                    resGeoLR.AddPoint_2D(coords[j], coords[j + 1]);
                }
                resGeoLR.AddPoint_2D(coords[0], coords[1]);
                Geometry resGeo = new Geometry(wkbGeometryType.wkbPolygon);
                resGeo.AddGeometry(resGeoLR);
                //判断是该Geometry与条件多边形是否相交
                if (resGeo.Intersects(queryGeo))
                {
                        //do something
                }
            }

MongoDB中空间数据的存储和操作,布布扣,bubuko.com

时间: 2024-08-04 22:15:32

MongoDB中空间数据的存储和操作的相关文章

MongoDB中数组类型相关的操作

概述 在MongoDB的模式中,我们经常将一些数据存储到数组类型中,即我们常见的嵌套模式设计的一种实现方式.数组的这种设计实现方式在关系数据库中是没有或者说不常见的.所以,通过本文我们来梳理一下MongoDB的数组的相关操作.关于数组的操作可以分成两类,一类是数组操作符,另一个是数组运算修饰符.  数组操作符 操作符 实现功能 $ 根据查询选择器定位要更新的文档 $push 添加值到数组中 $pushAll 添加数组到一个数组中.(将被$rach取代) $addToSet 添加值到数组中,重复了

在MongoDB中使用JOIN操作

SQL与NoSQL最大的不同之一就是不支持JOIN,在传统的数据库中,SQL JOIN子句允许你使用普通的字段,在两个或者是更多表中的组合表中的每行数据.例如,如果你有表books和publishers,你可以像下面这样写命令: SELECT book.title, publisher.name FROM book LEFT JOIN book.publisher_id ON publisher.id; 换句话说,book表中的publisher_id字段引用了publishers表中的id字典

MongoDB中的聚合操作

根据MongoDB的文档描述,在MongoDB的聚合操作中,有以下五个聚合命令. 其中,count.distinct和group会提供很基本的功能,至于其他的高级聚合功能(sum.average.max.min),就需要通过mapReduce来实现了. 在MongoDB2.2版本以后,引入了新的聚合框架(聚合管道,aggregation pipeline ,使用aggregate命令),是一种基于管道概念的数据聚合操作. Name Description count Counts the num

php操作mongodb中的ISODate格式日期

mongodb 中数据记录的日期格式为"dateCreated" : ISODate("2011-12-20T07:22:50.836Z")经过翻阅php官网中的mongodb部分得知,要操作mongodb中的日期须要使用以下关键语句:$start = new MongoDate(strtotime('-1 day'));$end = new MongoDate(time());$resultObject =$db->user->find(array(&

mongodb复制集里查看主从操作日志oplog

MongoDB的replica set架构是通过一个日志来存储写操作的,这个日志就叫做 oplog .oplog.rs 是一个固定长度的 Capped Collection,它存在于local数据库中,用于记录replicaSets操作日志.在默认情况下,对于64位的MongoDB,oplog是比较大的,可以达到5%的磁盘空间,oplog的大小是可以通过mongod的参数 “ -oplogSize”来改变oplog的日志大小. oplog内容样例: > use local > show col

golang解析mongodb中的ISODate类型

在golang中可以使用time.Time数据类型来保存mongodb中的ISODate时间. g type Model struct {     uploadDate time.Time `bson:"uploadDate"` } m := Model{} if err := c.Find(nil).Select({"_id": 0, "uploadDate": 1}).One(&m); err != nil {     fmt.Pri

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

[摘要]当今已进入大数据时代,特别是大规模互联网web2.0应用不断发展及云计算所需要的海量存储和海量计算发展,传统的关系型数据库已无法满足这方面的需求.随着NoSQL数据库的不断发展和成熟,可以较好地解决海量存储和海量计算方面的应用需求.本文重点描述作为NoSQL之一MongoDB数据库在海量数据存储方面的应用. 1 引言NoSQL,全称是“Not Only Sql”,指的是非关系型的数据库.这类数据库主要有这些特点:非关系型的.分布式.开源的.水平可扩展的.原始目的是为了大规模web应用,这

PHP 从 MongoDb 中查询数据怎么样实现

一.软件环境(版本非必须) php v5.6 扩展:MongoDB nginx v1.11 mongodb v3.2 note: 必须安装MongoDB扩展 二.连接 $client = new MongoClient($server, $option); $server 变量是个字符串,描述要连接的服务器 mongodb://[username:[email protected]]host1[:port1][,host2[:port2:],...]/db 其中必要的是: username 数据

【MongoDB】MongoDB数据库之海量存储机制

GridFS是一种将大型文件存储在Mongodb数据库中的文件规范. 一.如何实现海量存储 由于Mongodb中的bson对象大小是限制的,所以gridfs规范提供了一种透明的机制,可以将一个大文件分成多个较小的文件.这样的机制允许有效地保存大文件的对象,特别是哪些巨大的文件,比如视频,高清图片:该规范指定了一个将文件分块的标准,每个文件都在集合对象中保存一个元数据对象,一个或多个块对象可被组合在一个chunk块集合中.mongodb中主要是利用mongofiles工具. Grifs使用两个表来