|
|
@ -45,6 +45,7 @@ |
|
|
<!-- 定位弹窗 --> |
|
|
<!-- 定位弹窗 --> |
|
|
<locate-dialog |
|
|
<locate-dialog |
|
|
:visible="locateDialogVisible" |
|
|
:visible="locateDialogVisible" |
|
|
|
|
|
:coordinateFormat="coordinateFormat" |
|
|
@update:visible="locateDialogVisible = $event" |
|
|
@update:visible="locateDialogVisible = $event" |
|
|
@confirm="handleLocateConfirm" |
|
|
@confirm="handleLocateConfirm" |
|
|
@cancel="handleLocateCancel" |
|
|
@cancel="handleLocateCancel" |
|
|
@ -116,6 +117,10 @@ export default { |
|
|
unit: 'km', |
|
|
unit: 'km', |
|
|
position: 'bottom-right' |
|
|
position: 'bottom-right' |
|
|
}) |
|
|
}) |
|
|
|
|
|
}, |
|
|
|
|
|
coordinateFormat: { |
|
|
|
|
|
type: String, |
|
|
|
|
|
default: 'dms' // 'decimal' 或 'dms' |
|
|
} |
|
|
} |
|
|
}, |
|
|
}, |
|
|
watch: { |
|
|
watch: { |
|
|
@ -135,6 +140,11 @@ export default { |
|
|
this.updateScaleFromConfig(newVal) |
|
|
this.updateScaleFromConfig(newVal) |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
}, |
|
|
|
|
|
coordinateFormat: { |
|
|
|
|
|
handler(newVal) { |
|
|
|
|
|
this.updateCoordinatesDisplay() |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
}, |
|
|
}, |
|
|
data() { |
|
|
data() { |
|
|
@ -189,6 +199,7 @@ export default { |
|
|
}, |
|
|
}, |
|
|
// 鼠标经纬度 |
|
|
// 鼠标经纬度 |
|
|
coordinatesText: '经度: --, 纬度: --', |
|
|
coordinatesText: '经度: --, 纬度: --', |
|
|
|
|
|
currentCoordinates: null, |
|
|
// 比例尺(高德风格) |
|
|
// 比例尺(高德风格) |
|
|
scaleBarText: '--', |
|
|
scaleBarText: '--', |
|
|
scaleBarWidthPx: 80, |
|
|
scaleBarWidthPx: 80, |
|
|
@ -1689,10 +1700,12 @@ export default { |
|
|
// 允许截图时读取 canvas 内容(若截图仅用 readPixels 可改为 false 以提升性能) |
|
|
// 允许截图时读取 canvas 内容(若截图仅用 readPixels 可改为 false 以提升性能) |
|
|
contextOptions: { |
|
|
contextOptions: { |
|
|
preserveDrawingBuffer: true, |
|
|
preserveDrawingBuffer: true, |
|
|
antialias: true // 开启 WebGL 全局抗锯齿 |
|
|
webgl: { |
|
|
}, |
|
|
antialias: true, |
|
|
// 多重采样抗锯齿(WebGL2 下生效,8 在浏览器缩放时线条更平滑) |
|
|
msaa: true, |
|
|
msaaSamples: 8 |
|
|
msaaSamples: 4 |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
}) |
|
|
}) |
|
|
this.viewer.cesiumWidget.creditContainer.style.display = "none" |
|
|
this.viewer.cesiumWidget.creditContainer.style.display = "none" |
|
|
// 根据浏览器缩放比例提高渲染分辨率,减轻 80% 等缩放下线条锯齿 |
|
|
// 根据浏览器缩放比例提高渲染分辨率,减轻 80% 等缩放下线条锯齿 |
|
|
@ -1733,6 +1746,7 @@ export default { |
|
|
console.log('Cesium离线二维地图已加载') |
|
|
console.log('Cesium离线二维地图已加载') |
|
|
|
|
|
|
|
|
// 1. 定义全局拾取处理器(含防抖,避免双击误触导致相机高度剧烈变化) |
|
|
// 1. 定义全局拾取处理器(含防抖,避免双击误触导致相机高度剧烈变化) |
|
|
|
|
|
this.viewer.scene.postProcessStages.fxaa.enabled = true |
|
|
this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas); |
|
|
this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas); |
|
|
this.handler.setInputAction((click) => { |
|
|
this.handler.setInputAction((click) => { |
|
|
// 隐藏右键菜单 |
|
|
// 隐藏右键菜单 |
|
|
@ -2279,10 +2293,18 @@ export default { |
|
|
addCoordinateLabels() { |
|
|
addCoordinateLabels() { |
|
|
for (let lon = -180; lon <= 180; lon += 30) { |
|
|
for (let lon = -180; lon <= 180; lon += 30) { |
|
|
for (let lat = -90; lat <= 90; lat += 30) { |
|
|
for (let lat = -90; lat <= 90; lat += 30) { |
|
|
|
|
|
let latText, lonText |
|
|
|
|
|
if (this.coordinateFormat === 'dms') { |
|
|
|
|
|
latText = `${this.degreesToDMS(lat)}N` |
|
|
|
|
|
lonText = `${this.degreesToDMS(lon)}E` |
|
|
|
|
|
} else { |
|
|
|
|
|
latText = `${lat.toFixed(2)}°N` |
|
|
|
|
|
lonText = `${lon.toFixed(2)}°E` |
|
|
|
|
|
} |
|
|
this.viewer.entities.add({ |
|
|
this.viewer.entities.add({ |
|
|
position: Cesium.Cartesian3.fromDegrees(lon, lat), |
|
|
position: Cesium.Cartesian3.fromDegrees(lon, lat), |
|
|
label: { |
|
|
label: { |
|
|
text: `${this.degreesToDMS(lat)}N\n${this.degreesToDMS(lon)}E`, |
|
|
text: `${latText}\n${lonText}`, |
|
|
font: '12px sans-serif', |
|
|
font: '12px sans-serif', |
|
|
fillColor: Cesium.Color.BLACK, |
|
|
fillColor: Cesium.Color.BLACK, |
|
|
outlineColor: Cesium.Color.WHITE, |
|
|
outlineColor: Cesium.Color.WHITE, |
|
|
@ -2540,7 +2562,10 @@ export default { |
|
|
return this.drawingPoints.length > 0 ? [this.drawingPoints[this.drawingPoints.length - 1]] : []; |
|
|
return this.drawingPoints.length > 0 ? [this.drawingPoints[this.drawingPoints.length - 1]] : []; |
|
|
}, false), |
|
|
}, false), |
|
|
width: this.defaultStyles.line.width, |
|
|
width: this.defaultStyles.line.width, |
|
|
material: this.getPolylineSolidMaterial(this.defaultStyles.line.color), |
|
|
material: new Cesium.PolylineDashMaterialProperty({ |
|
|
|
|
|
color: Cesium.Color.fromCssColorString(this.defaultStyles.line.color), |
|
|
|
|
|
dashLength: 16 |
|
|
|
|
|
}), |
|
|
clampToGround: true |
|
|
clampToGround: true |
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|
@ -3792,113 +3817,6 @@ export default { |
|
|
return entity |
|
|
return entity |
|
|
}, |
|
|
}, |
|
|
// ================== 工具方法 ================== |
|
|
// ================== 工具方法 ================== |
|
|
/** 生成普通纯色折线材质(无轮廓/发光) */ |
|
|
|
|
|
getPolylineSolidMaterial(cssColor) { |
|
|
|
|
|
const color = Cesium.Color.fromCssColorString(cssColor) |
|
|
|
|
|
return new Cesium.ColorMaterialProperty(color) |
|
|
|
|
|
}, |
|
|
|
|
|
/** 根据浏览器缩放比例设置渲染分辨率,减轻 80% 等缩放下线条锯齿 */ |
|
|
|
|
|
applyResolutionScale() { |
|
|
|
|
|
if (!this.viewer || !this.viewer.cesiumWidget) return |
|
|
|
|
|
const vv = window.visualViewport |
|
|
|
|
|
const scale = (vv && typeof vv.scale === 'number') ? vv.scale : 1 |
|
|
|
|
|
// 始终使用至少 2.0 倍超采样,80% 缩放下再按比例提高(上限 2.5),显著减轻锯齿 |
|
|
|
|
|
let resolutionScale = 2.0 |
|
|
|
|
|
if (scale > 0 && scale < 1) { |
|
|
|
|
|
resolutionScale = Math.min(2.5, 1 / scale) |
|
|
|
|
|
if (resolutionScale < 2.0) resolutionScale = 2.0 |
|
|
|
|
|
} |
|
|
|
|
|
this.viewer.resolutionScale = resolutionScale |
|
|
|
|
|
try { |
|
|
|
|
|
if (typeof this.viewer.cesiumWidget.resize === 'function') { |
|
|
|
|
|
this.viewer.cesiumWidget.resize() |
|
|
|
|
|
} |
|
|
|
|
|
} catch (e) { /* 忽略 resize 异常 */ } |
|
|
|
|
|
if (this.viewer.scene.requestRenderMode) this.viewer.scene.requestRender() |
|
|
|
|
|
}, |
|
|
|
|
|
/** 监听浏览器缩放/窗口变化,更新 resolutionScale;返回取消监听的函数 */ |
|
|
|
|
|
setupResolutionScaleListener() { |
|
|
|
|
|
const onResize = () => { |
|
|
|
|
|
this.applyResolutionScale() |
|
|
|
|
|
} |
|
|
|
|
|
if (window.visualViewport) { |
|
|
|
|
|
window.visualViewport.addEventListener('resize', onResize) |
|
|
|
|
|
window.visualViewport.addEventListener('scroll', onResize) |
|
|
|
|
|
} |
|
|
|
|
|
window.addEventListener('resize', onResize) |
|
|
|
|
|
return () => { |
|
|
|
|
|
if (window.visualViewport) { |
|
|
|
|
|
window.visualViewport.removeEventListener('resize', onResize) |
|
|
|
|
|
window.visualViewport.removeEventListener('scroll', onResize) |
|
|
|
|
|
} |
|
|
|
|
|
window.removeEventListener('resize', onResize) |
|
|
|
|
|
} |
|
|
|
|
|
}, |
|
|
|
|
|
/** 按等经度/等纬度采样矩形边框,使在 2D 地图缩小时仍显示为直线(避免测地线弯弧) */ |
|
|
|
|
|
getRectangleBorderPositions(rect, segmentsPerEdge = 48) { |
|
|
|
|
|
const west = rect.west |
|
|
|
|
|
const south = rect.south |
|
|
|
|
|
const east = rect.east |
|
|
|
|
|
const north = rect.north |
|
|
|
|
|
const positions = [] |
|
|
|
|
|
const n = Math.max(2, segmentsPerEdge) |
|
|
|
|
|
for (let i = 0; i <= n; i++) { |
|
|
|
|
|
const lon = west + (east - west) * (i / n) |
|
|
|
|
|
positions.push(Cesium.Cartesian3.fromRadians(lon, south)) |
|
|
|
|
|
} |
|
|
|
|
|
for (let i = 1; i <= n; i++) { |
|
|
|
|
|
const lat = south + (north - south) * (i / n) |
|
|
|
|
|
positions.push(Cesium.Cartesian3.fromRadians(east, lat)) |
|
|
|
|
|
} |
|
|
|
|
|
for (let i = 1; i <= n; i++) { |
|
|
|
|
|
const lon = east - (east - west) * (i / n) |
|
|
|
|
|
positions.push(Cesium.Cartesian3.fromRadians(lon, north)) |
|
|
|
|
|
} |
|
|
|
|
|
for (let i = 1; i < n; i++) { |
|
|
|
|
|
const lat = north - (north - south) * (i / n) |
|
|
|
|
|
positions.push(Cesium.Cartesian3.fromRadians(west, lat)) |
|
|
|
|
|
} |
|
|
|
|
|
return positions |
|
|
|
|
|
}, |
|
|
|
|
|
/** 在 2D 墨卡托下将一段线采样为“直线”(避免测地线弯弧);返回 Cartesian3 数组含起点到终点 */ |
|
|
|
|
|
sampleSegmentStraightIn2D(cartesianA, cartesianB, numSegments = 32) { |
|
|
|
|
|
const cgA = Cesium.Cartographic.fromCartesian(cartesianA) |
|
|
|
|
|
const cgB = Cesium.Cartographic.fromCartesian(cartesianB) |
|
|
|
|
|
const lonA = cgA.longitude |
|
|
|
|
|
const latA = cgA.latitude |
|
|
|
|
|
const lonB = cgB.longitude |
|
|
|
|
|
const latB = cgB.latitude |
|
|
|
|
|
const yA = Math.log(Math.tan(Math.PI / 4 + latA / 2)) |
|
|
|
|
|
const yB = Math.log(Math.tan(Math.PI / 4 + latB / 2)) |
|
|
|
|
|
const n = Math.max(1, numSegments) |
|
|
|
|
|
const positions = [] |
|
|
|
|
|
for (let i = 0; i <= n; i++) { |
|
|
|
|
|
const t = i / n |
|
|
|
|
|
const x = lonA + t * (lonB - lonA) |
|
|
|
|
|
const y = yA + t * (yB - yA) |
|
|
|
|
|
const lat = 2 * Math.atan(Math.exp(y)) - Math.PI / 2 |
|
|
|
|
|
positions.push(Cesium.Cartesian3.fromRadians(x, lat)) |
|
|
|
|
|
} |
|
|
|
|
|
return positions |
|
|
|
|
|
}, |
|
|
|
|
|
/** 将折线/多边形顶点按 2D 墨卡托“直线”采样,使缩放小时仍显示为直;closed 为 true 表示多边形闭合 */ |
|
|
|
|
|
getPolylinePositionsStraightIn2D(positions, closed = false, segmentsPerEdge = 32) { |
|
|
|
|
|
if (!positions || positions.length < 2) return positions |
|
|
|
|
|
const n = positions.length |
|
|
|
|
|
const result = [] |
|
|
|
|
|
const segs = closed ? n : n - 1 |
|
|
|
|
|
for (let seg = 0; seg < segs; seg++) { |
|
|
|
|
|
const i = seg |
|
|
|
|
|
const j = closed ? (seg + 1) % n : seg + 1 |
|
|
|
|
|
const sampled = this.sampleSegmentStraightIn2D(positions[i], positions[j], segmentsPerEdge) |
|
|
|
|
|
if (seg === 0) { |
|
|
|
|
|
result.push(...sampled) |
|
|
|
|
|
} else { |
|
|
|
|
|
for (let k = 1; k < sampled.length; k++) result.push(sampled[k]) |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
return result |
|
|
|
|
|
}, |
|
|
|
|
|
getClickPosition(pixelPosition) { |
|
|
getClickPosition(pixelPosition) { |
|
|
const cartesian = this.viewer.camera.pickEllipsoid(pixelPosition, this.viewer.scene.globe.ellipsoid) |
|
|
const cartesian = this.viewer.camera.pickEllipsoid(pixelPosition, this.viewer.scene.globe.ellipsoid) |
|
|
return cartesian |
|
|
return cartesian |
|
|
@ -4160,6 +4078,9 @@ export default { |
|
|
entity.polyline.material = this.getPolylineSolidMaterial(data.borderColor || data.color) |
|
|
entity.polyline.material = this.getPolylineSolidMaterial(data.borderColor || data.color) |
|
|
entity.polyline.width = data.width |
|
|
entity.polyline.width = data.width |
|
|
} |
|
|
} |
|
|
|
|
|
if (data.name && data.centerEntity && data.centerEntity.label) { |
|
|
|
|
|
data.centerEntity.label.text = data.name |
|
|
|
|
|
} |
|
|
break |
|
|
break |
|
|
case 'sector': |
|
|
case 'sector': |
|
|
if (entity.polygon) { |
|
|
if (entity.polygon) { |
|
|
@ -4747,7 +4668,13 @@ export default { |
|
|
}, |
|
|
}, |
|
|
duration: 2 |
|
|
duration: 2 |
|
|
}) |
|
|
}) |
|
|
this.$message.success(`已定位到经度 ${this.degreesToDMS(lng)},纬度 ${this.degreesToDMS(lat)}`) |
|
|
let coordinateText |
|
|
|
|
|
if (this.coordinateFormat === 'dms') { |
|
|
|
|
|
coordinateText = `${this.degreesToDMS(lng)},${this.degreesToDMS(lat)}` |
|
|
|
|
|
} else { |
|
|
|
|
|
coordinateText = `${lng.toFixed(6)},${lat.toFixed(6)}` |
|
|
|
|
|
} |
|
|
|
|
|
this.$message.success(`已定位到经度 ${coordinateText}`) |
|
|
} |
|
|
} |
|
|
}, |
|
|
}, |
|
|
|
|
|
|
|
|
@ -5018,12 +4945,26 @@ export default { |
|
|
const cartographic = Cesium.Cartographic.fromCartesian(cartesian) |
|
|
const cartographic = Cesium.Cartographic.fromCartesian(cartesian) |
|
|
const longitude = Cesium.Math.toDegrees(cartographic.longitude) |
|
|
const longitude = Cesium.Math.toDegrees(cartographic.longitude) |
|
|
const latitude = Cesium.Math.toDegrees(cartographic.latitude) |
|
|
const latitude = Cesium.Math.toDegrees(cartographic.latitude) |
|
|
this.coordinatesText = `经度: ${this.degreesToDMS(longitude)}, 纬度: ${this.degreesToDMS(latitude)}` |
|
|
this.currentCoordinates = { longitude, latitude } |
|
|
|
|
|
this.updateCoordinatesDisplay() |
|
|
} else { |
|
|
} else { |
|
|
this.coordinatesText = '经度: --, 纬度: --' |
|
|
this.coordinatesText = '经度: --, 纬度: --' |
|
|
} |
|
|
} |
|
|
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE) |
|
|
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE) |
|
|
}, |
|
|
}, |
|
|
|
|
|
// 更新坐标显示 |
|
|
|
|
|
updateCoordinatesDisplay() { |
|
|
|
|
|
if (!this.currentCoordinates) { |
|
|
|
|
|
this.coordinatesText = '经度: --, 纬度: --' |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
const { longitude, latitude } = this.currentCoordinates |
|
|
|
|
|
if (this.coordinateFormat === 'dms') { |
|
|
|
|
|
this.coordinatesText = `经度: ${this.degreesToDMS(longitude)}, 纬度: ${this.degreesToDMS(latitude)}` |
|
|
|
|
|
} else { |
|
|
|
|
|
this.coordinatesText = `经度: ${longitude.toFixed(6)}, 纬度: ${latitude.toFixed(6)}` |
|
|
|
|
|
} |
|
|
|
|
|
}, |
|
|
destroyViewer() { |
|
|
destroyViewer() { |
|
|
this.stopDrawing() |
|
|
this.stopDrawing() |
|
|
this.clearAll(false) |
|
|
this.clearAll(false) |
|
|
@ -5162,6 +5103,17 @@ export default { |
|
|
outlineColor: Cesium.Color.WHITE, |
|
|
outlineColor: Cesium.Color.WHITE, |
|
|
outlineWidth: 2, |
|
|
outlineWidth: 2, |
|
|
disableDepthTestDistance: Number.POSITIVE_INFINITY |
|
|
disableDepthTestDistance: Number.POSITIVE_INFINITY |
|
|
|
|
|
}, |
|
|
|
|
|
label: { |
|
|
|
|
|
text: '', |
|
|
|
|
|
font: '14px Microsoft YaHei', |
|
|
|
|
|
fillColor: Cesium.Color.WHITE, |
|
|
|
|
|
outlineColor: Cesium.Color.BLACK, |
|
|
|
|
|
outlineWidth: 2, |
|
|
|
|
|
style: Cesium.LabelStyle.FILL_AND_OUTLINE, |
|
|
|
|
|
verticalOrigin: Cesium.VerticalOrigin.TOP, |
|
|
|
|
|
pixelOffset: new Cesium.Cartesian2(0, -20), |
|
|
|
|
|
disableDepthTestDistance: Number.POSITIVE_INFINITY |
|
|
} |
|
|
} |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
@ -5202,12 +5154,17 @@ export default { |
|
|
centerEntity: this.powerZoneCenterEntity, |
|
|
centerEntity: this.powerZoneCenterEntity, |
|
|
center: this.powerZoneCenter, |
|
|
center: this.powerZoneCenter, |
|
|
radius: radiusInMeters, |
|
|
radius: radiusInMeters, |
|
|
|
|
|
name: radiusData.name, |
|
|
color: '#FF0000', |
|
|
color: '#FF0000', |
|
|
opacity: 0, |
|
|
opacity: 0, |
|
|
borderColor: '#FF0000', |
|
|
borderColor: '#FF0000', |
|
|
width: 2 |
|
|
width: 2 |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
if (this.powerZoneCenterEntity && this.powerZoneCenterEntity.label) { |
|
|
|
|
|
this.powerZoneCenterEntity.label.text = radiusData.name; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
this.isDrawing = false; |
|
|
this.isDrawing = false; |
|
|
this.viewer.canvas.style.cursor = 'default'; |
|
|
this.viewer.canvas.style.cursor = 'default'; |
|
|
|
|
|
|
|
|
|