mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 10:24:02 +00:00
Main gained three imagery providers (Google Street View, KartaView,
Panoramax), shared line-coverage, cone and photo-viewer modules, one source
lit at a time and the translated interface. Their shared modules still
carried what this branch removes, so the merge ports them onto it:
- lineCoverage.js is this branch's coverage.js made provider-neutral
(draped lines near the ground only, the selection drawn over them, key
and rate-limit blocking); sequenceCones.js is sequences.js the same way.
Mapillary, KartaView and Panoramax all build on them; Mapillary moves
back to providers/mapillary/ beside the others.
- No ground casting, mesh sampling, surface modes, FOLLOW, legend, prewarm,
orbit overview points (Panoramax's density dots included), stale-tile
swaps or water-filled point budget come back.
- Kept from main because the new providers need them: requiresMapStack and
attachMapStackController (Street View on Google 3D only), ground clicks,
openNearest with an optional provider nearestImage, openImage {frame},
reportError, the view-cone marker (now an SVG glyph), filters /
coverageLabel / sinceBasis, OPEN STREET VIEW, keyAlternatives and i18n.
- photoViewer.js treats the viewer as expanded when its wrap is full screen
or the iPhone popover, as EXPAND now is.
- The fixture browser gate keeps its 8 steps and adds one each for
KartaView and Panoramax; catalogs, docs, boundaries and format scope
follow.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
110 lines
3.3 KiB
JavaScript
110 lines
3.3 KiB
JavaScript
import * as Cesium from 'cesium';
|
|
import { positionMarkerGlyph, viewConeMarkerGlyph } from './glyphs.js';
|
|
import { COLORS, POSITION_PICK_ID } from './policy.js';
|
|
import { createHorizonCull } from './view.js';
|
|
|
|
const SPRITE_ID = 'street-level:marker';
|
|
|
|
function markerLook(style) {
|
|
if (style?.kind === 'view-cone') {
|
|
const color = style.color || COLORS.position;
|
|
return {
|
|
key: `view-cone:${color}`,
|
|
image: viewConeMarkerGlyph({ color }),
|
|
size: 48,
|
|
};
|
|
}
|
|
return {
|
|
key: 'pin',
|
|
image: positionMarkerGlyph({ color: COLORS.position }),
|
|
size: 40,
|
|
};
|
|
}
|
|
|
|
/** On-globe marker for the open image, clamped to the ground. */
|
|
export function createMarker({ state }) {
|
|
const { render, sprites } = state.services;
|
|
|
|
function requestRender() {
|
|
render?.governorRequestRender?.('street-level-marker');
|
|
}
|
|
|
|
// It skips the depth test: hide it while it is behind the globe.
|
|
const horizon = createHorizonCull({
|
|
getViewer: () => state.viewer,
|
|
items: () => (state.marker.billboard ? [state.marker.billboard] : []),
|
|
onChange: requestRender,
|
|
});
|
|
|
|
function ensure(viewer) {
|
|
if (state.marker.collection) return;
|
|
state.marker.collection = new Cesium.BillboardCollection({
|
|
scene: viewer.scene,
|
|
});
|
|
viewer.scene.primitives.add(state.marker.collection);
|
|
sprites?.registerSpriteCollection?.(SPRITE_ID, state.marker.collection);
|
|
}
|
|
|
|
/** Move (or create) the marker; null removes it. `style`: the provider's `marker`. */
|
|
function set(position, bearing, style = null) {
|
|
const collection = state.marker.collection;
|
|
if (!collection) return;
|
|
if (!position) {
|
|
collection.removeAll();
|
|
state.marker.billboard = null;
|
|
horizon.stop();
|
|
requestRender();
|
|
return;
|
|
}
|
|
const cartesian = Cesium.Cartesian3.fromDegrees(position.lon, position.lat);
|
|
const look = markerLook(style);
|
|
if (state.marker.billboard && state.marker.look !== look.key) {
|
|
collection.remove?.(state.marker.billboard);
|
|
state.marker.billboard = null;
|
|
}
|
|
if (!state.marker.billboard) {
|
|
state.marker.look = look.key;
|
|
state.marker.billboard = collection.add({
|
|
id: POSITION_PICK_ID,
|
|
position: cartesian,
|
|
image: look.image,
|
|
imageId: `sl-position:${look.key}`,
|
|
width: look.size,
|
|
height: look.size,
|
|
alignedAxis: Cesium.Cartesian3.UNIT_Z,
|
|
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
|
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
|
verticalOrigin: Cesium.VerticalOrigin.CENTER,
|
|
});
|
|
} else {
|
|
state.marker.billboard.position = cartesian;
|
|
}
|
|
state.marker.billboard.rotation = -Cesium.Math.toRadians(bearing || 0);
|
|
horizon.update([state.marker.billboard]);
|
|
requestRender();
|
|
}
|
|
|
|
function setVisible(visible) {
|
|
if (state.marker.collection) state.marker.collection.show = visible;
|
|
requestRender();
|
|
}
|
|
|
|
function destroy(viewer) {
|
|
horizon.stop();
|
|
const collection = state.marker.collection;
|
|
if (!collection) return;
|
|
sprites?.unregisterSpriteCollection?.(SPRITE_ID, collection);
|
|
viewer?.scene?.primitives?.remove(collection);
|
|
state.marker.collection = null;
|
|
state.marker.billboard = null;
|
|
}
|
|
|
|
return {
|
|
ensure,
|
|
set,
|
|
clear: () => set(null),
|
|
setVisible,
|
|
destroy,
|
|
};
|
|
}
|