| | |
| | | let shapeDisplaySource |
| | | const deviceRangePrimitiveMap = new Map() |
| | | const activeToolMode = ref('') |
| | | const suppressDeviceFly = ref(false) |
| | | let lastSelectedDeviceIds = [] |
| | | let deviceRangeRenderToken = 0 |
| | | |
| | | const rules = { |
| | | areaName: fieldRules(true, 50), |
| | |
| | | |
| | | async function renderDeviceRanges (rows) { |
| | | if (!viewer) return |
| | | deviceRangeRenderToken += 1 |
| | | const renderToken = deviceRangeRenderToken |
| | | const selectedRows = Array.isArray(rows) ? rows : [] |
| | | const nextIds = new Set(selectedRows.map(row => String(row?.id))) |
| | | for (const [id, primitive] of deviceRangePrimitiveMap.entries()) { |
| | |
| | | return { row, primitive } |
| | | }) |
| | | ) |
| | | if (renderToken !== deviceRangeRenderToken) return |
| | | primitives.forEach(({ row, primitive }) => { |
| | | if (!primitive) return |
| | | const id = String(row?.id) |
| | | if (!nextIds.has(id)) return |
| | | deviceRangePrimitiveMap.set(id, primitive) |
| | | viewer.scene.primitives.add(primitive) |
| | | }) |
| | |
| | | material: style.outline, |
| | | }, |
| | | }) |
| | | }) |
| | | } |
| | | |
| | | function pushRadiusBoundingPoints (positions, center, radius) { |
| | | if (!center || !Number.isFinite(Number(radius)) || radius <= 0) return |
| | | const resolved = resolveLngLatPoint(center) || {} |
| | | const lng = Number(resolved.lng ?? resolved.longitude) |
| | | const lat = Number(resolved.lat ?? resolved.latitude) |
| | | if (!Number.isFinite(lng) || !Number.isFinite(lat)) return |
| | | const baseLat = Cesium.Math.toRadians(lat) |
| | | const earthRadius = Cesium.Ellipsoid.WGS84.maximumRadius |
| | | const deltaLat = radius / earthRadius |
| | | const deltaLng = radius / (earthRadius * Math.max(Math.cos(baseLat), 0.000001)) |
| | | const points = [ |
| | | { lng: Cesium.Math.toDegrees(Cesium.Math.toRadians(lng) + deltaLng), lat }, |
| | | { lng: Cesium.Math.toDegrees(Cesium.Math.toRadians(lng) - deltaLng), lat }, |
| | | { lng, lat: Cesium.Math.toDegrees(baseLat + deltaLat) }, |
| | | { lng, lat: Cesium.Math.toDegrees(baseLat - deltaLat) }, |
| | | ] |
| | | points.forEach(point => { |
| | | positions.push(Cesium.Cartesian3.fromDegrees(point.lng, point.lat, resolved.height || 0)) |
| | | }) |
| | | } |
| | | |
| | | function collectShapeBoundingPositions () { |
| | | const positions = [] |
| | | if (!viewer || !shapeList.value.length) return positions |
| | | shapeList.value.forEach(shape => { |
| | | let points = getShapeDisplayPoints(shape) |
| | | if (!Array.isArray(points) || !points.length) { |
| | | points = resolveControlPoints(shape?.meta, shape?.drawType) |
| | | } |
| | | if (Array.isArray(points) && points.length) { |
| | | points.forEach(point => { |
| | | const resolved = resolveLngLatPoint(point) |
| | | const normalized = normalizeShapePoint(resolved) |
| | | if (!normalized) return |
| | | positions.push(Cesium.Cartesian3.fromDegrees(normalized.lng, normalized.lat, normalized.height)) |
| | | }) |
| | | } |
| | | if (shape?.drawType === 'buffer' && shape?.meta?.center && Array.isArray(shape?.meta?.bufferRadii)) { |
| | | const maxRadius = Math.max(...shape.meta.bufferRadii.filter(item => Number.isFinite(item))) |
| | | pushRadiusBoundingPoints(positions, shape.meta.center, maxRadius) |
| | | } |
| | | if (shape?.drawType === 'ellipse' && shape?.meta?.center) { |
| | | const major = Number(shape?.meta?.semiMajor) |
| | | const minor = Number(shape?.meta?.semiMinor) |
| | | const radius = Number.isFinite(major) && Number.isFinite(minor) ? Math.max(major, minor) : null |
| | | pushRadiusBoundingPoints(positions, shape.meta.center, radius) |
| | | } |
| | | }) |
| | | return positions |
| | | } |
| | | |
| | | function fitViewToShapeList () { |
| | | if (!viewer || !shapeList.value.length) return |
| | | const positions = collectShapeBoundingPositions() |
| | | if (positions.length < 2) return |
| | | const boundingSphere = Cesium.BoundingSphere.fromPoints(positions) |
| | | const range = Math.max(boundingSphere.radius * 2.4, 200) |
| | | viewer.camera.flyToBoundingSphere(boundingSphere, { |
| | | duration: 0, |
| | | offset: new Cesium.HeadingPitchRange(viewer.camera.heading, Cesium.Math.toRadians(-90), range), |
| | | }) |
| | | } |
| | | |
| | |
| | | if (!viewer) return |
| | | if (shapeList.value.length) { |
| | | renderShapeList() |
| | | fitViewToShapeList() |
| | | return |
| | | } |
| | | if (!formData.value?.geom) return |
| | |
| | | const ids = rows.map(item => item.id) |
| | | formData.value.deviceIds = ids.join(',') |
| | | renderDeviceRanges(rows) |
| | | const target = rows[rows.length - 1] |
| | | if (target) { |
| | | void flyToDevice(target) |
| | | if (suppressDeviceFly.value) { |
| | | lastSelectedDeviceIds = [...ids] |
| | | return |
| | | } |
| | | const prevSet = new Set(lastSelectedDeviceIds) |
| | | const addedRow = rows.find(row => !prevSet.has(row.id)) |
| | | lastSelectedDeviceIds = [...ids] |
| | | if (addedRow) { |
| | | void flyToDevice(addedRow) |
| | | } |
| | | } |
| | | |
| | |
| | | viewPolygon() |
| | | } |
| | | await nextTick() |
| | | suppressDeviceFly.value = true |
| | | syncDeviceSelection() |
| | | lastSelectedDeviceIds = selectedDeviceRows.value.map(row => row.id) |
| | | await nextTick() |
| | | suppressDeviceFly.value = false |
| | | if (!readonly.value) { |
| | | renderDeviceRanges(selectedDeviceRows.value) |
| | | } |