Every marker used disableDepthTestDistance: Infinity, so points/billboards on the FAR side of the Earth drew on top of the near hemisphere instead of being occluded — reading as 'dots stuck, not moving with the globe' when you rotate. Changed to 50000 (50km) across all layers: the globe now occludes far-side markers, while you can still zoom right up to one without it clipping into the surface. Also made the billboard layers (aircraft/military/adsb/radius) explicit for consistent behavior. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
162 lines
5.7 KiB
JavaScript
162 lines
5.7 KiB
JavaScript
// Infrastructure overlay (SPEC §6.5): static vector context for the theater —
|
||
// maritime choke points, major oil pipelines, and key facilities. No time
|
||
// dynamics. All coordinates are approximate and every description says so.
|
||
|
||
export default function create(ctx) {
|
||
const { viewer, CONFIG, lib, ui, Cesium } = ctx;
|
||
const C = CONFIG.colors;
|
||
|
||
const infraDS = new Cesium.CustomDataSource('infra');
|
||
viewer.dataSources.add(infraDS);
|
||
const ents = infraDS.entities;
|
||
|
||
const APPROX = 'Location approximate — illustrative reference geometry, not survey-grade.';
|
||
|
||
// Shared label defaults: readable at range, fades in as you zoom.
|
||
const labelBase = {
|
||
font: '12px "Segoe UI", system-ui, sans-serif',
|
||
fillColor: Cesium.Color.WHITE,
|
||
outlineColor: Cesium.Color.fromCssColorString('#0a0f14'),
|
||
outlineWidth: 3,
|
||
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
|
||
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
||
disableDepthTestDistance: 50000,
|
||
translucencyByDistance: new Cesium.NearFarScalar(2.0e6, 1.0, 1.2e7, 0.0),
|
||
};
|
||
|
||
// ---- choke points: red ring + label ----
|
||
const chokepoints = [
|
||
{ name: 'Strait of Hormuz', lon: 56.5, lat: 26.6 },
|
||
{ name: 'Bab el-Mandeb', lon: 43.33, lat: 12.58 },
|
||
{ name: 'Suez Canal', lon: 32.35, lat: 30.45 },
|
||
{ name: 'Strait of Malacca', lon: 101.0, lat: 2.5 },
|
||
];
|
||
for (const cp of chokepoints) {
|
||
ents.add({
|
||
name: `Choke point — ${cp.name}`,
|
||
position: Cesium.Cartesian3.fromDegrees(cp.lon, cp.lat),
|
||
ellipse: {
|
||
semiMajorAxis: 80000,
|
||
semiMinorAxis: 80000,
|
||
material: Cesium.Color.fromCssColorString('rgba(255,0,60,1)').withAlpha(0.06),
|
||
outline: true,
|
||
outlineColor: lib.cz(C.chokepoint),
|
||
height: 0,
|
||
},
|
||
label: {
|
||
...labelBase,
|
||
text: cp.name,
|
||
fillColor: lib.cz(C.chokepoint),
|
||
pixelOffset: new Cesium.Cartesian2(0, -6),
|
||
},
|
||
description:
|
||
`<p><b>Maritime choke point.</b></p>` +
|
||
`<p>${cp.name} — a strategic strait for global energy and trade flows.</p>` +
|
||
`<p><i>${APPROX} The ring is a fixed ~80 km radius marker, not the true channel.</i></p>`,
|
||
});
|
||
}
|
||
|
||
// ---- pipelines: amber dashed polylines clamped to ground + midpoint label ----
|
||
const pipelines = [
|
||
{
|
||
name: 'East–West (Petroline) crude pipeline',
|
||
note: 'Abqaiq → Yanbu',
|
||
coords: [49.68, 25.94, 46.7, 24.6, 44.5, 24.3, 41.5, 24.2, 38.06, 24.09],
|
||
},
|
||
{
|
||
name: 'Habshan–Fujairah crude pipeline',
|
||
note: 'bypasses the Strait of Hormuz',
|
||
coords: [53.61, 23.75, 54.6, 24.2, 55.5, 24.8, 56.33, 25.12],
|
||
},
|
||
];
|
||
for (const pl of pipelines) {
|
||
ents.add({
|
||
name: `Pipeline — ${pl.name}`,
|
||
polyline: {
|
||
positions: Cesium.Cartesian3.fromDegreesArray(pl.coords),
|
||
width: 2,
|
||
clampToGround: true,
|
||
material: new Cesium.PolylineDashMaterialProperty({ color: lib.cz(C.pipeline) }),
|
||
},
|
||
description:
|
||
`<p><b>Oil pipeline.</b> ${pl.note}.</p>` +
|
||
`<p>${pl.name}.</p>` +
|
||
`<p><i>${APPROX} The route is a simplified schematic between endpoints.</i></p>`,
|
||
});
|
||
// Midpoint label at the middle vertex of the schematic route.
|
||
const mid = pipelineMidpoint(pl.coords);
|
||
ents.add({
|
||
name: `${pl.name} (label)`,
|
||
position: Cesium.Cartesian3.fromDegrees(mid[0], mid[1]),
|
||
label: { ...labelBase, text: pl.name, fillColor: lib.cz(C.pipeline) },
|
||
});
|
||
}
|
||
|
||
// ---- facilities: small square markers + type-annotated labels ----
|
||
const facilities = [
|
||
{ name: 'Ras Tanura', type: 'oil terminal', lon: 50.16, lat: 26.64 },
|
||
{ name: 'Abqaiq', type: 'oil processing plant', lon: 49.68, lat: 25.94 },
|
||
{ name: 'Yanbu', type: 'port / oil terminal', lon: 38.06, lat: 24.09 },
|
||
{ name: 'Kharg Island', type: 'oil export terminal', lon: 50.32, lat: 29.23 },
|
||
{ name: 'Fujairah', type: 'oil terminal', lon: 56.37, lat: 25.18 },
|
||
{ name: 'Jebel Ali', type: 'desalination plant', lon: 55.02, lat: 25.01 },
|
||
{ name: 'Ras Al-Khair', type: 'desalination plant', lon: 49.19, lat: 27.55 },
|
||
{ name: 'Al Udeid', type: 'air base', lon: 51.32, lat: 25.12 },
|
||
{ name: 'Haifa', type: 'refinery', lon: 35.05, lat: 32.79 },
|
||
];
|
||
const squareGlyph = facilitySquare(Cesium, C.facility);
|
||
for (const f of facilities) {
|
||
ents.add({
|
||
name: `Facility — ${f.name} ${f.type}`,
|
||
position: Cesium.Cartesian3.fromDegrees(f.lon, f.lat),
|
||
billboard: {
|
||
image: squareGlyph,
|
||
width: 9,
|
||
height: 9,
|
||
disableDepthTestDistance: 50000,
|
||
},
|
||
label: {
|
||
...labelBase,
|
||
text: f.name,
|
||
fillColor: lib.cz(C.facility),
|
||
pixelOffset: new Cesium.Cartesian2(0, -8),
|
||
},
|
||
description:
|
||
`<p><b>${f.name}</b> — ${f.type}.</p>` +
|
||
`<p><i>${APPROX}</i></p>`,
|
||
});
|
||
}
|
||
|
||
// ---- HUD row + status ----
|
||
ui.addLayer('infra', 'Infrastructure', true, (on) => { infraDS.show = on; });
|
||
ui.setStatus(
|
||
'infra',
|
||
`${chokepoints.length} choke points · ${pipelines.length} pipelines · ${facilities.length} facilities`,
|
||
'ok',
|
||
);
|
||
|
||
return { id: 'infra' };
|
||
}
|
||
|
||
// Geographic midpoint = the middle vertex of the route's [lon,lat,...] array.
|
||
function pipelineMidpoint(coords) {
|
||
const n = coords.length / 2;
|
||
const i = Math.floor(n / 2) * 2;
|
||
return [coords[i], coords[i + 1]];
|
||
}
|
||
|
||
// Tiny filled square canvas for facility markers (billboard image).
|
||
function facilitySquare(Cesium, hex) {
|
||
const s = 16;
|
||
const c = document.createElement('canvas');
|
||
c.width = s;
|
||
c.height = s;
|
||
const g = c.getContext('2d');
|
||
g.fillStyle = hex;
|
||
g.strokeStyle = '#0a0f14';
|
||
g.lineWidth = 2;
|
||
g.fillRect(2, 2, s - 4, s - 4);
|
||
g.strokeRect(2, 2, s - 4, s - 4);
|
||
return c;
|
||
}
|