mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 19:04:02 +00:00
Merge branch 'main' into codex/shared-geodesy
This commit is contained in:
commit
64d7ed3c61
1373 files changed
+245910
-10664
No files matched your search
+73
-540
@@ -1,3 +1,5 @@
|
||||
import { rangeForBoundingSphere } from './data/cameraFraming.js';
|
||||
import { isUnavailableCapability } from './sources/capability.js';
|
||||
import { createOverpassFeatureSource } from './sources/overpassFeatures.js';
|
||||
import { applicationServices } from './services/application.js';
|
||||
import * as Cesium from 'cesium';
|
||||
@@ -7,515 +9,12 @@ import {
|
||||
placesNearViewRecovery,
|
||||
} from './annotations/annotationResolver.js';
|
||||
import { unavailablePlaceSearch } from './search/placeSearch.js';
|
||||
import { cancelCameraArrival } from './data/cameraArrival.js';
|
||||
import { guardCameraAboveGround } from './cameraGroundGuard.js';
|
||||
import { CITY_POIS } from './cityPois.js';
|
||||
|
||||
/**
|
||||
* Points of Interest per city.
|
||||
* Each city has 5 POIs; the first is the default fly-to landmark.
|
||||
*
|
||||
* Field reference:
|
||||
* alt — RANGE (distance from target in meters), NOT absolute altitude
|
||||
* heading — optimal camera heading in degrees (0=N, 90=E, 180=S, 270=W)
|
||||
* pitch — camera tilt in degrees (negative = looking down)
|
||||
* buildingHeight — estimated height of landmark center above ground (meters)
|
||||
*/
|
||||
export const CITY_POIS = {
|
||||
austin: {
|
||||
name: 'Austin',
|
||||
groundElevation: 150, // meters above WGS84 ellipsoid
|
||||
viewBounds: {
|
||||
southwest: { lat: 30.1, lng: -97.95 },
|
||||
northeast: { lat: 30.52, lng: -97.55 },
|
||||
},
|
||||
pois: [
|
||||
{
|
||||
name: 'Texas State Capitol',
|
||||
lat: 30.2747,
|
||||
lon: -97.7403,
|
||||
alt: 550,
|
||||
pitch: -28,
|
||||
heading: 180,
|
||||
buildingHeight: 35,
|
||||
},
|
||||
{
|
||||
name: 'Frost Bank Tower',
|
||||
lat: 30.2674,
|
||||
lon: -97.7434,
|
||||
alt: 550,
|
||||
pitch: -22,
|
||||
heading: 30,
|
||||
buildingHeight: 80,
|
||||
},
|
||||
{
|
||||
name: 'Pennybacker Bridge',
|
||||
lat: 30.3451,
|
||||
lon: -97.7951,
|
||||
alt: 500,
|
||||
pitch: -25,
|
||||
heading: 90,
|
||||
buildingHeight: 40,
|
||||
},
|
||||
{
|
||||
name: 'The Jenga Tower',
|
||||
lat: 30.2642,
|
||||
lon: -97.75,
|
||||
alt: 500,
|
||||
pitch: -18,
|
||||
heading: 45,
|
||||
buildingHeight: 60,
|
||||
},
|
||||
{
|
||||
name: 'UT Tower',
|
||||
lat: 30.2862,
|
||||
lon: -97.7394,
|
||||
alt: 500,
|
||||
pitch: -22,
|
||||
heading: 180,
|
||||
buildingHeight: 50,
|
||||
},
|
||||
],
|
||||
},
|
||||
sf: {
|
||||
name: 'San Francisco',
|
||||
groundElevation: 15,
|
||||
viewBounds: {
|
||||
southwest: { lat: 37.7, lng: -122.53 },
|
||||
northeast: { lat: 37.84, lng: -122.35 },
|
||||
},
|
||||
pois: [
|
||||
{
|
||||
name: 'Golden Gate Bridge',
|
||||
lat: 37.8199,
|
||||
lon: -122.4783,
|
||||
alt: 1400,
|
||||
pitch: -20,
|
||||
heading: 45,
|
||||
buildingHeight: 100,
|
||||
},
|
||||
{
|
||||
name: 'Transamerica Pyramid',
|
||||
lat: 37.7952,
|
||||
lon: -122.4028,
|
||||
alt: 500,
|
||||
pitch: -25,
|
||||
heading: 30,
|
||||
buildingHeight: 85,
|
||||
},
|
||||
{
|
||||
name: 'Salesforce Tower',
|
||||
lat: 37.7897,
|
||||
lon: -122.3972,
|
||||
alt: 680,
|
||||
pitch: -25,
|
||||
heading: 330,
|
||||
buildingHeight: 100,
|
||||
},
|
||||
{
|
||||
name: 'Alcatraz Island',
|
||||
lat: 37.8267,
|
||||
lon: -122.423,
|
||||
alt: 800,
|
||||
pitch: -30,
|
||||
heading: 0,
|
||||
buildingHeight: 20,
|
||||
},
|
||||
{
|
||||
name: 'Coit Tower',
|
||||
lat: 37.8024,
|
||||
lon: -122.4058,
|
||||
alt: 420,
|
||||
pitch: -30,
|
||||
heading: 45,
|
||||
buildingHeight: 30,
|
||||
},
|
||||
],
|
||||
},
|
||||
nyc: {
|
||||
name: 'New York',
|
||||
groundElevation: 10,
|
||||
viewBounds: {
|
||||
southwest: { lat: 40.477, lng: -74.259 },
|
||||
northeast: { lat: 40.918, lng: -73.7 },
|
||||
},
|
||||
pois: [
|
||||
{
|
||||
name: 'Statue of Liberty',
|
||||
lat: 40.6892,
|
||||
lon: -74.0445,
|
||||
alt: 450,
|
||||
pitch: -25,
|
||||
heading: 315,
|
||||
buildingHeight: 45,
|
||||
},
|
||||
{
|
||||
name: 'Empire State Building',
|
||||
lat: 40.7484,
|
||||
lon: -73.9857,
|
||||
alt: 850,
|
||||
pitch: -12,
|
||||
heading: 30,
|
||||
buildingHeight: 130,
|
||||
buildingBounds: { height: 443, width: 130, depth: 75 },
|
||||
},
|
||||
{
|
||||
name: 'One World Trade Center',
|
||||
lat: 40.7127,
|
||||
lon: -74.0134,
|
||||
alt: 850,
|
||||
pitch: -25,
|
||||
heading: 0,
|
||||
buildingHeight: 170,
|
||||
},
|
||||
{
|
||||
name: 'Brooklyn Bridge',
|
||||
lat: 40.7061,
|
||||
lon: -73.9969,
|
||||
alt: 850,
|
||||
pitch: -25,
|
||||
heading: 45,
|
||||
buildingHeight: 40,
|
||||
},
|
||||
{
|
||||
name: 'Chrysler Building',
|
||||
lat: 40.7516,
|
||||
lon: -73.9755,
|
||||
alt: 700,
|
||||
pitch: -20,
|
||||
heading: 225,
|
||||
buildingHeight: 100,
|
||||
},
|
||||
],
|
||||
},
|
||||
tokyo: {
|
||||
name: 'Tokyo',
|
||||
groundElevation: 40,
|
||||
viewBounds: {
|
||||
southwest: { lat: 35.52, lng: 139.55 },
|
||||
northeast: { lat: 35.9, lng: 139.92 },
|
||||
},
|
||||
pois: [
|
||||
{
|
||||
name: 'Tokyo Tower',
|
||||
lat: 35.6586,
|
||||
lon: 139.7454,
|
||||
alt: 850,
|
||||
pitch: -25,
|
||||
heading: 0,
|
||||
buildingHeight: 110,
|
||||
},
|
||||
{
|
||||
name: 'Tokyo Skytree',
|
||||
lat: 35.7101,
|
||||
lon: 139.8107,
|
||||
alt: 900,
|
||||
pitch: -25,
|
||||
heading: 30,
|
||||
buildingHeight: 200,
|
||||
},
|
||||
{
|
||||
name: 'Imperial Palace',
|
||||
lat: 35.6852,
|
||||
lon: 139.7528,
|
||||
alt: 900,
|
||||
pitch: -35,
|
||||
heading: 0,
|
||||
buildingHeight: 20,
|
||||
},
|
||||
{
|
||||
name: 'Senso-ji Temple',
|
||||
lat: 35.7148,
|
||||
lon: 139.7967,
|
||||
alt: 400,
|
||||
pitch: -30,
|
||||
heading: 180,
|
||||
buildingHeight: 25,
|
||||
},
|
||||
{
|
||||
name: 'Mode Gakuen Cocoon Tower',
|
||||
lat: 35.6929,
|
||||
lon: 139.6925,
|
||||
alt: 350,
|
||||
pitch: -20,
|
||||
heading: 30,
|
||||
buildingHeight: 70,
|
||||
},
|
||||
],
|
||||
},
|
||||
london: {
|
||||
name: 'London',
|
||||
groundElevation: 15,
|
||||
viewBounds: {
|
||||
southwest: { lat: 51.28, lng: -0.51 },
|
||||
northeast: { lat: 51.7, lng: 0.33 },
|
||||
},
|
||||
pois: [
|
||||
{
|
||||
name: 'Tower Bridge',
|
||||
lat: 51.5055,
|
||||
lon: -0.0754,
|
||||
alt: 400,
|
||||
pitch: -25,
|
||||
heading: 270,
|
||||
buildingHeight: 65,
|
||||
},
|
||||
{
|
||||
name: 'The Shard',
|
||||
lat: 51.5045,
|
||||
lon: -0.0865,
|
||||
alt: 850,
|
||||
pitch: -20,
|
||||
heading: 0,
|
||||
buildingHeight: 100,
|
||||
},
|
||||
{
|
||||
name: 'Big Ben / Parliament',
|
||||
lat: 51.5007,
|
||||
lon: -0.1246,
|
||||
alt: 600,
|
||||
pitch: -25,
|
||||
heading: 180,
|
||||
buildingHeight: 50,
|
||||
},
|
||||
{
|
||||
name: "St. Paul's Cathedral",
|
||||
lat: 51.5138,
|
||||
lon: -0.0984,
|
||||
alt: 400,
|
||||
pitch: -30,
|
||||
heading: 270,
|
||||
buildingHeight: 55,
|
||||
},
|
||||
{
|
||||
name: 'The Gherkin',
|
||||
lat: 51.5145,
|
||||
lon: -0.0803,
|
||||
alt: 350,
|
||||
pitch: -20,
|
||||
heading: 30,
|
||||
buildingHeight: 60,
|
||||
},
|
||||
],
|
||||
},
|
||||
paris: {
|
||||
name: 'Paris',
|
||||
groundElevation: 35,
|
||||
viewBounds: {
|
||||
southwest: { lat: 48.815, lng: 2.224 },
|
||||
northeast: { lat: 48.902, lng: 2.47 },
|
||||
},
|
||||
pois: [
|
||||
{
|
||||
name: 'Eiffel Tower',
|
||||
lat: 48.8584,
|
||||
lon: 2.2945,
|
||||
alt: 750,
|
||||
pitch: -25,
|
||||
heading: 315,
|
||||
buildingHeight: 150,
|
||||
},
|
||||
{
|
||||
name: 'Arc de Triomphe',
|
||||
lat: 48.8738,
|
||||
lon: 2.295,
|
||||
alt: 400,
|
||||
pitch: -28,
|
||||
heading: 45,
|
||||
buildingHeight: 25,
|
||||
},
|
||||
{
|
||||
name: 'Notre-Dame',
|
||||
lat: 48.853,
|
||||
lon: 2.3499,
|
||||
alt: 400,
|
||||
pitch: -25,
|
||||
heading: 225,
|
||||
buildingHeight: 35,
|
||||
},
|
||||
{
|
||||
name: 'Sacré-Cœur',
|
||||
lat: 48.8867,
|
||||
lon: 2.3431,
|
||||
alt: 400,
|
||||
pitch: -30,
|
||||
heading: 180,
|
||||
buildingHeight: 40,
|
||||
},
|
||||
{
|
||||
name: 'Louvre Pyramid',
|
||||
lat: 48.8606,
|
||||
lon: 2.3376,
|
||||
alt: 500,
|
||||
pitch: -35,
|
||||
heading: 0,
|
||||
buildingHeight: 10,
|
||||
},
|
||||
],
|
||||
},
|
||||
dubai: {
|
||||
name: 'Dubai',
|
||||
groundElevation: 5,
|
||||
viewBounds: {
|
||||
southwest: { lat: 24.95, lng: 54.9 },
|
||||
northeast: { lat: 25.35, lng: 55.55 },
|
||||
},
|
||||
pois: [
|
||||
{
|
||||
name: 'Burj Khalifa',
|
||||
lat: 25.1972,
|
||||
lon: 55.2744,
|
||||
alt: 600,
|
||||
pitch: -20,
|
||||
heading: 200,
|
||||
buildingHeight: 270,
|
||||
},
|
||||
{
|
||||
name: 'Burj Al Arab',
|
||||
lat: 25.1412,
|
||||
lon: 55.1853,
|
||||
alt: 500,
|
||||
pitch: -25,
|
||||
heading: 90,
|
||||
buildingHeight: 100,
|
||||
},
|
||||
{
|
||||
name: 'Palm Jumeirah',
|
||||
lat: 25.1124,
|
||||
lon: 55.139,
|
||||
alt: 1200,
|
||||
pitch: -40,
|
||||
heading: 0,
|
||||
buildingHeight: 20,
|
||||
},
|
||||
{
|
||||
name: 'Dubai Frame',
|
||||
lat: 25.235,
|
||||
lon: 55.3003,
|
||||
alt: 400,
|
||||
pitch: -25,
|
||||
heading: 270,
|
||||
buildingHeight: 75,
|
||||
},
|
||||
{
|
||||
name: 'Museum of the Future',
|
||||
lat: 25.2197,
|
||||
lon: 55.2806,
|
||||
alt: 350,
|
||||
pitch: -20,
|
||||
heading: 30,
|
||||
buildingHeight: 35,
|
||||
},
|
||||
],
|
||||
},
|
||||
dc: {
|
||||
name: 'Washington DC',
|
||||
groundElevation: 10,
|
||||
viewBounds: {
|
||||
southwest: { lat: 38.79, lng: -77.12 },
|
||||
northeast: { lat: 38.995, lng: -76.91 },
|
||||
},
|
||||
pois: [
|
||||
{
|
||||
name: 'US Capitol',
|
||||
lat: 38.8897,
|
||||
lon: -77.0091,
|
||||
alt: 550,
|
||||
pitch: -25,
|
||||
heading: 270,
|
||||
buildingHeight: 45,
|
||||
},
|
||||
{
|
||||
name: 'Washington Monument',
|
||||
lat: 38.8895,
|
||||
lon: -77.0353,
|
||||
alt: 500,
|
||||
pitch: -30,
|
||||
heading: 0,
|
||||
buildingHeight: 85,
|
||||
},
|
||||
{
|
||||
name: 'Lincoln Memorial',
|
||||
lat: 38.8893,
|
||||
lon: -77.0502,
|
||||
alt: 400,
|
||||
pitch: -25,
|
||||
heading: 90,
|
||||
buildingHeight: 20,
|
||||
},
|
||||
{
|
||||
name: 'Pentagon',
|
||||
lat: 38.8711,
|
||||
lon: -77.0559,
|
||||
alt: 800,
|
||||
pitch: -40,
|
||||
heading: 0,
|
||||
buildingHeight: 20,
|
||||
},
|
||||
{
|
||||
name: 'Jefferson Memorial',
|
||||
lat: 38.8814,
|
||||
lon: -77.0365,
|
||||
alt: 400,
|
||||
pitch: -30,
|
||||
heading: 0,
|
||||
buildingHeight: 25,
|
||||
},
|
||||
],
|
||||
},
|
||||
tallinn: {
|
||||
name: 'Tallinn',
|
||||
groundElevation: 15,
|
||||
viewBounds: {
|
||||
southwest: { lat: 59.36, lng: 24.6 },
|
||||
northeast: { lat: 59.52, lng: 24.92 },
|
||||
},
|
||||
pois: [
|
||||
{
|
||||
name: 'Viru Square',
|
||||
lat: 59.4366,
|
||||
lon: 24.7527,
|
||||
alt: 450,
|
||||
pitch: -28,
|
||||
heading: 60,
|
||||
buildingHeight: 25,
|
||||
},
|
||||
{
|
||||
name: 'Old Town / Raekoja plats',
|
||||
lat: 59.4372,
|
||||
lon: 24.7452,
|
||||
alt: 400,
|
||||
pitch: -30,
|
||||
heading: 180,
|
||||
buildingHeight: 20,
|
||||
},
|
||||
{
|
||||
name: 'Teatri väljak',
|
||||
lat: 59.4344,
|
||||
lon: 24.7514,
|
||||
alt: 400,
|
||||
pitch: -25,
|
||||
heading: 220,
|
||||
buildingHeight: 25,
|
||||
},
|
||||
{
|
||||
name: 'Port of Tallinn',
|
||||
lat: 59.4445,
|
||||
lon: 24.7675,
|
||||
alt: 700,
|
||||
pitch: -30,
|
||||
heading: 90,
|
||||
buildingHeight: 20,
|
||||
},
|
||||
{
|
||||
name: 'Ülemiste',
|
||||
lat: 59.421,
|
||||
lon: 24.792,
|
||||
alt: 600,
|
||||
pitch: -28,
|
||||
heading: 45,
|
||||
buildingHeight: 30,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
/** Newest landmark arrival; an older post-arrival ground guard yields to it. */
|
||||
let arrivalGeneration = 0;
|
||||
|
||||
/**
|
||||
* Absolute full-earth camera preset for the zoom_to_globe voice tool. The height
|
||||
@@ -588,9 +87,12 @@ export const LOCATIONS = Object.entries(CITY_POIS).map(([id, city]) => ({
|
||||
* @param {number} options.buildingHeight - Estimated landmark center height above ground (default 30)
|
||||
* @param {number} options.groundElevation - Fallback ground elevation when terrain isn't loaded (default 0)
|
||||
* @param {number} options.duration - Flight duration in seconds (default 3.0)
|
||||
* @param {{cachedGroundFloor?: Function, warmGroundFloor?: Function}} [options.ground] -
|
||||
* Optional ground-floor service; its cached elevation outranks `groundElevation`
|
||||
* @returns {{ targetPosition: Cesium.Cartesian3 }} The computed target for orbit use
|
||||
*/
|
||||
export function flyToLandmark(viewer, lat, lon, options = {}) {
|
||||
cancelCameraArrival(viewer);
|
||||
const {
|
||||
range = 500,
|
||||
pitch = -30,
|
||||
@@ -602,18 +104,32 @@ export function flyToLandmark(viewer, lat, lon, options = {}) {
|
||||
onComplete = null,
|
||||
onCancel = null,
|
||||
buildingBounds = null,
|
||||
ground = null,
|
||||
} = options;
|
||||
|
||||
// Sample terrain height (sync — uses loaded tiles; 0 if globe/terrain not ready)
|
||||
const targetCartographic = Cesium.Cartographic.fromDegrees(lon, lat);
|
||||
const sampledHeight = viewer.scene.globe?.getHeight(targetCartographic);
|
||||
|
||||
// Use sampled height if available, otherwise fall back to pre-baked city ground elevation.
|
||||
// Google 3D Tiles don't populate globe terrain, so first fly-to always gets the fallback.
|
||||
// Use sampled height if available, otherwise the cached ground-floor
|
||||
// elevation (same ellipsoidal datum), then the pre-baked city elevation.
|
||||
// Google 3D Tiles don't populate globe terrain, and the preset defaults to sea
|
||||
// level, so without the ground-floor read an uncurated destination was framed
|
||||
// as if it stood on the ocean and the eye landed underground.
|
||||
const realGround = ground?.cachedGroundFloor?.(lat, lon);
|
||||
const terrainHeight =
|
||||
sampledHeight != null && sampledHeight > 0
|
||||
? sampledHeight
|
||||
: groundElevation;
|
||||
: Number.isFinite(realGround)
|
||||
? realGround
|
||||
: groundElevation;
|
||||
// Warm the cell so the next nearby arrival is already correct.
|
||||
try {
|
||||
ground?.warmGroundFloor?.([{ lat, lon }]);
|
||||
} catch {
|
||||
/* best-effort prefetch */
|
||||
}
|
||||
const generation = ++arrivalGeneration;
|
||||
|
||||
const bounds = normalizeBuildingBounds(buildingBounds);
|
||||
const targetHeight = bounds
|
||||
@@ -651,6 +167,18 @@ export function flyToLandmark(viewer, lat, lon, options = {}) {
|
||||
complete: () => {
|
||||
viewer.camera.lookAt(targetPosition, hpr);
|
||||
viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY);
|
||||
// Framing used a predicted ground height. Once tiles stream in, measure
|
||||
// the rendered surface and lift the eye if it is buried or pressed
|
||||
// against it; a newer arrival or a manual gesture ends the check.
|
||||
try {
|
||||
guardCameraAboveGround(
|
||||
viewer,
|
||||
{ lat, lon },
|
||||
{ isStale: () => generation !== arrivalGeneration },
|
||||
);
|
||||
} catch {
|
||||
/* best-effort */
|
||||
}
|
||||
if (typeof onComplete === 'function') {
|
||||
try {
|
||||
onComplete();
|
||||
@@ -807,6 +335,7 @@ export async function searchAndFlyTo(viewer, query, options = {}) {
|
||||
query,
|
||||
result && !outcome.fallbackUsed ? { lat, lon: lng } : null,
|
||||
signal,
|
||||
placeSearch,
|
||||
);
|
||||
signal?.throwIfAborted();
|
||||
if (recovered) {
|
||||
@@ -843,6 +372,7 @@ export async function searchAndFlyTo(viewer, query, options = {}) {
|
||||
pitch: swath.pitchDeg,
|
||||
heading: swath.headingDeg,
|
||||
buildingHeight: 0,
|
||||
ground: options.ground || null,
|
||||
duration,
|
||||
onStart: options.onStart,
|
||||
onComplete: options.onComplete,
|
||||
@@ -907,23 +437,35 @@ export async function searchAndFlyTo(viewer, query, options = {}) {
|
||||
)
|
||||
: null;
|
||||
const range = requestedRange || defaultRangeForNavigationMode(navigationMode);
|
||||
// Resolve the real ground height before framing, for the same reason as the
|
||||
// coordinate branch: with no detailed outline, a searched place on elevated
|
||||
// ground would otherwise be framed against sea level. Resolved before the
|
||||
// mayFly() gate so a cancellation still wins the race.
|
||||
const flyLat = buildingBounds?.lat ?? lat;
|
||||
const flyLon = buildingBounds?.lon ?? lng;
|
||||
const ground = options.ground || null;
|
||||
await ground?.resolveGroundFloorCellsBounded?.([
|
||||
{ lat: flyLat, lon: flyLon },
|
||||
]);
|
||||
const searchGroundElevation = ground?.cachedGroundFloor?.(flyLat, flyLon);
|
||||
if (!mayFly()) return CANCELLED_SEARCH;
|
||||
const flight = flyToLandmark(
|
||||
viewer,
|
||||
buildingBounds?.lat ?? lat,
|
||||
buildingBounds?.lon ?? lng,
|
||||
{
|
||||
range,
|
||||
pitch: buildingPitch(buildingBounds),
|
||||
heading: 30,
|
||||
buildingHeight: 30,
|
||||
buildingBounds,
|
||||
duration,
|
||||
onStart: options.onStart,
|
||||
onComplete: options.onComplete,
|
||||
onCancel: options.onCancel,
|
||||
},
|
||||
);
|
||||
const flight = flyToLandmark(viewer, flyLat, flyLon, {
|
||||
range,
|
||||
pitch: buildingPitch(buildingBounds),
|
||||
heading: 30,
|
||||
buildingHeight: 30,
|
||||
buildingBounds: isUnavailableCapability(buildingBounds)
|
||||
? null
|
||||
: buildingBounds,
|
||||
...(Number.isFinite(searchGroundElevation)
|
||||
? { groundElevation: searchGroundElevation }
|
||||
: {}),
|
||||
ground,
|
||||
duration,
|
||||
onStart: options.onStart,
|
||||
onComplete: options.onComplete,
|
||||
onCancel: options.onCancel,
|
||||
});
|
||||
return {
|
||||
label,
|
||||
navigationMode: requestedRange
|
||||
@@ -932,6 +474,9 @@ export async function searchAndFlyTo(viewer, query, options = {}) {
|
||||
? navigationMode.replace('-overview', '-close')
|
||||
: navigationMode,
|
||||
rangeM: Math.round(flight.range),
|
||||
...(isUnavailableCapability(buildingBounds)
|
||||
? { outlineUnavailable: true, message: 'Detailed outline unavailable' }
|
||||
: {}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1312,19 +857,6 @@ function buildingBoundingRadius(bounds) {
|
||||
return Math.hypot(halfHeight, halfWidth, halfDepth) * 1.18;
|
||||
}
|
||||
|
||||
function rangeForBoundingSphere(viewer, radius) {
|
||||
const frustum = viewer.camera.frustum;
|
||||
const verticalFov = Number(frustum?.fov) || Cesium.Math.toRadians(60);
|
||||
const aspectRatio = Math.max(0.5, Number(frustum?.aspectRatio) || 1);
|
||||
const horizontalFov = 2 * Math.atan(Math.tan(verticalFov / 2) * aspectRatio);
|
||||
const limitingFov = Math.min(verticalFov, horizontalFov);
|
||||
// Target 57% sphere occupancy so perspective still leaves at least 40%
|
||||
// measured roof-to-base breathing room in ordinary oblique building views.
|
||||
const occupiedViewportFraction = 0.57;
|
||||
const desiredAngularRadius = (limitingFov * occupiedViewportFraction) / 2;
|
||||
return (radius / Math.sin(desiredAngularRadius)) * 1.05;
|
||||
}
|
||||
|
||||
function buildingPitch(bounds) {
|
||||
if (!bounds) return -25;
|
||||
const footprint = Math.max(bounds.width, bounds.depth);
|
||||
@@ -1347,6 +879,7 @@ async function resolveBuildingBounds(
|
||||
{ lat, lon },
|
||||
{ signal },
|
||||
);
|
||||
if (isUnavailableCapability(candidates)) return candidates;
|
||||
return selectBuildingBounds(
|
||||
Array.isArray(candidates) ? candidates : [],
|
||||
lat,
|
||||
|
||||
Reference in new issue
Block a user