文章版權由做者李曉暉和博客園共有,若轉載請於明顯處標明出處:http://www.cnblogs.com/naaoveGIS/html
多個項目中實現範圍(圓)搜索的方案爲:依賴庫表中的X和Y字段構造一個矩形查詢範圍,再經過幾何計算範圍中的數據到指定座標的距離是否在閾值半徑中,最後返回閾值中的數據。
該方案有幾個優勢:算法
可是,該方案在表數據量龐大的狀況下,經過X和Y兩個字段,而且有四個查詢條件,對性能有必定損耗。
在以前我寫過一篇關於Geohash編碼研究的文章WebGIS中GeoHash編碼的研究和擴展,這裏提到了一種將X和Y以哈夫曼原理編碼成一維字符串的方案。那麼這裏若是咱們使用geohash編碼方案來優化查詢效率是否有用?sql
這裏重點給出查詢搜索代碼,即經過hash長度對應的精度、查詢範圍參數,進行網格切分和編碼。數據庫
/*** * 經過傳入指定範圍、指定座標、查詢範圍和geohash長度,返回查詢範圍中對應的全部geohash編碼 * @param minX * @param minY * @param maxX * @param maxY * @param X * @param Y * @param geohashLength geohash字符串編碼長度 * @param searchRange 查詢範圍,若是是平面座標系100M則傳入100,經緯度座標系0.0001度則傳入0.0001 * @return */ public static List<String> GeoHashSearch(double minX, double minY, double maxX, double maxY, double X, double Y, int geohashLength,double searchRange){ List<Integer> latLngLength = SetHashLength(geohashLength); double boundMinX = X - searchRange; double boundMaxX = X + searchRange; double boundMinY = Y - searchRange; double boundMaxY = Y + searchRange; List<Double> range = GetGoeHashRange(minX, minY, maxX, maxY, latLngLength.get(0), latLngLength.get(1)); List<String> searchResult= new ArrayList<String>(); double xrange = range.get(0); double yrange = range.get(1); double value = 0.5; for (int i = 0; boundMinX + (i - value) * xrange <= boundMaxX; i++) { for (int j = 0; boundMinY + (j - value) * yrange <= boundMaxY; j++) { String geohashCode = Encode(minX, minY, maxX, maxY, boundMinX + i* xrange, boundMinY + j * yrange, geohashLength); if (!searchResult.contains(geohashCode)) { searchResult.add(geohashCode); } } } return searchResult; }
geohash編碼因爲隨着地圖範圍不一樣各編碼長度精度沒法肯定、編碼只能以字符串存儲等問題,在咱們的業務場景上沒法使用。那麼,若是咱們讓編碼精度肯定、編碼能夠用數字替代,是否就能夠達到業務場景的須要呢?服務器
格網劃分算是GIS算法中的萬金油。之前博客中寫過的空間索引、地理插值、影像金字塔、矢量切片等等都可以基於格網的思路去探索。這裏,一樣能夠利用格網算法來進行編碼。微信
/*** * 經過傳入地圖起始點,待編碼座標,編碼的X和Y方向精確度,獲取網格編碼字符串 * @param minX 地圖起始點X座標 * @param minY 地圖起始點Y座標 * @param X * @param Y * @param gridXSize X方向精確度。平面座標爲M,經緯度座標爲度 * @param gridYSize Y方向精確度。平面座標爲M,經緯度座標爲度 * @return */ public static long GetGridCode(double minX, double minY, double X, double Y, double gridXSize,double gridYSize){ if (X < minX || Y < minY){ return -1; } int xNum = (int)Math.ceil(Math.abs(X - minX) / gridXSize); int yNum = (int)Math.ceil(Math.abs(Y - minY) / gridYSize); return CreateLongCode(xNum,yNum); }
若是咱們須要將編碼轉換成數字編碼,那麼咱們一樣須要設定一種規則。這裏,我規定xNum和yNum都必須是八個字符串長度,不足的在前綴以0補充,最後再合併轉換成整數。(注意,這裏我設計以0做爲前綴而不是後綴補充,是爲了及時轉換成數字後,之後能夠經過數字將編碼反轉換爲空間範圍)性能
/*** * 以8位數和8位數分別將col和row填充組合成一個整數 */ private static long CreateLongCode(int x,int y){ String sx=String.valueOf(y); String sy=String.valueOf(y); for(int i=sx.length();i<XLen;i++){ sx="0"+sx; } for(int j=sy.length();j<YLen;j++){ sy="0"+sy; } String scode=sx+sy; long code=Long.parseLong(scode); return code; } /*** * 獲取網格編碼所對應的真實地理範圍 * @param minX * @param minY * @param value 編碼值 * @param gridXSize X方向精確度。平面座標爲M,經緯度座標爲度 * @param gridYSize Y方向精確度。平面座標爲M,經緯度座標爲度 * @return */ public static List<Double> Decode(double minX, double minY, long value, double gridXSize,double gridYSize){ String svalue=String.valueOf(value); String sx=svalue.substring(0,svalue.length()-YLen-1); String sy=svalue.substring(svalue.length()-YLen); int xnum=Integer.parseInt(sx); int ynum=Integer.parseInt(sy); double boundMinX = minX + (xnum - 1) * gridXSize; double boundMaxX = boundMinX + gridXSize; double boundMinY = minY + (ynum - 1) * gridYSize; double boundMaxY = boundMinY + gridYSize; List<Double> bound = new ArrayList<Double>(); bound.add(boundMinX); bound.add(boundMinY); bound.add(boundMaxX); bound.add(boundMaxY); return bound; }
一樣,這裏也須要考慮與geohash查詢時同樣的狀況:優化
/*** * 經過傳入地圖起始點、網格X和Y方向精確度、查詢範圍和查詢點,返回對應查詢範圍內全部網格編碼 * @param minX * @param minY * @param X * @param Y * @param gridXSize X方向精確度。平面座標爲M,經緯度座標爲度 * @param gridYSize Y方向精確度。平面座標爲M,經緯度座標爲度 * @param range 查詢範圍,平面座標爲M,經緯度座標爲度 * @return */ public static List<Long> GridCodeSearch(double minX, double minY, double X, double Y, double gridXSize, double gridYSize,double range){ if (X < minX || Y < minY){ return null; } double boundMinX = X - range; double boundMinY = Y - range; double boundMaxX = X + range; double boundMaxY = Y + range; double value=0.5; List<Long> searchResult = new ArrayList<Long>(); for (int i = 0; boundMinX + (i - value) * gridXSize <= boundMaxX; i++){ for (int j = 0; boundMinY + (j - value) * gridYSize <= boundMaxY; j++){ long gridCode = GetGridCode(minX, minY, boundMinX + i * gridXSize, boundMinY + j * gridYSize, gridXSize, gridYSize); if (!searchResult.contains(gridCode)){ searchResult.add(gridCode); } } } return searchResult; }
-----歡迎轉載,但保留版權,請於明顯處標明出處:http://www.cnblogs.com/naaoveGIS/編碼
若是您以爲本文確實幫助了您,能夠微信掃一掃,進行小額的打賞和鼓勵,謝謝 ^_^spa