Postgres空间地理类型POINT POLYGON实现附近的定位和电子围栏功能
需求和背景
在已有大量经纬度坐标点的情况下,给定一组经纬度如何快速定位到附近的POI有哪些?
现在使用经纬度转geohash的算法,将二维的距离运算转换为like前缀匹配。通过比较9位到5位前缀,来获取附近5米到3km之内的坐标,为了寻求更快的定位方法,测试一下postgres的空间类型。
安装插件postgis
先安装了pg-10, 并且是通过yum安装的。导入过repo.
检查插件
yum search postgis
postgis-docs.x86_64 : Extra documentation for PostGIS
postgis-jdbc.x86_64 : The JDBC driver for PostGIS
postgis-utils.x86_64 : The utils for PostGIS
postgis23_10-client.x86_64 : Client tools and their libraries of PostGIS
postgis23_10-devel.x86_64 : Development headers and libraries for PostGIS
postgis23_10-docs.x86_64 : Extra documentation for PostGIS
postgis23_10-utils.x86_64 : The utils for PostGIS
postgis24_10-client.x86_64 : Client tools and their libraries of PostGIS
postgis24_10-debuginfo.x86_64 : Debug information for package postgis24_10
postgis24_10-devel.x86_64 : Development headers and libraries for PostGIS
postgis24_10-docs.x86_64 : Extra documentation for PostGIS
postgis24_10-utils.x86_64 : The utils for PostGIS
postgis.x86_64 : Geographic Information Systems Extensions to PostgreSQL
postgis23_10.x86_64 : Geographic Information Systems Extensions to PostgreSQL
postgis24_10.x86_64 : Geographic Information Systems Extensions to PostgreSQL
安装
yum install postgis.x86_64 postgis24_10.x86_64
系统安装了插件之后,数据库还要继续启用插件才行。
针对数据库启用插件
# 添加空间插件
CREATE EXTENSION postgis;
CREATE EXTENSION postgis_topology;
安装之后,public下会新增一个表spatial_ref_sys。
点POINT类型和距离
点POINT类型的数据结构为POINT(0 0)
,正好可以用作存储经纬度。
表添加POINT类型
AddGeometryColumn
使用函数AddGeometryColumn, 命令行查看函数
\df+ AddGeometryColumn
Synopsis
text AddGeometryColumn(varchar table_name, varchar column_name, integer srid, varchar type, integer dimension, boolean use_typmod=true);
text AddGeometryColumn(varchar schema_name, varchar table_name, varchar column_name, integer srid, varchar type, integer dimension, boolean use_typmod=true);
text AddGeometryColumn(varchar catalog_name, varchar schema_name, varchar table_name, varchar column_name, integer srid, varchar type, integer dimension, boolean use_typmod=true);
添加两个点字段
SELECT AddGeometryColumn ('poi', 'geom_point', 4326, 'POINT', 2);
SELECT AddGeometryColumn ('poi', 'geom_point_26986', 26986, 'POINT', 2);
其中两个重要的坐标体系
- 4326 \ GCS_WGS_1984 \ World Geodetic System (WGS)
- 26986 \ 美国马萨诸塞州地方坐标系(区域坐标系)\ 投影坐标, 平面坐标
直接添加
ALTER TABLE poi ADD COLUMN geom_p_alter geometry(POINT,4326);
添加空间索引
CREATE INDEX idx_point
ON poi
USING gist(geom_point);
插入点
使用函数将文本转换为几何类型: ST_GeomFromText
sdx=# SELECT ST_GeomFromText('POINT(120.377041 36.066019)', 4326);
st_geomfromtext
----------------------------------------------------
0101000020E61000001310937021185E4012F5824F73084240
(1 row)
使用坐标转换函数转换坐标体系:ST_Transform
sdx=# SELECT ST_Transform(ST_GeomFromText('POINT(120.377041 36.066019)', 4326),26986)
st_transform
----------------------------------------------------
01010000206A690000B6A9B046D9615AC162C3613707DD6441
使用函数将几何类型转换为文本描述:ST_AsText
SELECT ST_AsText(ST_GeomFromText('POINT(120.377041 36.066019)', 4326));
st_astext
-----------------------------
POINT(120.377041 36.066019)
(1 row)
插入三个点
update poi set
geom_point=ST_GeomFromText('POINT(121.248642 31.380415)', 4326),
geom_point_26986=ST_Transform(ST_GeomFromText('POINT(121.248642 31.380415)', 4326),26986),
geom_p_alter=ST_GeomFromText('POINT(121.248642 31.380415)', 4326)
WHERE uuid='462745f185a349bbb8454f70d085baae';
SELECT geom_point,geom_point_26986,geom_p_alter from poi WHERE uuid='462745f185a349bbb8454f70d085baae';
geom_point | geom_point_26986 | geom_p_alter
----------------------------------------------------+----------------------------------------------------+--
0101000020E610000085766FC1E94F5E400D1AFA27B858D83F | 01010000206A69000087930146005A5CC1ECE89370F91A6541 | 0101000020E610000085766FC1E94F5E400D1AFA27B858D83F
(1 row)
验证:
SELECT ST_AsText(geom_point),ST_AsText(geom_point_26986),geom_p_alter from s_poi_gaode WHERE uuid='462745f185a349bbb8454f70d085baae';
st_astext | st_astext | geom_p_alter
-----------------------------+-------------------------------------------+----------------------------------
POINT(121.248642 31.380415) | POINT(-7432193.09384621 11065291.5180554) | 0101000020E610000085766FC1E94F5E400D1AFA27B858D83F
(1 row)
批量更新现有的经纬度字段为POINT
update s_poi_gaode set
geom_point=ST_GeomFromText('POINT('||longitude||' ' ||latitude||')', 4326),
geom_point_26986=ST_Transform(ST_GeomFromText('POINT('||longitude||' ' ||latitude||')', 4326),26986);
验证:
SELECT longitude,latitude,ST_AsText(geom_point),ST_AsText(geom_point_26986) from s_poi_gaode WHERE uuid='e3ebbcf15cc545408ac8b22d4df64ca6';
longitude | latitude | st_astext | st_astext
------------+-----------+-----------------------------+------------------------------------------
121.417666 | 31.281433 | POINT(121.417666 31.281433) | POINT(-7448729.03389232 11054385.435284)
(1 row)
其中,需要注意的是,使用pg的字符串拼接符号||
,POINT经纬度之间要留空格。
两个点之间的距离
距离计算函数
ST_Distance
文本转换地理几何类型函数
ST_GeogFromText 。
文本转换为地理几何类型函数
ST_GeographyFromText
计算距离,单位是m的方法
-- 921.37629155
select ST_Distance(ST_GeographyFromText('SRID=4326;POINT(114.017299 22.537126)'),
ST_GeographyFromText('SRID=4326;POINT(114.025919 22.534866)')
);
-- 921.37629155
SELECT ST_Distance(ST_GeomFromText('POINT(114.017299 22.537126)',4326):: geography,
ST_GeomFromText('POINT(114.025919 22.534866)', 4326):: geography
);
-- 920.28519
SELECT ST_DistanceSphere(ST_GeomFromText('POINT(114.017299 22.537126)',4326),
ST_GeomFromText('POINT(114.025919 22.534866)', 4326)
);
-- unit=m 26986 马萨诸塞州 投影平面坐标系 单位m result=972.989337453172
SELECT ST_Distance(
ST_Transform(ST_GeomFromText('POINT(114.017299 22.537126)',4326 ),26986),
ST_Transform(ST_GeomFromText('POINT(114.025919 22.534866)', 4326 ),26986)
);
计算距离,单位是度
# unit=degrees result=0.00891134108875483
SELECT ST_Distance(ST_GeomFromText('POINT(114.017299 22.537126)',4326),
ST_GeomFromText('POINT(114.025919 22.534866)', 4326)
);
关于单位是m的, 前三种的计算结果是正确的。最后一种坐标转换的计算方法,
参考PostGIS 坐标转换(SRID)的边界问题引发的专业知识 - ST_Transform 建议国内不要使用马萨诸塞州的投影平面,会使得距离计算不够准确。
附近5公里内的点
使用函数ST_DWithin 可以计算两个点之间的距离是否在5公里内。
# 计算两个点是否在给定距离内
# 单位米m
SELECT ST_DWithin(
ST_GeographyFromText('SRID=4326;POINT(114.017299 22.537126)'),
ST_GeographyFromText('SRID=4326;POINT(114.025919 22.534866)'),
1000);
# 单位度degrees
SELECT ST_DWithin(
ST_GeomFromText('POINT(114.017299 22.537126)',4326),
ST_GeomFromText('POINT(114.025919 22.534866)', 4326),
0.00811134108875483);
-- 查找给定经纬度5km以内的点
SELECT
uuid,
longitude,
latitude,
ST_DistanceSphere (
geom_point,
ST_GeomFromText ( 'POINT(121.248642 31.380415)', 4326 )) distance
FROM
s_poi_gaode
WHERE
ST_DWithin ( geom_point :: geography, ST_GeomFromText ( 'POINT(121.248642 31.380415)', 4326 ) :: geography, 5000 ) IS TRUE
order by distance desc
LIMIT 30;
通过指定类型geom_point :: geography
,单位变成米, 否则默认距离单位是度。
最近的10个点
SELECT * FROM s_poi_gaode_gps ORDER BY geom_point <-> ST_GeomFromText ( 'POINT(121.248642 31.380415)', 4326 ) LIMIT 10;
速度极快。
面多边形'POLYGON'
添加字段类型
SELECT AddGeometryColumn ('basic_mall_v1', 'geom_fence', 4326, 'POLYGON', 2);
或者
ALTER TABLE basic_mall_v1 ADD COLUMN geom_fence_alter geometry(POLYGON,4326);
添加索引
CREATE INDEX idx_area_fence
ON basic_mall_v1
USING gist(geom_fence);
插入值
使用函数 ST_GeomFromText
SELECT ST_GeomFromText ( 'POLYGON((118.902957 32.085437,118.9041 32.086069,118.904754 32.085219,118.903592 32.084564,118.902957 32.085437))', 4326 );
st_geomfromtext
-----------------------------------------------------------------------
0103000020E610000001000000050000006F2C280CCAB95D40266F8099EF0A404012143FC6DCB95D4087191A4F040B4040363B527DE7B95D40B9FFC874E80A4040583B8A73D4B95D40A0353FFED20A40406F2C280CCAB95D40266F8099EF0A4040
(1 row)
-- 验证
SELECT
ST_AsText(
ST_GeomFromText ( 'POLYGON((118.902957 32.085437,118.9041 32.086069,118.904754 32.085219,118.903592 32.084564,118.902957 32.085437))', 4326 )
)
st_astext
-------------------------------------------------------------------------------------------------------------------
POLYGON((118.902957 32.085437,118.9041 32.086069,118.904754 32.085219,118.903592 32.084564,118.902957 32.085437))
(1 row)
更新到数据库字段
UPDATE basic_mall_v1 SET geom_fence=ST_GeomFromText ( 'POLYGON((118.902957 32.085437,118.9041 32.086069,118.904754 32.085219,118.903592 32.084564,118.902957 32.085437))', 4326 ) WHERE id=1000001;
-- 验证:
SELECT ST_AsText(geom_fence) FROM basic_mall_v1 WHERE id=1000001;
st_astext
-------------------------------------------------------------------------------------------------------------------
POLYGON((118.902957 32.085437,118.9041 32.086069,118.904754 32.085219,118.903592 32.084564,118.902957 32.085437))
(1 row)
实际上,我们原始围栏数据可能是这样的
-- longitude,latitude; longitude,latitude; longitude,latitude;
119.306413,26.131464;119.307575,26.131739;119.30776,26.131224;119.307336,26.131114;119.307438,26.130791;119.306776,26.13059;119.306413,26.131464
需要将这个字段转换成空间类型的围栏字段。
UPDATE basic_mall_v1 SET geom_fence=
ST_GeomFromText(
'POLYGON(('||
replace(
replace(gaode_shape, ',', ' ' ),
';', ',')
||'))'
, 4326)
计算gps附近30m内的围栏
使用函数ST_DWithin 判断一个几何对象是否在另一个的r距离以内:
SELECT
ST_Distance(ST_GeomFromText('POINT(120.731069 30.758984)',4326):: geography,
geom_fence :: geography
) AS distance, id, name
FROM
basic_mall_v1
WHERE
ST_DWithin (
geom_fence :: geography,
ST_GeomFromText ( 'POINT( 120.731069 30.758984)', 4326 ) :: geography,
30
) ORDER BY distance
LIMIT 10;
使用函数boolean ST_Within(geometry A, geometry B); 判断A是否完全在B内部
SELECT
id, name
FROM
basic_mall_v1
WHERE
ST_Within (
ST_GeomFromText('POINT('|| #{longitude} ||' '|| #{latitude} ||')',4326),
geom_fence
)
更多测试见菜鸟末端轨迹 - 电子围栏(解密支撑每天251亿个包裹的数据库)
关于坐标体系
地理坐标系(Geographic coordinate system)
首先理解地理坐标系(Geographic coordinate system),Geographic coordinate system直译为地理坐标系统,是以经纬度为地图的存储单位的。
很明显,Geographic coordinate system是球面坐标系统。我们要将地球上的数字化信息存放到球面坐标系统上,如何进行操作呢?地球是一个不规则的椭球,如何将数据信息以科学的方法存放到椭球上?
这必然要求我们找到这样的一个椭球体。这样的椭球体具有特点:
可以量化计算的。具有长半轴,短半轴,偏心率。
以下几行便是Krasovsky_1940椭球及其相应参数。
Alias:
Abbreviation:
Remarks:
Angular Unit: Degree (0.017453292519943299)
Prime Meridian(起始经度): Greenwich (0.000000000000000000)
Datum(大地基准面): D_Beijing_1954
Spheroid(参考椭球体): Krasovsky_1940
Semimajor Axis: 6378245.000000000000000000
Semiminor Axis: 6356863.018773047300000000
Inverse Flattening: 298.300000000000010000
然而有了这个椭球体以后还不够,还需要一个大地基准面将这个椭球定位。在坐标系统描述中,可以看到有这么一行:
Datum: D_Beijing_1954
表示,大地基准面是D_Beijing_1954。
Projection coordinate system(投影坐标系统)
投影坐标系统,实质上便是平面坐标系统,其地图单位通常为米.
投影的意义:将球面坐标转化为平面坐标的过程便称为投影。
参数
Projection: Gauss_Kruger
Parameters:
False_Easting: 500000.000000
False_Northing: 0.000000
Central_Meridian: 117.000000
Scale_Factor: 1.000000
Latitude_Of_Origin: 0.000000
Linear Unit: Meter (1.000000)
Geographic Coordinate System:
Name: GCS_Beijing_1954
Alias:
Abbreviation:
Remarks:
Angular Unit: Degree (0.017453292519943299)
Prime Meridian: Greenwich (0.000000000000000000)
Datum: D_Beijing_1954
Spheroid: Krasovsky_1940
Semimajor Axis: 6378245.000000000000000000
Semiminor Axis: 6356863.018773047300000000
Inverse Flattening: 298.300000000000010000
参考
Postgres空间地理类型POINT POLYGON实现附近的定位和电子围栏功能的更多相关文章
- Postgres 的 Range 类型
mysql 不支持 Range 类型 零.介绍 1. 适用场景: a.可以用于实现 是否满足薪资需求 的功能 b.可以用于实现 是否符合上线时间 的功能 一.定义 1.类型范围 Postgres Se ...
- Postgres 的 Array 类型
mysql 不支持 Array 类型 一.Postgres 原生SQL 适用场景:可以用于实现贴标签功能 1.定义 CREATE TABLE "Students" ( name V ...
- installscript类型 完成时实现推荐安装其他产品的功能
目前好多软件在安装完成时都有什么 立刻运行.打开网址.推荐安装其他工具等功能 我司领导也追时髦要求了这个功能而且要推荐多个,所以这个功能实现起来就需要自己去写代码了.陆陆续续研究了研究了好长时间,由于 ...
- 一起了解 .Net Foundation 项目 No.6
.Net 基金会中包含有很多优秀的项目,今天就和笔者一起了解一下其中的一些优秀作品吧. 中文介绍 中文介绍内容翻译自英文介绍,主要采用意译.如与原文存在出入,请以原文为准. .NET Micro Fr ...
- 【转】阿里巴巴技术专家杨晓明:基于Hadoop技术进行地理空间分析
转自:http://www.csdn.net/article/2015-01-23/2823687-geographic-space-base-Hadoop [编者按]交通领域正产生着海量的车辆位置点 ...
- MongoDB 支持地理空间数据存储
MongoDB 支持地理空间数据存储 官方文档 https://docs.mongodb.com/manual/geospatial-queries/ MongoDB 支持对于地理空间数据的查询操作. ...
- 地理信息系统 - ArcGIS - 高/低聚类分析工具(High/Low Clustering ---Getis-Ord General G)
前段时间在学习空间统计相关的知识,于是把ArcGIS里Spatial Statistics工具箱里的工具好好研究了一遍,同时也整理了一些笔记上传分享.这一篇先聊一些基础概念,工具介绍篇随后上传. 空间 ...
- 用MapX与C#开发地理信息系统
转:http://www.cnblogs.com/dachie/archive/2010/08/17/1801598.html 第四章 MapX与C#实例... 5 4.1 MapX图层建立... 5 ...
- 千万级数据迁移工具DataX实践和geom类型扩展
## DataX快速入门参考 > 官方https://github.com/alibaba/DataX/blob/master/userGuid.md ## 环境要求 > Linux JD ...
随机推荐
- Windows Mysql安装
一.从https://dev.mysql.com/downloads/windows/installer/5.6.html下载MySQL Installer 二.直接点击默认安装
- html----属性操作
1.文本 十六进制值 - 如: #FF0000 一个RGB值 - 如: RGB(255,0,0) 颜色的名称 - 如: red‘’RGBA() 2.水平对齐方式 text-align 属性规定元素中 ...
- python 全栈开发,Day50(Javascript简介,第一个JavaScript代码,数据类型,运算符,数据类型转换,流程控制,百度换肤,显示隐藏)
一.Javascript简介 Web前端有三层: HTML:从语义的角度,描述页面结构 CSS:从审美的角度,描述样式(美化页面) JavaScript:从交互的角度,描述行为(提升用户体验) Jav ...
- Ext.js入门
一:ExtJs简介: ExtJs通常简称为Ext,它是一个非常优秀的Ajax框架,用Javascript编写,它与后台技术无关,可以用来开发具有炫丽外观的富客户端应用.Ext所开发的多彩界面吸引了许多 ...
- WCF客户端从服务器下载数据
1.打开VS选择控制台项目新建一个解决方案Server,然后添加两个类库Contract和Service. 2.在Contract中添加一个接口IFileDownload using System; ...
- CentOS安装redis-audit 但执行时出错未解决 记录一下安装过程
网上很多安装过程都太老了,测试很多方法终于成功了,但执行时还是出错,哪位熟悉的可以告知一下. yum install -y ruby rubygems ruby-devel git gcc gem s ...
- springbank 开发日志 springbank是如何执行一个handler的requestMapping对应的方法的
占位 从dispatcher说起,方法doDispatch(Map request)的参数request是一个通过解析来报报文新城的map //获取HandlerExecutionChain,其中封装 ...
- .NET中JSON序列化(数据集转JSON)
Json序列化和反序列化指的是:对象序列化为JSON,并可用于从 JSON 反序列化对象 在.net 3.5中已支持JSON,引用命名空间: using System.Web.Script.Seria ...
- qrcodebox 面向移动设备的二维码弹出框
qrcodebox 面向移动设备的二维码弹出框 qrcodebox 简介 qrcode box,一个小小的二维码工具,通过调用它,可以在页面中间弹出一个二维码窗口,主要是面向移动设备的,对于PC端浏览 ...
- 2.Django|简介与静态文件| URL控制器
1.简介 MVC Web服务器开发领域里著名的MVC模式,所谓MVC就是把Web应用分为模型(M),控制器(C)和视图(V)三层,他们之间以一种插件式的.松耦合的方式连接在一起,模型负责业务对象与数 ...