1906 lines
67 KiB
HTML
1906 lines
67 KiB
HTML
<!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 {
|
||
// 天地图 CGCS2000,2000国家大地坐标系,与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> |