diff --git a/templates/index.html b/templates/index.html index fe7fb8b..47c64d2 100644 --- a/templates/index.html +++ b/templates/index.html @@ -672,7 +672,7 @@ font-weight: 500; padding: 2px 7px; white-space: nowrap; - pointer-events: none; + cursor: pointer; } /* Custom circular pin that reflects the POI type */ @@ -1064,14 +1064,12 @@ // Build a viewbox from the currently visible map area so // results are restricted to what the user can see. - const mb = map.getBounds(); - - const viewbox = [ - mb.west, - mb.north, - mb.east, - mb.south - ].join(","); + // + // Only send a viewbox once the map has loaded AND the + // bounds are geometrically valid. Otherwise we fall back + // straight to a global search (see below). + const viewbox = + getValidViewbox(); let data = @@ -1293,6 +1291,133 @@ } + /* ========================================================= + VIEWBOX HELPER + ========================================================= */ + + /* + * How far beyond the visible map area we extend the + * search box, in kilometres. This keeps results from + * being cut off at the screen edges. + */ + const VIEWBOX_PADDING_KM = 5; + + + /* + * Convert a distance in metres to degrees of longitude + * at a given latitude. + */ + function metersToLonDegrees(meters, lat) { + + const rad = + lat * Math.PI / 180; + + return meters / + (111320 * Math.cos(rad)); + } + + + /* + * Convert a distance in metres to degrees of latitude. + */ + function metersToLatDegrees(meters) { + + return meters / 110574; + } + + + /* + * Return a "west,north,east,south" string for the + * currently visible map area, expanded by + * VIEWBOX_PADDING_KM on every side, or null if the map + * isn't ready yet or the bounds aren't usable. A null + * value makes the caller perform an unrestricted + * (global) search instead. + */ + function getValidViewbox() { + + if (!map.loaded()) + return null; + + + const mb = map.getBounds(); + + let west = + mb.getWest(); + + let north = + mb.getNorth(); + + let east = + mb.getEast(); + + let south = + mb.getSouth(); + + + /* + * Reject NaN / infinite values and inverted boxes. + */ + if ( + !isFinite(west) || + !isFinite(north) || + !isFinite(east) || + !isFinite(south) + ) + return null; + + + if ( + east <= west || + north <= south + ) + return null; + + + /* + * Expand the box by the padding distance so that + * places just outside the viewport are still found. + */ + const padMeters = + VIEWBOX_PADDING_KM * 1000; + + const midLat = + (north + south) / 2; + + const dLat = + metersToLatDegrees(padMeters); + + const dLon = + metersToLonDegrees( + padMeters, + midLat + ); + + + west -= dLon; + east += dLon; + north += dLat; + south -= dLat; + + + /* + * Clamp to valid geographic ranges after expansion. + */ + north = Math.min(north, 90); + south = Math.max(south, -90); + west = Math.max(west, -180); + east = Math.min(east, 180); + + + return [ + west, + north, + east, + south + ].join(","); + } + + /* ========================================================= PERFORM SEARCH ========================================================= */