fix(map): keep ghost pin stems terrain-relative
This commit is contained in:
@@ -88,5 +88,10 @@ positions. The adapter only:
|
|||||||
- returns the current positions through `snapshotGhostPins()` for an explicit
|
- returns the current positions through `snapshotGhostPins()` for an explicit
|
||||||
product save.
|
product save.
|
||||||
|
|
||||||
|
The elevated head and stem use non-constant Cesium properties so their ground
|
||||||
|
height is resolved again after asynchronous terrain tiles arrive. The stem
|
||||||
|
also keeps the same material as its depth-fail fallback; an early ellipsoid
|
||||||
|
height must not leave a visible head with its stem hidden below terrain.
|
||||||
|
|
||||||
Motion never recreates the Cesium viewer or any provider. A renderer teardown
|
Motion never recreates the Cesium viewer or any provider. A renderer teardown
|
||||||
stops its single animation frame loop.
|
stops its single animation frame loop.
|
||||||
|
|||||||
@@ -152,6 +152,10 @@ billboard до label-слоя, поэтому непрозрачная плаш
|
|||||||
лежащий под ней маркер вместо «рентгеновского» проступания его обводки. Text
|
лежащий под ней маркер вместо «рентгеновского» проступания его обводки. Text
|
||||||
outline плашки отключён и не создаёт дополнительный halo. Контуры
|
outline плашки отключён и не создаёт дополнительный halo. Контуры
|
||||||
`surface-fill` остаются частью отдельного контракта геозон.
|
`surface-fill` остаются частью отдельного контракта геозон.
|
||||||
|
Высота elevated target и его стержня остаётся callback-свойством Cesium:
|
||||||
|
после асинхронной загрузки terrain она пересчитывается относительно уже
|
||||||
|
доступной поверхности, а depth-fail material не даёт стержню исчезнуть при
|
||||||
|
пересечении рельефа.
|
||||||
|
|
||||||
## Acceptance fixtures
|
## Acceptance fixtures
|
||||||
|
|
||||||
|
|||||||
@@ -605,33 +605,25 @@ function rebuildGhostPinEntities(
|
|||||||
presentation.label_hide_camera_height_meters,
|
presentation.label_hide_camera_height_meters,
|
||||||
);
|
);
|
||||||
for (const pin of runtime.currentGhostPins) {
|
for (const pin of runtime.currentGhostPins) {
|
||||||
const groundHeight = ghostPinGroundHeight(runtime, pin);
|
|
||||||
const headHeight = groundHeight + presentation.stem_height_meters;
|
|
||||||
const label = presentation.label_mode === "subject_id"
|
const label = presentation.label_mode === "subject_id"
|
||||||
? pin.id
|
? pin.id
|
||||||
: pin.label;
|
: pin.label;
|
||||||
ghostPins.entities.add({
|
ghostPins.entities.add({
|
||||||
id: pin.id,
|
id: pin.id,
|
||||||
position: Cesium.Cartesian3.fromDegrees(
|
position: ghostPinTopPosition(
|
||||||
pin.longitude,
|
runtime,
|
||||||
pin.latitude,
|
pin,
|
||||||
headHeight,
|
presentation.stem_height_meters,
|
||||||
),
|
),
|
||||||
polyline: {
|
polyline: {
|
||||||
positions: [
|
positions: ghostPinStemPositions(
|
||||||
Cesium.Cartesian3.fromDegrees(
|
runtime,
|
||||||
pin.longitude,
|
pin,
|
||||||
pin.latitude,
|
presentation.stem_height_meters,
|
||||||
groundHeight,
|
|
||||||
),
|
),
|
||||||
Cesium.Cartesian3.fromDegrees(
|
|
||||||
pin.longitude,
|
|
||||||
pin.latitude,
|
|
||||||
headHeight,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
width: presentation.stem_width_px,
|
width: presentation.stem_width_px,
|
||||||
material: fillColor,
|
material: fillColor,
|
||||||
|
depthFailMaterial: fillColor,
|
||||||
distanceDisplayCondition: targetDistance,
|
distanceDisplayCondition: targetDistance,
|
||||||
},
|
},
|
||||||
billboard: {
|
billboard: {
|
||||||
@@ -731,27 +723,55 @@ function updateGhostPinPositions(
|
|||||||
for (const pin of runtime.currentGhostPins) {
|
for (const pin of runtime.currentGhostPins) {
|
||||||
const entity = ghostPins.entities.getById(pin.id);
|
const entity = ghostPins.entities.getById(pin.id);
|
||||||
if (!entity) continue;
|
if (!entity) continue;
|
||||||
const groundHeight = ghostPinGroundHeight(runtime, pin);
|
entity.position = ghostPinTopPosition(
|
||||||
const headHeight =
|
runtime,
|
||||||
groundHeight + layer.presentation.stem_height_meters;
|
pin,
|
||||||
entity.position = new Cesium.ConstantPositionProperty(
|
layer.presentation.stem_height_meters,
|
||||||
Cesium.Cartesian3.fromDegrees(pin.longitude, pin.latitude, headHeight),
|
|
||||||
);
|
);
|
||||||
if (entity.polyline) {
|
if (entity.polyline) {
|
||||||
entity.polyline.positions = new Cesium.ConstantProperty([
|
entity.polyline.positions = ghostPinStemPositions(
|
||||||
Cesium.Cartesian3.fromDegrees(
|
runtime,
|
||||||
|
pin,
|
||||||
|
layer.presentation.stem_height_meters,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ghostPinTopPosition(
|
||||||
|
runtime: RendererRuntime,
|
||||||
|
pin: MapGhostPin,
|
||||||
|
stemHeightMeters: number,
|
||||||
|
): CesiumModule.CallbackPositionProperty {
|
||||||
|
return new runtime.Cesium.CallbackPositionProperty(() => (
|
||||||
|
runtime.Cesium.Cartesian3.fromDegrees(
|
||||||
|
pin.longitude,
|
||||||
|
pin.latitude,
|
||||||
|
ghostPinGroundHeight(runtime, pin) + stemHeightMeters,
|
||||||
|
)
|
||||||
|
), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ghostPinStemPositions(
|
||||||
|
runtime: RendererRuntime,
|
||||||
|
pin: MapGhostPin,
|
||||||
|
stemHeightMeters: number,
|
||||||
|
): CesiumModule.CallbackProperty {
|
||||||
|
return new runtime.Cesium.CallbackProperty(() => {
|
||||||
|
const groundHeight = ghostPinGroundHeight(runtime, pin);
|
||||||
|
return [
|
||||||
|
runtime.Cesium.Cartesian3.fromDegrees(
|
||||||
pin.longitude,
|
pin.longitude,
|
||||||
pin.latitude,
|
pin.latitude,
|
||||||
groundHeight,
|
groundHeight,
|
||||||
),
|
),
|
||||||
Cesium.Cartesian3.fromDegrees(
|
runtime.Cesium.Cartesian3.fromDegrees(
|
||||||
pin.longitude,
|
pin.longitude,
|
||||||
pin.latitude,
|
pin.latitude,
|
||||||
headHeight,
|
groundHeight + stemHeightMeters,
|
||||||
),
|
),
|
||||||
]);
|
];
|
||||||
}
|
}, false);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function ghostPinGroundHeight(
|
function ghostPinGroundHeight(
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
|
import { readFile } from "node:fs/promises";
|
||||||
import test from "node:test";
|
import test from "node:test";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -30,3 +31,14 @@ test("advanceGhostPins turns an escaping pin back toward its anchor", () => {
|
|||||||
assert.ok(geodesicDistanceMeters(anchor, advanced) < 990);
|
assert.ok(geodesicDistanceMeters(anchor, advanced) < 990);
|
||||||
assert.ok(advanced.heading_degrees > 260 && advanced.heading_degrees < 280);
|
assert.ok(advanced.heading_degrees > 260 && advanced.heading_degrees < 280);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("ghost pin stems stay terrain-relative after asynchronous tile loading", async () => {
|
||||||
|
const source = await readFile(
|
||||||
|
new URL("../src/CesiumMapRenderer.tsx", import.meta.url),
|
||||||
|
"utf8",
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.match(source, /new runtime\.Cesium\.CallbackPositionProperty/);
|
||||||
|
assert.match(source, /new runtime\.Cesium\.CallbackProperty/);
|
||||||
|
assert.match(source, /depthFailMaterial: fillColor/);
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user