mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-13 06:42:23 +00:00
Draw Data Library coverage outlines from the bundled Natural Earth countries
Imagery outlines fetched Natural Earth's 1:50m countries (about 3 MB) from the natural-earth-vector repository's master branch on GitHub at run time: a moving, rate-limited source whose failures silently removed the outlines. Core already ships the pinned 1:10m countries pack, so the outlines now read that, once, the first time an outline needs it.
This commit is contained in:
1 parent
8d90cac7dc
commit
9e57fd84c8
3 files changed
+55
-25
No files matched your search
@@ -3,9 +3,10 @@
|
||||
Offline polygons with no network dependency. The voice-annotation resolver
|
||||
(`src/data/naturalEarthRegions.js`) reads the named regions, so "outline the
|
||||
Alps" resolves to the real range geometry; area annotations read the
|
||||
countries, states and provinces (`src/data/adminBoundaries.js`); and the News
|
||||
countries, states and provinces (`src/data/adminBoundaries.js`); the News
|
||||
layer reads the countries and the disputed areas
|
||||
(`server/providers/news/areas.js`).
|
||||
(`server/providers/news/areas.js`); and Data Library imagery draws its
|
||||
coverage outlines from the countries (`src/library/outlines.js`).
|
||||
|
||||
| File | Source dataset | Features |
|
||||
|------|----------------|----------|
|
||||
|
||||
+30
-23
@@ -2,40 +2,47 @@
|
||||
* Coverage outlines for Data Library imagery: the borders of the countries an
|
||||
* entry covers, so the edge of an aerial photo layer is plain on the map.
|
||||
*
|
||||
* Borders come from Natural Earth's 1:50m admin-0 countries (public domain),
|
||||
* a provider-hosted file fetched live and kept for the session, so nothing is
|
||||
* bundled. Pure apart from `loadCountryOutlines`, which takes `fetchImpl`.
|
||||
* Borders come from the bundled Natural Earth 1:10m admin-0 countries pack
|
||||
* (public domain, pinned; see src/data/local_data/natural_earth/README.md),
|
||||
* read once the first outline needs it. Pure apart from
|
||||
* `loadCountryOutlines`.
|
||||
*/
|
||||
import { readResponseTextCapped } from '../sources/httpBody.js';
|
||||
import { loadBundledJson } from '../data/bundledJson.js';
|
||||
import { decodeRing } from '../data/adminBoundaries.js';
|
||||
|
||||
export const COUNTRY_OUTLINES_URL =
|
||||
'https://raw.githubusercontent.com/nvkelso/natural-earth-vector/master/geojson/ne_50m_admin_0_countries.geojson';
|
||||
/** The 1:50m file is about 3 MB. */
|
||||
const OUTLINES_MAX_BYTES = 8 * 1024 * 1024;
|
||||
const COUNTRIES_PACK_URL = new URL(
|
||||
'../data/local_data/natural_earth/countries.json',
|
||||
import.meta.url,
|
||||
);
|
||||
/** Outline modes a manifest may ask for. */
|
||||
export const OUTLINE_MODES = Object.freeze(['coverage', 'countries', 'bbox']);
|
||||
|
||||
let pending = null;
|
||||
|
||||
/**
|
||||
* Natural Earth country features, fetched once per page.
|
||||
* @param {typeof fetch} fetchImpl
|
||||
* Country features with closed outer rings, in the GeoJSON shape
|
||||
* `outlineRings` reads, loaded once per page.
|
||||
* @param {(url: URL) => Promise<object>} [loadPack]
|
||||
* @returns {Promise<object[]>}
|
||||
*/
|
||||
export function loadCountryOutlines(
|
||||
fetchImpl = (...args) => globalThis.fetch(...args),
|
||||
) {
|
||||
export function loadCountryOutlines(loadPack = loadBundledJson) {
|
||||
pending ||= (async () => {
|
||||
const response = await fetchImpl(COUNTRY_OUTLINES_URL, {
|
||||
credentials: 'omit',
|
||||
referrerPolicy: 'no-referrer',
|
||||
redirect: 'error',
|
||||
});
|
||||
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
||||
const text = await readResponseTextCapped(response, OUTLINES_MAX_BYTES);
|
||||
const features = JSON.parse(text)?.features;
|
||||
if (!Array.isArray(features)) throw new Error('not a FeatureCollection');
|
||||
return features;
|
||||
const pack = await loadPack(COUNTRIES_PACK_URL);
|
||||
if (!Array.isArray(pack?.features)) throw new Error('not a country pack');
|
||||
const decimals = pack.meta?.decimals;
|
||||
return pack.features.map((feature) => ({
|
||||
type: 'Feature',
|
||||
properties: { ISO_A2: feature.iso2 },
|
||||
geometry: {
|
||||
type: 'MultiPolygon',
|
||||
coordinates: (feature.polygons || []).map((polygon) =>
|
||||
polygon.map((encoded) => {
|
||||
const ring = decodeRing(encoded, feature.d ?? decimals);
|
||||
return ring.length ? [...ring, ring[0]] : ring;
|
||||
}),
|
||||
),
|
||||
},
|
||||
}));
|
||||
})().catch((error) => {
|
||||
pending = null;
|
||||
throw error;
|
||||
|
||||
@@ -68,3 +68,25 @@ test('a bbox outline is the coverage rectangle, with no border file needed', ()
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
test('outlines come from the bundled Natural Earth countries, closed and in coverage', async () => {
|
||||
const { loadCountryOutlines, resetCountryOutlines } = await import(
|
||||
'./outlines.js'
|
||||
);
|
||||
resetCountryOutlines();
|
||||
const countries = await loadCountryOutlines();
|
||||
for (const id of ['ign-orthophotos-france', 'ee-maa-amet-ortofoto']) {
|
||||
const entry = libraryEntry(id);
|
||||
const rings = outlineRings(countries, entry);
|
||||
assert.ok(rings.length > 0, id);
|
||||
for (const ring of rings) assert.deepEqual(ring.at(-1), ring[0]);
|
||||
}
|
||||
// A pack that cannot load is retried on the next call.
|
||||
resetCountryOutlines();
|
||||
await assert.rejects(
|
||||
loadCountryOutlines(async () => {
|
||||
throw new Error('unreadable');
|
||||
}),
|
||||
);
|
||||
assert.ok((await loadCountryOutlines()).length > 200);
|
||||
});
|
||||
Reference in new issue
Block a user