【原創】大叔案例分享(1)基於地址位置的用戶人羣定位方案

背景

假設一個應用有不少的用戶位置信息基礎數據,抽象數據格式以下:html

user_id
date
time
address
province
city
district
latitude
longitude
user1 2018-01-01 12:00:00 北京市朝陽區朝陽大悅城 北京 北京 朝陽 1.1 1.2
...                

應用裏有一些須要根據地址位置定位用戶的需求,抽象問題是任給一塊區域找出該區域內全部的user_id,具體以下:git

  1. 給定一個經緯度參數以及半徑千米數參數,提取該圓範圍內全部的user_id;
  2. 給定一個任意多邊形的全部頂點的經緯度參數,提取該多邊形範圍內全部的user_id;

這裏須要兩個基礎函數sql

一個是根據兩個點的經緯度計算距離,calDistance;http://www.javashuo.com/article/p-nfffkexv-gr.html函數

一個是判斷一個點是否在給定的多邊形內,isInPolygon;http://www.javashuo.com/article/p-aektebzd-bg.html優化

 

方案一:SQL

步驟

  1. 將calDistance和isInPolygon定義爲UDF,一個sql便可獲得結果;

分析

  • 優勢:簡單;
  • 缺點:計算量大,運行緩慢,包含大量無用計算;

 

方案一中會作大量的無用計算,並且calDistance和isInPolygon這兩個高階浮點計算很是昂貴spa

  1. 好比給定區域在河南省,可是其餘省(好比河北)的用戶數據也會參與計算,而且計算以後會被拋棄;
  2. 好比給定區域在河南省鄭州市,可是其餘市(好比洛陽)的數據也會參與計算,而且計算以後會被拋棄;
  3. 好比給定區域在河南省鄭州市中原區,可是其餘區(好比金水區)的數據也會參與計算而且計算以後會被拋棄;

爲了優化這些無用計算獲得方案二以下:htm

 

方案二

 步驟

  1. 根據給定區域(河南省鄭州市中原區內)只查詢必要的用戶位置數據,好比只查詢鄭州市的用戶數據(爲何不直接查中原區的數據?由於第一種情形雖然中心店在中原區,可是半徑較大時可能區域包含其餘區),讀取數據量縮小到原來的1%如下,避免了無用計算中的1和2;
  2. 對給定區域(圓或者多邊形)求一個能包含該區域同時邊長最小的長方形,這樣能夠很容易的經過判斷一個點是否長方形內來避免calDistance或isInPolygon計算,好比若是一個點在長方形外能夠直接拋棄,將高階浮點運算轉化爲簡單的數字比較運算,計算數據量縮小到原來的萬分之一如下,簡化和避免了無用計算中的3;
  3. 對長方形範圍內的全部用戶數據計算calDistance或isInPolygon來獲取精確結果;

分析

  • 優勢:改進了方案一中的大量無用計算,避免或者簡化;
  • 缺點:依然包含部分無用計算,好比區域內的點也要計算一遍,而且沒法快速響應一些需求,好比快速返回區域內的用戶數量;

 

方案二能夠抽象爲映射,第一輪映射是利用省市區信息來減小讀取數據量,第二輪映射是利用是否在正方形外來減小計算數據量;而且這兩輪映射都是現成的,或者很容易計算的;若是把映射改成網格grid,則效果比上述兩輪映射更好,獲得方案三以下:blog

 

方案三

步驟

  1. 預處理,將全部用戶位置數據進行網格劃分並標記存儲,好比按每平方千米劃分網格(同時獲得網格的頂點經緯度),網格內存儲該網格內全部的用戶位置數據,同時存儲一些統計信息,好比用戶總量,不重複的用戶總量,不重複的用戶集合等,放到任意KV存儲中;
  2. 根據給定區域計算哪些網格在區域內(區域內的網格不須要計算),哪些網格在區域邊上(區域邊上的網格須要計算,由於網格內部分用戶在區域內,部分用戶在區域外),這個是純圖形計算;
  3. 讀取全部區域內的網格的不重複的用戶集合數據(直接使用),讀取全部區域邊上的網格的全部用戶位置數據(計算後使用),能夠快速獲得區域內用戶數量或者用戶列表;

分析

  • 優勢:相比方案二進一步減小讀取數據量和計算數據量,而且能夠知足一些快速響應的需求,能夠經過減少網格的面積來進一步減小讀取數據量和計算數據量,當網格的面積足夠小之後,還能夠在偏差允許的範圍內,將區域邊上的網格‘認爲’在區域內,避免全部的計算;
  • 缺點:須要預處理,而且須要額外的KV存儲;

 

另外還能夠利用ElasticSearch對GIS的支持 https://www.elastic.co/blog/geo-location-and-search內存

相關文章
相關標籤/搜索