背景:公司业务需要在地图上绘画出以任意地址且斜长为500m的正方形区域点坐标集合作为电子围栏集合区域,绘画出来的正方形区域点坐标集合通过前端APP作为触发电子围栏条件。
实现方法:
1、将中心点地址通过高德API接口地理编码解析为高德经纬度
2、通过中心点经纬度、斜长、角度计算出正方形(矩形)每个点坐标经纬度
3、最后四个点坐标组合成区域点集合
现通过java实现代码如下:
/**
*
* @Description: 计算正方(矩)形区域点坐标集合
* @param address 地址
* @param margin 斜长(km)
* @param angle 角度
* @return
* @throws NumberFormatException
* @author
* @date 2019年1月3日 下午8:07:58
*/
public String calculatUtilSquareRegion(String address,double margin,double angle){
// 地理编码(地址解析为高德经纬度)
List<AmapGeocodes> amapGeocodesList = this.adressAnalysis(address, false);
JSONArray jsonObject = null;
String location = "";
if (!CollectionUtils.isEmpty(amapGeocodesList)) {
location = amapGeocodesList.get(0).getLocation();
}
String pointX = "";
String pointY = "";
// 获取原始点经纬度
if (StringUtils.isNotBlank(location)) {
pointX = location.split(",")[0];
pointY = location.split(",")[1];
List<LngLatPointVo> list = new ArrayList<LngLatPointVo>();
// 计算角度为margin的经纬度
LngLatPointVo pointA = getLngLat(Double.parseDouble(pointY), Double.parseDouble(pointX), margin, angle);
list.add(pointA);
// 计算角度为margin+90的经纬度
LngLatPointVo pointB = getLngLat(Double.parseDouble(pointY), Double.parseDouble(pointX), margin, angle + 90);
list.add(pointB);
// 计算角度为margin+180的经纬度
LngLatPointVo pointC = getLngLat(Double.parseDouble(pointY), Double.parseDouble(pointX), margin, angle + 180);
list.add(pointC);
// 计算角度为margin+270的经纬度
LngLatPointVo pointD = getLngLat(Double.parseDouble(pointY), Double.parseDouble(pointX), margin, angle + 270);
list.add(pointD);
list.toArray();
jsonObject = (JSONArray) JSONArray.toJSON(list.toArray());
}
return jsonObject == null ? "" : jsonObject.toString();
}
/**
* @Description 根据一个点的经纬度坐标、边长距离、角度计算另一个点的经纬度(方法1)
* @param lat 纬度
* @param lng 经度
* @param margin 斜长距离(km)
* @param angle 角度
* @return
* @author
*/
public LngLatPointVo getLngLat(double lat, double lng, double margin, double angle){
LngLatPointVo lngLat = new LngLatPointVo();
double Ea = 6378137; // 赤道半径
double Eb = 6356725; // 极半径
double dx = margin * 1000 * Math.sin(angle * Math.PI / 180.0);
double dy = margin * 1000 * Math.cos(angle * Math.PI / 180.0);
double ec = Eb + (Ea - Eb) * (90.0 - lat) / 90.0;
double ed = ec * Math.cos(lat * Math.PI / 180);
double newLng = (dx / ed + lng * Math.PI / 180.0) * 180.0 / Math.PI;
double newLat = (dy / ec + lat * Math.PI / 180.0) * 180.0 / Math.PI;
lngLat.setLat(newLat);
lngLat.setLng(newLng);
return lngLat;
}
/**
* @Description 根据一个点的经纬度坐标、边长距离、角度计算另一个点的经纬度(方法2)
* @param lat 纬度
* @param lng 经度
* @param distance 边长距离
* @param angle 角度
* @return
* @author liuhai
*/
public LngLatPointVo getLngLat1(double lat, double lng, double distance,
double angle) {
LngLatPointVo lngLat = new LngLatPointVo();
double R = 6378.137;
double φ1 = ConvertDegreesToRadians(lat);
double λ1 = ConvertDegreesToRadians(lng);
double θ = ConvertDegreesToRadians(angle);
double φ2 = Math.asin(Math.sin(φ1) * Math.cos(distance / R)
+ Math.cos(φ1) * Math.sin(distance / R) * Math.cos(θ));
double λ2 = λ1
+ Math.atan2(
Math.sin(θ) * Math.sin(distance / R) * Math.cos(φ1),
Math.cos(distance / R) - Math.sin(φ1) * Math.sin(φ2));
λ2 = (λ2 + 3 * Math.PI) % (2 * Math.PI) - Math.PI; // normalise to
// -180..+180°
double lat2 = ConvertRadiansToDegrees(φ2);
double lon2 = ConvertRadiansToDegrees(λ2);
lngLat.setLat(lat2);
lngLat.setLng(lon2);
return lngLat;
}
public double ConvertDegreesToRadians(double degrees) {
return degrees * Math.PI / 180;
}
public static double ConvertRadiansToDegrees(double radian) {
return radian * 180.0 / Math.PI;
}
/**
* @Title: adressAnalysis
* @param adress
* ,batch(批量查询控制:batch 参数设置为 true 时进行批量查询操作,最多支持 10 个地址进行批量查询)
* @Description: 地理编码(地址解析为高德经纬度)
* @return 返回List
*/
public List<AmapGeocodes> adressAnalysis(String adress, Boolean batch) {
List<AmapGeocodes> list = new ArrayList<AmapGeocodes>();
SystemConfig scKey = systemConfigBiz.findCacheByCode("AMAP_API_SERVER_AK");
SystemConfig scUrl = systemConfigBiz.findCacheByCode("AMP_API_ADRESS_ANALYSIS");
String key = "", url = "";
// 非空判断
if (null != scUrl && null != scKey) {
key = scKey.getConfigValue();
url = scUrl.getConfigValue();
}
if (StringUtils.isBlank(key) || StringUtils.isBlank(url)) {
log.info("缺少AMAP_API_SERVER_AK或者AMP_API_ADRESS_ANALYSIS的系統配置項,請先在系統配置管理中進行配置");
return list;
}
Map<String, Object> params = new HashMap<String, Object>();
params.put("address", adress.trim());
params.put("output", "JSON");
params.put("batch", false);
params.put("key", key);
String json = HttpUtil.sendGet(url, params, "UTF-8");
// 将返回结果转换成map
Map<?, ?> reMap = JSON.parseObject(json);
// 请求成功
if (reMap != null && ((reMap.get("status") == null ? "" : reMap.get("status").toString()).equals("1"))) {
String geocodesArray = reMap.get("geocodes") == null ? "" : reMap.get("geocodes").toString();
if (Integer.parseInt(reMap.get("count") == null ? "0" : reMap.get("count").toString()) > 0) {
list = JSONObject.parseArray(geocodesArray, AmapGeocodes.class);
}
}
return list;
}