diff --git a/services/pcp-ui-service/src/main/resources/static/angular_motion_scripts.js b/services/pcp-ui-service/src/main/resources/static/angular_motion_scripts.js index 29adcc6..b6d348a 100644 --- a/services/pcp-ui-service/src/main/resources/static/angular_motion_scripts.js +++ b/services/pcp-ui-service/src/main/resources/static/angular_motion_scripts.js @@ -8,7 +8,9 @@ viewer: null, mapDataSource: null, pickHandler: null, + resizeObserver: null, cursorCoordsPanel: null, + targetOverlay: null, }; function el(id) { @@ -169,13 +171,15 @@ ...naturalEarthLayerOptions(), }); state.viewer.scene.globe.enableLighting = false; - state.viewer.scene.requestRenderMode = true; + state.viewer.scene.requestRenderMode = false; state.mapDataSource = new Cesium.CustomDataSource('angular-motion-map-data'); state.viewer.dataSources.add(state.mapDataSource); initCursorCoordinates(); + initTargetOverlay(); state.pickHandler = new Cesium.ScreenSpaceEventHandler(state.viewer.scene.canvas); state.pickHandler.setInputAction(function (movement) { if (!state.picking) return; + resizeMap(); const cartesian = pickGlobePoint(movement.position); if (!cartesian) return; const cartographic = Cesium.Cartographic.fromCartesian(cartesian); @@ -183,10 +187,37 @@ el('angular-motion-lon').value = Cesium.Math.toDegrees(cartographic.longitude).toFixed(6); setPicking(false); drawMap(); + updateTargetOverlay(movement.position); }, Cesium.ScreenSpaceEventType.LEFT_CLICK); + initMapResizeSync(); + state.viewer.scene.postRender.addEventListener(() => updateTargetOverlay()); drawMap(); } + function initMapResizeSync() { + window.addEventListener('resize', scheduleMapResize); + if (typeof ResizeObserver !== 'undefined') { + state.resizeObserver = new ResizeObserver(scheduleMapResize); + state.resizeObserver.observe(el('angular-motion-map')); + } + scheduleMapResize(); + } + + function scheduleMapResize() { + resizeMap(); + requestAnimationFrame(() => { + resizeMap(); + requestAnimationFrame(resizeMap); + }); + setTimeout(resizeMap, 100); + } + + function resizeMap() { + if (!state.viewer) return; + state.viewer.resize(); + state.viewer.scene.requestRender(); + } + function initCursorCoordinates() { state.cursorCoordsPanel = document.createElement('div'); state.cursorCoordsPanel.className = 'angular-motion-cursor-coords-panel'; @@ -207,6 +238,13 @@ }); } + function initTargetOverlay() { + state.targetOverlay = document.createElement('div'); + state.targetOverlay.className = 'angular-motion-target-marker'; + state.targetOverlay.innerHTML = 'ЦЛМ'; + state.viewer.cesiumWidget.container.appendChild(state.targetOverlay); + } + function naturalEarthLayerOptions() { const url = '/webjars/cesium/1.107.2/Build/Cesium/Assets/Textures/NaturalEarthII'; if (Cesium.ImageryLayer?.fromProviderAsync && Cesium.TileMapServiceImageryProvider?.fromUrl) { @@ -225,9 +263,10 @@ function pickGlobePoint(position) { if (!state.viewer || !position) return null; const scene = state.viewer.scene; - if (scene.pickPositionSupported) { - const picked = scene.pickPosition(position); - if (Cesium.defined(picked)) return picked; + const ray = state.viewer.camera.getPickRay(position); + if (ray) { + const globePoint = scene.globe.pick(ray, scene); + if (Cesium.defined(globePoint)) return globePoint; } return state.viewer.camera.pickEllipsoid(position, scene.globe.ellipsoid); } @@ -324,32 +363,27 @@ }); } - function addTarget() { - if (!state.mapDataSource) return; + function updateTargetOverlay(screenPosition = null) { + if (!state.viewer || !state.targetOverlay) return; const lat = Number(el('angular-motion-lat').value); const lon = Number(el('angular-motion-lon').value); - if (!Number.isFinite(lat) || !Number.isFinite(lon)) return; - state.mapDataSource.entities.add({ - name: 'Точка съемки', - position: Cesium.Cartesian3.fromDegrees(lon, lat, 0), - point: { - pixelSize: 10, - color: Cesium.Color.RED, - outlineColor: Cesium.Color.WHITE, - outlineWidth: 2, - heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, - }, - label: { - text: 'ЦЛМ', - font: '12px sans-serif', - fillColor: Cesium.Color.RED, - outlineColor: Cesium.Color.WHITE, - outlineWidth: 2, - style: Cesium.LabelStyle.FILL_AND_OUTLINE, - pixelOffset: new Cesium.Cartesian2(0, -18), - heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, - }, - }); + if (!Number.isFinite(lat) || !Number.isFinite(lon)) { + state.targetOverlay.style.display = 'none'; + return; + } + + const position = screenPosition || Cesium.SceneTransforms.wgs84ToWindowCoordinates( + state.viewer.scene, + Cesium.Cartesian3.fromDegrees(lon, lat, 0) + ); + if (!Cesium.defined(position)) { + state.targetOverlay.style.display = 'none'; + return; + } + + state.targetOverlay.style.display = 'block'; + state.targetOverlay.style.left = `${position.x}px`; + state.targetOverlay.style.top = `${position.y}px`; } function flightLineBounds() { @@ -400,19 +434,29 @@ function drawMap(centerMode = null) { if (!state.mapDataSource || typeof Cesium === 'undefined') return; + resizeMap(); state.mapDataSource.entities.removeAll(); const points = Array.isArray(state.flightLine) ? state.flightLine : []; addPolyline(points, 'leftLatitudeDeg', 'leftLongitudeDeg', Cesium.Color.CORNFLOWERBLUE.withAlpha(0.85), 2, 'Левая граница полосы'); addPolyline(points, 'rightLatitudeDeg', 'rightLongitudeDeg', Cesium.Color.CORNFLOWERBLUE.withAlpha(0.85), 2, 'Правая граница полосы'); addPolyline(points, 'latitudeDeg', 'longitudeDeg', Cesium.Color.YELLOW, 3, 'Трасса полета'); addSurveyContour(); - addTarget(); + updateTargetOverlay(); if (centerMode === 'flight-line') { fitMapToFlightLine(); } else if (centerMode === 'target') { centerMapOnTarget(); } + requestMapRender(); + } + + function requestMapRender() { + if (!state.viewer) return; state.viewer.scene.requestRender(); + requestAnimationFrame(() => { + state.viewer?.scene.requestRender(); + requestAnimationFrame(() => state.viewer?.scene.requestRender()); + }); } function setPicking(value) { diff --git a/services/pcp-ui-service/src/main/resources/static/css/angular-motion.css b/services/pcp-ui-service/src/main/resources/static/css/angular-motion.css index ad19eb3..f0e5d3c 100644 --- a/services/pcp-ui-service/src/main/resources/static/css/angular-motion.css +++ b/services/pcp-ui-service/src/main/resources/static/css/angular-motion.css @@ -65,6 +65,39 @@ white-space: nowrap; } +.angular-motion-target-marker { + position: absolute; + z-index: 1001; + display: none; + pointer-events: none; + transform: translate(-50%, -50%); +} + +.angular-motion-target-marker__dot { + display: block; + width: 14px; + height: 14px; + border: 2px solid #fff; + border-radius: 50%; + background: #ff1f2f; + box-shadow: 0 0 0 1px rgba(255, 31, 47, 0.65); +} + +.angular-motion-target-marker__label { + position: absolute; + left: 50%; + bottom: 16px; + color: #ff4d5d; + font: 700 12px sans-serif; + text-shadow: + -1px -1px 0 #fff, + 1px -1px 0 #fff, + -1px 1px 0 #fff, + 1px 1px 0 #fff; + transform: translateX(-50%); + white-space: nowrap; +} + .angular-motion-result-card { min-height: 360px; }