文章版权由做者李晓晖和博客园共有,若转载请于明显处标明出处: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/ui
若是您以为本文确实帮助了您,能够微信扫一扫,进行小额的打赏和鼓励,谢谢 ^_^编码