]> git.openstreetmap.org Git - rails.git/commitdiff
Merge remote-tracking branch 'upstream/pull/5736'
authorAnton Khorev <tony29@yandex.ru>
Sat, 26 Apr 2025 03:01:36 +0000 (06:01 +0300)
committerAnton Khorev <tony29@yandex.ru>
Sat, 26 Apr 2025 03:01:36 +0000 (06:01 +0300)
app/assets/favicons/manifest.json.erb
app/assets/javascripts/index.js
app/assets/javascripts/osm.js.erb
test/javascripts/osm_test.js

index 2e97cebc3247e3f4ac25aa90401d67dec766422c..53fdfcc77378f605fee759f10fc0408fb83748f9 100644 (file)
        "start_url": "/",
        "theme_color": "#7ebc6f",
        "background_color": "#fff",
-       "display": "minimal-ui"
+       "display": "minimal-ui",
+       "protocol_handlers": [
+               {
+                       "protocol": "geo",
+                       "url": "/?geouri=%s"
+               }
+       ]
 }
index 7ff8a40d1474905b9d706968e75c35b90b22149f..9bce95798c73c96e3860120f0356877369f42946 100644 (file)
@@ -211,7 +211,9 @@ $(function () {
     map.setView([params.lat, params.lon], params.zoom);
   }
 
-  if (params.marker) {
+  if (params.marker && params.mrad) {
+    L.circle([params.mlat, params.mlon], { radius: params.mrad }).addTo(map);
+  } else if (params.marker) {
     L.marker([params.mlat, params.mlon]).addTo(map);
   }
 
index 31f92da84a846644e9e99fe2c653dc18bc983a9b..c7887b63dcbb35bd89855c32d6d1de8368f37587 100644 (file)
@@ -56,12 +56,20 @@ OSM = {
 
   mapParams: function (search) {
     const params = new URLSearchParams(search || location.search),
+          geoURI = OSM.parseGeoURI(params.get("geouri")),
           mapParams = {};
 
     if (params.has("mlon") && params.has("mlat")) {
       mapParams.marker = true;
       mapParams.mlon = parseFloat(params.get("mlon"));
       mapParams.mlat = parseFloat(params.get("mlat"));
+      mapParams.mrad = parseFloat(params.get("mrad"));
+    }
+    if (geoURI) {
+      mapParams.marker = true;
+      mapParams.mlon = geoURI.coords.lng;
+      mapParams.mlat = geoURI.coords.lat;
+      mapParams.mrad = geoURI.uncertainty;
     }
 
     // Old-style object parameters; still in use for edit links e.g. /edit?way=1234
@@ -93,6 +101,12 @@ OSM = {
       mapParams.lon = params.get("mlon");
       mapParams.lat = params.get("mlat");
       mapParams.zoom = params.get("zoom") || 12;
+    } else if (geoURI?.uncertainty > 0 && !("zoom" in geoURI)) {
+      mapParams.bounds = geoURI.coords.toBounds(geoURI.uncertainty * 4);
+    } else if (geoURI) {
+      mapParams.lon = geoURI.coords.lng;
+      mapParams.lat = geoURI.coords.lat;
+      mapParams.zoom = geoURI.zoom || 12;
     } else if (loc) {
       [mapParams.lon, mapParams.lat, mapParams.zoom] = loc;
     } else if (OSM.home) {
@@ -121,6 +135,29 @@ OSM = {
     return mapParams;
   },
 
+  parseGeoURI: function (geoURI) {
+    let url;
+    try {
+      url = new URL(geoURI);
+    } catch (e) { return; }
+    if (!url?.pathname || url.protocol !== "geo:") return;
+    const [path, ...params] = url.pathname.split(";");
+    let coords;
+    try {
+      coords = L.latLng(path.split(","));
+    } catch (e) { return; }
+    if (!coords) return;
+    const searchParams = new URLSearchParams(params.join("&").toLowerCase());
+    const crs = searchParams.get("crs");
+    if (crs && crs !== "wgs84") return;
+    const uncertainty = parseFloat(searchParams.get("u"));
+    const zoom = parseFloat(url.searchParams.get("z"));
+    const out = { coords };
+    if (uncertainty >= 0) out.uncertainty = uncertainty;
+    if (zoom >= 0) out.zoom = zoom;
+    return out;
+  },
+
   parseHash: function (hash = location.hash) {
     const args = {};
 
index 10d562959f39bd5a0d9a7284a5b8a1a5764b5162..8f4471140d21e59e8002f6fc596af464d9d03389 100644 (file)
@@ -73,6 +73,33 @@ describe("OSM", function () {
       expect(params).to.have.property("zoom", 16);
     });
 
+    it("parses geoURIs", function () {
+      const params = OSM.mapParams("?geouri=geo%3A57.6247%2C-3.6845");
+      expect(params).to.have.property("lat", 57.6247);
+      expect(params).to.have.property("lon", -3.6845);
+      expect(params).to.have.property("mlat", 57.6247);
+      expect(params).to.have.property("mlon", -3.6845);
+      expect(params).to.have.property("zoom", 12);
+    });
+
+    it("parses zoom in geoURIs", function () {
+      const params = OSM.mapParams("?geouri=geo%3A57.6247%2C-3.6845%3Fz%3D16");
+      expect(params).to.have.property("lat", 57.6247);
+      expect(params).to.have.property("lon", -3.6845);
+      expect(params).to.have.property("mlat", 57.6247);
+      expect(params).to.have.property("mlon", -3.6845);
+      expect(params).to.have.property("zoom", 16);
+    });
+
+    it("parses uncertainty in geoURIs", function () {
+      const params = OSM.mapParams("?geouri=geo%3A57.6247%2C-3.6845%3Bu%3D100");
+      const expected = L.latLngBounds([57.62290336944585, -3.6878552857327764], [57.62649663055414, -3.6811447142672233]);
+      expect(params).to.have.property("mlat", 57.6247);
+      expect(params).to.have.property("mlon", -3.6845);
+      expect(params).to.have.property("mrad", 100);
+      expect(params).to.have.property("bounds").deep.equal(expected);
+    });
+
     it("parses lat/lon/zoom from the hash", function () {
       location.hash = "#map=16/57.6247/-3.6845";
       const params = OSM.mapParams("?");
@@ -130,6 +157,99 @@ describe("OSM", function () {
     });
   });
 
+  describe(".parseGeoURI", function () {
+    it("parses basic geoURIs", function () {
+      let params = OSM.parseGeoURI("geo:57.6247,-3.6845");
+      expect(params.coords).to.deep.equal(L.latLng(57.6247, -3.6845));
+      expect(params.zoom).to.be.undefined;
+      expect(params.uncertainty).to.be.undefined;
+      params = OSM.parseGeoURI("GEO:57.6247,-3.6845");
+      expect(params.coords).to.deep.equal(L.latLng(57.6247, -3.6845));
+    });
+    it("parses only geoURIs", function () {
+      let params = OSM.parseGeoURI("latlng:57.6247,-3.6845");
+      expect(params).to.be.undefined;
+      params = OSM.parseGeoURI("geo57.6247,-3.6845");
+      expect(params).to.be.undefined;
+    });
+    it("rejects geoURIs with less than 2 coordinates", function () {
+      const params = OSM.parseGeoURI("geo:57.6247");
+      expect(params).to.be.undefined;
+    });
+    it("parses geoURIs with altitude", function () {
+      const params = OSM.parseGeoURI("geo:57.6247,-3.6845,100");
+      expect(params.coords).to.deep.equal(L.latLng(57.6247, -3.6845, 100));
+    });
+    it("rejects geoURIs with more than 3 coordinates", function () {
+      const params = OSM.parseGeoURI("geo:123,57.6247,-3.6845,100");
+      expect(params).to.be.undefined;
+    });
+    it("ignores non-numeric coordinates", function () {
+      let params = OSM.parseGeoURI("geo:57.6247,-3.6845,abc");
+      expect(params.coords.lat).to.equal(57.6247);
+      expect(params.coords.lng).to.equal(-3.6845);
+      expect(isNaN(params.coords.alt)).to.be.true;
+      params = OSM.parseGeoURI("geo:57.6247,abc");
+      expect(params).to.be.undefined;
+    });
+    it("parses geoURIs with crs", function () {
+      let params = OSM.parseGeoURI("geo:57.6247,-3.6845;crs=wgs84");
+      expect(params.coords).to.deep.equal(L.latLng(57.6247, -3.6845));
+      params = OSM.parseGeoURI("geo:57.6247,-3.6845;CRS=wgs84");
+      expect(params.coords).to.deep.equal(L.latLng(57.6247, -3.6845));
+      params = OSM.parseGeoURI("geo:57.6247,-3.6845;CRS=WGS84");
+      expect(params.coords).to.deep.equal(L.latLng(57.6247, -3.6845));
+    });
+    it("rejects geoURIs with different crs", function () {
+      const params = OSM.parseGeoURI("geo:57.6247,-3.6845;crs=utm");
+      expect(params).to.be.undefined;
+    });
+    it("parses geoURIs with uncertainty", function () {
+      let params = OSM.parseGeoURI("geo:57.6247,-3.6845;u=100");
+      expect(params.uncertainty).to.equal(100);
+      params = OSM.parseGeoURI("geo:57.6247,-3.6845;U=100");
+      expect(params.uncertainty).to.equal(100);
+    });
+    it("ignores negative uncertainty", function () {
+      const params = OSM.parseGeoURI("geo:57.6247,-3.6845;u=-100");
+      expect(params.uncertainty).to.be.undefined;
+    });
+    it("ignores non-numeric uncertainty", function () {
+      const params = OSM.parseGeoURI("geo:57.6247,-3.6845;u=abc");
+      expect(params.uncertainty).to.be.undefined;
+    });
+    it("parses uncertainty 0", function () {
+      const params = OSM.parseGeoURI("geo:57.6247,-3.6845;u=0");
+      expect(params.uncertainty).to.equal(0);
+    });
+    it("ignores uncertainty in the query parameters", function () {
+      const params = OSM.parseGeoURI("geo:57.6247,-3.6845?u=100");
+      expect(params.uncertainty).to.be.undefined;
+    });
+    it("parses geoURIs with zoom", function () {
+      let params = OSM.parseGeoURI("geo:57.6247,-3.6845?z=16");
+      expect(params.zoom).to.equal(16);
+      params = OSM.parseGeoURI("geo:57.6247,-3.6845?Z=16");
+      expect(params.zoom).to.be.undefined;
+    });
+    it("ignores non-numeric zoom", function () {
+      const params = OSM.parseGeoURI("geo:57.6247,-3.6845?z=abc");
+      expect(params.zoom).to.be.undefined;
+    });
+    it("ignores negative zoom", function () {
+      const params = OSM.parseGeoURI("geo:57.6247,-3.6845?z=-100");
+      expect(params.zoom).to.be.undefined;
+    });
+    it("parses geoURIs with zoom level 0", function () {
+      const params = OSM.parseGeoURI("geo:57.6247,-3.6845?z=0");
+      expect(params.zoom).to.equal(0);
+    });
+    it("ignores zoom in the geouri parameters", function () {
+      const params = OSM.parseGeoURI("geo:57.6247,-3.6845;z=16");
+      expect(params.zoom).to.be.undefined;
+    });
+  });
+
   describe(".parseHash", function () {
     it("parses lat/lon/zoom params", function () {
       const args = OSM.parseHash("#map=5/57.6247/-3.6845&layers=M");