Rails 和 PostgreSQL 的连接、postGIS 的 安装之类就都不赘述了。
PostgreSQL里postGIS安装好之后,就可以开始配置Rails这边了:
ActiveRecord with PostGIS
Gemfile 添加 activerecord-postgis-adapter 。
database.yml 里修改 adapter 和 schema_search_path
default: &default adapter: postgis schema_search_path: public,postgis
其他设置就让他默认吧。创建 migration:
class CreatePortals < ActiveRecord::Migration[5.0] def change create_table :portals do |t| t.st_point :lonlat, null: false, geographic: true t.timestamps end add_index :portals, :lonlat, using: :gist end end
这里主要解释一下 geographic: true 这个选项。
Geography & Geometry
Geometry:平面坐标系,计算出的两点间距离单位是度(degree)
Geography:地理坐标系,SRID-4326计算出的两点间距离单位是米(meter)
没研究过地理,从头到尾都没怎么明白什么SRID 4326/3785/3857 是什么,好希望有人可以翻译一下 Web Mercator(Wiki) 给我等小白说明下是什么东西。
按照 activerecord-postgis-adapter 的文档,默认 geography 的 SRID 是 4326。
Geometry 和 Geograpy 的具体区别,这里用一个例子说明一下:
已知东京站(Lat: 35.681167, Lng: 139.767052)和横滨站(Lat: 35.465786, Lng: 139.622313),计算东京站到横滨站到距离,可以用 ST_Distance 方法,这个方法即支持Geometry又支持Geography。
- ST_GeomFromText: Return a specified ST_Geometry value from Well-Known Text representation (WKT)
- ST_GeogFromText: Return a specified ST_Geography value from Well-Known Text representation (WKT)
SELECT ST_Distance( ST_GeomFromText('POINT(139.767052 35.681167)'), ST_GeomFromText('POINT(139.622313 35.465786)') );
输出结果是 0.259496345411655
度。
SELECT ST_Distance( ST_GeogFromText('POINT(139.767052 35.681167)'), ST_GeogFromText('POINT(139.622313 35.465786)') );
输出结果是 27261.54830124
米。
为了验证结果是否正确,高三以后就没学过数学的我用了一个很蠢的方法:用GoogleMaps的工具来测量距离。结果如图:
结果和第二个值几乎是一样的。
至于弧度结果的验证我不会,抱歉。
范围n米以内的所有结果
基于Geography是以米为单位的优势,完成“取范围n千米以内所有的记录”这个需求变简单了,数学不好的我也不用纠结“怎么把度换算成米”这个可能会耗费我大量时间的难题。
但是这里要说一下,Geometry的方法比Geography多很多很多很多,参见文档PostGIS Function Support Matrix。至于什么时候用Geometry什么时候用Geography,还是参见文档,基本上Geography的计算对比Geometry需要更多CPU。
接下来直接用 ST_DWithin 这个方法就能取到需要的结果。
(写不动了,到这里结束分三章写吧……)