日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

基于多种服务的地理位置查询系统

發布時間:2025/7/25 windows 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 基于多种服务的地理位置查询系统 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
  • 本文為掘金投稿,譯文出自 :?掘金翻譯計劃
  • 原文鏈接 :?Geolocation using multiple services
  • 原文作者 :?wsdookadr,感謝作者對本篇文章的翻譯授權。
  • 譯者 :?emmiter
  • 校對者:?a-voyager,?jamweak
  • 感謝譯者的辛苦翻譯與校對者的審校,歡迎技術投稿、約稿,給文章糾錯,請發送郵件至tangxy@csdn.net。

簡介

我的這篇文章討論了?PostGIS?以及查詢地理數據的幾種方法。這篇文章將集中討論構建一個免費的地理服務系統,并聚合呈現結果。

概述

總的來說,我們將會向不同的網絡服務(或APIs)發起請求,對響應結果做反向地理編碼后再聚合展示。

比較?Geonames?和?OpenStreetMap

下表羅列了二者之間的部分差別:

二者用途不同。Geonomes 用于城市/行政區/國家數據,可被用于地理編碼。OpenStreetMap 擁有更加詳盡的數據(使用者基本上都可以從 OpenStreetMap 中提取出Geonames數據),這些數據可被用作地理編碼,路線規劃以及這些和基于 OpenStreetMap 的服務。

發送給地理位置服務的異步請求

我們使用?gevent?庫來向地理位置服務發起異步請求。

import gevent import gevent.greenlet from gevent import monkey; gevent.monkey.patch_all()geoip_service_urls=[['geoplugin' , 'http://www.geoplugin.net/json.gp?ip={ip}' ],['ip-api' , 'http://ip-api.com/json/{ip}' ],['nekudo' , 'https://geoip.nekudo.com/api/{ip}' ],['geoiplookup' , 'http://api.geoiplookup.net/?query={ip}' ],]# fetch url in asynchronous mode (makes use of gevent) def fetch_url_async(url, tag, timeout=2.0):data = Nonetry:opener = urllib2.build_opener(urllib2.HTTPSHandler())opener.addheaders = [('User-agent', 'Mozilla/')]urllib2.install_opener(opener)data = urllib2.urlopen(url,timeout=timeout).read()except Exception, e:passreturn [tag, data]# expects req_data to be in this format: [ ['tag', url], ['tag', url], .. ] def fetch_multiple_urls_async(req_data):# start the threads (greenlets)threads_ = []for u in req_data:(tag, url) = unew_thread = gevent.spawn(fetch_url_async, url, tag)threads_.append(new_thread)# wait for threads to finishgevent.joinall(threads_)# retrieve threads return valuesresults = []for t in threads_:results.append(t.get(block=True, timeout=5.0))return resultsdef process_service_answers(location_data):# 1) extract lat/long data from responses# 2) reverse geocoding using geonames# 3) aggregate location data# (for example, one way of doing this would# be to choose the location that most services# agree on)passdef geolocate_ip(ip):urls = []for grp in geoip_service_urls:tag, url = grpurls.append([tag, url.format(ip=ip)])results = fetch_multiple_urls_async(urls)answer = process_service_answers(results)return answer

引發歧義的城市名

同一國家中具有相同名字的城市

同個國家里,有非常多的分屬于不同州或行政區的同名城市。也有很多同名不同國的城市。例如,根據 Geonames 的數據顯示,美國一共有24個名叫 Clinton 的城市(這24個城市共分布在23個州,其中有兩個是在密歇根州)

WITH duplicate_data AS (SELECTcity_name,array_agg(ROW(country_code, region_code)) AS dupesFROM city_region_dataWHERE country_code = 'US'GROUP BY city_name, country_codeORDER BY COUNT(ROW(country_code, region_code)) DESC ) SELECT city_name, ARRAY_LENGTH(dupes, 1) AS duplicity, ( CASE WHEN ARRAY_LENGTH(dupes,1) > 9 THEN CONCAT(SUBSTRING(ARRAY_TO_STRING(dupes,','), 1, 50), '...')ELSE ARRAY_TO_STRING(dupes,',') END ) AS sample FROM duplicate_data LIMIT 5;

同一國家,同一行政區的同名城市

從全世界范圍來看,即便是在同個國家的同個行政區,都會出現多個名字完全相同的城市。就拿位于美國印第安納州(Indiana)的喬治城(Georgetown)來說,Geonames 表明該州共有3個同名城鎮。維基百科則顯示了更多:

  • 喬治城,弗洛伊德縣,印第安納州

  • 喬治城小鎮,弗洛伊德縣,印第安納州

  • 喬治城,卡斯縣,印第安納州

  • 喬治城,蘭道夫縣,印第安納州

WITH duplicate_data AS (SELECTcity_name,array_agg(ROW(country_code, region_code)) AS dupesFROM city_region_dataWHERE country_code = 'US'GROUP BY city_name, region_code, country_codeORDER BY COUNT(ROW(country_code, region_code)) DESC ) SELECT city_name, ARRAY_LENGTH(dupes, 1) AS duplicity, ( CASE WHEN ARRAY_LENGTH(dupes,1) > 9 THEN CONCAT(SUBSTRING(ARRAY_TO_STRING(dupes,','), 1, 50), '...')ELSE ARRAY_TO_STRING(dupes,',') END ) AS sample FROM duplicate_data LIMIT 4;

反向地理編碼

(city_name, country_code),(city_name, country_code, region_name) 這兩個元組都不能唯一地確定一個位置。我們可以使用郵政編碼 (zip codes?或者叫做?postal codes),除非地理位置服務不提供他們。但是大部分的地理位置服務卻提供經緯度,可以使用這兩者來消除歧義。

PostgreSQL 數據庫中的圖形數據類型

我深入研究了 PostgreSQL 數據庫的文檔,發現它也擁有幾何數據類型和用于2D 幾何(平面幾何)的函數。你可以使用這些現成的數據類型和函數來模擬點,框,路徑,多邊形和圓并且可以將他們存儲,之后還可以查詢。PostgreSQL 還有一些存在于普通發布目錄的額外擴展。這些擴展需要大部分 Postgres 安裝后才可以使用。當下的情況,我們對?cube 類型?和earthdistance?擴展感興趣,earthdistance 擴展使用?3-cubes?來存儲向量和表示地球上的點。我們要用到的東西如下所示:

  • earth_distance?函數是可用的,允許你計算球面上兩點之間的最短距離?great-circle-distance
  • earth_box?函數用于檢查對于給定的參考點,和給定的距離,該點是否位于該距離以內
  • 一個?gist?位于表達式上的索引(expression index),表達式?ll_to_earth(lat,long)?執行快速的空間查詢以及尋找附近點。

為城市 & 行政區數據設計一個視圖

Geonames 數據被導入到3個表中:

  • geo_geoname?(數據來自?cities1000.zip)
  • geo_admin1?(數據來自?admin1CodesASCII.txt?)
  • geo_countryinfo (數據來自?countryInfo.txt?)

然后我們來創建一個可以將所有東西拉取到一起的視圖3。現在我們有了人口數據,城市/行政區/國家數據以及經度/維度數據,都在同個地方了。

CREATE OR REPLACE VIEW city_region_data AS ( SELECTb.country AS country_code,b.asciiname AS city_name,a.name AS region_name,b.region_code,b.population,b.latitude AS city_lat,b.longitude AS city_long,c.name AS country_nameFROM geo_admin1 aJOIN (SELECT *, (country || '.' || admin1) AS country_region, admin1 AS region_codeFROM geo_geonameWHERE fclass = 'P') b ON a.code = b.country_regionJOIN geo_countryinfo c ON b.country = c.iso_alpha2 );

設計一個城市周邊查詢函數

在大多數嵌套?SELECT?語句中,我們都確保城市是在以參考點為圓心,以大約23km為半徑的區域內,再對結果應用國家過濾器和城市模式過濾器(這兩個過濾器均為可選),最后僅得到接近50個結果。下一步,我們用人口數據對結果重新排序,因為有時候會在較大城市附近有一些區和鄰域?4,而 Geonames 不會用特定的方式標記他們,我們只是想選出較大的城市而不是一個區域(比如說地理位置服務返回了經緯度信息,該信息可被解析為一個較大城市的地區。于我而言,我比較愿意去把它解析成經緯度相對應的大城市)。我們也創建了一個 gist 索引(@>?該符號將會使用 gist 索引 ),用于尋找以參照點為圓心,特定半徑范圍內的點。這個查詢函數接受一個點(以緯度和經度表示)作為輸入,返回該輸入點相關聯的城市,地區和國家。

CREATE INDEX geo_geoname_latlong_idx ON geo_geoname USING gist(ll_to_earth(latitude,longitude)); CREATE OR REPLACE FUNCTION geo_find_nearest_city_and_region(latitude double precision,longitude double precision,filter_countries_arr varchar[],filter_city_pattern varchar, ) RETURNS TABLE(country_code varchar,city_name varchar,region_name varchar,region_code varchar,population bigint,_lat double precision,_long double precision,country_name varchar,distance numeric) AS $ BEGINRETURN QUERYSELECT *FROM (SELECT*FROM (SELECT *,ROUND(earth_distance(ll_to_earth(c.city_lat, c.city_long),ll_to_earth(latitude, longitude))::numeric, 3) AS distance_FROM city_region_data cWHERE earth_box(ll_to_earth(latitude, longitude), 23000) @> ll_to_earth(c.city_lat, c.city_long) AND(filter_countries_arr IS NULL OR c.country_code=ANY(filter_countries_arr)) AND(filter_city_pattern IS NULL OR c.city_name LIKE filter_city_pattern)ORDER BY distance_ ASCLIMIT 50) dORDER BY population DESC) eLIMIT 1; END; $ LANGUAGE plpgsql;

總結

我們從系統設計著手,讓這個系統可以查詢多個Geoip 服務,可以收集這些服務返回的數據對其聚合后得到一個更加可靠的結果。我們首先考慮了唯一確定位置的幾種方式。隨后選取了一種可以在確認位置時消除歧義的方法。第二部分中,我們著眼于構建,存儲以及查詢PostgreSQL中地理數據的不同方法。然后我們建立了一個視圖和函數,用來找出參考點附近的允許我們用來進行反向編碼的城市。

附注

1?通過使用多種服務(并且假定這些服務內部使用了不同的數據源)聚合后的結果,將會比我們只使用其中某一種服務得到的答案更為可靠。

此處還有一點優勢就,我們使用了免費服務,不需要什么設置,也無需關心更新;因為這些服務都是由各自的擁有者在維護。

然而,比起查詢一個本地的 geoip(基于 IP 查詢的地理位置)數據結構,查詢這些網絡地理位置服務則會比較緩慢。好在像城市/國家/行政區這種定位數據庫已經有了,例如?MaxMind GeoIP2,?IP2Location?以及?DB-IP?。

2?介紹一篇好文章,講述了使用?earthdistance?模塊來計算附近或更遠處酒吧的距離。

3?Genomes 也有 geonamelds,我們可以使用這些 genomes-specific ids 來精確匹配其位置。

4?Geonames 沒有關于 城市/鄰域的多邊形數據,或者城市地區類型的元數據(參考概述中 Geonames 和 OpenStreetMap 差異對照表中 criteria 一列的數據),所以你無法查詢包含那個點的所有的城市多邊形(不是指區域/鄰域)。

總結

以上是生活随笔為你收集整理的基于多种服务的地理位置查询系统的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。