diff --git a/web/src/maplibre/custom3DLayer.ts b/web/src/maplibre/custom3DLayer.ts index 3ae0ee7..5fdb559 100644 --- a/web/src/maplibre/custom3DLayer.ts +++ b/web/src/maplibre/custom3DLayer.ts @@ -71,6 +71,7 @@ export function createCustom3DLayer(id: string, map: Map): Layer3DHandle { let renderer: THREE.WebGLRenderer | null = null let origin: MercatorCoordinate | null = null + let originLngLat: [number, number] | null = null let dirty = true let currentLines: Line3D[] = [] let currentPoints: Point3D[] = [] @@ -96,6 +97,7 @@ export function createCustom3DLayer(id: string, map: Map): Layer3DHandle { sphereMeshes.length = 0 sphereIds.length = 0 origin = null + originLngLat = null } function rebuildGeometry() { @@ -109,6 +111,7 @@ export function createCustom3DLayer(id: string, map: Map): Layer3DHandle { if (!firstPt) return const [lon0, lat0, alt0] = firstPt + originLngLat = [lon0, lat0] // Use exaggerated altitude so geometry aligns with visual terrain. origin = MercatorCoordinate.fromLngLat([lon0, lat0], alt0 * TERRAIN_EXAGGERATION) const mpu = origin.meterInMercatorCoordinateUnits() @@ -198,9 +201,14 @@ export function createCustom3DLayer(id: string, map: Map): Layer3DHandle { // modelViewProjectionMatrix (used by terrain tiles) but absent from // mercatorMatrix (passed to custom layers). Subtracting it here makes // our exaggerated-altitude objects align with the visual terrain surface. - const mpu = origin.meterInMercatorCoordinateUnits() - const centerElevM = (map as unknown as { transform: { elevation: number } }).transform?.elevation ?? 0 - const centerElevMerc = centerElevM * mpu + // + // Use MercatorCoordinate.fromLngLat (not origin.meterInMercatorCoordinateUnits) + // because altitude z uses 1/(circumference*cos(lat)) while mpu gives + // cos(lat)/circumference — they differ by cos²(lat), causing ~2× error at lat 50°. + const centerElevM = (map as unknown as { transform: { elevation: number } }).transform?.elevation ?? 0 + const centerElevMerc = originLngLat + ? MercatorCoordinate.fromLngLat(originLngLat, centerElevM).z + : centerElevM * origin.meterInMercatorCoordinateUnits() const m = new THREE.Matrix4().fromArray(matrix) const t = new THREE.Matrix4().makeTranslation(