前言:项目中用到了postgreSQL中的earthdistance()函数功能计算地球上两点之间的距离,中文的资料太少了,我找到了一篇英文的、讲的很好的文章,特此翻译,希望能够帮助到以后用到earthdistance的同学。

一、两种可用的选择
当我们想用Postgres作为GEO函数使用时,我们通常有2中选择(据我所知):

1.PostGIS: 为postgreSQL提供了高级GEO函数功能。我用了它一段时间,但是它对于我的需求来说太笨重了。
2.Cube和Earthdistance: 这两个拓展为轻量级的Geo关系实体提供了简单、快速的实现方法。

二、为什么在数据库服务器端做计算
这是件非常明显的事。服务器存储了所有的数据,服务器拓展是用C/C++实现的,非常快。为数据表做索引也能加快计算速度。

三、使用我的选择--Cube and EarthDistance
作为开始,你应该先建一个数据库(我想你知道该怎么做),然后使它们能用我们的架构。 执行:

  1. CREATE EXTENSION cube;
  2. CREATE EXTENSION earthdistance;

上面的命令创建了大约40个函数,以后我们做数据查询的时候就可以用了。
在我们的例子中,我创建了名为events的表,字段有:id(serial), name(varchar 255), lat(double), lng(double)。(别忘了~~)

四、计算2个坐标之间的距离

计算2个坐标之间的距离,我们要用到earth_distance(ll_to_earth($latlngcube), ll_to_earth($latlng_cube))这个函数。 earth_distance()函数接受2组坐标值,返回值一个以米为单位的的数值。这能用于很多场景,比如根据某一位置找到离其最近的发生的新闻事件的列表。

【译者注】这里要提下几个重要的函数:(参考:http://www.postgresql.org/docs/8.3/static/earthdistance.html

Table F-3. Cube-based earthdistance functions

Function Returns Description
earth() float8 Returns the assumed radius of the Earth.
sec_to_gc(float8) float8 Converts the normal straight line (secant) distance between between two points on the surface of the Earth to the great circle distance between them.
gc_to_sec(float8) float8 Converts the great circle distance between two points on the surface of the Earth to the normal straight line (secant) distance between them.
ll_to_earth(float8, float8) earth Returns the location of a point on the surface of the Earth given its latitude (argument 1) and longitude (argument 2) in degrees.
latitude(earth) float8 Returns the latitude in degrees of a point on the surface of the Earth.
longitude(earth) float8 Returns the longitude in degrees of a point on the surface of the Earth.
earth_distance(earth, earth) float8 Returns the great circle distance between two points on the surface of the Earth.
earth_box(earth, float8) cube Returns a box suitable for an indexed search using the cube @> operator for points within a given great circle distance of a location. Some points in this box are further than the specified great circle distance from the location, so a second check using earth_distance should be included in the query.

数据库的操作可能就像下面这样:

  1. SELECT events.id events.name, eaerthdiatance(ll_to_earth({currentuserlat}, {currentuserlng}), llto_earth(events.lat, events.lng))
  2. as distancefromcurrentlocation FROM events
  3. ORDER BY distancefromcurretnlocation ASC;

这将给我们一个很nice的新闻事件列表,按他们的离我们当前位置的距离由近到远排序。第一个是离我们最近的。


五、找到某个半径范围内的记录

Cube和Earthdiatance拓展提供的另一个伟大的函数是earth_box(ll_to_earch($latlngcub), $radiusinmetres)。 这个函数通过简单的比较就能到找到某个半径范围内的所有记录。它是靠返回2点之间的“大圆距离”实现的。

【译者注】大圆距离(Great circle disstance)指的是从球面的一点A出发到达球面上另一点B,所经过的最短路径的长度。一般说来,球面上任意两点A和B都可以与球心确定唯一的大圆,这个大圆被称为黎曼圆,而在大圆上连接这两点的较短的一条弧的长度就是大圆距离。如果想了解更多,请看wiki:大圆距离

它能用于查询我们城市中所有的新闻事件:

  1. SELECT events.id, events.name FROM events
  2. WHERE earth_box({currentuserlat}, {currentuserlng}, {radiusinmetres}) @> ll_to_earth(events.lat, events.lng);

这条查询语句仅仅会返回在radius_in_metres指定的半径范围内的记录,非常简单吧!

六、提高查询速度

你可能会发现上面的查询有不小的开销。以我的经验,最好对一些字段建立索引。 (下面这条语句假定你又events表, 同时events表有字段lat和lng)

  1. CREATE INDEX ${nameofindex} on events USING gits(lltoearth(lat, lng));

七、数据类型

我的应用比较简单,所以我把经纬度(lat和lng)都设成了double类型。这使得我用Node.js开发起来更加快速,而不用再去自己定制针对GIST类型的解决方案。

八、就这些!

很神奇,对么?!?我们仅仅用常用的数据类型(double)就足以去用一些GEO函数创建基于地理位置的社交app(【译者注】知乎上的一个回答)!

---------------------------
英语水平有限,如有翻译不周之处,请您指点!

------------------------------

update:

我使用的postgreSQL语句总结:

  1. /*
  2. * postgreSQL之earthdistance学习笔记
  3. * author: wusuopubupt
  4. * date: 2013-03-31
  5. */
  6. /*创建表*/
  7. CREATE TABLE picture (
  8. id serial PRIMARY KEY ,
  9. p_uid char(12) NOT NULL,
  10. p_key char(23) NOT NULL,
  11. lat real not null,
  12. lng real NOT NULL,
  13. up int NOT NULL,
  14. down int NOT NULL,
  15. ip varchar(15) DEFAULT NULL,
  16. address varchar(256) DEFAULT NULL
  17. );
  18. /*插入记录*/
  19. INSERT INTO picture(p_uid, p_key, lat, lng, up, down, ip, address)
  20. VALUES('aaaabbbbcccc', '2014032008164023279.png', 40.043945, 116.413668, 0, 0, '', '');
  21. /*插入记录*/
  22. INSERT INTO picture(p_uid, p_key, lat, lng, up, down, ip, address)
  23. VALUES('xxxxccccmmmm', '2014032008164023111.png', 40.067183, 116.415230, 0, 0, '', '');
  24. /*选择记录*/
  25. SELECT * FROM picture;
  26. /*更新记录*/
  27. UPDATE picture SET address='LiShuiqiao' WHERE id=1;
  28. UPDATE picture SET address='TianTongyuan' WHERE id=2;
  29. /*对经纬度列创建索引*/
  30. CREATE INDEX ll_idx on picture USING gist(ll_to_earth(lat, lng));
  31. /*根据半径(1000米)选择记录*/
  32. SELECT * FROM picture where earth_box(ll_to_earth(40.059286,116.418773),1000) @> ll_to_earth(picture.lat, picture.lng);
  33. /*选择距离当前用户的距离*/
  34. SELECT picture.id, earth_distance(ll_to_earth(picture.lat, picture.lng), ll_to_earth(40.059286,116.418773))
  35. AS dis FROM picture
  36. ORDER BY dis ASC;
  37. /*
  38. * 以下内容是网上的一篇教程
  39. * 地址:http://www.cse.iitb.ac.in/dbms/Data/Courses/CS631/PostgreSQL-Resources/postgresql-9.2.4/contrib/earthdistance/expected/earthdistance.out
  40. */
  41. --
  42. --  Test earthdistance extension
  43. --
  44. -- In this file we also do some testing of extension create/drop scenarios.
  45. -- That's really exercising the core database's dependency logic, so ideally
  46. -- we'd do it in the core regression tests, but we can't for lack of suitable
  47. -- guaranteed-available extensions.  earthdistance is a good test case because
  48. -- it has a dependency on the cube extension.
  49. --
  50. CREATE EXTENSION earthdistance;  -- fail, must install cube first
  51. ERROR:  required extension "cube" is not installed
  52. CREATE EXTENSION cube;
  53. CREATE EXTENSION earthdistance;
  54. --
  55. -- The radius of the Earth we are using.
  56. --
  57. SELECT earth()::numeric(20,5);
  58. earth
  59. ---------------
  60. 6378168.00000
  61. (1 row)
  62. --
  63. -- Convert straight line distances to great circle distances.把直线距离转成大圆距离
  64. --
  65. SELECT (pi()*earth())::numeric(20,5);
  66. numeric
  67. ----------------
  68. 20037605.73216
  69. (1 row)
  70. SELECT sec_to_gc(0)::numeric(20,5);
  71. sec_to_gc
  72. -----------
  73. 0.00000
  74. (1 row)
  75. --
  76. -- Convert great circle distances to straight line distances.
  77. --
  78. SELECT gc_to_sec(0)::numeric(20,5);
  79. gc_to_sec
  80. -----------
  81. 0.00000
  82. (1 row)
  83. SELECT gc_to_sec(sec_to_gc(2*earth()))::numeric(20,5);
  84. gc_to_sec
  85. ----------------
  86. 12756336.00000
  87. (1 row)
  88. --
  89. -- Set coordinates using latitude and longitude.
  90. -- Extract each coordinate separately so we can round them.
  91. --
  92. SELECT cube_ll_coord(ll_to_earth(0,0),1)::numeric(20,5),
  93. cube_ll_coord(ll_to_earth(0,0),2)::numeric(20,5),
  94. cube_ll_coord(ll_to_earth(0,0),3)::numeric(20,5);
  95. cube_ll_coord | cube_ll_coord | cube_ll_coord
  96. ---------------+---------------+---------------
  97. 6378168.00000 |       0.00000 |       0.00000
  98. (1 row)
  99. SELECT cube_ll_coord(ll_to_earth(360,360),1)::numeric(20,5),
  100. cube_ll_coord(ll_to_earth(360,360),2)::numeric(20,5),
  101. cube_ll_coord(ll_to_earth(360,360),3)::numeric(20,5);
  102. cube_ll_coord | cube_ll_coord | cube_ll_coord
  103. ---------------+---------------+---------------
  104. 6378168.00000 |       0.00000 |       0.00000
  105. (1 row)
  106. --
  107. -- Test getting the latitude of a location.
  108. --
  109. SELECT latitude(ll_to_earth(0,0))::numeric(20,10);
  110. latitude
  111. --------------
  112. 0.0000000000
  113. (1 row)
  114. SELECT latitude(ll_to_earth(45,0))::numeric(20,10);
  115. latitude
  116. ---------------
  117. 45.0000000000
  118. (1 row)
  119. --
  120. -- Test getting the longitude of a location.
  121. --
  122. SELECT longitude(ll_to_earth(0,0))::numeric(20,10);
  123. longitude
  124. --------------
  125. 0.0000000000
  126. (1 row)
  127. SELECT longitude(ll_to_earth(45,0))::numeric(20,10);
  128. longitude
  129. --------------
  130. 0.0000000000
  131. (1 row)
  132. --
  133. -- For the distance tests the following is some real life data.
  134. --
  135. -- Chicago has a latitude of 41.8 and a longitude of 87.6.
  136. -- Albuquerque has a latitude of 35.1 and a longitude of 106.7.
  137. -- (Note that latitude and longitude are specified differently
  138. -- in the cube based functions than for the point based functions.)
  139. --
  140. --
  141. -- Test getting the distance between two points using earth_distance.
  142. --
  143. SELECT earth_distance(ll_to_earth(0,0),ll_to_earth(0,0))::numeric(20,5);
  144. earth_distance
  145. ----------------
  146. 0.00000
  147. (1 row)
  148. SELECT earth_distance(ll_to_earth(0,0),ll_to_earth(0,180))::numeric(20,5);
  149. earth_distance
  150. ----------------
  151. 20037605.73216
  152. (1 row)
  153. --
  154. -- Test getting the distance between two points using geo_distance.
  155. --
  156. SELECT geo_distance('(0,0)'::point,'(0,0)'::point)::numeric(20,5);
  157. geo_distance
  158. --------------
  159. 0.00000
  160. (1 row)
  161. SELECT geo_distance('(0,0)'::point,'(180,0)'::point)::numeric(20,5);
  162. geo_distance
  163. --------------
  164. 12436.77274
  165. (1 row)
  166. --
  167. -- Test getting the distance between two points using the <@> operator.
  168. --
  169. SELECT ('(0,0)'::point <@> '(0,0)'::point)::numeric(20,5);
  170. numeric
  171. ---------
  172. 0.00000
  173. (1 row)
  174. SELECT ('(0,0)'::point <@> '(180,0)'::point)::numeric(20,5);
  175. numeric
  176. -------------
  177. 12436.77274
  178. (1 row)
  179. --
  180. -- Test for points that should be in bounding boxes.
  181. --
  182. SELECT earth_box(ll_to_earth(0,0),
  183. earth_distance(ll_to_earth(0,0),ll_to_earth(0,1))*1.00001) @>
  184. ll_to_earth(0,1);
  185. ?column?
  186. ----------
  187. t
  188. (1 row)
  189. SELECT earth_box(ll_to_earth(0,0),
  190. earth_distance(ll_to_earth(0,0),ll_to_earth(0,0.1))*1.00001) @>
  191. ll_to_earth(0,0.1);
  192. ?column?
  193. ----------
  194. t
  195. (1 row)
  196. --
  197. -- Test for points that shouldn't be in bounding boxes. Note that we need
  198. -- to make points way outside, since some points close may be in the box
  199. -- but further away than the distance we are testing.
  200. --
  201. SELECT earth_box(ll_to_earth(0,0),
  202. earth_distance(ll_to_earth(0,0),ll_to_earth(0,1))*.57735) @>
  203. ll_to_earth(0,1);
  204. ?column?
  205. ----------
  206. f
  207. (1 row)
  208. SELECT earth_box(ll_to_earth(0,0),
  209. earth_distance(ll_to_earth(0,0),ll_to_earth(0,0.1))*.57735) @>
  210. ll_to_earth(0,0.1);
  211. ?column?
  212. ----------
  213. f
  214. (1 row)  

用postgreSQL做基于地理位置的app(zz)的更多相关文章

  1. HMS Core基于地理位置请求广告,流量变现快人一步

    对于想买车的用户来说,如果走在路上刷社交软件时突然在App里收到一条广告:"前方500米商圈里的某品牌汽车正在做优惠,力度大福利多."不管买不买,八成都会去看看,原因有三:距离近. ...

  2. 在做基于LBS应用的一些随笔

    公司做了一个基于LBS的APP,在做服务端的时候出现了一些注意事项,还是记录下把. 首先是关于坐标: 弧长公式:L=nπr/180°或l=|α|r.地球半径大致是6400千米.以纬度0.000001为 ...

  3. 【转】基于laravel制作APP接口(API)

    这篇文章主要介绍了基于laravel制作APP接口(API)的相关资料,需要的朋友可以参考下 前期准备 前言,为什么做以及要做个啥本人姓小名白,不折不扣编程届小白一名,但是自从大一那会儿接触到编程这件 ...

  4. Recommending music on Spotify with deep learning 采用深度学习算法为Spotify做基于内容的音乐推荐

    本文参考http://blog.csdn.net/zdy0_2004/article/details/43896015译文以及原文file:///F:/%E6%9C%BA%E5%99%A8%E5%AD ...

  5. 【LBS】基于地理位置的搜索之微信 附近的人 简单实现

    缘由 本周技术群有一个同学说我们该怎么实现 由近到远的基于地理位置的搜索,我创业做电商的系统做过类似这样的服务,我把我们以前的操作给大家分享下 什么是LBS LBS 全称是 Location  Bas ...

  6. 基于phonegap开发app的实践

    app开发告一段落.期间遇到不少问题,写篇文章记录一下. 为虾米要用phonegap 开发app,至少要考虑android和ios两个版本号吧,android偶能够应付,ios表示全然木有接触过.于是 ...

  7. 如何做个简单安卓App流程

    有同学做毕业设计,问怎样做个简单安卓App流程,我是做服务端的,也算是经常接触app,想着做app应该很简单吧,不就做个页面,会跳转,有数据不就行了,我解释了半天,人家始终没听懂,算了,我第二天问了下 ...

  8. 做个这样的APP要多久?[转]

    这是一个“如有雷同,纯属巧合”的故事,外加一些废话,大家请勿对号入座.开始了…… 我有些尴尬地拿着水杯,正对面坐着来访的王总,他是在别处打拼的人,这几年据说收获颇丰,见移动互联网如火如荼,自然也想着要 ...

  9. 个人开发者做一款Android App需要知道的事情

    个人开发者做一款Android App需要知道的事情 在大学时, 自己是学计算机专业的,而且还和老师一起做过一年半的项目. 有时候是不是有这样的想法,做一个自己的网站.但一直未付诸行动.2012年时, ...

随机推荐

  1. 小波说雨燕 第三季 构建 swift UI 之 度假清单 学习笔记

    最终的效果: <1>第一个场景: 1.本地化 界面简体中文化 Supporting Files - info.plist Localization native development r ...

  2. iOS证书失效

    iOS证书突然失效 今早上班打包直接报错,错误如图 根据错误信息到“钥匙串”里面看了一下证书,证书都莫名其妙的失效了,昨天还是好好的. 重新去钥匙串从证颁发中心获取证书,然后登陆开发者账号重新申请证书 ...

  3. LVS四种实现模式详解

    一.集群cluster 当后端服务器承受不住访问的压力,提高服务器性能的解决方案会极大增加成本时,人们提出了横向扩展的解决方案.增加一台或几台服务器,提供相同的服务,通过前段分发器将访问量均匀的分配到 ...

  4. nodejs 导出excel

    nodejs 对查询数据生成excel并下载,采用方式先生成本excel文件,然后再下载:通过比较采用excel-export插件代码如下: excel.js代码: var extend = requ ...

  5. 03_汇编语言(n个数找最大值)

    程序要求: 先输入一个数n(0<n<=100),再输入n个无符号数K(0<=K<=65535),找出这n个数的最大值并输出 测试实例保证输入每个数之后,都会以回车结束 代码: ...

  6. javascript原型Prototype

    在javaScript创建对象一文中提到过:用构造函数创建对象存在一个问题即同一构造函数的不同实例的相同方法是不一样的,所以我们用原型把构造函数中公共的属性和方法提取出来进行封装,达到让所有实例共享的 ...

  7. C++ 之 常量成员函数

    类的常量成员函数 (const member function), 可以读取类的数据成员,但是不能修改. 1  声明 1.1  const 关键字 在参数列表后,加 const 关键字,声明为常量成员 ...

  8. 2014 Super Training #2 F The Bridges of Kolsberg --DP

    原题:UVA 1172  http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_ ...

  9. UESTC 923 稳住GCD DP + GCD

    定义:dp[i][j] 表示 在前i个数中,使整个gcd值为j时最少取的数个数. 则有方程: gg = gcd(a[i],j) gg == j : 添加这个数gcd不变,不添加,  dp[i][j] ...

  10. 什么是Activity

    Activity 的生命周期是被以下的函数控制的.public class Activity extends ApplicationContext {      protected void onCr ...