mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-11 05:42:22 +00:00
fix(shipwrecks): small row and query fixes: era counts in one pass, pickers list only years with a wreck, no Portuguese default
- The legend counted each era twice, scanning every record eight times per row refresh; it now counts once. - Each year picker listed every year from the oldest dated wreck (well over a thousand entries); it lists the years that have a wreck, plus the chosen ends. - Labels fell back to Portuguese and links to Portuguese Wikipedia while the app ships 11 locales: labels now fall back through the app's languages after English, and a wreck with no English article links its Wikidata item, which lists the article in every language.
This commit is contained in:
1 parent
38cf1e2be5
commit
0c159186f9
5 files changed
+71
-17
No files matched your search
@@ -178,7 +178,7 @@ cull GhostMaps uses, sized by deaths (a bigger toll draws on top) and coloured
|
||||
by era (a legend under the row): before 1900
|
||||
(ancient wrecks dated only by period count here), 1900–1945, after 1945, and
|
||||
date unknown. A period filter under the row picks a start year and an end year
|
||||
side by side (two pickers over the years in the data; a reversed pair is
|
||||
side by side (two pickers over the years that have a wreck; a reversed pair is
|
||||
stored swapped); undated wrecks show only when every year
|
||||
is selected. The filter is session-only (not in the share link). Clicking a marker opens a `stateless` card: deaths and
|
||||
survivors first, then the sinking date or year (or the archaeological dating) and
|
||||
|
||||
@@ -277,11 +277,20 @@ export function createShipwrecksLayer({
|
||||
picking?.unregisterPickOwner?.(SHIPWRECKS_LAYER_ID);
|
||||
}
|
||||
|
||||
function countOf(era) {
|
||||
let count = 0;
|
||||
/** Wrecks shown per era, in one pass over the records. */
|
||||
function eraCounts() {
|
||||
const counts = Object.fromEntries(SHIPWRECK_ERAS.map((era) => [era, 0]));
|
||||
for (const record of _records.values())
|
||||
if (eraOf(record) === era && inPeriod(record, _range)) count++;
|
||||
return count;
|
||||
if (inPeriod(record, _range)) counts[eraOf(record)]++;
|
||||
return counts;
|
||||
}
|
||||
|
||||
/** The years that have a dated wreck, plus `extra` ones, newest first. */
|
||||
function yearsInData(extra = []) {
|
||||
const years = new Set(extra);
|
||||
for (const record of _records.values())
|
||||
if (Number.isInteger(record.year)) years.add(record.year);
|
||||
return [...years].sort((a, b) => b - a);
|
||||
}
|
||||
|
||||
/** The start and end year pickers, side by side. */
|
||||
@@ -293,9 +302,13 @@ export function createShipwrecksLayer({
|
||||
: range.from === range.to
|
||||
? String(range.from)
|
||||
: `${range.from}–${range.to}`;
|
||||
const options = [];
|
||||
for (let year = _extent.max; year >= _extent.min; year--)
|
||||
options.push({ value: year, label: String(year) });
|
||||
// Only years with a wreck: a picker of every year since the oldest
|
||||
// dated wreck would run to well over a thousand entries.
|
||||
// The chosen ends stay listed even when no wreck sank that year.
|
||||
const options = yearsInData([range.from, range.to]).map((year) => ({
|
||||
value: year,
|
||||
label: String(year),
|
||||
}));
|
||||
return {
|
||||
range: {
|
||||
id: 'period',
|
||||
@@ -425,16 +438,17 @@ export function createShipwrecksLayer({
|
||||
},
|
||||
|
||||
getRowControls() {
|
||||
const counts = eraCounts();
|
||||
return {
|
||||
chips: [],
|
||||
...periodControls(),
|
||||
// The colours are a legend: when the ship sank.
|
||||
legend: SHIPWRECK_ERAS.map((era) => ({
|
||||
label: t(ERA_LABELS[era]),
|
||||
count: countOf(era),
|
||||
count: counts[era],
|
||||
color: ERA_COLORS[era],
|
||||
blurb: tn(countOf(era), '{count} wreck', '{count} wrecks', {
|
||||
count: formatNumber(countOf(era)),
|
||||
blurb: tn(counts[era], '{count} wreck', '{count} wrecks', {
|
||||
count: formatNumber(counts[era]),
|
||||
}),
|
||||
})),
|
||||
info: t('EMODnet (European waters) · Wikidata (worldwide)'),
|
||||
|
||||
@@ -208,3 +208,39 @@ test('a click opens the card; a period that excludes the wreck closes it', async
|
||||
);
|
||||
assert.equal(h.layer.getStats().count, 1);
|
||||
});
|
||||
|
||||
test('the year pickers list only years with a wreck, and each era is counted', async () => {
|
||||
const h = harness({
|
||||
snapshots: [
|
||||
snapshot([
|
||||
wreck('a', { year: 300 }),
|
||||
wreck('b', { year: 1912 }),
|
||||
wreck('c', { year: 1912 }),
|
||||
wreck('d', { year: 1990 }),
|
||||
wreck('e', { year: null }),
|
||||
]),
|
||||
],
|
||||
});
|
||||
h.layer.enable();
|
||||
await h.layer.update();
|
||||
let controls = h.layer.getRowControls();
|
||||
assert.deepEqual(
|
||||
controls.range.options.map((option) => option.value),
|
||||
[1990, 1912, 300],
|
||||
);
|
||||
assert.deepEqual(
|
||||
controls.legend.map((era) => era.count),
|
||||
[1, 2, 1, 1],
|
||||
);
|
||||
h.layer.setParams({ from: 1900, to: 1950 });
|
||||
controls = h.layer.getRowControls();
|
||||
assert.deepEqual(
|
||||
controls.range.options.map((option) => option.value),
|
||||
[1990, 1950, 1912, 1900, 300],
|
||||
'the chosen ends are listed too',
|
||||
);
|
||||
assert.deepEqual(
|
||||
controls.legend.map((era) => era.count),
|
||||
[0, 2, 0, 0],
|
||||
);
|
||||
});
|
||||
@@ -20,7 +20,7 @@ export const SHIPWRECK_SOURCES = Object.freeze(['emodnet', 'wikidata']);
|
||||
* come with their precision so a month-precise date is not shown as the 1st.
|
||||
* Only ship and event facts are read: no items about people.
|
||||
*/
|
||||
export const WIKIDATA_QUERY = `SELECT ?item ?itemLabel ?itemDescription ?coord ?sunk ?sunkPrecision ?end ?endPrecision ?deaths ?survivors ?typeLabel ?enwiki ?ptwiki ?event WHERE {
|
||||
export const WIKIDATA_QUERY = `SELECT ?item ?itemLabel ?itemDescription ?coord ?sunk ?sunkPrecision ?end ?endPrecision ?deaths ?survivors ?typeLabel ?enwiki ?event WHERE {
|
||||
{
|
||||
?item wdt:P31/wdt:P279* wd:Q852190 ; wdt:P625 ?coord ; wikibase:sitelinks ?links .
|
||||
FILTER(?links > 0)
|
||||
@@ -35,8 +35,7 @@ export const WIKIDATA_QUERY = `SELECT ?item ?itemLabel ?itemDescription ?coord ?
|
||||
}
|
||||
OPTIONAL { ?item wdt:P1120 ?deaths }
|
||||
OPTIONAL { ?enwiki schema:about ?item ; schema:isPartOf <https://en.wikipedia.org/> }
|
||||
OPTIONAL { ?ptwiki schema:about ?item ; schema:isPartOf <https://pt.wikipedia.org/> }
|
||||
SERVICE wikibase:label { bd:serviceParam wikibase:language "en,pt,mul". }
|
||||
SERVICE wikibase:label { bd:serviceParam wikibase:language "en,mul,fr,de,es,it,pt,ru,uk,ja,zh,ar". }
|
||||
}`;
|
||||
|
||||
const MAX_TEXT = 160;
|
||||
@@ -300,9 +299,9 @@ export function normalizeWikidataResults(payload) {
|
||||
country: null,
|
||||
originator: null,
|
||||
url:
|
||||
wikiUrl(value('enwiki')) ||
|
||||
wikiUrl(value('ptwiki')) ||
|
||||
`https://www.wikidata.org/wiki/${qid}`,
|
||||
// English Wikipedia, else the Wikidata item, which links the
|
||||
// article in every language that has one.
|
||||
wikiUrl(value('enwiki')) || `https://www.wikidata.org/wiki/${qid}`,
|
||||
});
|
||||
}
|
||||
return foldSinkingEvents([...byItem.values()]);
|
||||
|
||||
@@ -307,3 +307,8 @@ test('the query reads the end of service with its precision', () => {
|
||||
);
|
||||
assert.doesNotMatch(WIKIDATA_QUERY, /wdt:P576/);
|
||||
});
|
||||
|
||||
test('labels and links do not default to one other language', () => {
|
||||
assert.doesNotMatch(WIKIDATA_QUERY, /ptwiki|pt\.wikipedia/);
|
||||
assert.match(WIKIDATA_QUERY, /wikibase:language "en,mul,/);
|
||||
});
|
||||
Reference in new issue
Block a user