2025-07-04 15:00:31 +08:00

1906 lines
67 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>设备总览</title>
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="mobile-web-app-capable" content="yes">
<link rel="stylesheet" href="../lib/layui-v2.6.3/css/layui.css" media="all">
<link rel="stylesheet" href="../lib/font-awesome-4.7.0/css/font-awesome.min.css" media="all">
<link rel="stylesheet" href="../css/public.css" media="all">
<link rel="stylesheet" href="../css/ol.css">
<!-- 谷歌地图的API接口这个访问没有天地图的次数多所以这边限制只有管理员能使用 -->
<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyBEBxB5n9vikPHbfNMzo_dzneXJi77YqjE" async defer></script>
<style>
.top-panel {
border: 1px solid #eceff9;
border-radius: 5px;
text-align: center;
}
.stats-panel {
height: 100%;
display: flex;
flex-direction: column;
}
.stats-panel > .layui-card-body {
height: auto;
min-height: 60px;
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
}
.top-panel-number {
font-size: 30px;
border-right: 1px solid #eceff9;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.top-panel-tips{
line-height:30px;
font-size: 12px
}
.flex-row {
display: flex;
align-items: stretch;
flex-wrap: wrap;
}
.flex-row > .layui-col-xs12,
.flex-row > .layui-col-md6 {
display: flex;
flex-direction: column;
}
.stats-panel .layui-col-xs3 a,
.stats-panel .layui-col-md4 a,
.stats-panel .layui-col-xs3 div a,
.stats-panel .layui-col-md4 div a {
display: block;
margin-bottom: 2px;
}
ul li {
list-style: none;
}
.layuimini-main {
height: 96vh;
display: flex;
flex-direction: column;
}
.map-wrapper {
flex: 1;
display: flex;
flex-direction: column;
position: relative;
}
.map-card {
flex: 1;
background: white;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
border: 1px solid #e6e8eb;
display: flex;
flex-direction: column;
overflow: hidden;
}
#map-container {
flex: 1;
width: 100%;
padding: 0;
margin: 0;
position: relative;
}
.device-overview-bar {
background: white;
border: 1px solid #e6e8eb;
border-radius: 6px;
box-shadow: 0 2px 6px rgba(0,0,0,0.08);
padding: 10px 15px;
margin-bottom: 10px;
color: #333;
}
.map-toolbar {
position: absolute;
top: 15px;
left: 50%;
transform: translateX(-50%);
z-index: 1000;
background: rgba(255, 255, 255, 0.96);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 12px;
padding: 8px 16px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
display: flex;
align-items: center;
gap: 12px;
width:auto;
max-width: 95vw;
}
.toolbar-item {
display: flex;
align-items: center;
gap: 6px;
white-space: nowrap;
}
.toolbar-label {
font-size: 12px;
color: #666;
white-space: nowrap;
font-weight: 500;
}
.toolbar-select {
border: 1px solid rgba(230, 230, 230, 0.8);
border-radius: 6px;
padding: 4px 8px;
font-size: 12px;
background: rgba(255, 255, 255, 0.9);
min-width: 120px;
height: 28px;
}
.toolbar-input {
border: 1px solid rgba(230, 230, 230, 0.8);
border-radius: 6px;
padding: 4px 8px;
font-size: 12px;
background: rgba(255, 255, 255, 0.9);
width: 140px;
height: 28px;
}
.toolbar-btn {
background: rgba(26, 160, 148, 0.9);
color: white;
border: none;
border-radius: 6px;
padding: 4px 12px;
font-size: 12px;
cursor: pointer;
height: 28px;
transition: all 0.2s ease;
}
.toolbar-btn:hover {
background: rgba(26, 160, 148, 1);
transform: translateY(-1px);
}
.toolbar-divider {
width: 1px;
height: 20px;
background: rgba(230, 230, 230, 0.6);
}
.overview-stats {
display: flex;
flex-wrap: wrap;
gap: 8px;
align-items: center;
}
.stat-item {
display: flex;
align-items: center;
/* background: #f8f9fa;
border: 1px solid #e9ecef; */
border-radius: 4px;
padding: 5px 10px;
min-width: 85px;
transition: background-color 0.2s ease;
cursor: pointer;
height: 28px;
}
.stat-item:hover {
background: #e9ecef;
}
.stat-item.non-clickable {
cursor: default;
}
.stat-item.non-clickable:hover {
background: #f8f9fa;
}
.stat-dot {
width: 6px;
height: 6px;
border-radius: 50%;
margin-right: 6px;
flex-shrink: 0;
}
.dot-green { background-color: #48bb78; }
.dot-blue { background-color: #4299e1; }
.dot-gray { background-color: #a0aec0; }
.dot-orange { background-color: #ed8936; }
.dot-red { background-color: #f56565; }
.dot-yellow { background-color: #ecc94b; }
.stat-item span:nth-child(2) {
font-size: 12px;
color: #666;
margin-right: 4px;
white-space: nowrap;
}
.stat-number {
font-size: 13px;
font-weight: bold;
color: #333;
}
/* 由于不使用layui的元素所以这边还得适配手机端 */
@media screen and (max-width: 768px) {
.layuimini-main {
height: 108vh;
overflow: hidden;
}
.map-wrapper {
flex: 1;
overflow: hidden;
}
.map-card {
margin: 8px;
height: calc(100% - 16px);
overflow: hidden;
}
#map-container {
height: 100%;
overflow: hidden;
}
.device-overview-bar {
padding: 8px 12px;
margin-bottom: 8px;
flex-shrink: 0;
}
.overview-stats {
gap: 6px;
flex-wrap: wrap;
justify-content: flex-start;
}
.stat-item {
flex: 0 0 auto;
min-width: 70px;
width: auto;
max-width: calc(50% - 4px);
padding: 4px 6px;
font-size: 10px;
height: 24px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: flex;
align-items: center;
gap: 2px;
}
.stat-item span:not(.stat-dot):not(.stat-number) {
font-size: 9px;
max-width: 40px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.stat-dot {
width: 6px;
height: 6px;
margin-right: 0;
flex-shrink: 0;
}
.stat-number {
font-size: 12px;
font-weight: bold;
color: #333;
}
.map-toolbar {
position: absolute;
transform: none;
left: auto;
margin: 8px;
margin-bottom: 8px;
min-width: auto;
max-width: none;
flex-wrap: wrap;
gap: 6px;
padding: 6px 10px;
top:0px;
}
.toolbar-item {
gap: 4px;
flex: 0 0 auto;
width:100%
}
.toolbar-label {
font-size: 10px;
display: none;
}
.toolbar-select,
.toolbar-input {
font-size: 11px;
height: 24px;
width: 100%;
}
.toolbar-btn {
font-size: 11px;
height: 24px;
padding: 2px 8px;
}
.toolbar-divider {
display: none;
}
#toolbar-device-locate span:not(.toolbar-label) {
font-size: 10px;
padding: 2px 4px;
}
.dropdown-menu {
min-width: 180px;
max-width: 95vw;
}
.dropdown-item {
padding: 8px 12px;
font-size: 12px;
}
}
.ol-tooltip {
position: absolute;
background: rgba(255,255,255,0.8);
border-radius: 4px;
padding: 4px 8px;
border: 1px solid #d9d9d9;
color: #333;
font-size: 12px;
white-space: nowrap;
pointer-events: none;
z-index: 1001;
}
.ol-tooltip-static {
background: #ffcc33;
color: #222;
border: 1px solid #ffcc33;
}
.dropdown-container {
position: relative;
display: inline-block;
width: 100%;
}
.dropdown-toggle {
cursor: pointer;
display: flex;
align-items: center;
width: 100%;
justify-content: center;
}
.dropdown-toggle::after {
content: '';
display: inline-block;
margin-left: 6px;
border-top: 4px solid;
border-right: 4px solid transparent;
border-left: 4px solid transparent;
vertical-align: middle;
}
.dropdown-menu {
display: none;
position: absolute;
top: 100%;
left: 0;
z-index: 1100;
min-width: 200px;
padding: 8px 0;
margin-top: 2px;
background: white;
border-radius: 6px;
box-shadow: 0 3px 12px rgba(0,0,0,0.15);
border: 1px solid rgba(0,0,0,0.1);
}
.dropdown-menu.show {
display: block;
}
.dropdown-group {
padding: 0 8px;
}
.dropdown-group-title {
font-size: 12px;
color: #666;
padding: 6px 8px;
font-weight: 500;
}
.dropdown-item {
padding: 6px 12px;
cursor: pointer;
font-size: 13px;
color: #333;
border-radius: 4px;
margin: 2px 0;
}
.dropdown-item:hover {
background: rgba(26, 160, 148, 0.1);
}
.dropdown-divider {
height: 1px;
background: #e9ecef;
margin: 8px 0;
}
.switch-label {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
cursor: pointer;
}
.switch-label input {
opacity: 0;
width: 0;
height: 0;
position: absolute;
}
.switch-slider {
position: relative;
display: inline-block;
width: 36px;
height: 18px;
background-color: #ccc;
border-radius: 18px;
transition: .4s;
flex-shrink: 0;
}
.switch-slider:before {
position: absolute;
content: "";
height: 14px;
width: 14px;
left: 2px;
bottom: 2px;
background-color: white;
border-radius: 50%;
transition: .4s;
}
input:checked + .switch-slider {
background-color: #1aa094;
}
input:checked + .switch-slider:before {
transform: translateX(18px);
}
/* 测量状态指示器样式 */
#measureStatus {
background: rgba(255, 255, 255, 0.98);
border: 1px solid rgba(72, 187, 120, 0.3);
border-radius: 8px;
padding: 6px 12px;
box-shadow: 0 2px 8px rgba(72, 187, 120, 0.2);
align-items: center;
gap: 8px;
}
#measureStatus .toolbar-btn {
padding: 2px 6px;
height: 24px;
min-width: 24px;
}
</style>
<script src="../js/ol.js"></script>
</head>
<body>
<div class="layuimini-main">
<div class="device-overview-bar">
<div class="overview-stats">
<div class="stat-item non-clickable">
<span class="stat-dot dot-green"></span>
<span>设备数量</span>
<span class="stat-number" th:text="${deviceOnlineNum}">1101</span>
</div>
<div class="stat-item non-clickable">
<span class="stat-dot dot-blue"></span>
<span>装机量</span>
<span class="stat-number" th:text="${deviceDeployedNum}">1123</span>
</div>
<div class="stat-item" onclick="queryDevices('offline')" th:if="${deviceOfflineNum > 0}">
<span class="stat-dot dot-gray"></span>
<span>掉线数</span>
<span class="stat-number" th:text="${deviceOfflineNum}">22</span>
</div>
<div class="stat-item" onclick="queryDevices('no_fwd')" th:if="${deviceNoFwdNum > 0}">
<span class="stat-dot dot-orange"></span>
<span>未推送数</span>
<span class="stat-number" th:text="${deviceNoFwdNum}">11</span>
</div>
<div class="stat-item" onclick="queryDevices('nofixed')" th:if="${noFix > 0}">
<span class="stat-dot dot-red"></span>
<span>长期无效解</span>
<span class="stat-number" th:text="${noFix}">10</span>
</div>
<div class="stat-item" onclick="queryDevices('warning2')" th:if="${warning2Num > 0}">
<span class="stat-dot dot-red"></span>
<span>严重告警</span>
<span class="stat-number" th:text="${warning2Num}">40</span>
</div>
<div class="stat-item" onclick="queryDevices('warning1')" th:if="${warning1Num > 0}">
<span class="stat-dot dot-orange"></span>
<span>一般告警</span>
<span class="stat-number" th:text="${warning1Num}">79</span>
</div>
<div class="stat-item" onclick="queryDevices('nogga')" th:if="${noGGA > 0}">
<span class="stat-dot dot-gray"></span>
<span>无GGA告警</span>
<span class="stat-number" th:text="${noGGA}">4</span>
</div>
</div>
</div>
<div class="map-wrapper">
<div class="map-card">
<div id="map-container">
<div class="map-toolbar">
<div class="toolbar-item">
<select id="mapTypeSelectNew" class="toolbar-select" lay-filter="mapTypeNew" onchange="onMapTypeChange()">
<option value="tianditu_satellite" selected>天地图-卫星影像</option>
<option value="tianditu_terrain_hybrid" >天地图-地形混合</option>
<option value="tianditu_normal">天地图-矢量</option>
<option value="tianditu_terrain">天地图-地形</option>
<option value="amap_satellite">高德-卫星影像</option>
<option value="amap">高德-矢量</option>
<!-- 只能超级管理员使用 -->
<option value="google_satellite" th:if="${role=='SUPER_ADMIN'}">谷歌-卫星影像</option>
<option value="google_terrain" th:if="${role=='SUPER_ADMIN'}">谷歌-地形图</option>
<option value="google_roadmap" th:if="${role=='SUPER_ADMIN'}">谷歌-道路图</option>
<option value="google_hybrid" th:if="${role=='SUPER_ADMIN'}">谷歌-混合</option>
</select>
</div>
<div class="toolbar-divider"></div>
<div class="toolbar-item">
<input type="text" id="deviceSearchNew" class="toolbar-input" placeholder="搜索设备编号">
<button class="toolbar-btn" onclick="searchDeviceNew()">搜索</button>
</div>
<div class="toolbar-divider"></div>
<div class="toolbar-item">
<select id="warningFilter" class="toolbar-select" onchange="onWarningFilterChange()">
<option value="all">全部设备</option>
<option value="warning1">一般告警</option>
<option value="warning2" selected>严重告警</option>
</select>
</div>
<div class="toolbar-divider"></div>
<div class="toolbar-item">
<div class="dropdown-container">
<button class="toolbar-btn dropdown-toggle" onclick="toggleMapFunctionsMenu()">地图功能</button>
<div id="mapFunctionsMenu" class="dropdown-menu">
<div class="dropdown-group">
<div class="dropdown-group-title">信息显示</div>
<div class="dropdown-item">
<label class="switch-label">
<span>显示设备信息</span>
<input type="checkbox" id="showDeviceIdSwitch" checked onchange="toggleDeviceId()">
<span class="switch-slider"></span>
</label>
</div>
<div class="dropdown-item">
<label class="switch-label">
<span>显示集群</span>
<input type="checkbox" id="showClusterSwitch" checked onchange="toggleCluster()">
<span class="switch-slider"></span>
</label>
</div>
</div>
<div class="dropdown-divider"></div>
<div class="dropdown-group">
<div class="dropdown-group-title">测量工具</div>
<div class="dropdown-item" onclick="toggleMeasureDistance()">
测距
</div>
<div class="dropdown-item" onclick="clearMeasure()">
清除测距
</div>
</div>
</div>
</div>
</div>
<!-- 测量状态指示器 -->
<div id="measureStatus" class="toolbar-item" style="display: none;">
<span style="color: #48bb78; font-weight: bold; font-size: 12px;">测量中</span>
<button class="toolbar-btn" onclick="finishMeasuring()" style="background: #f56565; margin-left: 6px;">
<i class="layui-icon layui-icon-close" style="font-size: 12px;"></i>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="../lib/layui-v2.6.3/layui.js" charset="utf-8"></script>
<script th:inline="javascript">
var map;
var vectorSource;
var vectorLayer;
var currentBaseLayer;
var greenFeatures = [];
var orangeFeatures = [];
var redFeatures = [];
var marker_state = 3; // 1:all; 2:orange; 3:red
var allFeatures = []; // 存储所有设备标记,用于搜索功能
var currentSearchedDevice = null; // 当前搜索的设备ID
var myLocationFeature = null; // 存储"我的位置"标记
var myLocationInterval; // 存储定时更新位置的interval对象
var clusterSource;
var clusterLayer;
var showDeviceId = true;
var showCluster = true;
var minZoomForLabels = 4;
var maxZoomForClustering = 8;
// 测距
var measureSource;
var measureLayer;
// 天地图 API 密钥fengyarnom@gmail.com
var TIANDITU_KEY = '0c260b8a094a4e0bc507808812cefdac';
var deviceList = [
[# th:each="device : ${deviceList}"]
{
deviceid: [[${device.deviceid}]],
latitude: [[${device.latitude}]],
longitude: [[${device.longitude}]],
warning: [[${device.warning}]]
},
[/]
];
var pi = 3.14159265358979324;
var a = 6378245.0;
var ee = 0.00669342162296594323;
/*判断是否在国内,不在国内则不做偏移*/
function outOfChina(lon, lat) {
if ((lon < 72.004 || lon > 137.8347) && (lat < 0.8293 || lat > 55.8271)) {
return true;
} else {
return false;
}
}
function transformLat(x, y) {
var ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(y * pi) + 40.0 * Math.sin(y / 3.0 * pi)) * 2.0 / 3.0;
ret += (160.0 * Math.sin(y / 12.0 * pi) + 320 * Math.sin(y * pi / 30.0)) * 2.0 / 3.0;
return ret;
}
function transformLon(x, y) {
var ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
ret += (20.0 * Math.sin(x * pi) + 40.0 * Math.sin(x / 3.0 * pi)) * 2.0 / 3.0;
ret += (150.0 * Math.sin(x / 12.0 * pi) + 300.0 * Math.sin(x / 30.0 * pi)) * 2.0 / 3.0;
return ret;
}
function transform(wgLat, wgLon) {
var mars_point = {lon: 0, lat: 0};
if (outOfChina(wgLon, wgLat)) {
mars_point.lat = wgLat;
mars_point.lon = wgLon;
return mars_point;
}
var dLat = transformLat(wgLon - 105.0, wgLat - 35.0);
var dLon = transformLon(wgLon - 105.0, wgLat - 35.0);
var radLat = wgLat / 180.0 * pi;
var magic = Math.sin(radLat);
magic = 1 - ee * magic * magic;
var sqrtMagic = Math.sqrt(magic);
dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi);
mars_point.lat = wgLat + dLat;
mars_point.lon = wgLon + dLon;
return mars_point;
}
var mapLayers = {
amap: new ol.layer.Tile({
source: new ol.source.XYZ({
url: 'https://webrd0{1-4}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}'
})
}),
amap_satellite: new ol.layer.Group({
layers: [
new ol.layer.Tile({
source: new ol.source.XYZ({
url: 'https://webst0{1-4}.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}'
})
}),
new ol.layer.Tile({
source: new ol.source.XYZ({
url: 'https://webst0{1-4}.is.autonavi.com/appmaptile?style=8&x={x}&y={y}&z={z}'
})
})
]
}),
google_satellite: new ol.layer.Tile({
source: new ol.source.XYZ({
url: 'https://mt{0-3}.google.com/vt/lyrs=s&x={x}&y={y}&z={z}',
crossOrigin: 'anonymous'
})
}),
google_terrain: new ol.layer.Tile({
source: new ol.source.XYZ({
url: 'https://mt{0-3}.google.com/vt/lyrs=p&x={x}&y={y}&z={z}',
crossOrigin: 'anonymous'
})
}),
google_roadmap: new ol.layer.Tile({
source: new ol.source.XYZ({
url: 'https://mt{0-3}.google.com/vt/lyrs=m&x={x}&y={y}&z={z}',
crossOrigin: 'anonymous'
})
}),
google_hybrid: new ol.layer.Tile({
source: new ol.source.XYZ({
url: 'https://mt{0-3}.google.com/vt/lyrs=y&x={x}&y={y}&z={z}',
crossOrigin: 'anonymous'
})
}),
tianditu_satellite: new ol.layer.Group({
layers: [
new ol.layer.Tile({
source: new ol.source.XYZ({
url: 'https://t{0-7}.tianditu.gov.cn/img_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=' + TIANDITU_KEY,
tileLoadFunction: function(imageTile, src) {
imageTile.getImage().src = src;
imageTile.getImage().onerror = function() {
// 天地图加载失败时切换到高德地图
var mapTypeSelect = document.getElementById('mapTypeSelect');
if(mapTypeSelect.value.startsWith('tianditu_')) {
mapTypeSelect.value = 'amap';
switchMapType('amap');
layer.msg('天地图加载失败,已自动切换到高德地图');
layui.form.render('select');
}
};
}
})
}),
new ol.layer.Tile({
source: new ol.source.XYZ({
url: 'https://t{0-7}.tianditu.gov.cn/cia_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cia&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=' + TIANDITU_KEY,
tileLoadFunction: function(imageTile, src) {
imageTile.getImage().src = src;
imageTile.getImage().onerror = function() {
// 天地图加载失败时切换到高德地图
var mapTypeSelect = document.getElementById('mapTypeSelect');
if(mapTypeSelect.value.startsWith('tianditu_')) {
mapTypeSelect.value = 'amap';
switchMapType('amap');
layer.msg('天地图加载失败,已自动切换到高德地图');
layui.form.render('select');
}
};
}
})
})
]
}),
tianditu_normal: new ol.layer.Group({
layers: [
new ol.layer.Tile({
source: new ol.source.XYZ({
url: 'https://t{0-7}.tianditu.gov.cn/vec_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=vec&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=' + TIANDITU_KEY,
tileLoadFunction: function(imageTile, src) {
imageTile.getImage().src = src;
imageTile.getImage().onerror = function() {
// 天地图加载失败时切换到高德地图
var mapTypeSelect = document.getElementById('mapTypeSelect');
if(mapTypeSelect.value.startsWith('tianditu_')) {
mapTypeSelect.value = 'amap';
switchMapType('amap');
layer.msg('天地图加载失败,已自动切换到高德地图');
layui.form.render('select');
}
};
}
})
}),
new ol.layer.Tile({
source: new ol.source.XYZ({
url: 'https://t{0-7}.tianditu.gov.cn/cva_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cva&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=' + TIANDITU_KEY,
tileLoadFunction: function(imageTile, src) {
imageTile.getImage().src = src;
imageTile.getImage().onerror = function() {
// 天地图加载失败时切换到高德地图
var mapTypeSelect = document.getElementById('mapTypeSelect');
if(mapTypeSelect.value.startsWith('tianditu_')) {
mapTypeSelect.value = 'amap';
switchMapType('amap');
layer.msg('天地图加载失败,已自动切换到高德地图');
layui.form.render('select');
}
};
}
})
})
]
}),
tianditu_terrain: new ol.layer.Group({
layers: [
new ol.layer.Tile({
source: new ol.source.XYZ({
url: 'https://t{0-7}.tianditu.gov.cn/ter_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=ter&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=' + TIANDITU_KEY,
tileLoadFunction: function(imageTile, src) {
imageTile.getImage().src = src;
imageTile.getImage().onerror = function() {
// 天地图加载失败时切换到高德地图
var mapTypeSelect = document.getElementById('mapTypeSelect');
if(mapTypeSelect.value.startsWith('tianditu_')) {
mapTypeSelect.value = 'amap';
switchMapType('amap');
layer.msg('天地图矢量图加载失败,已自动切换到高德矢量图');
layui.form.render('select');
}
};
}
})
})
]
}),
tianditu_terrain_hybrid: new ol.layer.Group({
layers: [
new ol.layer.Tile({
source: new ol.source.XYZ({
url: 'https://t{0-7}.tianditu.gov.cn/ter_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=ter&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=' + TIANDITU_KEY,
tileLoadFunction: function(imageTile, src) {
imageTile.getImage().src = src;
imageTile.getImage().onerror = function() {
// 天地图加载失败时切换到高德地图
var mapTypeSelect = document.getElementById('mapTypeSelect');
if(mapTypeSelect.value.startsWith('tianditu_')) {
mapTypeSelect.value = 'amap';
switchMapType('amap');
layer.msg('天地图加载失败,已自动切换到高德地图');
layui.form.render('select');
}
};
}
})
}),
new ol.layer.Tile({
source: new ol.source.XYZ({
url: 'https://t{0-7}.tianditu.gov.cn/cta_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cta&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=' + TIANDITU_KEY,
tileLoadFunction: function(imageTile, src) {
imageTile.getImage().src = src;
imageTile.getImage().onerror = function() {
// 天地图加载失败时切换到高德地图
var mapTypeSelect = document.getElementById('mapTypeSelect');
if(mapTypeSelect.value.startsWith('tianditu_')) {
mapTypeSelect.value = 'amap';
switchMapType('amap');
layer.msg('天地图加载失败,已自动切换到高德地图');
layui.form.render('select');
}
};
}
})
})
]
})
};
function initialize() {
vectorSource = new ol.source.Vector();
vectorLayer = new ol.layer.Vector({
source: vectorSource,
style: function(feature) {
if (!feature.get('isMyLocation')) {
var deviceInfo = feature.get('deviceInfo');
var iconSrc;
var color = '#000';
if (deviceInfo.warning == 2) {
iconSrc = '../images/loc1_red.png';
} else if (deviceInfo.warning == 1) {
iconSrc = '../images/loc1_orange.png';
} else {
iconSrc = '../images/loc1_green.png';
}
var style = new ol.style.Style({
image: new ol.style.Icon({
anchor: [0.5, 1],
src: iconSrc,
scale: 0.7
})
});
// 根据缩放级别和设置决定是否显示设备ID
if (showDeviceId && map.getView().getZoom() >= minZoomForLabels) {
style.setText(new ol.style.Text({
text: deviceInfo.deviceid,
offsetY: -30,
fill: new ol.style.Fill({ color: color }),
stroke: new ol.style.Stroke({ color: '#fff', width: 2 }),
font: '12px Arial'
}));
}
return style;
}
return null;
}
});
clusterSource = new ol.source.Cluster({
distance: 40,
source: vectorSource
});
clusterLayer = new ol.layer.Vector({
source: clusterSource,
style: function(feature) {
var size = feature.get('features').length;
var style = new ol.style.Style({
image: new ol.style.Circle({
radius: 15,
fill: new ol.style.Fill({
color: '#3399CC'
})
}),
text: new ol.style.Text({
text: size.toString(),
fill: new ol.style.Fill({
color: '#fff'
})
})
});
return style;
}
});
var initialMapType = document.getElementById('mapTypeSelectNew').value;
currentBaseLayer = mapLayers[initialMapType];
map = new ol.Map({
target: 'map-container',
layers: [
currentBaseLayer,
clusterLayer,
vectorLayer
],
view: new ol.View({
center: ol.proj.fromLonLat([116.404, 39.915]),
zoom: 7
})
});
// 测距专用图层
measureSource = new ol.source.Vector();
measureLayer = new ol.layer.Vector({
source: measureSource,
style: new ol.style.Style({
fill: new ol.style.Fill({ color: 'rgba(255,255,255,0.2)' }),
stroke: new ol.style.Stroke({ color: '#ffcc33', width: 2 }),
image: new ol.style.RegularShape({
points: 4,
radius: 8,
radius2: 0,
angle: Math.PI / 4,
stroke: new ol.style.Stroke({ color: '#ed8936', width: 2 })
})
})
});
map.addLayer(measureLayer);
var scaleLineControl = new ol.control.ScaleLine();
map.addControl(scaleLineControl);
var initialZoom = map.getView().getZoom();
if (showCluster && initialZoom < maxZoomForClustering) {
clusterLayer.setVisible(true);
vectorLayer.setVisible(false);
} else {
clusterLayer.setVisible(false);
vectorLayer.setVisible(true);
}
map.getView().on('change:resolution', function() {
var zoom = map.getView().getZoom();
if (showCluster) {
if (zoom >= maxZoomForClustering) {
clusterLayer.setVisible(false);
vectorLayer.setVisible(true);
} else {
clusterLayer.setVisible(true);
vectorLayer.setVisible(false);
}
}
vectorLayer.changed();
});
map.on('click', function(evt) {
var feature = map.forEachFeatureAtPixel(evt.pixel, function(feature) {
return feature;
});
if (feature) {
var features = feature.get('features');
if (features && features.length > 1) {
var extent = vectorSource.getExtent();
map.getView().fit(extent, {
padding: [50, 50, 50, 50],
duration: 1000
});
}
}
});
if (deviceList.length > 0) {
var centerLat = 0;
var centerLon = 0;
for (var i = 0; i < deviceList.length; i++) {
centerLat += deviceList[i].latitude;
centerLon += deviceList[i].longitude;
}
centerLat = centerLat / deviceList.length;
centerLon = centerLon / deviceList.length;
map.getView().setCenter(ol.proj.fromLonLat([centerLon, centerLat]));
}
addDeviceMarkers();
myLocation();
startLocationUpdates();
showDeviceId = true;
showCluster = true;
document.getElementById('showDeviceIdSwitch').checked = showDeviceId;
document.getElementById('showClusterSwitch').checked = showCluster;
}
function switchMapType(mapType) {
map.removeLayer(currentBaseLayer);
currentBaseLayer = mapLayers[mapType];
map.getLayers().insertAt(0, currentBaseLayer);
updateMyLocationForMapType(mapType);
addDeviceMarkers();
}
// 根据地图类型更新我的位置标记
function updateMyLocationForMapType(mapType) {
if (myLocationFeature) {
var originalCoords = myLocationFeature.get('originalCoords');
if (originalCoords) {
var lat = originalCoords.lat;
var lon = originalCoords.lon;
var coordinates;
if (mapType === 'amap' || mapType === 'amap_satellite') {
// 高德地图使用GCJ-02坐标系需要转换
var gcjCoord = transform(lat, lon);
coordinates = ol.proj.fromLonLat([gcjCoord.lon, gcjCoord.lat]);
} else if (mapType.startsWith('google_')) {
// Google地图使用WGS84坐标系直接使用
coordinates = ol.proj.fromLonLat([lon, lat]);
} else {
// 天地图使用CGCS2000/WGS84坐标系直接使用
coordinates = ol.proj.fromLonLat([lon, lat]);
}
myLocationFeature.getGeometry().setCoordinates(coordinates);
}
}
}
function addDeviceMarkers() {
var savedMyLocationFeature = myLocationFeature;
vectorSource.clear();
greenFeatures = [];
orangeFeatures = [];
redFeatures = [];
allFeatures = [];
for (var i = 0; i < deviceList.length; i++) {
var device = deviceList[i];
var mapCoordinates;
var currentMapType = document.getElementById('mapTypeSelectNew').value;
if (currentMapType === 'amap' || currentMapType === 'amap_satellite') {
// 高德地图 WGS84 转换为 GCJ-02
var gcjCoord = transform(device.latitude, device.longitude);
mapCoordinates = ol.proj.fromLonLat([gcjCoord.lon, gcjCoord.lat]);
} else if (currentMapType.startsWith('google_')) {
// Google地图使用WGS84坐标系直接使用
mapCoordinates = ol.proj.fromLonLat([device.longitude, device.latitude]);
} else {
// 天地图 CGCS20002000国家大地坐标系与WGS84实质一样
mapCoordinates = ol.proj.fromLonLat([device.longitude, device.latitude]);
}
var feature = new ol.Feature({
geometry: new ol.geom.Point(mapCoordinates),
deviceInfo: device
});
if (device.warning == 2) {
redFeatures.push(feature);
} else if (device.warning == 1) {
orangeFeatures.push(feature);
} else {
greenFeatures.push(feature);
}
allFeatures.push(feature);
vectorSource.addFeature(feature);
}
if (savedMyLocationFeature) {
vectorSource.addFeature(savedMyLocationFeature);
}
if (currentSearchedDevice) {
filterByDeviceId(currentSearchedDevice);
} else {
if (marker_state === 2) {
hideGreen();
hideRed();
} else if (marker_state === 3) {
hideGreen();
hideOrange();
}
}
// 强制更新样式
vectorLayer.changed();
}
function onMapTypeChange() {
var mapType = document.getElementById('mapTypeSelectNew').value;
if(mapType.startsWith('google_') && [[${role}]] != "SUPER_ADMIN") {
layer.msg('拒绝使用');
document.getElementById('mapTypeSelectNew').value = 'tianditu_terrain_hybrid';
mapType = 'tianditu_terrain_hybrid';
}
switchMapType(mapType);
}
function searchDeviceNew() {
var deviceId = document.getElementById('deviceSearchNew').value.trim();
if (!deviceId) {
layer.msg('请输入设备编号');
return;
}
filterByDeviceId(deviceId);
}
function clearDeviceSearch() {
document.getElementById('deviceSearchNew').value = '';
}
function myLocation() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
var lon = position.coords.longitude;
var lat = position.coords.latitude;
var currentMapType = document.getElementById('mapTypeSelectNew').value;
var coordinates;
if (currentMapType === 'amap' || currentMapType === 'amap_satellite') {
// 高德地图使用GCJ-02坐标系需要转换
var gcjCoord = transform(lat, lon);
coordinates = ol.proj.fromLonLat([gcjCoord.lon, gcjCoord.lat]);
} else if (currentMapType.startsWith('google_')) {
// Google地图使用WGS84坐标系直接使用
coordinates = ol.proj.fromLonLat([lon, lat]);
} else {
// 天地图使用CGCS2000/WGS84坐标系直接使用
coordinates = ol.proj.fromLonLat([lon, lat]);
}
// 如果已经存在位置标记,则先移除
if (myLocationFeature) {
vectorSource.removeFeature(myLocationFeature);
}
myLocationFeature = new ol.Feature({
geometry: new ol.geom.Point(coordinates),
isMyLocation: true,
originalCoords: {lat: lat, lon: lon}
});
var style = new ol.style.Style({
image: new ol.style.Icon({
anchor: [0.5, 1],
src: '../images/loc_blue.png',
scale: 0.7
})
});
myLocationFeature.setStyle(style);
vectorSource.addFeature(myLocationFeature);
map.getView().setCenter(coordinates);
});
}
}
function startLocationUpdates() {
if (myLocationInterval) {
clearInterval(myLocationInterval);
}
myLocationInterval = setInterval(function() {
myLocation();
}, 10 * 60 * 1000); // 10 min = 10 * 60 * 1000ms
}
function showAll() {
// document.getElementById('btn-all').classList.add('active');
// document.getElementById('btn-warning1').classList.remove('active');
// document.getElementById('btn-warning2').classList.remove('active');
// document.getElementById('btn-device-locate').classList.remove('active');
// document.getElementById('btn-device-locate').style.display = 'none';
currentSearchedDevice = null;
// document.getElementById('deviceSearch').value = '';
document.getElementById('deviceSearchNew').value = '';
var savedMyLocationFeature = myLocationFeature;
vectorSource.clear();
if (savedMyLocationFeature) {
vectorSource.addFeature(savedMyLocationFeature);
}
for (var i = 0; i < allFeatures.length; i++) {
vectorSource.addFeature(allFeatures[i]);
}
marker_state = 1;
}
function showWarning1() {
// document.getElementById('btn-all').classList.remove('active');
// document.getElementById('btn-warning1').classList.add('active');
// document.getElementById('btn-warning2').classList.remove('active');
// document.getElementById('btn-device-locate').classList.remove('active');
// document.getElementById('btn-device-locate').style.display = 'none';
currentSearchedDevice = null;
// document.getElementById('deviceSearch').value = '';
document.getElementById('deviceSearchNew').value = '';
var savedMyLocationFeature = myLocationFeature;
vectorSource.clear();
if (savedMyLocationFeature) {
vectorSource.addFeature(savedMyLocationFeature);
}
for (var i = 0; i < allFeatures.length; i++) {
vectorSource.addFeature(allFeatures[i]);
}
hideGreen();
hideRed();
marker_state = 2;
}
function showWarning2() {
// 注释掉原有按钮操作
// document.getElementById('btn-all').classList.remove('active');
// document.getElementById('btn-warning1').classList.remove('active');
// document.getElementById('btn-warning2').classList.add('active');
// document.getElementById('btn-device-locate').classList.remove('active');
// document.getElementById('btn-device-locate').style.display = 'none';
currentSearchedDevice = null;
// document.getElementById('deviceSearch').value = '';
document.getElementById('deviceSearchNew').value = '';
var savedMyLocationFeature = myLocationFeature;
vectorSource.clear();
if (savedMyLocationFeature) {
vectorSource.addFeature(savedMyLocationFeature);
}
for (var i = 0; i < allFeatures.length; i++) {
vectorSource.addFeature(allFeatures[i]);
}
hideGreen();
hideOrange();
marker_state = 3;
}
function showGreen() {
for (var i = 0; i < greenFeatures.length; i++) {
if (!vectorSource.hasFeature(greenFeatures[i])) {
vectorSource.addFeature(greenFeatures[i]);
}
}
}
function showOrange() {
for (var i = 0; i < orangeFeatures.length; i++) {
if (!vectorSource.hasFeature(orangeFeatures[i])) {
vectorSource.addFeature(orangeFeatures[i]);
}
}
}
function showRed() {
for (var i = 0; i < redFeatures.length; i++) {
if (!vectorSource.hasFeature(redFeatures[i])) {
vectorSource.addFeature(redFeatures[i]);
}
}
}
function hideGreen() {
for (var i = 0; i < greenFeatures.length; i++) {
vectorSource.removeFeature(greenFeatures[i]);
}
}
function hideOrange() {
for (var i = 0; i < orangeFeatures.length; i++) {
vectorSource.removeFeature(orangeFeatures[i]);
}
}
function hideRed() {
for (var i = 0; i < redFeatures.length; i++) {
vectorSource.removeFeature(redFeatures[i]);
}
}
function searchDevice() {
var deviceId = document.getElementById('deviceSearch').value.trim();
if (!deviceId) {
currentSearchedDevice = null;
vectorSource.clear();
for (var i = 0; i < allFeatures.length; i++) {
vectorSource.addFeature(allFeatures[i]);
}
if (marker_state === 2) {
hideGreen();
hideRed();
} else if (marker_state === 3) {
hideGreen();
hideOrange();
}
// document.getElementById('btn-device-locate').style.display = 'none';
// updateFilterButtonsState(marker_state);
return;
}
currentSearchedDevice = deviceId;
filterByDeviceId(deviceId);
}
function locateDeviceOnMap(deviceId, latitude, longitude) {
currentSearchedDevice = deviceId;
filterByDeviceId(deviceId);
layer.closeAll();
}
function locateDeviceDirectly(deviceId) {
currentSearchedDevice = deviceId;
filterByDeviceId(deviceId);
}
function updateFilterButtonsState(state) {
// document.getElementById('btn-all').classList.remove('active');
// document.getElementById('btn-warning1').classList.remove('active');
// document.getElementById('btn-warning2').classList.remove('active');
// if (state === 1) {
// document.getElementById('btn-all').classList.add('active');
// } else if (state === 2) {
// document.getElementById('btn-warning1').classList.add('active');
// } else if (state === 3) {
// document.getElementById('btn-warning2').classList.add('active');
// }
}
function filterByDeviceId(deviceId) {
var found = false;
var targetFeature = null;
// 查找匹配的设备
for (var i = 0; i < allFeatures.length; i++) {
var feature = allFeatures[i];
var deviceInfo = feature.get('deviceInfo');
if (deviceInfo && deviceInfo.deviceid.includes(deviceId)) {
found = true;
targetFeature = feature;
break;
}
}
if (found && targetFeature) {
var geometry = targetFeature.getGeometry();
var deviceCoord = geometry.getCoordinates();
map.getView().animate({
center: deviceCoord,
zoom: 16,
duration: 800
});
layer.msg('已定位到设备: ' + deviceId);
} else {
layer.msg('未找到设备: ' + deviceId);
}
}
layui.use(['form'], function(){
var form = layui.form;
// form.on('select(mapType)', function(data){
// switchMapType(data.value);
// });
form.on('select(mapTypeNew)', function(data){
switchMapType(data.value);
});
// form.on('switch(showDeviceId)', function(data){
// showDeviceId = data.elem.checked;
// vectorLayer.changed();
// });
// // 聚合显示
// form.on('switch(showCluster)', function(data){
// showCluster = data.elem.checked;
// if (showCluster) {
// var zoom = map.getView().getZoom();
// if (zoom < maxZoomForClustering) {
// clusterLayer.setVisible(true);
// vectorLayer.setVisible(false);
// } else {
// clusterLayer.setVisible(false);
// vectorLayer.setVisible(true);
// }
// } else {
// clusterLayer.setVisible(false);
// vectorLayer.setVisible(true);
// }
// });
initialize();
// 默认显示所有设备
document.getElementById('warningFilter').value = 'all';
showAll();
});
function queryDevices(status_type) {
// 首先过滤地图上的设备显示
filterDevicesByStatus(status_type);
// 然后打开设备列表弹窗
var index = layer.open({
title: '设备列表',
type: 2,
shade: 0.2,
maxmin: true,
shadeClose: true,
anim: 2,
offset: 'rb',
area: ['100%', '50%'],
content: '../page/gnss_q_status?query=' + status_type,
});
}
// 新增:根据状态过滤地图上的设备
function filterDevicesByStatus(status_type) {
var savedMyLocationFeature = myLocationFeature;
vectorSource.clear();
if (savedMyLocationFeature) {
vectorSource.addFeature(savedMyLocationFeature);
}
// 根据状态类型显示对应的设备
switch(status_type) {
case 'warning1':
for (var i = 0; i < orangeFeatures.length; i++) {
vectorSource.addFeature(orangeFeatures[i]);
}
marker_state = 2;
break;
case 'warning2':
for (var i = 0; i < redFeatures.length; i++) {
vectorSource.addFeature(redFeatures[i]);
}
marker_state = 3;
break;
case 'offline':
case 'no_fwd':
case 'nofixed':
case 'nogga':
// 对于这些状态,显示所有设备,让用户在弹窗中选择
for (var i = 0; i < allFeatures.length; i++) {
vectorSource.addFeature(allFeatures[i]);
}
marker_state = 1;
break;
default:
// 默认显示所有设备
for (var i = 0; i < allFeatures.length; i++) {
vectorSource.addFeature(allFeatures[i]);
}
marker_state = 1;
break;
}
vectorLayer.changed();
}
function onWarningFilterChange() {
var filterValue = document.getElementById('warningFilter').value;
// document.getElementById('btn-all').classList.remove('active');
// document.getElementById('btn-warning1').classList.remove('active');
// document.getElementById('btn-warning2').classList.remove('active');
// document.getElementById('btn-device-locate').classList.remove('active');
// document.getElementById('btn-device-locate').style.display = 'none';
currentSearchedDevice = null;
// document.getElementById('deviceSearch').value = '';
document.getElementById('deviceSearchNew').value = '';
if (filterValue === 'all') {
showAll();
} else if (filterValue === 'warning1') {
showWarning1();
} else if (filterValue === 'warning2') {
showWarning2();
}
}
function toggleMapFunctionsMenu() {
document.getElementById('mapFunctionsMenu').classList.toggle('show');
document.addEventListener('click', closeMapFunctionsMenu);
}
function closeMapFunctionsMenu(event) {
var dropdown = document.getElementById('mapFunctionsMenu');
var toggleBtn = document.querySelector('.dropdown-toggle');
if (dropdown && !dropdown.contains(event.target) && !toggleBtn.contains(event.target)) {
dropdown.classList.remove('show');
document.removeEventListener('click', closeMapFunctionsMenu);
}
}
function toggleDeviceId() {
showDeviceId = document.getElementById('showDeviceIdSwitch').checked;
updateLayerVisibility();
layer.msg(showDeviceId ? '已显示设备信息' : '已隐藏设备信息');
}
// 集群显示开关
function toggleCluster() {
showCluster = document.getElementById('showClusterSwitch').checked;
updateLayerVisibility();
layer.msg(showCluster ? '已启用集群显示' : '已禁用集群显示');
}
// 更新图层可见性
function updateLayerVisibility() {
var zoom = map.getView().getZoom();
if (showCluster) {
if (zoom >= maxZoomForClustering) {
clusterLayer.setVisible(false);
vectorLayer.setVisible(true);
} else {
clusterLayer.setVisible(true);
vectorLayer.setVisible(false);
}
} else {
clusterLayer.setVisible(false);
vectorLayer.setVisible(true);
}
vectorLayer.changed();
}
var measureActive = false;
var measureDraw; // 当前测距交互
var measureTooltipElement;
var measureTooltip;
var measureTooltips = []; // 存储所有测距点的overlay
var measureFeatures = []; // 存储所有测距线段
var currentMeasureTooltips = []; // 当前测距的临时tooltip
var currentSketch; // 当前绘制的线段
var currentListener; // 当前的监听器
var segmentTooltips = []; // 存储每个节点的距离显示
function toggleMeasureDistance() {
if (measureActive) {
deactivateMeasure();
layer.msg('测距功能已关闭');
} else {
activateMeasure();
layer.msg('点击左键添加距离节点,点击右键结束测量 :)');
}
document.getElementById('mapFunctionsMenu').classList.remove('show');
}
function finishMeasuring() {
if (measureActive && currentSketch) {
measureDraw.finishDrawing();
}
}
function activateMeasure() {
measureActive = true;
document.getElementById('measureStatus').style.display = 'flex';
measureDraw = new ol.interaction.Draw({
source: measureSource,
type: 'LineString',
style: new ol.style.Style({
fill: new ol.style.Fill({ color: 'rgba(255,255,255,0.2)' }),
stroke: new ol.style.Stroke({ color: '#ffcc33', width: 2 }),
image: new ol.style.RegularShape({
points: 4,
radius: 8,
radius2: 0,
angle: Math.PI / 4,
stroke: new ol.style.Stroke({ color: '#ed8936', width: 2 })
})
})
});
map.addInteraction(measureDraw);
map.getViewport().addEventListener('contextmenu', function(e) {
if (measureActive && currentSketch) {
e.preventDefault();
measureDraw.finishDrawing();
}
});
measureDraw.on('drawstart', function(evt) {
currentSketch = evt.feature;
currentMeasureTooltips = [];
segmentTooltips = [];
currentListener = currentSketch.getGeometry().on('change', function(e) {
var geom = e.target;
var coords = geom.getCoordinates();
clearTemporaryTooltips();
clearSegmentTooltips();
// 计算并显示每个节点的距离
var total = 0;
for (var i = 0; i < coords.length; i++) {
if (i === 0) {
// 起点
var startTooltip = createMeasureTooltip(coords[0], '起点');
currentMeasureTooltips.push(startTooltip);
} else {
// 计算段距离
var seg = new ol.geom.LineString([coords[i-1], coords[i]]);
var segmentLength = ol.sphere.getLength(seg);
total += segmentLength;
// 显示每个节点的累计距离
var output = (total > 1000 ?
(Math.round(total / 100) / 10) + ' km' :
(Math.round(total * 10) / 10) + ' m');
var tooltip = createMeasureTooltip(coords[i], output);
segmentTooltips.push(tooltip);
if (i === coords.length - 1) {
currentMeasureTooltips.push(tooltip);
}
}
}
});
});
measureDraw.on('drawend', function(evt) {
var coords = evt.feature.getGeometry().getCoordinates();
clearTemporaryTooltips();
clearSegmentTooltips();
var total = 0;
for (var i = 0; i < coords.length; i++) {
if (i === 0) {
var startTooltip = createMeasureTooltip(coords[0], '起点', true);
measureTooltips.push(startTooltip);
} else {
var seg = new ol.geom.LineString([coords[i-1], coords[i]]);
total += ol.sphere.getLength(seg);
var output = (total > 1000 ?
(Math.round(total / 100) / 10) + ' km' :
(Math.round(total * 10) / 10) + ' m');
var tooltip = createMeasureTooltip(coords[i], output, true);
measureTooltips.push(tooltip);
}
}
measureFeatures.push(evt.feature);
ol.Observable.unByKey(currentListener);
currentSketch = null;
currentListener = null;
document.getElementById('measureStatus').style.display = 'none';
map.removeInteraction(measureDraw);
measureActive = false;
layer.msg('测量完成');
});
}
function deactivateMeasure() {
measureActive = false;
// 隐藏测量状态指示器
document.getElementById('measureStatus').style.display = 'none';
if (measureDraw) {
map.removeInteraction(measureDraw);
measureDraw = null;
}
if (currentListener) {
ol.Observable.unByKey(currentListener);
currentListener = null;
}
currentSketch = null;
clearTemporaryTooltips();
clearSegmentTooltips();
}
function createMeasureTooltip(coord, text, isStatic) {
var elem = document.createElement('div');
elem.className = isStatic ? 'ol-tooltip ol-tooltip-static' : 'ol-tooltip ol-tooltip-measure';
elem.innerHTML = text;
var overlay = new ol.Overlay({
element: elem,
offset: [0, -15],
positioning: 'bottom-center'
});
overlay.setPosition(coord);
map.addOverlay(overlay);
return overlay;
}
function clearAllMeasureTooltips() {
for (var i = 0; i < measureTooltips.length; i++) {
map.removeOverlay(measureTooltips[i]);
}
measureTooltips = [];
clearTemporaryTooltips();
}
function clearTemporaryTooltips() {
for (var i = 0; i < currentMeasureTooltips.length; i++) {
map.removeOverlay(currentMeasureTooltips[i]);
}
currentMeasureTooltips = [];
}
function clearSegmentTooltips() {
for (var i = 0; i < segmentTooltips.length; i++) {
map.removeOverlay(segmentTooltips[i]);
}
segmentTooltips = [];
}
function updateMeasureCount() {
}
function clearMeasure() {
measureSource.clear();
clearAllMeasureTooltips();
measureFeatures = [];
layer.msg('测距标记已清除');
document.getElementById('mapFunctionsMenu').classList.remove('show');
}
</script>
</body>
</html>