95 lines
3.4 KiB
JavaScript
95 lines
3.4 KiB
JavaScript
// Submarine cables — TeleGeography routes + landing points, bundled in
|
|
// assets/cables/ (CC BY-NC-SA 3.0, © TeleGeography; see assets/README.md).
|
|
// Static context like infra.js: no time dynamics, loaded once at boot.
|
|
|
|
export default function create(ctx) {
|
|
const { viewer, lib, ui, Cesium } = ctx;
|
|
|
|
const ds = new Cesium.CustomDataSource('cables');
|
|
viewer.dataSources.add(ds);
|
|
|
|
let enabled = true;
|
|
ui.addLayer('cables', 'Submarine cables', true, (on) => { enabled = on; ds.show = on; }, 'Context');
|
|
ui.setStatus('cables', 'loading…', 'warn');
|
|
|
|
const CREDIT = '<p style="opacity:.7">© TeleGeography — submarinecablemap.com (CC BY-NC-SA 3.0). Routes are schematic.</p>';
|
|
|
|
// Landing-point labels only appear zoomed well in; the dots a bit earlier.
|
|
const labelFade = new Cesium.NearFarScalar(2.0e5, 1.0, 1.2e6, 0.0);
|
|
const dotFade = new Cesium.NearFarScalar(1.0e6, 1.0, 8.0e6, 0.0);
|
|
|
|
async function load() {
|
|
let cables, landings;
|
|
try {
|
|
[cables, landings] = await Promise.all([
|
|
fetch('assets/cables/cable-geo.json').then((r) => r.json()),
|
|
fetch('assets/cables/landing-point-geo.json').then((r) => r.json()),
|
|
]);
|
|
} catch (err) {
|
|
ui.setStatus('cables', 'failed to load bundled data', 'err');
|
|
return;
|
|
}
|
|
|
|
let nCables = 0;
|
|
for (const f of cables.features || []) {
|
|
const g = f.geometry;
|
|
if (!g || g.type !== 'MultiLineString') continue;
|
|
const p = f.properties || {};
|
|
const color = lib.cz(p.color || '#5aa7d0', 0.85);
|
|
const name = p.name || 'Submarine cable';
|
|
for (const line of g.coordinates) {
|
|
if (!Array.isArray(line) || line.length < 2) continue;
|
|
ds.entities.add({
|
|
name: `Cable — ${name}`,
|
|
polyline: {
|
|
positions: Cesium.Cartesian3.fromDegreesArray(line.flat()),
|
|
width: 1.4,
|
|
material: color,
|
|
},
|
|
description: `<p><b>${lib.escapeHtml(name)}</b> — international submarine cable system.</p>` + CREDIT,
|
|
});
|
|
}
|
|
nCables++;
|
|
}
|
|
|
|
let nLandings = 0;
|
|
for (const f of landings.features || []) {
|
|
const g = f.geometry;
|
|
if (!g || g.type !== 'Point') continue;
|
|
const [lon, lat] = g.coordinates;
|
|
if (!isFinite(lon) || !isFinite(lat)) continue;
|
|
const name = (f.properties || {}).name || 'Landing point';
|
|
ds.entities.add({
|
|
name: `Landing — ${name}`,
|
|
position: Cesium.Cartesian3.fromDegrees(lon, lat),
|
|
point: {
|
|
pixelSize: 4, color: lib.cz('#8fd8ff'),
|
|
outlineColor: Cesium.Color.BLACK, outlineWidth: 1,
|
|
translucencyByDistance: dotFade,
|
|
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
|
},
|
|
label: {
|
|
text: name,
|
|
font: '10px "Segoe UI", system-ui, sans-serif',
|
|
fillColor: lib.cz('#8fd8ff'),
|
|
outlineColor: Cesium.Color.fromCssColorString('#0a0f14'),
|
|
outlineWidth: 3,
|
|
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
|
|
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
|
pixelOffset: new Cesium.Cartesian2(0, -6),
|
|
translucencyByDistance: labelFade,
|
|
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
|
},
|
|
description: `<p><b>${lib.escapeHtml(name)}</b> — cable landing point.</p>` + CREDIT,
|
|
});
|
|
nLandings++;
|
|
}
|
|
|
|
ds.show = enabled;
|
|
ui.setStatus('cables', `${nCables} cables · ${nLandings} landings`, 'ok');
|
|
}
|
|
|
|
load();
|
|
return { id: 'cables' };
|
|
}
|