查指定範圍內的街道(基於經緯度)
阿新 • • 發佈:2019-02-03
一、表結構:
CREATE TABLE `district` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `p_id` bigint(20) NOT NULL COMMENT '父ID', `zipcode` varchar(4) DEFAULT NULL COMMENT '城市編碼', `adcode` varchar(6) NOT NULL DEFAULT '' COMMENT '區域編碼', `name` varchar(64) NOT NULL DEFAULT '' COMMENT '行政區名稱', `center` varchar(30) NOT NULL DEFAULT '' COMMENT '城市中心點', `lon` double(9,6) NOT NULL COMMENT '經度', `lat` double(8,6) NOT NULL COMMENT '維度', `geo_code` varchar(12) DEFAULT NULL COMMENT 'geohash編碼', `level` varchar(10) NOT NULL DEFAULT '' COMMENT '取值:province省份,city市,district區縣,street街道', `area` text COMMENT '街道覆蓋域', PRIMARY KEY (`id`), KEY `p_id` (`p_id`) USING BTREE, KEY `zipcode` (`zipcode`) USING BTREE, KEY `adcode` (`adcode`) USING BTREE, KEY `name` (`name`) USING BTREE, KEY `level` (`level`) USING BTREE, KEY `lon` (`lon`) USING BTREE, KEY `lat` (`lat`) USING BTREE, KEY `geo_code` (`geo_code`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=51264 DEFAULT CHARSET=utf8mb4 COMMENT='行政區域';
二、引入jar包
三、Java程式碼<dependency> <groupId>com.spatial4j</groupId> <artifactId>spatial4j</artifactId> <version>0.5</version> </dependency> <dependency> <groupId>ch.hsr</groupId> <artifactId>geohash</artifactId> <version>1.3.0</version> </dependency>
/** * 方法一,查詢指定範圍內的所有街道(區間查詢) * @return */ public List<District> findNearbyDistrictByRadius(Double lon, Double lat, int radius){ SpatialContext geo = SpatialContext.GEO; Rectangle rectangle = geo.getDistCalc().calcBoxByDistFromPt(geo.makePoint(lon, lat), radius * DistanceUtils.KM_TO_DEG, geo, null); double minX = rectangle.getMinX(); double maxX = rectangle.getMaxX(); double minY = rectangle.getMinY(); double maxY = rectangle.getMaxY(); LOGGER.info(minX + " - " + maxX); // 經度範圍 LOGGER.info(minY + " - " + maxY); // 緯度範圍 String sql = "select * from district where (lon BETWEEN ? AND ?) AND (lat BETWEEN ? AND ?) and level = 'street' "; List<District> districtList = dao.find(sql, minX, maxX, minY, maxY); LOGGER.info("districtList1={}", JSON.toJSONString(districtList)); //按照距離排序 Collections.sort(districtList, new DistrictComparator(lon, lat)); return districtList; } /** * 方法二,查詢指定範圍內的所有街道(geohash查詢) * @param lon * @param lat * @return */ public List<District> findNearbyDistrictByRadius2(Double lon, Double lat, int geohashLength){ String geoCode = GeohashUtils.encodeLatLon(lat, lon, geohashLength); //1公里 LOGGER.info("geoCode={}", geoCode); StringBuilder sql = new StringBuilder("select * from district where "); GeoHash geoHash = GeoHash.withCharacterPrecision(lat, lon, geohashLength); // 當前 sql.append(" geo_code LIKE CONCAT('" + geoHash.toBase32() + "', '%') "); // N, NE, E, SE, S, SW, W, NW GeoHash[] adjacent = geoHash.getAdjacent(); for (GeoHash hash : adjacent) { sql.append("or geo_code LIKE CONCAT('" + hash.toBase32() +"', '%') "); } LOGGER.info("sql={}", sql.toString()); List<District> districtList = dao.find(sql.toString()); LOGGER.info("districtList2={}", JSON.toJSONString(districtList)); //按照距離排序 Collections.sort(districtList, new DistrictComparator(lon, lat)); return districtList; }
/**
* 按照距離遠近排序
*/
class DistrictComparator implements Comparator<District>{
private SpatialContext geo = SpatialContext.GEO;
private Double centerLon; //中心點經度
private Double centerLat; //中心點緯度
public DistrictComparator(Double centerLon, Double centerLat) {
this.centerLon = centerLon;
this.centerLat = centerLat;
}
@Override
public int compare(District o1, District o2) {
double o1Distance = geo.calcDistance(geo.makePoint(o1.getDouble("lon"), o1.getDouble("lat")), geo.makePoint(centerLon, centerLat)) * DistanceUtils.DEG_TO_KM;
o1.put("distance", o1Distance);
double o2Distance = geo.calcDistance(geo.makePoint(o2.getDouble("lon"), o2.getDouble("lat")), geo.makePoint(centerLon, centerLat)) * DistanceUtils.DEG_TO_KM;
o2.put("distance", o2Distance);
if(o1Distance < o2Distance){
return -1;
}else if(o1Distance > o2Distance){
return 1;
}else{
return 0;
}
}
}