c#使用MongoDB开发LBS应用
MongoDB下载地址
http://www.mongodb.org/downloads
.NET驱动
https://github.com/mongodb/mongo-csharp-driver
MongoDB常用命令:
成功启动MongoDB后,再打开一个命令行窗口输入mongo,就可以进行数据库的一些操作。
输入help可以看到基本操作命令:
show dbs:显示数据库列表
show collections:显示当前数据库中的集合(类似关系数据库中的表)
show users:显示用户
use <db name>:切换当前数据库,这和MS-SQL里面的意思一样
db.help():显示数据库操作命令,里面有很多的命令
db.foo.help():显示集合操作命令,同样有很多的命令,foo指的是当前数据库下,一个叫foo的集合,并非真正意义上的命令
db.foo.find():对于当前数据库中的foo集合进行数据查找(由于没有条件,会列出所有数据)
db.foo.find( { a : 1 } ):对于当前数据库中的foo集合进行查找,条件是数据中有一个属性叫a,且a的值为1
理位置索引:
MongoDB地理位置索引常用的有两种。
2d 平面坐标索引,适用于基于平面的坐标计算。也支持球面距离计算,不过官方推荐使用2dsphere索引。
2dsphere 几何球体索引,适用于球面几何运算。
关于两个坐标之间的距离,官方推荐2dsphere:
MongoDB supports rudimentary spherical queries on flat 2d indexes for legacy reasons. In general, spherical calculations should use a 2dsphere index, as described in 2dsphere Indexes.
不过,只要坐标跨度不太大(比如几百几千公里),这两个索引计算出的距离相差几乎可以忽略不计。
建立索引:
> db.places.ensureIndex({'coordinate':'2d'})
> db.places.ensureIndex({'coordinate':'2dsphere'})
查询方式:
查询方式分三种情况:
Inclusion。范围查询,如百度地图“视野内搜索”。
Inetersection。交集查询。不常用。
Proximity。周边查询,如“附近500内的餐厅”。
而查询坐标参数则分两种:
坐标对(经纬度)根据查询命令的不同,$maxDistance距离单位可能是 弧度 和 平面单位(经纬度的“度”):
db.<collection>.find( { <location field> :
{ $nearSphere: [ <x> , <y> ] ,
$maxDistance: <distance in radians>
} } )
GeoJson $maxDistance距离单位默认为米:
db.<collection>.find( { <location field> :
{ $nearSphere :
{ $geometry :
{ type : "Point" ,
coordinates : [ <longitude> , <latitude> ] } ,
$maxDistance : <distance in meters>
} } } )
案例A:附近的人
查询当前坐标附近的目标,由近到远排列。
可以通过$near或$nearSphere,这两个方法类似,但默认情况下所用到的索引和距离单位不同。
查询方式:
> db.places.find({'coordinate':{$near: [121.4905, 31.2646]}})
> db.places.find({'coordinate':{$nearSphere: [121.4905, 31.2646]}})
查询结果:
{
"_id" : 115,
"coordinate" : {
"longitude" : 121.4915,
"latitude" : 31.25933
},
"title" : "仅售148元,市场价298元的星程上服假日酒店全日房一间入住一天,
节假日通用,精致生活,品质享受",
"address" : "虹口区天水路90号"
}
…(100条)
上述查询坐标[121.4905, 31.2646]附近的100个点,从最近到最远排序。
默认返回100条数据,也可以用limit()指定结果数量,如
> db.places.find({'coordinate':{$near: [121.4905, 31.2646]}}).limit(2)
指定最大距离 $maxDistance
> db.places.find({'coordinate':{$near: [121.4905, 31.2646], $maxDistance:2}})
结合Symfony2进行演示:
这里用near,默认以度为单位,公里数除以111(关于该距离单位后文有详细解释)。
/**
* @Route("/near", name="near")
* @Template()
*/
public function nearAction(){
$longitude = (float)$this->getRequest()->get('lon',121.4905);
$latitude = (float)$this->getRequest()->get('lat',31.2646);
//2km
$max = (float)$this->getRequest()->get('max', 2);
$places = $this->getPlaceRepository()->createQueryBuilder()
->field('coordinate')->near($longitude, $latitude)
->maxDistance($max/111)
->getQuery()->toarray();
return compact('places','max','longitude','latitude');
}
通过 domain.dev/near 访问,效果如下:
longitude: xxx, latitude: xxx为当前位置,我们在地图上显示了周边100条目标记录
案例B:区域内搜索
MongoDB中的范围搜索(Inclusion)主要用$geoWithin这个命令,它又细分为3种不同类型,如下:
$box 矩形
$center 圆(平面),$centerSphere圆(球面)
$polygon 多边形
$center和$centerSphere在小范围内的应用几乎没差别(除非这个圆半径几百上千公里)。
下面我们介绍一下这三种查询的案例。
矩形区域
这个比较常用,比如百度地图的视野内搜索(矩形)、或搜狗地图的“拉框搜索”
定义一个矩形范围,我们需要指定两个坐标,在MongoDB的查询方式如下:
> db.places.find(
{
coordinate : {
$geoWithin : {
$box :[ [ 121.44, 31.25 ] , [ 121.5005, 31.2846 ] ]
}
}
}
)
查询结果:
{
"_id" : 90472,
"title" : "【鲁迅公园】仅售99元!酒店门市价288元的上海虹元商务宾馆客房一间入住一天(需持本人有效
身份证件办理登记):大床房/标准房(2选1)!不含早餐!不涉外!2012年9月29日-10月6日
不可使用拉手券!可延迟退房至14:00!",
"address" : "上海市虹口区柳营路8号",
"coordinate" : {
"longitude" : 121.47,
"latitude" : 31.27145
}
}
Symfony2演示代码:
指定两个坐标点
/**
* @Route("/box", name="box")
* @Template()
*/
public function boxAction(){
$request = $this->getRequest();
$longitude = (float)$request->get('lon',121.462035);
$latitude = (float)$request->get('lat',31.237641);
$longitude2 = (float)$request->get('lon2',121.522098);
$latitude2 = (float)$request->get('lat2',31.215284);
$places = $this->getPlaceRepository()->createQueryBuilder()
->field('coordinate')->withinBox($longitude, $latitude,
$longitude2, $latitude2)
->getQuery()->toarray();
return compact('places','longitude','latitude', 'longitude2', 'latitude2');
}
圆形区域
应用场景有:地图搜索租房信息
查询以某坐标为圆心,指定半径的圆内的数据。
前面已提到,圆形区域搜索分为$center和$centerSphere这两种类型,它们的区别主要在于支持的索引和默认距离单位不同。
2d索引能同时支持$center和$centerSphere,2dsphere索引支持$centerSphere。关于距离单位,$center默认是度,$centerSphere默认距离是弧度。
查询方式如下:
> db.places.find({'coordinate':{$geoWithin:{$centerSphere:[ [121.4905, 31.2646] ,
0.6/111] }}})
或
> db.places.find({'coordinate':{$geoWithin:{$centerSphere:[ [121.4905, 31.2646] ,
0.6/6371] }}})
查询结果
{
"_id" : 115,
"coordinate" : {
"longitude" : 121.4915,
"latitude" : 31.25933
},
"title" : "仅售148元,市场价298元的星程上服假日酒店全日房一间入住一天,节假日通用,
精致生活,品质享受",
"address" : "虹口区天水路90号"
}
Symfony2演示代码:
指定圆心坐标和半径
/**
* @Route("/center", name="center")
* @Template()
*/
public function centerAction(){
$request = $this->getRequest();
$longitude = (float)$request->get('lon',121.4905);
$latitude = (float)$request->get('lat',31.2646);
//10km
$max = (float)$request->get('max', 10);
$places = $this->getPlaceRepository()->createQueryBuilder()
->field('coordinate')->withinCenter($longitude, $latitude, $max/111)
->getQuery()->toarray();
return compact('places','max','longitude','latitude');
}
以longitude: xxx,latitude: xxx为中心点,半径10km的圆内
多边形
复杂区域内的查询,这个应用场景比较少见。指定至少3个坐标点,查询方式如下(五边形):
> db.places.find( { coordinate : { $geoWithin : { $polygon : [
[121.45183 , 31.243816] ,
[121.533181, 31.24344] ,
[121.535049, 31.208983] ,
[121.448955, 31.214913] ,
[121.440619, 31.228748]
] } } } )
查询结果
{
"_id" : 90078,
"title" : "仅售9.9元,市场价38元的燕太太燕窝单人甜品餐,用耐心守候一盅炖品,用爱滋补一生情谊",
"address" : "河南南路489号香港名都购物广场1F125燕太太燕窝",
"coordinate" : {
"longitude" : 121.48912,
"latitude" : 31.22355
}
}
Symfony2演示代码(这里为方便,直接写死了5个坐标点):
/**
* @Route("/polygon", name="polygon")
* @Template()
*/
public function polygonAction(){
$points = [];
$points[] = [121.45183,31.243816];
$points[] = [121.533181,31.24344];
$points[] = [121.535049,31.208983];
$points[] = [121.448955,31.214913];
$points[] = [121.440619,31.228748];
$sumlon = $sumlat = 0;
foreach($points as $p){
$sumlon += $p[0];
$sumlat += $p[1];
}
$center = [$sumlon/count($points), $sumlat/count($points)];
$places = $this->getPlaceRepository()->createQueryBuilder()
->field('coordinate')->withinPolygon($points[0], $points[1], $points[2],
$points[3], $points[4])
->getQuery()->toarray();
return compact('places','points', 'center');
}
案例C:附近的餐厅
我们假设需要以当前坐标为原点,查询附近指定范围内的餐厅,并直接显示距离。
这个需求用前面提到的$near是可以实现的,但是距离需要二次计算。这里我们用$geoNear这个命令查询。
$geoNear与$near功能类似,但提供更多功能和返回更多信息,官方文档是这么解释的:
The geoNear command provides an alternative to the $near operator. In addition to the functionality of $near, geoNear returns additional diagnostic information.
查询方式如下(关于下面的示例用到了distanceMultipler函数,后文会详细解释):
> db.runCommand( { geoNear: "places", near: [ 121.4905, 31.2646 ], spherical: true,
maxDistance:1/6371, num:2 })
{
"ns" : "mongo_test.places",
"near" : "1110001100111100001011010110010111001000110011111101",
"results" : [
{
"dis" : 0.00009318095248858048,
"obj" : {
"_id" : 115,
"coordinate" : {
"longitude" : 121.4915,
"latitude" : 31.25933
},
"title" : "仅售148元,市场价298元的星程上服假日酒店全日房一间入住一天,
节假日通用,精致生活,品质享受",
"address" : "虹口区天水路90号"
}
},
{
"dis" : 0.00010610660597329082,
"obj" : {
"_id" : 465,
"coordinate" : {
"longitude" : 121.48406,
"latitude" : 31.26202
},
"title" : "【四川北路】热烈庆祝康骏会馆成立8周年!仅售169元!市场价399元的
康骏会馆四川北路一店(仅限3星级技师)全身精油按摩一人次!全程约90分钟!
男女不限!仅限四川北路一店使用,非本市所有门店通用!拉手券消费仅限每日19:00前!
健康有道,骏越万里!",
"address" : "虹口区四川北路1896号-1904号201室"
}
}
],
"stats" : {
"time" : 0,
"btreelocs" : 0,
"nscanned" : 18,
"objectsLoaded" : 12,
"avgDistance" : 0.00009964377923093564,
"maxDistance" : 0.0001064199324957278
},
"ok" : 1
}
可以看到返回了很多详细信息,如查询时间、返回数量、最大距离、平均距离等。
另外,results里面直接返回了距离目标点的距离dis。
Symfony2演示代码:
/**
* @Route("/distance", name="distance")
* @Template()
*/
public function distanceAction(){
$longitude = (float)$this->getRequest()->get('lon',121.4905);
$latitude = (float)$this->getRequest()->get('lat',31.2646);
//2km
$max = (float)$this->getRequest()->get('max', 2);
$places = $this->getPlaceRepository()->createQueryBuilder()
->field('coordinate')
->geoNear($longitude, $latitude)
->spherical(true)
->distanceMultiplier(6371)
->maxDistance($max/6371)
->limit(100)
->getQuery()
->execute()
->toArray();
return compact('places','longitude', 'latitude', 'max');
}
更新于:1个月前
相关文章
- .NET9 C# 13 有哪些新特性?
- C#中的String和StringBuilder的区别
- .NET C#中的IEnumerable和IEnumerator的区别
- C# Const 和 ReadOnly的区别
- C# 使用Barrier进行多线程同步
- C#发送邮件代码简洁示例(附源码下载)
- C# Word转换成Pdf的方法
- hprose for C#使用教程
- c#实现与Java无差异的GZip压缩和GZip解压缩
- .NET Core c#使用SkiaSharp压缩裁切图片去除水印
- c# decimal保留2位小数 并向下舍入
- .NET Core c#使用SkiaSharp压缩图片
- C#复制文件到指定文件夹
- c#获取枚举的Description
- c#读取pdf里的表格
- c# HttpClient下载图片
- c#使用HtmlAgilityPack编辑html并保存
- c#获取文件夹所有文件列表
- c# int数值转enum枚举
- c#获取枚举的描述Description和摘要Summary