fix:高德地图探索
This commit is contained in:
parent
652a32b797
commit
9bc4889309
@ -18,7 +18,7 @@ spring:
|
||||
master:
|
||||
type: com.alibaba.druid.pool.DruidDataSource
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
url: jdbc:mysql://localhost:3306/fastbee?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||
url: jdbc:mysql://1.116.124.85:3306/fastbee?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||
username: root
|
||||
password: fastbee
|
||||
druid:
|
||||
@ -56,7 +56,7 @@ spring:
|
||||
# ds0: # 配置 mysql 数据源
|
||||
# type: com.alibaba.druid.pool.DruidDataSource
|
||||
# driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
# url: jdbc:mysql://localhost:3306/fastbee?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||
# url: jdbc:mysql://1.116.124.85:3306/fastbee?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||
# username: root
|
||||
# password: fastbee
|
||||
# filters: stat,wall
|
||||
@ -100,9 +100,9 @@ spring:
|
||||
|
||||
# redis 配置
|
||||
redis:
|
||||
host: localhost # 地址
|
||||
host: 1.116.124.85 # 地址
|
||||
port: 6379 # 端口,默认为6379
|
||||
database: 3 # 数据库索引
|
||||
database: 0 # 数据库索引
|
||||
password: fastbee # 密码
|
||||
timeout: 10s # 连接超时时间
|
||||
lettuce:
|
||||
@ -115,7 +115,7 @@ spring:
|
||||
mqtt:
|
||||
username: fastbee # 账号
|
||||
password: fastbee # 密码
|
||||
host-url: tcp://localhost:1883 # mqtt连接tcp地址
|
||||
host-url: tcp://1.116.124.85:1883 # mqtt连接tcp地址
|
||||
client-id: ${random.int} # 客户端Id,不能相同,采用随机数 ${random.value}
|
||||
default-topic: test # 默认主题
|
||||
timeout: 30 # 超时时间
|
||||
@ -123,7 +123,7 @@ spring:
|
||||
clearSession: true # 清除会话(设置为false,断开连接,重连后使用原来的会话 保留订阅的主题,能接收离线期间的消息)
|
||||
# EMQX API配置需要在运行emqx之后去EMQX管理后台手动创建API Key和Secret,并配置到application.yml中
|
||||
emqx:
|
||||
host: localhost # EMQX服务器地址
|
||||
host: 1.116.124.85 # EMQX服务器地址
|
||||
port: 18083
|
||||
ApiKey: emqx.key.123 # EMQX API Key
|
||||
ApiSecret: emqx.api.123
|
||||
|
||||
@ -72,7 +72,7 @@ public class HttpUtils {
|
||||
}
|
||||
log.info("recv - {}", result);
|
||||
} catch (ConnectException e) {
|
||||
log.error("调用HttpUtils.sendGet ConnectException, url=" + url + ",param=" + param, e);
|
||||
log.error("调用HttpUtils.sendGet ConnectException, url={},param={}", url, param, e);
|
||||
} catch (SocketTimeoutException e) {
|
||||
log.error("调用HttpUtils.sendGet SocketTimeoutException, url=" + url + ",param=" + param, e);
|
||||
} catch (IOException e) {
|
||||
@ -123,13 +123,13 @@ public class HttpUtils {
|
||||
}
|
||||
log.info("recv - {}", result);
|
||||
} catch (ConnectException e) {
|
||||
log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + param, e);
|
||||
log.error("调用HttpUtils.sendPost ConnectException, url={},param={}", url, param, e);
|
||||
} catch (SocketTimeoutException e) {
|
||||
log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + param, e);
|
||||
log.error("调用HttpUtils.sendPost SocketTimeoutException, url={},param={}", url, param, e);
|
||||
} catch (IOException e) {
|
||||
log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + param, e);
|
||||
log.error("调用HttpUtils.sendPost IOException, url={},param={}", url, param, e);
|
||||
} catch (Exception e) {
|
||||
log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + param, e);
|
||||
log.error("调用HttpsUtil.sendPost Exception, url={},param={}", url, param, e);
|
||||
} finally {
|
||||
try {
|
||||
if (out != null) {
|
||||
@ -139,7 +139,7 @@ public class HttpUtils {
|
||||
in.close();
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
log.error("调用in.close Exception, url=" + url + ",param=" + param, ex);
|
||||
log.error("调用in.close Exception, url={},param={}", url, param, ex);
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
@ -177,13 +177,13 @@ public class HttpUtils {
|
||||
conn.disconnect();
|
||||
br.close();
|
||||
} catch (ConnectException e) {
|
||||
log.error("调用HttpUtils.sendSSLPost ConnectException, url=" + url + ",param=" + param, e);
|
||||
log.error("调用HttpUtils.sendSSLPost ConnectException, url={},param={}", url, param, e);
|
||||
} catch (SocketTimeoutException e) {
|
||||
log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url=" + url + ",param=" + param, e);
|
||||
log.error("调用HttpUtils.sendSSLPost SocketTimeoutException, url={},param={}", url, param, e);
|
||||
} catch (IOException e) {
|
||||
log.error("调用HttpUtils.sendSSLPost IOException, url=" + url + ",param=" + param, e);
|
||||
log.error("调用HttpUtils.sendSSLPost IOException, url={},param={}", url, param, e);
|
||||
} catch (Exception e) {
|
||||
log.error("调用HttpsUtil.sendSSLPost Exception, url=" + url + ",param=" + param, e);
|
||||
log.error("调用HttpsUtil.sendSSLPost Exception, url={},param={}", url, param, e);
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
@ -213,13 +213,13 @@ public class HttpUtils {
|
||||
}
|
||||
log.info("recv - {}", result);
|
||||
} catch (ConnectException e) {
|
||||
log.error("调用HttpUtils.sendPost ConnectException, url=" + url + ",param=" + json, e);
|
||||
log.error("调用HttpUtils.sendPost ConnectException, url={},param={}", url, json, e);
|
||||
} catch (SocketTimeoutException e) {
|
||||
log.error("调用HttpUtils.sendPost SocketTimeoutException, url=" + url + ",param=" + json, e);
|
||||
log.error("调用HttpUtils.sendPost SocketTimeoutException, url={},param={}", url, json, e);
|
||||
} catch (IOException e) {
|
||||
log.error("调用HttpUtils.sendPost IOException, url=" + url + ",param=" + json, e);
|
||||
log.error("调用HttpUtils.sendPost IOException, url={},param={}", url, json, e);
|
||||
} catch (Exception e) {
|
||||
log.error("调用HttpsUtil.sendPost Exception, url=" + url + ",param=" + json, e);
|
||||
log.error("调用HttpsUtil.sendPost Exception, url={},param={}", url, json, e);
|
||||
} finally {
|
||||
try {
|
||||
if (out != null) {
|
||||
|
||||
@ -132,7 +132,7 @@ public class DevicePropFetchConsumer {
|
||||
modbusJob.setStatus("0");
|
||||
List<ModbusJob> modbusJobList = modbusJobService.selectModbusJobList(modbusJob);
|
||||
if (CollectionUtils.isEmpty(modbusJobList)) return modbusPollMsg;
|
||||
//处理子设备情况
|
||||
// 处理子设备情况
|
||||
handleSubDeviceStatus(modbusJobList);
|
||||
List<String> commandList = modbusJobList.stream().map(ModbusJob::getCommand).collect(Collectors.toList());
|
||||
|
||||
|
||||
@ -43,7 +43,7 @@ public class DeviceJobServiceImpl implements IDeviceJobService {
|
||||
/**
|
||||
* 项目启动时,初始化定时器 主要是防止手动修改数据库导致未同步到定时任务处理(注:不能手动修改数据库ID和任务组名,否则会导致脏数据)
|
||||
*/
|
||||
@PostConstruct
|
||||
//@PostConstruct
|
||||
public void init() throws SchedulerException, TaskException {
|
||||
scheduler.clear();
|
||||
// 设备定时任务
|
||||
|
||||
@ -47,7 +47,6 @@ public class DeviceOtherMsgHandler {
|
||||
* @param bo
|
||||
*/
|
||||
public void messageHandler(DeviceReportBo bo) {
|
||||
String type = "";
|
||||
String name = topicsUtils.parseTopicName(bo.getTopicName());
|
||||
if (StringUtils.isEmpty(name) || name.endsWith(TopicType.FUNCTION_GET.getTopicSuffix())) return;
|
||||
ReportDataBo data = this.buildReportData(bo);
|
||||
|
||||
@ -43,10 +43,10 @@ public class MessageEncoderWrapper extends ChannelOutboundHandlerAdapter {
|
||||
}
|
||||
output = null;
|
||||
} catch (EncoderException e) {
|
||||
log.error("消息编码异常" + packet.message, e);
|
||||
log.error("消息编码异常{}", packet.message, e);
|
||||
throw e;
|
||||
} catch (Throwable e) {
|
||||
log.error("消息编码异常" + packet.message, e);
|
||||
log.error("消息编码异常{}", packet.message, e);
|
||||
throw new EncoderException(e);
|
||||
} finally {
|
||||
if (output != null) {
|
||||
|
||||
@ -133,7 +133,7 @@ public class RegisterReqHandler extends ReqAbstractHandler implements Initializi
|
||||
rPort = viaHeader.getPort();
|
||||
}
|
||||
SipDevice device = new SipDevice();
|
||||
;
|
||||
|
||||
device.setStreammode("UDP");
|
||||
device.setDeviceSipId(sipId);
|
||||
device.setIp(received);
|
||||
@ -156,7 +156,7 @@ public class RegisterReqHandler extends ReqAbstractHandler implements Initializi
|
||||
sipSender.transmitRequest(Sipreq.getLocalAddress().getHostAddress(), response);
|
||||
// 注册成功
|
||||
if (registerFlag == 1) {
|
||||
log.info("注册成功! sipId:" + device.getDeviceSipId());
|
||||
log.info("注册成功! sipId:{}", device.getDeviceSipId());
|
||||
device.setRegistertime(DateUtils.getNowDate());
|
||||
sipDeviceService.updateDevice(device);
|
||||
List<SipDeviceChannel> channels = sipDeviceChannelService.selectSipDeviceChannelByDeviceSipId(device.getDeviceSipId());
|
||||
@ -166,7 +166,7 @@ public class RegisterReqHandler extends ReqAbstractHandler implements Initializi
|
||||
//添加到统一设备管理 默认添加到admin账号下
|
||||
int result = deviceService.insertDeviceAuto(device.getDeviceSipId(), 1L, sipConfig.getProductId());
|
||||
if (result == 1) {
|
||||
log.info("-----------sip设备认证成功,并自动添加设备到系统,SipId:" + device.getDeviceSipId() + "---------------");
|
||||
log.info("-----------sip设备认证成功,并自动添加设备到系统,SipId:{}---------------", device.getDeviceSipId());
|
||||
}
|
||||
iotdev = new Device();
|
||||
}
|
||||
@ -177,10 +177,10 @@ public class RegisterReqHandler extends ReqAbstractHandler implements Initializi
|
||||
// 重新注册更新设备和通道,以免设备替换或更新后信息无法更新
|
||||
onRegister(device);
|
||||
} else {
|
||||
log.info("未找到改设备! deviceId:" + device.getDeviceId());
|
||||
log.info("未找到改设备! deviceId:{}", device.getDeviceId());
|
||||
}
|
||||
} else if (registerFlag == 2) {
|
||||
log.info("注销成功! deviceId:" + device.getDeviceId());
|
||||
log.info("注销成功! deviceId:{}", device.getDeviceId());
|
||||
mqttService.publishStatus(device, 4);
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,23 @@
|
||||
package com.fastbee.iot.model.dto;
|
||||
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 坐标点
|
||||
* @Date: 2026-01-23
|
||||
* @Author: zhugongping
|
||||
**/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class Position {
|
||||
|
||||
@ApiModelProperty("经度")
|
||||
private double lng; // longitude
|
||||
|
||||
@ApiModelProperty("纬度")
|
||||
private double lat; // latitude
|
||||
}
|
||||
@ -46,6 +46,7 @@ import com.fastbee.iot.model.gateWay.ProductSubGatewayVO;
|
||||
import com.fastbee.iot.model.gateWay.SubDeviceListVO;
|
||||
import com.fastbee.iot.service.*;
|
||||
import com.fastbee.iot.tdengine.service.ILogService;
|
||||
import com.fastbee.iot.util.CoordinateTransform;
|
||||
import com.fastbee.system.mapper.SysDeptMapper;
|
||||
import com.fastbee.system.service.ISysUserService;
|
||||
import org.quartz.SchedulerException;
|
||||
@ -1581,9 +1582,10 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
|
||||
/**
|
||||
* 根据IP获取地址
|
||||
*
|
||||
* @param ip
|
||||
* @return
|
||||
* 流程是:
|
||||
* 调用太平洋网络IP查询接口,根据IP获取城市名称
|
||||
* 调用百度地图地理编码API,根据城市名称获取经纬度
|
||||
* @param ip IP
|
||||
*/
|
||||
private void setLocation(String ip, Device device) {
|
||||
String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp";
|
||||
@ -1607,9 +1609,10 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置经纬度
|
||||
* 根据地理编码获取经纬度
|
||||
* 调用百度地理编码获取的是BD09
|
||||
*
|
||||
* @param city
|
||||
* @param city 地理编码,如:北京市海淀区上地十街10号
|
||||
*/
|
||||
private void setLatitudeAndLongitude(String city, Device device) throws UnsupportedEncodingException {
|
||||
String BAIDU_URL = "http://api.map.baidu.com/geocoding/v3/";
|
||||
@ -1620,8 +1623,10 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
if (!StringUtils.isEmpty(baiduResponse)) {
|
||||
JSONObject baiduObject = JSONObject.parseObject(baiduResponse);
|
||||
JSONObject location = baiduObject.getJSONObject("result").getJSONObject("location");
|
||||
device.setLongitude(location.getBigDecimal("lng"));
|
||||
device.setLatitude(location.getBigDecimal("lat"));
|
||||
// 将 BD09转为GCJ02 (百度坐标系转火星坐标系)
|
||||
double[] gcj02 = CoordinateTransform.bd09ToGcj02(location.getDouble("lng"), location.getDouble("lat"));
|
||||
device.setLongitude(BigDecimal.valueOf(gcj02[0]));
|
||||
device.setLatitude(BigDecimal.valueOf(gcj02[1]));
|
||||
System.out.println(device.getSerialNumber() + "- 设置经度:" + location.getBigDecimal("lng") + ",设置纬度:"
|
||||
+ location.getBigDecimal("lat"));
|
||||
}
|
||||
|
||||
@ -318,13 +318,13 @@ public class ToolServiceImpl implements IToolService {
|
||||
if (productModel.getStatus() == 2) {
|
||||
return returnUnauthorized(mqttModel, "设备简单认证,设备处于禁用状态");
|
||||
}
|
||||
log.info("-----------设备简单认证成功,clientId:" + mqttModel.getClientId() + "---------------");
|
||||
log.info("-----------设备简单认证成功,clientId:{}---------------", mqttModel.getClientId());
|
||||
return ResponseEntity.ok().body("ok");
|
||||
} else {
|
||||
// 自动添加设备
|
||||
int result = deviceService.insertDeviceAuto(mqttModel.getDeviceNumber(), mqttModel.getUserId(), mqttModel.getProductId());
|
||||
if (result == 1) {
|
||||
log.info("-----------设备简单认证成功,并自动添加设备到系统,clientId:" + mqttModel.getClientId() + "---------------");
|
||||
log.info("-----------设备简单认证成功,并自动添加设备到系统,clientId:{}---------------", mqttModel.getClientId());
|
||||
return ResponseEntity.ok().body("ok");
|
||||
}
|
||||
return returnUnauthorized(mqttModel, "设备简单认证,自动添加设备失败");
|
||||
|
||||
@ -0,0 +1,223 @@
|
||||
package com.fastbee.iot.util;
|
||||
|
||||
/**
|
||||
* 坐标系转换工具类(WGS-84 ⇄ GCJ-02 ⇄ BD-09)
|
||||
* 基于 double 类型实现,适用于 LBS、地图开发等场景
|
||||
* 精度:约 1~10 米级
|
||||
* GPS 是 WGS-84
|
||||
* 国内地图要 GCJ-02(火星坐标系)
|
||||
* 百度再加一层 BD-09
|
||||
* OSM 坚持用 WGS-84
|
||||
* @Date: 2026-01-23
|
||||
* @Author: zhugongping
|
||||
**/
|
||||
public class CoordinateTransform {
|
||||
|
||||
// ====================== 常量定义 ======================
|
||||
|
||||
private static final double PI = Math.PI;
|
||||
private static final double A = 6378245.0; // 长半轴 (km)
|
||||
private static final double EE = 0.00669342162296594323; // 扁率平方
|
||||
|
||||
/**
|
||||
* 判断是否在国内(粗略判断,用于避免国外偏移)
|
||||
* @param lng 经度
|
||||
* @param lat 纬度
|
||||
* @return true:国内;false:国外
|
||||
*/
|
||||
private static boolean inOfChina(double lng, double lat) {
|
||||
return (lng > 73.66 && lng < 135.05 && lat > 3.86 && lat < 53.55);
|
||||
}
|
||||
|
||||
// 弧度转角度
|
||||
private static double radToDeg(double rad) {
|
||||
return rad * 180.0 / PI;
|
||||
}
|
||||
|
||||
// 角度转弧度
|
||||
private static double degToRad(double deg) {
|
||||
return deg * PI / 180.0;
|
||||
}
|
||||
|
||||
// 求两点间距离(简化球面距离,单位:米)
|
||||
private static double distance(double lng1, double lat1, double lng2, double lat2) {
|
||||
double dLat = degToRad(lat2 - lat1);
|
||||
double dLng = degToRad(lng2 - lng1);
|
||||
double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
|
||||
Math.cos(degToRad(lat1)) * Math.cos(degToRad(lat2)) *
|
||||
Math.sin(dLng / 2) * Math.sin(dLng / 2);
|
||||
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||
return A * c; // A 是地球半径(米)
|
||||
}
|
||||
|
||||
// ==================== WGS-84 → GCJ-02 ====================
|
||||
|
||||
/**
|
||||
* WGS-84 转 GCJ-02(火星坐标系)
|
||||
*/
|
||||
public static double[] wgs84ToGcj02(double wgsLng, double wgsLat) {
|
||||
if (!inOfChina(wgsLng, wgsLat)) {
|
||||
return new double[]{wgsLng, wgsLat};
|
||||
}
|
||||
|
||||
double dLat = transformLat(wgsLng - 105.0, wgsLat - 35.0);
|
||||
double dLng = transformLng(wgsLng - 105.0, wgsLat - 35.0);
|
||||
|
||||
double radLat = degToRad(wgsLat);
|
||||
double magic = Math.sin(radLat);
|
||||
magic = 1 - EE * magic * magic;
|
||||
double sqrtMagic = Math.sqrt(magic);
|
||||
|
||||
dLat = (dLat * 180.0) / ((A * (1 - EE)) / (magic * sqrtMagic) * PI);
|
||||
dLng = (dLng * 180.0) / (A / sqrtMagic * Math.cos(radLat) * PI);
|
||||
|
||||
return new double[]{wgsLng + dLng, wgsLat + dLat};
|
||||
}
|
||||
|
||||
// ==================== GCJ-02 → WGS-84 ====================
|
||||
|
||||
/**
|
||||
* GCJ-02 转 WGS-84(近似还原,存在微小误差)
|
||||
*/
|
||||
public static double[] gcj02ToWgs84(double gcjLng, double gcjLat) {
|
||||
if (!inOfChina(gcjLng, gcjLat)) {
|
||||
return new double[]{gcjLng, gcjLat};
|
||||
}
|
||||
|
||||
double[] gps = wgs84ToGcj02(gcjLng, gcjLat);
|
||||
double dLng = gps[0] - gcjLng;
|
||||
double dLat = gps[1] - gcjLat;
|
||||
return new double[]{gcjLng - dLng, gcjLat - dLat};
|
||||
}
|
||||
|
||||
// ==================== GCJ-02 → BD-09 ====================
|
||||
|
||||
/**
|
||||
* GCJ-02 转 BD-09(百度坐标系)
|
||||
*/
|
||||
public static double[] gcj02ToBd09(double gcjLng, double gcjLat) {
|
||||
double z = Math.sqrt(gcjLng * gcjLng + gcjLat * gcjLat) + 0.00002 * Math.sin(gcjLat * PI);
|
||||
double theta = Math.atan2(gcjLat, gcjLng) + 0.000003 * Math.cos(gcjLng * PI);
|
||||
double bdLng = z * Math.cos(theta) + 0.0065;
|
||||
double bdLat = z * Math.sin(theta) + 0.006;
|
||||
return new double[]{bdLng, bdLat};
|
||||
}
|
||||
|
||||
// ==================== BD-09 → GCJ-02 ====================
|
||||
|
||||
/**
|
||||
* BD-09 转 GCJ-02
|
||||
*/
|
||||
public static double[] bd09ToGcj02(double bdLng, double bdLat) {
|
||||
double x = bdLng - 0.0065;
|
||||
double y = bdLat - 0.006;
|
||||
double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * PI);
|
||||
double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * PI);
|
||||
double gcjLng = z * Math.cos(theta);
|
||||
double gcjLat = z * Math.sin(theta);
|
||||
return new double[]{gcjLng, gcjLat};
|
||||
}
|
||||
|
||||
// ==================== WGS-84 ⇄ BD-09 快捷转换 ====================
|
||||
|
||||
/**
|
||||
* WGS-84 转 BD-09(先转 GCJ-02,再转 BD-09)
|
||||
*/
|
||||
public static double[] wgs84ToBd09(double wgsLng, double wgsLat) {
|
||||
double[] gcj = wgs84ToGcj02(wgsLng, wgsLat);
|
||||
return gcj02ToBd09(gcj[0], gcj[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* BD-09 转 WGS-84(先转 GCJ-02,再转 WGS-84)
|
||||
*/
|
||||
public static double[] bd09ToWgs84(double bdLng, double bdLat) {
|
||||
double[] gcj = bd09ToGcj02(bdLng, bdLat);
|
||||
return gcj02ToWgs84(gcj[0], gcj[1]);
|
||||
}
|
||||
|
||||
// ==================== 内部辅助函数 ====================
|
||||
|
||||
private static double transformLat(double lng, double lat) {
|
||||
double ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng));
|
||||
ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0;
|
||||
ret += (20.0 * Math.sin(lat * PI) + 40.0 * Math.sin(lat / 3.0 * PI)) * 2.0 / 3.0;
|
||||
ret += (160.0 * Math.sin(lat / 12.0 * PI) + 320 * Math.sin(lat * PI / 30.0)) * 2.0 / 3.0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static double transformLng(double lng, double lat) {
|
||||
double ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng));
|
||||
ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0;
|
||||
ret += (20.0 * Math.sin(lng * PI) + 40.0 * Math.sin(lng / 3.0 * PI)) * 2.0 / 3.0;
|
||||
ret += (150.0 * Math.sin(lng / 12.0 * PI) + 300.0 * Math.sin(lng / 30.0 * PI)) * 2.0 / 3.0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// ==================== 工具方法 ====================
|
||||
|
||||
/**
|
||||
* 格式化坐标输出(保留6位小数)
|
||||
*/
|
||||
public static String formatCoord(double lng, double lat) {
|
||||
return String.format("%.6f, %.6f", lng, lat);
|
||||
}
|
||||
|
||||
/**
|
||||
* 四舍五入到指定位数
|
||||
*/
|
||||
public static double round(double value, int scale) {
|
||||
double factor = Math.pow(10, scale);
|
||||
return Math.round(value * factor) / factor;
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印转换结果(调试用)
|
||||
*/
|
||||
public static void printConvert(String fromType, double fromLng, double fromLat,
|
||||
String toType, double[] result) {
|
||||
System.out.printf("%s [%.6f, %.6f] → %s [%s]%n",
|
||||
fromType, fromLng, fromLat,
|
||||
toType, formatCoord(result[0], result[1]));
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
// 北京天安门 GPS 坐标(WGS-84)116.397428,39.90923
|
||||
// 长沙市政府 BD-09 112.945473,28.234889
|
||||
// 岩安测控 WGS-84 112.862816,28.125462
|
||||
|
||||
double wgsLng = 112.862816;
|
||||
double wgsLat = 28.125462;
|
||||
|
||||
System.out.println("是否在国内 = " + inOfChina(wgsLng, wgsLat));
|
||||
|
||||
|
||||
System.out.println("=== 坐标系转换测试 ===");
|
||||
|
||||
// WGS-84 → GCJ-02 111.94760023918425,28.812219998012168
|
||||
double[] gcj = CoordinateTransform.wgs84ToGcj02(wgsLng, wgsLat);
|
||||
CoordinateTransform.printConvert("WGS-84", wgsLng, wgsLat, "GCJ-02", gcj);
|
||||
|
||||
// GCJ-02 → BD-09
|
||||
double[] bd = CoordinateTransform.gcj02ToBd09(gcj[0], gcj[1]);
|
||||
CoordinateTransform.printConvert("GCJ-02", gcj[0], gcj[1], "BD-09", bd);
|
||||
|
||||
// BD-09 → GCJ-02
|
||||
double[] gcjBack = CoordinateTransform.bd09ToGcj02(112.945473,28.234889);
|
||||
CoordinateTransform.printConvert("BD-09", bd[0], bd[1], "GCJ-02", gcjBack);
|
||||
|
||||
// GCJ-02 → WGS-84 111.944921,28.809167
|
||||
double[] wgsBack = CoordinateTransform.gcj02ToWgs84(111.944921,28.809167);
|
||||
CoordinateTransform.printConvert("GCJ-02", gcjBack[0], gcjBack[1], "WGS-84", wgsBack);
|
||||
|
||||
// 直接计算 WGS-84 → BD-09
|
||||
double[] directBd = CoordinateTransform.wgs84ToBd09(wgsLng, wgsLat);
|
||||
CoordinateTransform.printConvert("WGS-84", wgsLng, wgsLat, "BD-09(Direct)", directBd);
|
||||
|
||||
// 计算偏移距离
|
||||
double offset = CoordinateTransform.distance(wgsLng, wgsLat, gcj[0], gcj[1]);
|
||||
System.out.printf("WGS-84 与 GCJ-02 偏移距离:%.2f 米%n", offset);
|
||||
}
|
||||
|
||||
}
|
||||
23428
vue/package-lock.json
generated
23428
vue/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -44,8 +44,10 @@
|
||||
"axios": "0.24.0",
|
||||
"clipboard": "2.0.8",
|
||||
"codemirror": "^5.65.2",
|
||||
"coordtransform": "^2.1.2",
|
||||
"core-js": "3.25.3",
|
||||
"echarts": "^5.4.0",
|
||||
"echarts-extension-leaflet": "^1.2.2",
|
||||
"echarts-liquidfill": "^3.0.6",
|
||||
"element-china-area-data": "^4.1.1",
|
||||
"element-ui": "2.15.10",
|
||||
@ -62,6 +64,7 @@
|
||||
"json-loader": "^0.5.7",
|
||||
"jsonlint": "^1.6.3",
|
||||
"jszip": "^3.10.1",
|
||||
"leaflet": "^1.9.4",
|
||||
"lodash": "^4.17.21",
|
||||
"minimatch": "^3.1.2",
|
||||
"moment": "^2.29.4",
|
||||
|
||||
@ -1,28 +1,34 @@
|
||||
// 使用OpenStreetMap和Leaflet替代百度地图
|
||||
export function loadBMap() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
if (typeof BMap !== 'undefined') {
|
||||
resolve(BMap)
|
||||
return true
|
||||
// eslint-disable-next-line no-undef
|
||||
if (typeof L !== 'undefined') {
|
||||
// eslint-disable-next-line no-undef
|
||||
resolve(L);
|
||||
return true;
|
||||
}
|
||||
window.onBMapCallback = function() {
|
||||
resolve(BMap)
|
||||
|
||||
// 加载Leaflet CSS
|
||||
if (!document.querySelector('link[href*="leaflet.css"]')) {
|
||||
const link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
link.href = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css';
|
||||
link.integrity = 'sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=';
|
||||
link.crossOrigin = '';
|
||||
document.head.appendChild(link);
|
||||
}
|
||||
// 使用https协议需要添加一下meta标签
|
||||
var protocolStr = document.location.protocol;
|
||||
if(protocolStr == "https:")
|
||||
{
|
||||
let meta = document.createElement('meta')
|
||||
meta.httpEquiv = 'Content-Security-Policy'
|
||||
meta.content ='upgrade-insecure-requests'
|
||||
meta.onerror = reject
|
||||
document.head.appendChild(meta)
|
||||
}
|
||||
// 引入百度地图
|
||||
let script = document.createElement('script')
|
||||
script.type = 'text/javascript'
|
||||
script.src =
|
||||
'http://api.map.baidu.com/api?v=2.0&ak='+ process.env.VUE_APP_BAI_DU_AK +'&s=1&__ec_v__=20190126&callback=onBMapCallback'
|
||||
script.onerror = reject
|
||||
document.head.appendChild(script)
|
||||
})
|
||||
|
||||
// 加载Leaflet JS
|
||||
const script = document.createElement('script');
|
||||
script.type = 'text/javascript';
|
||||
script.src = 'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js';
|
||||
script.integrity = 'sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=';
|
||||
script.crossOrigin = '';
|
||||
script.onload = function() {
|
||||
// eslint-disable-next-line no-undef
|
||||
resolve(L);
|
||||
};
|
||||
script.onerror = reject;
|
||||
document.head.appendChild(script);
|
||||
});
|
||||
}
|
||||
|
||||
@ -319,10 +319,9 @@ import axios from 'axios';
|
||||
import { getDeviceStatistic } from '@/api/iot/device';
|
||||
import { listNotice, getNotice } from '@/api/system/notice';
|
||||
import CountTo from 'vue-count-to';
|
||||
import { loadBMap } from '@/utils/map.js';
|
||||
//安装的是echarts完整包,里面包含百度地图扩展,路径为 echarts/extension/bmap/bmap.js,将其引入
|
||||
//ECharts的百度地图扩展,可以在百度地图上展现点图,线图,热力图等可视化
|
||||
// 注意:bmap扩展需要在BMap加载后再引入
|
||||
import 'leaflet/dist/leaflet.css';
|
||||
import 'echarts-extension-leaflet';
|
||||
import coordtransform from 'coordtransform';
|
||||
import { getServer } from '@/api/monitor/server';
|
||||
import { listAllDeviceShort } from '@/api/iot/device';
|
||||
import 'echarts-liquidfill';
|
||||
@ -1157,23 +1156,10 @@ export default {
|
||||
/**加载地图*/
|
||||
loadMap() {
|
||||
this.$nextTick(() => {
|
||||
loadBMap().then(() => {
|
||||
if (typeof BMap === 'undefined') {
|
||||
this.$message.error('百度地图API未正确加载');
|
||||
return;
|
||||
}
|
||||
// 确保bmap扩展已加载
|
||||
try {
|
||||
require('echarts/extension/bmap/bmap');
|
||||
} catch (e) {
|
||||
// 忽略扩展加载错误
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.getmap();
|
||||
}, 500);
|
||||
}).catch((error) => {
|
||||
this.$message.error('百度地图加载失败,请检查API Key配置');
|
||||
});
|
||||
// 使用 echarts-extension-leaflet,已在 import 时加载
|
||||
setTimeout(() => {
|
||||
this.getmap();
|
||||
}, 300);
|
||||
});
|
||||
},
|
||||
/** 查询服务器信息 */
|
||||
@ -1229,13 +1215,14 @@ export default {
|
||||
|
||||
/** 地图 */
|
||||
getmap() {
|
||||
if (typeof BMap === 'undefined') {
|
||||
this.$message.error('百度地图API未正确加载,请刷新页面重试');
|
||||
if (!this.$refs.map) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.$refs.map) {
|
||||
return;
|
||||
// 销毁旧的ECharts实例,避免多个实例叠加
|
||||
var existingChart = this.$echarts.getInstanceByDom(this.$refs.map);
|
||||
if (existingChart) {
|
||||
existingChart.dispose();
|
||||
}
|
||||
|
||||
var myChart = this.$echarts.init(this.$refs.map);
|
||||
@ -1258,8 +1245,14 @@ export default {
|
||||
let convertData = function (data, status) {
|
||||
var res = [];
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
var geoCoord = [data[i].longitude, data[i].latitude];
|
||||
if (geoCoord && data[i].status == status) {
|
||||
// 验证经纬度有效性
|
||||
var lng = parseFloat(data[i].longitude);
|
||||
var lat = parseFloat(data[i].latitude);
|
||||
if (isNaN(lng) || isNaN(lat) || lng === 0 || lat === 0) {
|
||||
continue;
|
||||
}
|
||||
var geoCoord = [lng, lat];
|
||||
if (data[i].status == status) {
|
||||
res.push({
|
||||
name: data[i].deviceName,
|
||||
value: geoCoord,
|
||||
@ -1320,14 +1313,9 @@ export default {
|
||||
return { center: [centerLng, centerLat], zoom: zoom };
|
||||
};
|
||||
|
||||
// 使用长沙作为默认中心点,如果有设备数据则使用计算的中心点
|
||||
let mapConfig = { center: [112.867037, 28.11857], zoom: 12 };
|
||||
if (this.deviceList && this.deviceList.length > 0) {
|
||||
const calculated = calculateMapCenter(this.deviceList);
|
||||
if (calculated.center && calculated.center[0] > 0) {
|
||||
mapConfig = calculated;
|
||||
}
|
||||
}
|
||||
// 使用中国长沙作为地图中心点
|
||||
// 长沙坐标:经度 112.94, 纬度 28.23
|
||||
let mapConfig = { center: [112.94, 28.23], zoom: 5 };
|
||||
|
||||
option = {
|
||||
title: {
|
||||
@ -1382,21 +1370,27 @@ export default {
|
||||
return htmlStr;
|
||||
},
|
||||
},
|
||||
bmap: {
|
||||
center: mapConfig.center,
|
||||
zoom: mapConfig.zoom,
|
||||
leaflet: {
|
||||
// echarts-extension-leaflet center 使用 [lng, lat] 格式
|
||||
// 长沙坐标:经度 112.94, 纬度 28.23
|
||||
center: [112.94, 28.23],
|
||||
zoom: 5,
|
||||
roam: true,
|
||||
// 确保地图正确初始化
|
||||
enableMapClick: true,
|
||||
// 暂时不使用自定义样式,确保地图能正常显示
|
||||
// mapStyle: {
|
||||
// styleJson: [...]
|
||||
// },
|
||||
tiles: [
|
||||
{
|
||||
// 使用高德地图瓦片(国内访问稳定)
|
||||
urlTemplate: 'https://webrd0{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}',
|
||||
options: {
|
||||
subdomains: ['1', '2', '3', '4'],
|
||||
attribution: '© 高德地图',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'scatter',
|
||||
coordinateSystem: 'bmap',
|
||||
coordinateSystem: 'leaflet',
|
||||
data: convertData(this.deviceList, 1),
|
||||
symbolSize: 15,
|
||||
itemStyle: {
|
||||
@ -1405,7 +1399,7 @@ export default {
|
||||
},
|
||||
{
|
||||
type: 'scatter',
|
||||
coordinateSystem: 'bmap',
|
||||
coordinateSystem: 'leaflet',
|
||||
data: convertData(this.deviceList, 2),
|
||||
symbolSize: 15,
|
||||
itemStyle: {
|
||||
@ -1414,7 +1408,7 @@ export default {
|
||||
},
|
||||
{
|
||||
type: 'scatter',
|
||||
coordinateSystem: 'bmap',
|
||||
coordinateSystem: 'leaflet',
|
||||
data: convertData(this.deviceList, 4),
|
||||
symbolSize: 15,
|
||||
itemStyle: {
|
||||
@ -1423,7 +1417,7 @@ export default {
|
||||
},
|
||||
{
|
||||
type: 'effectScatter',
|
||||
coordinateSystem: 'bmap',
|
||||
coordinateSystem: 'leaflet',
|
||||
data: convertData(this.deviceList, 3),
|
||||
symbolSize: 15,
|
||||
showEffectOn: 'render',
|
||||
@ -1465,35 +1459,10 @@ export default {
|
||||
// 设置地图配置
|
||||
myChart.setOption(option, true);
|
||||
|
||||
// 等待地图初始化后调整大小和刷新
|
||||
setTimeout(() => {
|
||||
// 等待地图初始化完成
|
||||
this.$nextTick(() => {
|
||||
myChart.resize();
|
||||
|
||||
const bmapModel = myChart.getModel().getComponent('bmap');
|
||||
if (bmapModel && bmapModel.getBMap) {
|
||||
const bmapInstance = bmapModel.getBMap();
|
||||
if (bmapInstance) {
|
||||
// 启用地图功能
|
||||
bmapInstance.enableScrollWheelZoom(true);
|
||||
bmapInstance.enableDoubleClickZoom(true);
|
||||
bmapInstance.enableDragging(true);
|
||||
|
||||
// 设置中心点和缩放级别
|
||||
const centerPoint = new BMap.Point(mapConfig.center[0], mapConfig.center[1]);
|
||||
bmapInstance.setCenter(centerPoint);
|
||||
bmapInstance.setZoom(mapConfig.zoom);
|
||||
|
||||
// 刷新地图确保瓦片加载
|
||||
setTimeout(() => {
|
||||
myChart.resize();
|
||||
bmapInstance.reset();
|
||||
bmapInstance.setCenter(centerPoint);
|
||||
bmapInstance.setZoom(mapConfig.zoom);
|
||||
myChart.resize();
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
}, 800);
|
||||
});
|
||||
} catch (error) {
|
||||
this.$message.error('地图初始化失败: ' + error.message);
|
||||
}
|
||||
|
||||
@ -79,7 +79,7 @@
|
||||
<script type="text/javascript">
|
||||
// 从环境变量获取AK(这里需要手动设置,因为这是静态HTML文件)
|
||||
// 实际项目中应该从 process.env.VUE_APP_BAI_DU_AK 获取
|
||||
const AK = 's9mUXN9a7bKgVu6U6Z3bXhKSxtz97YPI'; // 请替换为您的AK
|
||||
const AK = 'FN2xbG1m5q9iwNq1heNF9nBOb2uhv3Om'; // 请替换为您的AK
|
||||
|
||||
// 更新状态显示
|
||||
function updateStatus(elementId, text, className) {
|
||||
|
||||
@ -1088,21 +1088,44 @@ export default {
|
||||
},
|
||||
// 地图定位
|
||||
getmap() {
|
||||
this.map = new BMap.Map('map');
|
||||
let point = null;
|
||||
if (this.form.longitude != null && this.form.longitude != '' && this.form.latitude != null && this.form.latitude != '') {
|
||||
point = new BMap.Point(this.form.longitude, this.form.latitude);
|
||||
} else {
|
||||
point = new BMap.Point(116.404, 39.915);
|
||||
// 清除旧地图实例
|
||||
if (this.map) {
|
||||
this.map.remove();
|
||||
this.map = null;
|
||||
}
|
||||
|
||||
// 使用Leaflet和高德地图
|
||||
let lat, lng;
|
||||
let parsedLat = parseFloat(this.form.latitude);
|
||||
let parsedLng = parseFloat(this.form.longitude);
|
||||
|
||||
// 验证坐标有效性
|
||||
if (!isNaN(parsedLat) && !isNaN(parsedLng) && parsedLat !== 0 && parsedLng !== 0) {
|
||||
lat = parsedLat;
|
||||
lng = parsedLng;
|
||||
} else {
|
||||
// 默认使用中国长沙坐标
|
||||
lat = 28.23;
|
||||
lng = 112.94;
|
||||
}
|
||||
|
||||
// 创建地图
|
||||
this.map = L.map('map').setView([lat, lng], 15);
|
||||
|
||||
// 添加高德地图图层(国内访问稳定)
|
||||
L.tileLayer('https://webrd0{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}', {
|
||||
subdomains: ['1', '2', '3', '4'],
|
||||
attribution: '© 高德地图',
|
||||
maxZoom: 18
|
||||
}).addTo(this.map);
|
||||
|
||||
// 添加标记点
|
||||
this.mk = L.marker([lat, lng]).addTo(this.map);
|
||||
|
||||
// 添加popup提示
|
||||
if (this.form.deviceName) {
|
||||
this.mk.bindPopup(`<b>${this.form.deviceName}</b><br>${this.form.networkAddress || ''}`).openPopup();
|
||||
}
|
||||
this.map.centerAndZoom(point, 19);
|
||||
this.map.enableScrollWheelZoom(true); // 开启鼠标滚轮缩放
|
||||
this.map.addControl(new BMap.NavigationControl());
|
||||
|
||||
// 标注设备位置
|
||||
this.mk = new BMap.Marker(point);
|
||||
this.map.addOverlay(this.mk);
|
||||
this.map.panTo(point);
|
||||
},
|
||||
// 生成随机字母和数字
|
||||
generateNum() {
|
||||
|
||||
@ -1185,20 +1185,40 @@ export default {
|
||||
},
|
||||
// 地图定位
|
||||
getmap() {
|
||||
this.map = new BMap.Map('map');
|
||||
let point = null;
|
||||
if (this.form.longitude != null && this.form.longitude != '' && this.form.latitude != null && this.form.latitude != '') {
|
||||
point = new BMap.Point(this.form.longitude, this.form.latitude);
|
||||
} else {
|
||||
point = new BMap.Point(116.404, 39.915);
|
||||
// 清除旧地图实例
|
||||
if (this.map) {
|
||||
this.map.remove();
|
||||
this.map = null;
|
||||
}
|
||||
|
||||
// 使用Leaflet和OpenStreetMap
|
||||
let lat, lng;
|
||||
if (this.form.latitude != null && this.form.latitude != '' && this.form.longitude != null && this.form.longitude != '') {
|
||||
lat = parseFloat(this.form.latitude);
|
||||
lng = parseFloat(this.form.longitude);
|
||||
} else {
|
||||
// 默认使用北京天安门坐标
|
||||
lat = 39.915;
|
||||
lng = 116.404;
|
||||
}
|
||||
|
||||
// 创建地图
|
||||
this.map = L.map('map').setView([lat, lng], 15);
|
||||
|
||||
// 添加高德地图图层(国内访问稳定)
|
||||
L.tileLayer('https://webrd0{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}', {
|
||||
subdomains: ['1', '2', '3', '4'],
|
||||
attribution: '© 高德地图',
|
||||
maxZoom: 18
|
||||
}).addTo(this.map);
|
||||
|
||||
// 添加标记点
|
||||
this.mk = L.marker([lat, lng]).addTo(this.map);
|
||||
|
||||
// 添加popup提示
|
||||
if (this.form.deviceName) {
|
||||
this.mk.bindPopup(`<b>${this.form.deviceName}</b><br>${this.form.networkAddress || ''}`).openPopup();
|
||||
}
|
||||
this.map.centerAndZoom(point, 19);
|
||||
this.map.enableScrollWheelZoom(true); // 开启鼠标滚轮缩放
|
||||
this.map.addControl(new BMap.NavigationControl());
|
||||
// 标注设备位置
|
||||
this.mk = new BMap.Marker(point);
|
||||
this.map.addOverlay(this.mk);
|
||||
this.map.panTo(point);
|
||||
},
|
||||
// 生成随机字母和数字
|
||||
generateNum() {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user