Skip to content

Commit fa2414d

Browse files
committed
interim commit
1 parent 56c7f57 commit fa2414d

4 files changed

Lines changed: 156 additions & 91 deletions

File tree

.github/workflows/ohm-tests.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ on:
44
push:
55
branches:
66
- 1180-hundreds-of-ohm-website-test-failures
7+
- 1270-inspector-end-year
78
# pull_request:
89
concurrency:
910
group: ${{ github.workflow }}-${{ github.head_ref || github.ref }}

app/assets/javascripts/index/query.js

Lines changed: 5 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
//= require qs/dist/qs
2+
//= require ../ohm/dates
23

34
OSM.Query = function (map) {
45
const control = $(".control-query"),
@@ -114,52 +115,6 @@ OSM.Query = function (map) {
114115
return prefix;
115116
}
116117

117-
function featureSuffix(feature) {
118-
const tags = feature.tags;
119-
const startDate = parseYear(tags.start_date);
120-
const endDate = parseYear(tags.end_date);
121-
122-
if (!startDate && !endDate) {
123-
return null;
124-
}
125-
126-
// Keep the date range suffix succinct by only including the year and era.
127-
let options = {
128-
timeZone: "UTC",
129-
year: "numeric"
130-
};
131-
if (endDate) {
132-
options.era = endDate.getUTCFullYear() < 1 ? "short" : undefined;
133-
}
134-
if (startDate) {
135-
// Override any settings from the end of the range.
136-
options.era = startDate.getUTCFullYear() < 1 ? "short" : undefined;
137-
}
138-
139-
// Get the date range format in structured form, then filter out anything untagged.
140-
let format = new Intl.DateTimeFormat(OSM.i18n.locale, options);
141-
let lateDate = new Date(Date.UTC(9999));
142-
let parts = format.formatRangeToParts(startDate || lateDate, endDate || lateDate);
143-
if (!startDate) {
144-
parts = parts.filter(p => p.source !== "startRange");
145-
}
146-
if (!endDate) {
147-
parts = parts.filter(p => p.source !== "endRange");
148-
}
149-
150-
return parts.map(p => p.value).join("");
151-
}
152-
153-
function parseYear(iso8601) {
154-
if (!iso8601) {
155-
return null;
156-
}
157-
158-
const date = new Date(0);
159-
date.setUTCFullYear(parseInt(iso8601, 10));
160-
return isNaN(date.getDate()) ? null : date;
161-
}
162-
163118
function featureName(feature) {
164119
const tags = feature.tags,
165120
localeKeys = OSM.preferred_languages.map(locale => `name:${locale}`);
@@ -289,52 +244,7 @@ OSM.Query = function (map) {
289244
function size({ maxlon, minlon, maxlat, minlat }) {
290245
return (maxlon - minlon) * (maxlat - minlat);
291246
}
292-
293-
// Check if date is before 1000 CE (OverpassQL doesn't support these dates)
294-
function isBeforeYear1000(dateStr) {
295-
if (!dateStr) return false;
296-
const match = dateStr.match(/^(-?\d+)/);
297-
return match ? parseInt(match[1], 10) < 1000 : false;
298-
}
299-
300247
// Compare ISO 8601 dates correctly (handles BCE dates where string comparison fails)
301-
function compareDates(date1, date2) {
302-
if (!date1 && !date2) return 0;
303-
if (!date1) return -1;
304-
if (!date2) return 1;
305-
306-
const match1 = date1.match(/^(-?\d+)(?:-(\d{1,2}))?(?:-(\d{1,2}))?/);
307-
const match2 = date2.match(/^(-?\d+)(?:-(\d{1,2}))?(?:-(\d{1,2}))?/);
308-
if (!match1 || !match2) return date1.localeCompare(date2);
309-
310-
const [, year1Str, month1Str, day1Str] = match1;
311-
const [, year2Str, month2Str, day2Str] = match2;
312-
const year1Int = parseInt(year1Str, 10);
313-
const year2Int = parseInt(year2Str, 10);
314-
315-
if (year1Int !== year2Int) return year1Int - year2Int;
316-
317-
const month1 = month1Str ? parseInt(month1Str, 10) : 1;
318-
const month2 = month2Str ? parseInt(month2Str, 10) : 1;
319-
if (month1 !== month2) return month1 - month2;
320-
321-
const day1 = day1Str ? parseInt(day1Str, 10) : 1;
322-
const day2 = day2Str ? parseInt(day2Str, 10) : 1;
323-
return day1 - day2;
324-
}
325-
326-
function filterByDate(elements, currentDate) {
327-
if (!currentDate) return elements;
328-
return elements.filter(element => {
329-
const tags = element.tags || {};
330-
const startDate = tags.start_date;
331-
const endDate = tags.end_date;
332-
if (startDate && compareDates(startDate, currentDate) > 0) return false;
333-
if (endDate && compareDates(endDate, currentDate) <= 0) return false;
334-
return true;
335-
});
336-
}
337-
338248
/*
339249
* QUERY MECHANISM:
340250
*
@@ -383,11 +293,13 @@ OSM.Query = function (map) {
383293

384294
if (map.timeslider) {
385295
const currentDate = map.timeslider.getDate();
296+
console.info(currentDate);
386297
if (currentDate) {
387298
if (isBeforeYear1000(currentDate)) {
388299
// OverpassQL doesn't support dates < 1000 CE, filter in JavaScript instead
389300
jsDateFilter = currentDate;
390301
} else {
302+
expandEndDate();
391303
dateFilter = `(if: (!is_tag("start_date") || t["start_date"] <= "${currentDate}") && (!is_tag("end_date") || t["end_date"] > "${currentDate}"))`;
392304
}
393305
}
@@ -412,6 +324,8 @@ OSM.Query = function (map) {
412324
...featureStyle
413325
}).addTo(map);
414326

327+
console.info(`dateFilter: ${dateFilter}`);
328+
console.info(`jsDateFilter: ${jsDateFilter}`);
415329
runQuery(nearby, $("#query-nearby"), false, null, jsDateFilter);
416330
runQuery(isin, $("#query-isin"), true, (feature1, feature2) => size(feature1.bounds) - size(feature2.bounds), jsDateFilter);
417331
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
const isBeforeYear1000 = (dateStr) => {
2+
// Check if date is before 1000 CE (OverpassQL doesn't support these dates)
3+
if (!dateStr) return false;
4+
const match = dateStr.match(/^(-?\d+)/);
5+
return match ? parseInt(match[1], 10) < 1000 : false;
6+
}
7+
8+
const parseYear = (iso8601) => {
9+
if (!iso8601) {
10+
return null;
11+
}
12+
13+
const date = new Date(0);
14+
date.setUTCFullYear(parseInt(iso8601, 10));
15+
return isNaN(date.getDate()) ? null : date;
16+
}
17+
18+
const compareDates = (date1, date2) => {
19+
// Compare ISO 8601 dates correctly (handles BCE dates where string comparison fails)
20+
console.info(`compareDates: date1: ${date1} date2: ${date2}`)
21+
if (!date1 && !date2) return 0;
22+
if (!date1) return -1;
23+
if (!date2) return 1;
24+
25+
const match1 = date1.match(/^(-?\d+)(?:-(\d{1,2}))?(?:-(\d{1,2}))?/);
26+
const match2 = date2.match(/^(-?\d+)(?:-(\d{1,2}))?(?:-(\d{1,2}))?/);
27+
console.info(`compareDates: match1: ${match1} match2: ${match2}`)
28+
if (!match1 || !match2) return date1.localeCompare(date2);
29+
30+
const [, year1Str, month1Str, day1Str] = match1;
31+
const [, year2Str, month2Str, day2Str] = match2;
32+
const year1Int = parseInt(year1Str, 10);
33+
const year2Int = parseInt(year2Str, 10);
34+
if (year1Int !== year2Int) return year1Int - year2Int;
35+
36+
const month1 = month1Str ? parseInt(month1Str, 10) : 1;
37+
const month2 = month2Str ? parseInt(month2Str, 10) : 1;
38+
if (month1 !== month2) return month1 - month2;
39+
40+
const day1 = day1Str ? parseInt(day1Str, 10) : 1;
41+
const day2 = day2Str ? parseInt(day2Str, 10) : 1;
42+
return day1 - day2;
43+
}
44+
45+
const filterByDate = (elements, currentDate) => {
46+
// Despite its name, filterByDate is only used when YYYY < 1000 CE (JavaScript not Overpass filtering)
47+
console.info(`filterByDate: elements: ${elements} currentDate: ${currentDate}`)
48+
if (!currentDate) return elements;
49+
return elements.filter(element => {
50+
const tags = element.tags || {};
51+
const startDate = tags.start_date;
52+
const endDate = tags.end_date;
53+
if (startDate && compareDates(startDate, currentDate) > 0) return false;
54+
if (endDate && compareDates(endDate, currentDate) <= 0) return false;
55+
return true;
56+
});
57+
}
58+
59+
const featureSuffix = (feature) => {
60+
const tags = feature.tags;
61+
const startDate = parseYear(tags.start_date);
62+
const endDate = parseYear(tags.end_date);
63+
64+
if (!startDate && !endDate) {
65+
return null;
66+
}
67+
68+
// Keep the date range suffix succinct by only including the year and era.
69+
let options = {
70+
timeZone: "UTC",
71+
year: "numeric"
72+
};
73+
if (endDate) {
74+
options.era = endDate.getUTCFullYear() < 1 ? "short" : undefined;
75+
}
76+
if (startDate) {
77+
// Override any settings from the end of the range.
78+
options.era = startDate.getUTCFullYear() < 1 ? "short" : undefined;
79+
}
80+
81+
// Get the date range format in structured form, then filter out anything untagged.
82+
let format = new Intl.DateTimeFormat(OSM.i18n.locale, options);
83+
let lateDate = new Date(Date.UTC(9999));
84+
let parts = format.formatRangeToParts(startDate || lateDate, endDate || lateDate);
85+
if (!startDate) {
86+
parts = parts.filter(p => p.source !== "startRange");
87+
}
88+
if (!endDate) {
89+
parts = parts.filter(p => p.source !== "endRange");
90+
}
91+
92+
return parts.map(p => p.value).join("");
93+
}

test/javascripts/ohm_test.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
//= require ohm/dates.js
2+
3+
describe("OHM", function () {
4+
describe("isBeforeYear1000", function () {
5+
it("returns false when no date is provided", function () {
6+
expect(isBeforeYear1000()).to.be.false;
7+
});
8+
it("returns true when a date is before 1000", function () {
9+
expect(isBeforeYear1000('999-12-31')).to.be.true;
10+
});
11+
it("returns false when a date is after 1000", function () {
12+
expect(isBeforeYear1000('1000.01.01')).to.be.false;
13+
});
14+
});
15+
16+
describe("compareDates", function () {
17+
describe("with missing data", function () {
18+
it("returns 0 when no dates are provided", function () {
19+
expect(compareDates()).to.be.eq(0);
20+
});
21+
it("returns -1 when date1 is not provided", function () {
22+
expect(compareDates(null, '2026-01-01')).to.be.eq(-1);
23+
});
24+
it("returns 1 when date2 is not provided", function () {
25+
expect(compareDates('2026-01-01')).to.be.eq(1);
26+
});
27+
});
28+
29+
describe("with contemporary dates", function () {
30+
it("returns -5 when YYYY dates are 5 years apart", function () {
31+
expect(compareDates('2020', '2025')).to.be.eq(-5);
32+
});
33+
it("returns -4 when YYYY-MM dates are 4 months apart", function () {
34+
expect(compareDates('2025-02', '2025-06')).to.be.eq(-4);
35+
});
36+
it("returns -3 when YYYY-MM-DD dates are 3 days apart", function () {
37+
expect(compareDates('2025-02-02', '2025-02-05')).to.be.eq(-3);
38+
});
39+
});
40+
41+
});
42+
43+
// describe("filterByDate", function () {
44+
// describe("with pre-1000 dates", function () {
45+
// it("returns 0 when no dates are provided", function () {
46+
// expect(filterByDate('2025, 2026')).to.be.eq(0);
47+
// });
48+
// it("returns 0 when no dates are provided", function () {
49+
// expect(filterByDate('2025-02, 2026-11')).to.be.eq(0);
50+
// });
51+
// it("returns 0 when no dates are provided", function () {
52+
// expect(filterByDate('2025-02-02, 2026-11-11')).to.be.eq(0);
53+
// });
54+
// });
55+
// });
56+
57+
});

0 commit comments

Comments
 (0)