Skip to content

Commit b29601a

Browse files
authored
Merge pull request #356 from OpenHistoricalMap/1163_rub21_features_filter_date
Filter query by current date slider
2 parents 951f691 + cd15db6 commit b29601a

File tree

3 files changed

+103
-17
lines changed

3 files changed

+103
-17
lines changed

app/assets/javascripts/index/query.js

Lines changed: 99 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ OSM.Query = function (map) {
187187
}
188188
}
189189

190-
function runQuery(query, $section, merge, compare) {
190+
function runQuery(query, $section, merge, compare, dateFilter) {
191191
const $ul = $section.find("ul");
192192

193193
$ul.empty();
@@ -198,6 +198,7 @@ OSM.Query = function (map) {
198198
}
199199

200200
$section.data("ajax", new AbortController());
201+
201202
fetch(OSM.OVERPASS_URL, {
202203
method: "POST",
203204
body: new URLSearchParams({
@@ -208,7 +209,7 @@ OSM.Query = function (map) {
208209
})
209210
.then(response => response.json())
210211
.then(function (results) {
211-
let elements = results.elements;
212+
let elements = results.elements || [];
212213

213214
$section.find(".loader").hide();
214215

@@ -228,6 +229,11 @@ OSM.Query = function (map) {
228229
}, {}));
229230
}
230231

232+
// Apply JavaScript date filtering if needed (for dates < 1000 CE)
233+
if (dateFilter) {
234+
elements = filterByDate(elements, dateFilter);
235+
}
236+
231237
if (compare) {
232238
elements = elements.sort(compare);
233239
}
@@ -284,25 +290,81 @@ OSM.Query = function (map) {
284290
return (maxlon - minlon) * (maxlat - minlat);
285291
}
286292

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+
300+
// 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+
287338
/*
288-
* To find nearby objects we ask overpass for the union of the
289-
* following sets:
339+
* QUERY MECHANISM:
290340
*
341+
* To find nearby objects we ask Overpass for the union of the following sets:
291342
* node(around:<radius>,<lat>,<lng>)
292343
* way(around:<radius>,<lat>,<lng>)
293344
* relation(around:<radius>,<lat>,<lng>)
294345
*
295-
* to find enclosing objects we first find all the enclosing areas:
296-
*
346+
* To find enclosing objects we first find all the enclosing areas:
297347
* is_in(<lat>,<lng>)->.a
298348
*
299349
* and then return the union of the following sets:
300-
*
301350
* relation(pivot.a)
302351
* way(pivot.a)
303352
*
304-
* In both cases we then ask to retrieve tags and the geometry
305-
* for each object.
353+
* In both cases we then ask to retrieve tags and the geometry for each object.
354+
*
355+
* TEMPORAL FILTERING (OpenHistoricalMap - Added Feature):
356+
* Filter objects to only include those that existed at the time slider date.
357+
* - Dates >= 1000 CE: Use OverpassQL filtering via `if:` condition
358+
* - Dates < 1000 CE: Use JavaScript filtering (OverpassQL limitation + BCE date comparison bug)
359+
* Filter logic: start_date <= currentDate AND (no end_date OR end_date > currentDate)
360+
*
361+
* Examples:
362+
* - currentDate="1914-07-28": start_date="1910", end_date="1920" → MATCHES
363+
* - currentDate="1914-07-28": start_date="1915" → EXCLUDED (started after)
364+
* - currentDate="-0003-01-01": start_date="-0003-06-01" → EXCLUDED (JavaScript handles BCE correctly)
365+
* - currentDate="-0003-01-01": start_date="-0004-01-01", end_date="-0002-01-01" → MATCHES
366+
*
367+
* https://wiki.openstreetmap.org/wiki/OpenHistoricalMap/Overpass#Country_boundaries_at_a_given_date_(start_of_WWI)
306368
*/
307369
function queryOverpass(latlng) {
308370
const bounds = map.getBounds(),
@@ -312,10 +374,31 @@ OSM.Query = function (map) {
312374
.join(),
313375
geom = `geom(${bbox})`,
314376
radius = 10 * Math.pow(1.5, 19 - zoom),
315-
here = `(around:${radius},${latlng})`,
316-
enclosed = "(pivot.a);out tags bb",
317-
nearby = `(node${here};way${here};);out tags ${geom};relation${here};out ${geom};`,
318-
isin = `is_in(${latlng})->.a;way${enclosed};out ids ${geom};relation${enclosed};`;
377+
here = `(around:${radius},${latlng})`;
378+
379+
let dateFilter = ""; // OverpassQL filter (dates >= 1000 CE)
380+
let jsDateFilter = null; // JavaScript filter (dates < 1000 CE)
381+
382+
if (map.timeslider) {
383+
const currentDate = map.timeslider.getDate();
384+
if (currentDate) {
385+
if (isBeforeYear1000(currentDate)) {
386+
// OverpassQL doesn't support dates < 1000 CE, filter in JavaScript instead
387+
jsDateFilter = currentDate;
388+
} else {
389+
dateFilter = `(if: (!is_tag("start_date") || t["start_date"] <= "${currentDate}") && (!is_tag("end_date") || t["end_date"] > "${currentDate}"))`;
390+
}
391+
}
392+
}
393+
394+
// Build queries - match original format when no dateFilter
395+
const enclosed = dateFilter ? `(pivot.a)${dateFilter}` : "(pivot.a);out tags bb",
396+
nearby = dateFilter
397+
? `(node${here}${dateFilter};way${here}${dateFilter};);out tags ${geom};relation${here}${dateFilter};out ${geom};`
398+
: `(node${here};way${here};);out tags ${geom};relation${here};out ${geom};`,
399+
isin = dateFilter
400+
? `is_in(${latlng})->.a;way${enclosed};out geom;relation${enclosed};out geom;`
401+
: `is_in(${latlng})->.a;way${enclosed};out ids ${geom};relation${enclosed};`;
319402

320403
$("#sidebar_content .query-intro")
321404
.hide();
@@ -327,8 +410,8 @@ OSM.Query = function (map) {
327410
...featureStyle
328411
}).addTo(map);
329412

330-
runQuery(nearby, $("#query-nearby"), false);
331-
runQuery(isin, $("#query-isin"), true, (feature1, feature2) => size(feature1.bounds) - size(feature2.bounds));
413+
runQuery(nearby, $("#query-nearby"), false, null, jsDateFilter);
414+
runQuery(isin, $("#query-isin"), true, (feature1, feature2) => size(feature1.bounds) - size(feature2.bounds), jsDateFilter);
332415
}
333416

334417
function clickHandler(e) {
@@ -361,7 +444,7 @@ OSM.Query = function (map) {
361444
page.load = function (path, noCentre) {
362445
// the original page.load content is the function below, and is used when one visits this page, be it first load OR later routing change
363446
// below, we wrap "if map.timeslider" so we only try to add the timeslider if we don't already have it
364-
function originalLoadFunction () {
447+
function originalLoadFunction() {
365448
const params = new URLSearchParams(path.substring(path.indexOf("?"))),
366449
latlng = L.latLng(params.get("lat"), params.get("lon"));
367450

config/environments/development.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,4 +72,5 @@
7272

7373
# Disable host validation.
7474
config.hosts = []
75+
7576
end

docker-compose.dev.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ services:
3232
- web_network
3333

3434
db:
35-
image: ghcr.io/openhistoricalmap/db:0.0.1-0.dev.git.2836.hbee0465
35+
image: ghcr.io/openhistoricalmap/db:0.0.1-0.dev.git.3101.ha4a4757
3636
ports:
3737
- "54321:5432"
3838
env_file:
@@ -46,6 +46,8 @@ volumes:
4646
web-tmp:
4747
web-storage:
4848
db-data:
49+
driver: local
50+
name: ohm_web_pg17
4951

5052
networks:
5153
web_network:

0 commit comments

Comments
 (0)