##环境
Django,Mysql,空间数据是通过Google地图和高德地图进行采集的
$ mysql -V |
##需求描述
mysql中存储了一些地理空间点类型的数,要进行周边查询。
##MySQL空间相关的局限性
MySQL空间扩展的功能仅支持包络框相关的操作(MySQL称之为最小边框,或简称为 MBR)。也就是说,MySQL符合OGC标准。
目前,MySQL没有实现Contains,Crosses,Disjoint,Intersects,Overlaps,Touches函数,可以通过MBR来实现同样效果操作。
也就是说,在MySQL进行如contains类似的空间查询时,可以通过bbcontains来实现同样效果的操作。
注意:
只有MyISAM引擎的MySQL表才真正的支持空间索引(R-trees)。也就是说,当你要使用MySQL提供的空间扩展时,你要在快速查询空间数据和数据的完整性之间做一个选择 - MyISAM的表不支持事务和外键约束。 |
##数据库配置
空间表引擎 Engine:InnoDB
###创建数据库
mysql>GRANT ALL PRIVILEGES ON *.* TO project_test@localhost IDENTIFIED BY 'project_test' WITH GRANT OPTION; |
第一句增加了一个 project_test 用户授权通过本地机(localhost)访问,密码“project_test”。
第二句则是授与 project_test 用户从任何其它主机发起的访问(通配符%)。
###配置数据源
'default': { |
##数据模型的构建
from django.contrib.gis.db import models as gismodels |
Django Geographic framework 1.7
GeoDjango打算做世界级的地理学Web框架。它的目标是尽可能方便是的利用强大空间数据构建GIS Web 应用。
###GeoQuerySet API
class GeoQuerySet([model=None])
####空间查询
正如使用QuerySet API时一样,在过滤器链(chaining filters)上加上GeoQuerySet进行筛选。除了通常的字段(Field lookups)查询,它还提供了空间字段GeometryField的查询。
可以在这里查看空间查询介绍
下面Django对不同数据库 空间查询操作支持统计表:
Lookup Type PostGIS Oracle MySQL [7] SpatiaLite
bbcontains X X X
bboverlaps X X X
contained X X X
contains X X X X
contains_properly X
coveredby X X
covers X X
crosses X X
disjoint X X X X
distance_gt X X X
distance_gte X X X
distance_lt X X X
distance_lte X X X
dwithin X X
equals X X X X
exact X X X X
intersects X X X X
overlaps X X X X
relate X X X
same_as X X X X
touches X X X X
within X X X X
left X
right X
overlaps_left X
overlaps_right X
overlaps_above X
overlaps_below X
strictly_above X
strictly_below X
####我这里只关注一下对mysql的空间操作支持
按我们的需要我们选用
within
bbcontains
支持:
PostGIS
,MySQL
,SpatiaLite
查询数据库中空间数据的bbox包含在指定的空间bbox内的数据。
数据库 操作 PostGIS poly ~ geom MySQL MBRContains(poly,geom) SpatiaLite MbrContains(poly,geom)
bboverlaps
支持:
PostGIS
,MySQL
,SpatiaLite
查询数据库中空间数据的bbox与指定的空间bbox相交的数据。
数据库 操作 PostGIS poly && geom MySQL MBROverlops(poly,geom) SpatiaLite MbrOverlops(poly,geom)
contained
支持:
PostGIS
,MySQL
,SpatiaLite
查询数据库中空间数据的bbox完全包含指定的空间bbox的数据。
数据库 操作 PostGIS poly @ geom MySQL MBRWithin(poly,geom) SpatiaLite MbrWithin(poly,geom)
contains
支持:
PostGIS
,Oracle
,MySQL
,SpatiaLite
Example:
Zipcode.objects.filter(poly__contains=geom)
查询数据库中空间数据包含指定的空间图形的数据。
数据库 操作 PostGIS ST_Contains(poly, geom) Oracle SDO_CONTAINS(poly, geom) MySQL MBRContains(poly, geom) SpatiaLite Contains(poly, geom)
disjoint
支持:
PostGIS
,Oracle
,MySQL
,SpatiaLite
Example:
Zipcode.objects.filter(poly__disjoint=geom)
查询数据库中与指定的空间图形相离的空间数据。
数据库 操作 PostGIS ST_Disjoint(poly, geom) Oracle SDO_GEOM.RELATE(poly, geom) MySQL MBRDisjoint(poly, geom) SpatiaLite Disjoint(poly, geom)
equals
支持:
PostGIS
,Oracle
,MySQL
,SpatiaLite
exact,same_as
支持:
PostGIS
,Oracle
,MySQL
,SpatiaLite
intersects
支持:
PostGIS
,Oracle
,MySQL
,SpatiaLite
查询数据库中与指定的空间图形相交的空间数据。
Example:
Zipcode.objects.filter(poly__intersects=geom)
数据库 操作
PostGIS ST_Intersects(poly, geom)
Oracle SDO_OVERLAPBDYINTERSECT(poly, geom)
MySQL MBRIntersects(poly, geom)
SpatiaLite Intersects(poly, geom)
overlaps
支持:
PostGIS
,Oracle
,MySQL
,SpatiaLite
touches
支持:
PostGIS
,Oracle
,MySQL
,SpatiaLite
Example:
Zipcode.objects.filter(poly__touches=geom)
查询与指定的空间几何图形相接的数据。
数据库 操作 PostGIS ST_Touches(poly, geom) Oracle SDO_TOUCH(poly, geom) MySQL MBRTouches(poly, geom) SpatiaLite Touches(poly, geom)
within
支持:
PostGIS
,Oracle
,MySQL
,SpatiaLite
Example:
Zipcode.objects.filter(poly__within=geom)
查询包含在指定的空间几何图形中的数据。
数据库 操作 PostGIS ST_Within(poly, geom) Oracle SDO_INSIDE(poly, geom) MySQL MBRWithin(poly, geom) SpatiaLite Within(poly, geom)
现在知道了要用 within 来查询数据,另一个问题来了,如何生成半径大小为R中心坐标为(x,y)的geom呢。
####创建空间几何图形
可以通过多种方式创建GeosGeometry。第一种方法,就是通过一些参数直接实例化。
- 下面是分别通过WKT,HEX,WKB和GeoJSON方式直接创建 Geometry 的方法:
In [30]: pnt = GEOSGeometry('POINT(5 23)') |
- 另一种方式就是通过特定类型的空间几何对象的构造器来进行创建该类型的Geometry实例
In [34]: from django.contrib.gis.geos import Point |
In [37]: from django.contrib.gis.geos import fromstr,fromfile |
####实现查询周边几何点的功能
通过上面的学习,在Django中实现mysql数据的周边查询只能通过模糊的查询,
我们这里通过构建一个包络框进行模糊查询:
- 构建一个包络框
from django.contrib.gis.geos import (Polygon,Point) |
- 进行within查询
AppPoint.objects.filter(point__within=buffer) |
- 问题
这里给的半径通常是米为km,但是这个构建buffer的方法需要的参数是一个度。
degree=l*180/(math.pi*6371)
##测试方法和数据
def get_point(point,r): |
其中点与点间的距离方法distance在django中解释为:
Returns the distance between the closest points on this Geometry |
下面是测试数据:
b = [[116.27497,39.95708,2573], |
##总结
from django.contrib.gis.geos import (Polygon,Point) |
queryset.extra(select={‘distance_factor’: “SQRT(POWER(ABS(X(point) - “+str(x)+”),2) + POWER(ABS(Y(point) - “+str(y)+”),2))”}).order_by(‘distance_factor’)
|
rps[0].point.distance(rps[1].point)
python: GeometryComponentFilter.cpp:35: virtual void geos::geom::GeometryComponentFilter::filter_ro(const geos::geom::Geometry*): Assertion `0’ failed.
线上直接使用distance时报错。 |
(‘…..python’, 0.0071949078163964595)
(‘self’, 0.0071949078163964595)
故处些最终到终心点的距离使用了 `distance_factor` 来代替。 |
var a = 6378245.0
var ee = 0.00669342162296594323
function out_of_china(lat,lon){
if (lon < 72.004 || lon > 137.8347)
return true
if (lat < 0.8293 || lat > 55.8271)
return true
}
function transformlat(x, y) {
var result = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x))
result += (20.0 * Math.sin(6.0 * Math.PI * x) + 20.0 * Math.sin(2.0 * Math.PI * x)) * 2.0 / 3.0
result += (20.0 * Math.sin(Math.PI * y) + 40.0 * Math.sin(Math.PI / 3.0 * y)) * 2.0 / 3.0
result += (160.0 * Math.sin(Math.PI / 12.0 * y) + 320.0 * Math.sin(Math.PI / 30.0 * y)) * 2.0 / 3.0
return result
}
function transformlon(x, y) {
var result = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x))
result += (20.0 * Math.sin(6.0 * Math.PI * x) + 20.0 * Math.sin(2.0 * Math.PI * x)) * 2.0 / 3.0
result += (20.0 * Math.sin(Math.PI * x) + 40.0 * Math.sin(Math.PI / 3.0 * x)) * 2.0 / 3.0
result += (150.0 * Math.sin(Math.PI / 12.0 * x) + 300.0 * Math.sin(Math.PI / 30.0 * x)) * 2.0 / 3.0
return result
}
function wgs2gcj(wgslat, wgslon) {
if (out_of_china(wgslat, wgslon)) {
return [wgslat, wgslon]
}
var lat = transformlat(wgslon - 105.0, wgslat - 35.0)
var lon = transformlon(wgslon - 105.0, wgslat - 35.0)
var rad_lat = Math.PI / 180.0 * wgslat
var magic = Math.sin(rad_lat)
magic = 1 - ee * magic * magic
var sqrt_magic = Math.sqrt(magic)
lat = (180.0 * lat) / (Math.PI * (a * (1 - ee)) / (magic * sqrt_magic))
lon = (180.0 * lon) / (Math.PI * a * Math.cos(rad_lat) / sqrt_magic)
var chnlat = wgslat + lat
var chnlon = wgslon + lon
return [chnlat, chnlon]
}
##参考
>0. [JAVSCRIPT Math](http://www.w3school.com.cn/jsref/jsref_obj_math.asp)
>1. [MySQL空间数据库–查询点到多点间的最短路径](http://www.javabloger.com/article/mysql-spatial-database.html)
>2. [W3 Geolocation API Specification](http://www.w3.org/TR/geolocation-API/#position_interface)
>3. [关于百度map和高德map,关于map坐标系](http://wangsheng2008love.blog.163.com/blog/static/78201689201461674727642/)
>4. [iOS 火星坐标相关整理及解决方案汇总](http://it.taocms.org/04/507.htm)