mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-15 07:42:23 +00:00
Preserve cyclone bulletins, forecast geometry and storm selection across engines
This commit is contained in:
1 parent
cb350fa58d
commit
868d061856
10 files changed
+394
-65
No files matched your search
@@ -0,0 +1,119 @@
|
||||
import * as geo from '../../../geo/index.js';
|
||||
import { distanceToSegment } from '../../../globe/cpuPicks.js';
|
||||
import { containsGroundPoint } from './groundRing.js';
|
||||
|
||||
function groundScreen(engine, [lon, lat]) {
|
||||
const point = geo.cartographic.fromDegrees(lon, lat);
|
||||
const height = engine.surface.terrainHeight(point) ?? 0;
|
||||
const position = geo.cartesian3.fromDegrees(lon, lat, height);
|
||||
return (
|
||||
engine.projector.horizonVisible(position) &&
|
||||
engine.projector.worldToWindow(position, {})
|
||||
);
|
||||
}
|
||||
|
||||
// The GPU may return the preceding gesture's vector feature. Resolve against
|
||||
// the current gesture and let the engine arbitrate the result with sibling layers.
|
||||
export function bindGroundShapePicks(engine, owner, shapes) {
|
||||
const regions = [];
|
||||
for (const { id, geometry, style } of shapes) {
|
||||
const coords =
|
||||
geometry.type === 'point'
|
||||
? [geometry.position]
|
||||
: geometry.type === 'line'
|
||||
? geometry.positions
|
||||
: geometry.outer;
|
||||
const west = coords[0][0];
|
||||
const local = (lon) => lon - west - 360 * Math.round((lon - west) / 360);
|
||||
let south = 90,
|
||||
north = -90,
|
||||
min = Infinity,
|
||||
max = -Infinity;
|
||||
for (const [lon, lat] of coords) {
|
||||
south = Math.min(south, lat);
|
||||
north = Math.max(north, lat);
|
||||
min = Math.min(min, local(lon));
|
||||
max = Math.max(max, local(lon));
|
||||
}
|
||||
regions.push({
|
||||
geometry,
|
||||
style,
|
||||
coords,
|
||||
south,
|
||||
north,
|
||||
min,
|
||||
max,
|
||||
local,
|
||||
id,
|
||||
});
|
||||
}
|
||||
const pick = (at) => {
|
||||
const world = engine.picker.pickGlobe(at);
|
||||
if (!world) return [];
|
||||
const carto = geo.cartographic.fromCartesian(world);
|
||||
const lon = geo.math.toDegrees(carto.longitude),
|
||||
lat = geo.math.toDegrees(carto.latitude);
|
||||
const depth = geo.cartesian3.distance(world, engine.camera.livePosition);
|
||||
const pose = engine.camera.pose();
|
||||
// Conservative geographic margin for a displaced terrain pixel/marker at this zoom.
|
||||
const margin =
|
||||
(2 * Math.tan(pose.fovy / 2) * depth * 32) /
|
||||
Math.max(1, engine.host.size().height) /
|
||||
110000;
|
||||
const lonMargin = margin / Math.max(0.00001, Math.cos(carto.latitude));
|
||||
const hits = [];
|
||||
for (const region of regions) {
|
||||
const x = region.local(lon);
|
||||
if (
|
||||
lat < region.south - margin ||
|
||||
lat > region.north + margin ||
|
||||
(region.max - region.min < 180 &&
|
||||
(x < region.min - lonMargin || x > region.max + lonMargin))
|
||||
)
|
||||
continue;
|
||||
const { geometry, style, coords } = region;
|
||||
let hit = false;
|
||||
if (geometry.type === 'point') {
|
||||
const screen = groundScreen(engine, geometry.position);
|
||||
const size = style.pointRadius ?? 7;
|
||||
hit =
|
||||
screen &&
|
||||
Math.abs(at.x - screen.x) <= size &&
|
||||
Math.abs(at.y - screen.y) <= size;
|
||||
} else {
|
||||
if (geometry.type === 'polygon' && style.fill !== false)
|
||||
hit =
|
||||
containsGroundPoint(coords, lon, lat) &&
|
||||
!geometry.holes.some((ring) => containsGroundPoint(ring, lon, lat));
|
||||
if (!hit && (geometry.type === 'line' || style.outline !== false)) {
|
||||
let last =
|
||||
geometry.type === 'polygon'
|
||||
? groundScreen(engine, coords.at(-1))
|
||||
: null;
|
||||
for (const coord of coords) {
|
||||
const next = groundScreen(engine, coord);
|
||||
if (
|
||||
last &&
|
||||
next &&
|
||||
distanceToSegment(at, last, next) <=
|
||||
Math.max(2, Math.min(8, style.lineWidth ?? 2) / 2)
|
||||
) {
|
||||
hit = true;
|
||||
break;
|
||||
}
|
||||
last = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hit)
|
||||
hits.push({
|
||||
onTop: 0,
|
||||
order: geometry.type === 'point' ? 1 : 0,
|
||||
depth,
|
||||
result: { id: region.id, owner },
|
||||
});
|
||||
}
|
||||
return hits;
|
||||
};
|
||||
return engine.picker.registerMeshPicker(pick);
|
||||
}
|
||||
Reference in new issue
Block a user