GODSIGH/js/layers/cables.js

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' };
}