feat: 新增测距

This commit is contained in:
yarnom 2025-07-04 09:37:25 +08:00
parent 4425fcd7e6
commit a70852fdc9

View File

@ -190,6 +190,54 @@
background: rgba(230, 230, 230, 0.6);
}
/* 比例尺样式 */
.map-scale {
position: absolute;
bottom: 20px;
left: 20px;
z-index: 1000;
background: rgba(255, 255, 255, 0.9);
padding: 8px 12px;
border-radius: 6px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
font-size: 12px;
color: #333;
font-weight: bold;
border: 1px solid rgba(0, 0, 0, 0.1);
}
/* 测距工具样式 */
.measure-tooltip {
position: relative;
background: rgba(0, 0, 0, 0.8);
border-radius: 4px;
color: white;
padding: 4px 8px;
opacity: 0.8;
white-space: nowrap;
font-size: 12px;
}
.measure-tooltip-measure {
opacity: 1;
font-weight: bold;
}
.measure-tooltip-static {
background-color: #ffcc33;
color: black;
border: 1px solid white;
}
.help-tooltip {
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
white-space: nowrap;
}
.overview-stats {
@ -450,20 +498,21 @@
<div class="toolbar-item">
<select id="warningFilter" class="toolbar-select" onchange="onWarningFilterChange()">
<option value="all">全部设备</option>
<option value="all" selected>全部设备</option>
<option value="warning1">一般告警</option>
<option value="warning2" selected>严重告警</option>
<option value="warning2">严重告警</option>
</select>
</div>
<div class="toolbar-divider"></div>
<div class="toolbar-item">
<select id="displayOptions" class="toolbar-select" onchange="onDisplayOptionsChange()">
<option value="both" selected>信息+集群</option>
<option value="device">仅设备信息</option>
<option value="cluster">仅集群显示</option>
<option value="none">隐藏标签</option>
<select id="mapFeatures" class="toolbar-select" onchange="onMapFeaturesChange()">
<option value="" selected>地图功能 ▼</option>
<option value="toggle_labels">切换设备标签</option>
<option value="toggle_cluster">切换设备集群</option>
<option value="measure_distance">开始测距</option>
<option value="clear_measure">清除测距</option>
</select>
</div>
@ -473,6 +522,8 @@
</div>
</div>
<!-- 比例尺 -->
<div id="map-scale" class="map-scale">比例尺 1:1000</div>
</div>
</div>
@ -490,7 +541,7 @@
var greenFeatures = [];
var orangeFeatures = [];
var redFeatures = [];
var marker_state = 3; // 1:all; 2:orange; 3:red
var marker_state = 1; // 1:all; 2:orange; 3:red
var allFeatures = []; // 存储所有设备标记,用于搜索功能
var currentSearchedDevice = null; // 当前搜索的设备ID
var myLocationFeature = null; // 存储"我的位置"标记
@ -501,6 +552,16 @@
var showCluster = true;
var minZoomForLabels = 4;
var maxZoomForClustering = 8;
// 测距相关变量
var measureSource;
var measureVector;
var measureTooltipElement;
var measureTooltip;
var helpTooltipElement;
var helpTooltip;
var draw;
var measuring = false;
// 天地图 API 密钥fengyarnom@gmail.com
var TIANDITU_KEY = '0c260b8a094a4e0bc507808812cefdac';
@ -589,38 +650,12 @@
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');
}
};
}
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
})
}),
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');
}
};
}
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
})
})
]
@ -629,38 +664,12 @@
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');
}
};
}
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
})
}),
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');
}
};
}
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
})
})
]
@ -669,20 +678,7 @@
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');
}
};
}
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
})
})
]
@ -691,38 +687,12 @@
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');
}
};
}
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
})
}),
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');
}
};
}
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
})
})
]
@ -837,6 +807,7 @@
}
}
vectorLayer.changed();
updateScale(); // 更新比例尺
});
map.on('click', function(evt) {
@ -874,6 +845,7 @@
addDeviceMarkers();
myLocation();
startLocationUpdates();
updateScale(); // 初始化比例尺
}
function switchMapType(mapType) {
@ -970,6 +942,229 @@
// 新的地图功能事件处理
function onMapFeaturesChange() {
var selectElement = document.getElementById('mapFeatures');
var value = selectElement.value;
switch(value) {
case 'toggle_labels':
showDeviceId = !showDeviceId;
vectorLayer.changed();
break;
case 'toggle_cluster':
showCluster = !showCluster;
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);
}
break;
case 'measure_distance':
startMeasuring();
break;
case 'clear_measure':
clearMeasurements();
break;
}
// 重置下拉框到默认状态
selectElement.value = '';
}
// 测距功能
function startMeasuring() {
if (measuring) return;
measuring = true;
// 创建测距图层
if (!measureSource) {
measureSource = new ol.source.Vector();
measureVector = 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.Circle({
radius: 7,
fill: new ol.style.Fill({
color: '#ffcc33'
})
})
})
});
map.addLayer(measureVector);
}
// 创建绘制交互
draw = 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: 'rgba(0, 0, 0, 0.5)',
lineDash: [10, 10],
width: 2
}),
image: new ol.style.Circle({
radius: 5,
stroke: new ol.style.Stroke({
color: 'rgba(0, 0, 0, 0.7)'
}),
fill: new ol.style.Fill({
color: 'rgba(255, 255, 255, 0.2)'
})
})
})
});
map.addInteraction(draw);
createMeasureTooltip();
createHelpTooltip();
var listener;
draw.on('drawstart', function(evt) {
var sketch = evt.feature;
var tooltipCoord = evt.coordinate;
listener = sketch.getGeometry().on('change', function(evt) {
var geom = evt.target;
var output;
if (geom instanceof ol.geom.LineString) {
output = formatLength(geom);
tooltipCoord = geom.getLastCoordinate();
}
measureTooltipElement.innerHTML = output;
measureTooltip.setPosition(tooltipCoord);
});
});
draw.on('drawend', function() {
measureTooltipElement.className = 'measure-tooltip measure-tooltip-static';
measureTooltip.setOffset([0, -7]);
measureTooltipElement = null;
createMeasureTooltip();
ol.Observable.unByKey(listener);
// 结束测距
map.removeInteraction(draw);
measuring = false;
// 移除帮助提示
if (helpTooltip) {
map.removeOverlay(helpTooltip);
helpTooltip = null;
helpTooltipElement = null;
}
});
}
function clearMeasurements() {
if (measureSource) {
measureSource.clear();
}
if (draw) {
map.removeInteraction(draw);
draw = null;
}
measuring = false;
// 清除所有测距相关的覆盖物
var overlays = map.getOverlays().getArray().slice();
overlays.forEach(function(overlay) {
if (overlay.getElement() &&
(overlay.getElement().classList.contains('measure-tooltip') ||
overlay.getElement().classList.contains('help-tooltip'))) {
map.removeOverlay(overlay);
}
});
measureTooltip = null;
measureTooltipElement = null;
helpTooltip = null;
helpTooltipElement = null;
}
function createMeasureTooltip() {
if (measureTooltipElement) {
measureTooltipElement.parentNode.removeChild(measureTooltipElement);
}
measureTooltipElement = document.createElement('div');
measureTooltipElement.className = 'measure-tooltip measure-tooltip-measure';
measureTooltip = new ol.Overlay({
element: measureTooltipElement,
offset: [0, -15],
positioning: 'bottom-center'
});
map.addOverlay(measureTooltip);
}
function createHelpTooltip() {
if (helpTooltipElement) {
helpTooltipElement.parentNode.removeChild(helpTooltipElement);
}
helpTooltipElement = document.createElement('div');
helpTooltipElement.className = 'measure-tooltip help-tooltip';
helpTooltipElement.innerHTML = '点击开始测距,双击结束';
helpTooltip = new ol.Overlay({
element: helpTooltipElement,
offset: [15, 0],
positioning: 'center-left'
});
map.addOverlay(helpTooltip);
}
function formatLength(line) {
var length = ol.Sphere.getLength(line);
var output;
if (length > 100) {
output = (Math.round(length / 1000 * 100) / 100) + ' km';
} else {
output = (Math.round(length * 100) / 100) + ' m';
}
return output;
}
// 比例尺更新功能
function updateScale() {
var view = map.getView();
var resolution = view.getResolution();
var units = view.getProjection().getUnits();
var dpi = 25.4 / 0.28;
var mpu = ol.proj.METERS_PER_UNIT[units];
var scale = resolution * mpu * 39.37 * dpi;
if (scale >= 9500 && scale <= 950000) {
scale = Math.round(scale / 1000) * 1000;
} else if (scale >= 950000) {
scale = Math.round(scale / 100000) * 100000;
} else {
scale = Math.round(scale);
}
document.getElementById('map-scale').textContent = '比例尺 1:' + scale.toLocaleString();
}
function onMapTypeChange() {
var mapType = document.getElementById('mapTypeSelectNew').value;
switchMapType(mapType);
@ -1032,7 +1227,7 @@
navigator.geolocation.getCurrentPosition(function(position) {
var lon = position.coords.longitude;
var lat = position.coords.latitude;
var currentMapType = document.getElementById('mapTypeSelect').value;
var currentMapType = document.getElementById('mapTypeSelectNew').value;
var coordinates;
if (currentMapType === 'amap' || currentMapType === 'amap_satellite') {
@ -1390,47 +1585,6 @@
showWarning2();
}
}
function onDisplayOptionsChange() {
var optionValue = document.getElementById('displayOptions').value;
switch(optionValue) {
case 'both':
showDeviceId = true;
showCluster = true;
break;
case 'device':
showDeviceId = true;
showCluster = false;
break;
case 'cluster':
showDeviceId = false;
showCluster = true;
break;
case 'none':
showDeviceId = false;
showCluster = false;
break;
}
// 更新图层可见性
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();
}
</script>
</body>