在LBS應用中,根據坐標來解析獲得對應是哪個城市是一個很常見的功能,比如App里面通過手機定位自動選擇城市;本文介紹的是通過自己建的數據庫,利用SQL空間查詢來進行坐標解析得到對應的省市區,絕大部分支持空間數據類型(Spatial
)的數據庫均支持,包括但不限於:MySQL
、SQL Server
、Oracle
、PostgreSQL
等;開發語言不限,只要能進行數據庫查詢就都支持,用Java
、Python
、PHP
、C#
均能很簡單的實現。
省市區邊界數據在線預覽、下載:https://xiangyuecn.gitee.io/areacity-jsspider-statsgov/
GitHub地址:https://github.com/xiangyuecn/AreaCity-JsSpider-StatsGov
通過坐標來獲取地址:百度地圖提供的接口叫 “地址逆解析”,高德地圖提供的接口叫 “地理逆編碼”,它們開放平台均提供了前后端接口,發個http請求就可以拿到數據,相關的接口調用請自行閱讀平台開發文檔,使用起來很方便。
當然這篇文章不會去介紹怎么搞街道門牌號這些完整地址,也不講怎么樣調用人家的接口,只介紹坐標對應的省市區名稱的獲取,自建數據庫寫SQL進行空間查詢,完全自己實現,比調開放平台接口相對會復雜些。
由於全國區縣每年都會有比較多的變更,所以省市區邊界數據需要經常去同步維護,好在這上面這個開源庫有在持續的長期維護,新數據發布后更新相對容易很多。由於開源庫更新維護數據比較及時,所以只要開源庫沒有被關閉,本文介紹的提取方法就一直適用; 比那些上傳到下載平台就萬年不更新的數據優秀很多。
查詢效果展示:
從這里隨便拿的一個坐標:
直觀的效果如上圖,在百度地圖里面隨便點選一下(或App定位獲得的坐標)得到一個坐標,然后到數據庫里面利用空間查詢SQL就能查詢到坐標所在的城市。
步驟一、下載省市區邊界數據
到開源庫里面下載最新的 ok_geo.csv.7z
文件(13M大小,解壓130M+),點此下載。下載好后解壓得到 ok_geo.csv
,此文件內包含了最新全國所有的省市區縣坐標邊界矢量數據。
注:這個文件只包含三級(省市區)數據不含第四級(鄉鎮街道),如需鄉鎮坐標邊界數據,可以請點此下載
ok_geo4_*.csv
文件(90MB+壓縮包 導出后300M+)。
步驟二、解析CSV文件導入數據庫
下載好的文件 ok_geo.csv
是純文本文件,可以自行編寫腳本進行解析,然后導進數據庫中,自行解析處理比較復雜,請參考開源庫內文檔;開源庫內提供了一個格式轉換工具,此工具支持將CSV數據導入數據庫,因此我們直接在下載數據時順帶把工具下載好,點此下載。
此轉換工具除了支持將 ok_geo.csv
導入數據庫外,還支持導出:sql
、shp
、geojson
,和坐標系轉換;還可執行自定義 JavaScript 腳本,擴展出豐富功能;軟件是Windows版,如果需要在MacOs中用,可以用虛擬機。
轉換工具執行導入數據庫操作:
- 點擊
選擇ok_geo.csv文件
按鈕,選擇解壓出來的CSV文件; - 數據庫設置中選擇要導入的數據庫類型,這里選的是
MySQL
,再填寫數據庫連接,包括:端口、數據庫名稱、賬號密碼; - 點擊
導入數據庫
按鈕,等待一會,大約3分鍾左右,所有數據就都導入到了數據庫按今天日期新建的表里面。
注:csv文件內的邊界數據默認是高德地圖GCJ-02
火星坐標系,如果需要其他坐標系,比如百度的BD-09
或GPS的WGS-84
,可以通過高級腳本中的坐標系轉換插件進行轉換,選擇好對應的插件后,點擊應用就可以了,在導入數據庫時會自動進行坐標系轉換。
注:這個工具限制每次操作只可導出一個城市和它的下一級數據,導出少量數據還是很輕松的,所以我們可以多操作幾次,將需要的城市數據全部導入數據庫;比如要深圳的所有區縣數據:第一遍導入全國所有的省,第二遍在城市名前綴中填寫廣東省
(結尾帶一個空格)導入廣東所有的市,第三遍在城市名前綴中填寫廣東省 深圳市
(結尾帶一個空格)導入深圳所有的區。如果在密鑰輸入框中填寫了密鑰,此工具就沒有這些限制了,開源庫里面會不定期發放密鑰搞福利,點擊一次操作即可導出全國所有的省市區三級數據。
表結構和空間字段(MySQL版,其他數據庫類似):
CREATE TABLE Areacity_Geo_20220216 (
id int NOT NULL, --城市id
pid int NOT NULL, --上級城市id
deep int NOT NULL, --層級:0省、1市、2區
name varchar(250) NOT NULL, --城市名稱:`深圳市`
ext_path varchar(255) NOT NULL, --省市區三級完整名稱:廣東省 深圳市 羅湖區
geo geometry NOT NULL, --城市中心坐標,空間數據格式
--,ST_AsText轉成WKT文本后:`POINT EMPTY`、`POINT (123.456 34.567)`
polygon geometry NOT NULL --城市邊界范圍圖形,空間數據格式
--,ST_AsText轉成WKT文本后:`POLYGON EMPTY`、`POLYGON ((123.456 34.567,...))`、`MULTIPOLYGON (((123.456 34.567,...)),...)`
)
對空間字段的查詢,需要用`ST_AsText()`方法才能查詢出字符串文本(WKT: Well Known Text),否則查詢出來的是二進制數據
-- MySQL版:
SELECT id, name, ST_AsText(geo) AS geo, ST_AsText(polygon) AS polygon FROM 表名
-- SQL Server版:
SELECT id, name, geo.STAsText() AS geo, polygon.STAsText() AS polygon FROM 表名
步驟三、在程序中根據坐標解析獲得城市
省市區邊界導入到了數據庫后,我們就可以在在 Java
、Python
、PHP
、C#
等程序中對數據庫進行查詢,通過SQL的空間計算函數ST_Intersects
來查詢一個坐標在哪些邊界范圍內,就能得到對應的省市區信息了。
空間查詢SQL語句
比如要查詢坐標`lng:113.929976 lat:22.529497`是在哪個城市
-- MySQL版:
SELECT id,deep,name FROM 表名 WHERE ST_Intersects(polygon, ST_GeomFromText('POINT(113.929976 22.529497)',0))=1
-- SQL Server版:
SELECT id,deep,name FROM 表名 WHERE polygon.STIntersects(geometry::STGeomFromText('POINT(113.929976 22.529497)',0))=1
查詢結果例子(MySQL版,其他數據庫類似)
程序代碼連接上數據庫,通過以上SQL查詢到數據庫數據后,就獲得了省市區信息,可以通過deep
字段來區分哪條數據是省(deep=0)
、市(deep=1)
、區縣(deep=2)
。
通過以上三步,我們就完全是自己實現了根據經緯度坐標來解析獲得對應是哪個城市這一功能。
【END】