leaflet-rotate 0.2.8 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/debug.min.js CHANGED
@@ -1 +1 @@
1
- L.Rotate={debug:function(map,options={}){this._map=map,document.head.insertAdjacentHTML("beforeend","<style>\n .leaflet-rotate-info {\n width: 100%;\n }\n .leaflet-rotate-info td {\n /* width: 7em; */\n overflow: hidden;\n /* max-width: 7em; */\n text-overflow: ellipsis;\n /* white-space: nowrap; */\n }\n .leaflet-rotate-info td:nth-of-type(3) {\n font-family: Monospace;\n }\n .leaflet-rotate-info th {\n text-align: left;\n }\n div.crosshair {\n position:absolute;\n width:1px;\n height:1px;\n border: 1px solid green;\n z-index: 100;\n left: 50%;\n right: 50%;\n }\n div.pivot {\n position:absolute;\n width:1px;\n height:1px;\n border: 5px solid yellow;\n border-radius: 100%;\n z-index: 1000;\n }\n div.pixelorigin {\n position: absolute;\n width: 1px;\n height: 1px;\n border: 5px solid yellow;\n border-radius: 100%;\n z-index: 1000;\n }\n div.pivot:before {\n left: -1.5em;\n bottom: -2em;\n position: absolute;\n content: 'PIVOT';\n color: yellow;\n }\n div.pixelorigin:before {\n left: -1.75em;\n top: -2em;\n position: absolute;\n content: 'ORIGIN';\n color: yellow;\n }\n div.panebounds.main {\n position:absolute;\n width:800px;\n height:600px;\n border: 1px solid red;\n border-bottom: 1px red dashed;\n border-right: 1px red dashed;\n z-index: 100;\n }\n div.panebounds.rotate {\n position:absolute;\n width:800px;\n height:600px;\n border: 1px solid purple;\n border-bottom: 1px purple dashed;\n border-right: 1px purple dashed;\n z-index: 100;\n }\n div.crosshair.c1 {\n left:calc(50% - 5px);\n top:50%;\n height: 0;\n width: 10px;\n }\n div.crosshair.c2 {\n left:50%;\n top:calc(50% - 5px);\n width: 0;\n height: 10px;\n }\n #rho_input {\n vertical-align: middle;\n }\n .panebounds:before {\n padding: 1ch;\n }\n .panebounds.rotate:before {\n float: right;\n content: 'ROTATED';\n }\n .panebounds.main:before {\n float: left;\n content: 'MAIN';\n }\n </style>"),map._container.insertAdjacentHTML("afterbegin",'<div class="crosshair c1"></div><div class="crosshair c2"></div>'),map._container.insertAdjacentHTML("afterend",'<hr>\n <table class="leaflet-rotate-info">\n <tr>\n <td>\n <button onclick="L.Rotate._map.setBearing(0);" title="map.setBearing(0);"> 0</button>\n <button onclick="L.Rotate._map.setBearing(15);" title="map.setBearing(15);">15</button>\n <button onclick="L.Rotate._map.setBearing(30);" title="map.setBearing(30);">30</button>\n <button onclick="L.Rotate._map.setBearing(45);" title="map.setBearing(45);">45</button>\n <button onclick="L.Rotate._map.setBearing(60);" title="map.setBearing(60);">60</button>\n <button onclick="L.Rotate._map.setBearing(75);" title="map.setBearing(75);">75</button>\n <button onclick="L.Rotate._map.setBearing(90);" title="map.setBearing(90);">90</button>\n <button onclick="L.Rotate._map.setBearing(180);" title="map.setBearing(180);">180</button>\n </td>\n <td>\n <input title="increase/decrease rotation angle" type="range" min="0" max="360" step="1" value="0" name="rho" id=\'rho_input\' /><span id=\'rho\'></span>\n </td>\n <td>\n <input title="increase/decrease padding bounds" type="range" min="-0.25" max="0" value="0" step="0.01" name="pad" id=\'pad_input\' />\n </td>\n <td>\n <button onclick="L.Rotate.randomRotateMarkers(L.Rotate._map);" title="markers.setRotation(Math.random() * Math.PI / 30 + 0.1)">Rotate Markers</button>\n <button onclick="L.Rotate.randomMarkers(L.Rotate._map, 100);" title="add 100 random markers to map">Add Markers</button>\n </td>\n </tr>\n <tr>\n <th>LatLng </th>\n <td id=\'llx\'></td>\n <td id=\'lly\'></td>\n <td class=\'long\'>LatLng of mouse pointer</td>\n </tr>\n <tr>\n <th>Rel to pane </th>\n <td id=\'lyx\'></td>\n <td id=\'lyy\'></td>\n <td class=\'long\'>Mouse pointer pixel coords relative to _rotatePane</td>\n </tr>\n <tr>\n <th>Rel to container</th>\n <td id=\'cnx\'></td>\n <td id=\'cny\'></td>\n <td class=\'long\'>Mouse pointer coords relative to map &lt;div&gt;</td>\n </tr>\n <tr>\n <th>Pivot </th>\n <td id=\'pvx\'></td>\n <td id=\'pvy\'></td>\n <td class=\'long\'>Last _rotationPane pivot pixel coords relative to _rotationPane</td>\n </tr>\n <tr>\n <th>Pane offset </th>\n <td id=\'pox\'></td>\n <td id=\'poy\'></td>\n <td class=\'long\'>Pixel offset of _mapPane</td>\n </tr>\n <tr>\n <th>Pixel origin </th>\n <td id=\'ogx\'></td>\n <td id=\'ogy\'></td>\n <td class=\'long\'>Negative pixel coords of the (0,0) CRS point relative to _rotatePane</td>\n </tr>\n <tr>\n <th>Pixel bounds </th>\n <td id=\'ogbx\'></td>\n <td id=\'ogby\'></td>\n <td class=\'long\'>Bounds of the current map view in projected pixel coordinates {min, max}.</td>\n </tr>\n </table>'),map.___rotatePixelOrigin=L.DomUtil.create("div","pixelorigin",map._rotatePane),map.___rotatePivotDot=L.DomUtil.create("div","pivot",map._mapPane),L.DomUtil.create("div","panebounds rotate",map._rotatePane),L.DomUtil.create("div","panebounds main",map._mapPane);var rhoInput=document.getElementById("rho_input"),padInput=document.getElementById("pad_input");rhoInput.addEventListener("change",L.Rotate.rotate.bind(this)),rhoInput.addEventListener("mousemove",L.Rotate.rotate.bind(this)),padInput.addEventListener("change",L.Rotate.displayCenter.bind(this)),padInput.addEventListener("mousemove",L.Rotate.displayCenter.bind(this)),padInput.addEventListener("change",L.Rotate.displayBounds.bind(this)),padInput.addEventListener("mousemove",L.Rotate.displayBounds.bind(this)),padInput.addEventListener("change",L.Rotate.displayLayersBounds.bind(this)),padInput.addEventListener("mousemove",L.Rotate.displayLayersBounds.bind(this)),padInput.addEventListener("change",L.Rotate.displayPixelBounds.bind(this)),padInput.addEventListener("mousemove",L.Rotate.displayPixelBounds.bind(this)),map.on("move rotate zoomend",this.updatePanePos),map.on("rotate zoomend load",this.updatePixelOrigin),map.on("rotate",this.updateRhoInput),map.on("rotate",this.updatePivotDot),map.on("moveend zoomend resetview rotate",this.displayCenter.bind(this)),map.on("moveend zoomend resetview rotate",this.displayBounds.bind(this)),map.on("moveend zoomend resetview rotate",this.displayLayersBounds.bind(this)),map.on("moveend zoomend resetview rotate",this.displayPixelBounds.bind(this)),this.updatePixelOrigin.call(map),this.updateRhoInput.call(map),this.updatePivotDot.call(map),this.displayCenter.call(this),this.displayBounds.call(this),this.displayLayersBounds.call(this),this.displayPixelBounds.call(this),L.Browser.mobile&&map.compassBearing.enable(),map.on("mousemove",this.logMouse),map.on("locationfound",this.resetHeading),options.log&&map.on("click movestart move moveend zoomstart zoomend locationfound locationerror locationfound",this.logEvent)},logEvent:function(e){console.log(e,e.type)},logMouse:function(e){document.getElementById("llx").innerHTML=e.latlng.lng,document.getElementById("lly").innerHTML=e.latlng.lat,document.getElementById("lyx").innerHTML=e.layerPoint.x,document.getElementById("lyy").innerHTML=e.layerPoint.y,document.getElementById("cnx").innerHTML=e.containerPoint.x,document.getElementById("cny").innerHTML=e.containerPoint.y},rotate:function(e){return 0!==e.buttons&&this._map.setBearing(e.target.valueAsNumber)},updatePixelOrigin:function(){this._pixelOrigin&&(document.getElementById("ogx").innerHTML=this._pixelOrigin.x,document.getElementById("ogy").innerHTML=this._pixelOrigin.y,this.___rotatePixelOrigin.style.left=-this._pixelOrigin.x-1+"px",this.___rotatePixelOrigin.style.top=-this._pixelOrigin.y-1+"px")},updatePivotDot:function(){this._pivot&&(document.getElementById("pvx").innerHTML=this._pivot.x,document.getElementById("pvy").innerHTML=this._pivot.y,this.___rotatePivotDot.style.left=this._pivot.x-1+"px",this.___rotatePivotDot.style.top=this._pivot.y-1+"px")},updatePanePos:function(){var panePos=this._getMapPanePos();document.getElementById("pox").innerHTML=panePos.x,document.getElementById("poy").innerHTML=panePos.y},updateRhoInput:function(){document.getElementById("rho_input").value=this.getBearing()},rotateOneDegree:function(){var angle=document.getElementById("rho_input").valueAsNumber++;this.setBearing(angle<360?angle:0)},randomRotateMarkers:function(map){var markers=[],steps=[];for(var i in map._layers)map._layers[i]instanceof L.Marker&&(markers.push(map._layers[i]),steps.push(Math.random()*Math.PI/30+.1));function stepOn(steps_left){for(var i=0;i<markers.length;i++)markers[i].setRotation(markers[i].options.rotation+steps[i]);--steps_left&&setTimeout(stepOn.bind(null,steps_left),20)}stepOn(25)},randomMarkers:function(map,number){var map=this._map,populate=function(){for(var i=0;i<number;i++){var bounds=map.getBounds(),southWest=bounds.getSouthWest(),northEast=bounds.getNorthEast(),lngSpan=northEast.lng-southWest.lng,latSpan=northEast.lat-southWest.lat,m=L.marker(L.latLng(southWest.lat+latSpan*Math.random(),southWest.lng+lngSpan*Math.random()));L.Rotate.___cluster.addLayer(m)}};this.___cluster?"function"==typeof L.markerClusterGroup&&populate():this.___cluster=import("https://unpkg.com/leaflet.markercluster@1.4.1/src/index.js").then(m=>{document.head.insertAdjacentHTML("beforeend",'\n \x3c!-- Leaflet-MarkerCluster --\x3e\n <link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster@1.4.1/dist/MarkerCluster.css">\n <link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster@1.4.1/dist/MarkerCluster.Default.css">\n '),Object.assign(globalThis.L,m),this.___cluster=L.markerClusterGroup({spiderfyOnMaxZoom:!1,showCoverageOnHover:!1,zoomToBoundsOnClick:!1}).on("clusterclick",a=>a.layer.spiderfy()).addTo(map),populate()})},displayCenter:function(){var map=this._map;this.___centerMarker&&this.___centerMarker.remove(),this.___centerMarker=L.circleMarker(map.getCenter(),{radius:3,color:"red"}).addTo(map).bindTooltip("<b>center</b>",{direction:"right",permanent:!0}),this.___centerMarker.getTooltip().getElement().setAttribute("tabindex","-1")},displayBounds:function(){var map=this._map;this.___bounds&&this.___bounds.remove();var padding=document.getElementById("pad_input").valueAsNumber,latLngBounds=map.getBounds().pad(padding);this.___bounds=L.rectangle(latLngBounds,{fill:!1,lineCap:"square",color:"yellow",dashArray:"5, 10",weight:2}).addTo(map)},displayLayersBounds:function(){for(const flytopath of["path","polygon","circle"]){if(!globalThis[flytopath])continue;this[`___${flytopath}bounds`]&&this[`___${flytopath}bounds`].remove();let layer,layerBounds=globalThis[flytopath].getBounds();this[`___${flytopath}bounds`]=L.featureGroup([L.rectangle(layerBounds,{fill:!1,lineCap:"square",color:"yellow",dashArray:"5, 10",weight:1,opacity:.5}),L.marker(layerBounds.getNorthWest(),{scale:.5,rotation:-45*L.DomUtil.DEG_TO_RAD,rotateWithView:!0}).bindTooltip("<b>NW</b>",{direction:"center"}),L.marker(layerBounds.getNorthEast(),{scale:.5,rotation:45*L.DomUtil.DEG_TO_RAD,rotateWithView:!0}).bindTooltip("<b>NE</b>",{direction:"center"}),L.marker(layerBounds.getSouthEast(),{scale:.5,rotation:135*L.DomUtil.DEG_TO_RAD,rotateWithView:!0}).bindTooltip("<b>SE</b>",{direction:"center"}),L.marker(layerBounds.getSouthWest(),{scale:.5,rotation:-135*L.DomUtil.DEG_TO_RAD,rotateWithView:!0}).bindTooltip("<b>SW</b>",{direction:"center"})]).addTo(map).eachLayer(m=>{m.getElement&&m.getElement()&&(m.getElement().style.filter="hue-rotate(150deg)")})}},displayPixelBounds:function(){var map=this._map;this.___corners&&this.___corners.remove(),this.___pixelbounds&&this.___pixelbounds.remove();const size=map.getSize(),rect=map._container.getBoundingClientRect(),pixelBounds=map.getPixelBounds(),TL=L.point(rect),TR=L.point(rect).add(L.point(rect.width,0)),BR=L.point(rect).add(L.point(rect.width,rect.height)),BL=L.point(rect).add(L.point(0,rect.height)),topLeft=L.marker(map.containerPointToLatLng(TL),{title:"Top Left corner",rotation:-45*L.DomUtil.DEG_TO_RAD}),topRight=L.marker(map.containerPointToLatLng(TR),{title:"Top Right corner",rotation:45*L.DomUtil.DEG_TO_RAD}),bottomRight=L.marker(map.containerPointToLatLng(BR),{title:"Bottom Right corner",rotation:135*L.DomUtil.DEG_TO_RAD}),bottomLeft=L.marker(map.containerPointToLatLng(BL),{title:"Bottom Left corner",rotation:-135*L.DomUtil.DEG_TO_RAD});this.___corners=L.featureGroup([topLeft,topRight,bottomRight,bottomLeft]).addTo(map).eachLayer(marker=>marker.getElement().style.filter="hue-rotate(150deg)"),document.getElementById("ogbx").innerHTML=JSON.stringify(pixelBounds.min),document.getElementById("ogby").innerHTML=JSON.stringify(pixelBounds.max)},resetHeading:function(e){null!==e.heading&&this.setBearing(e.heading)},locate:function(){this._map.locate({setView:!0,enableHighAccuracy:!0})}};
1
+ L.Rotate={debug:function(t,n={}){this._map=t,document.head.insertAdjacentHTML("beforeend",'<style>.leaflet-rotate-info{width:100%}.leaflet-rotate-info td{overflow:hidden;text-overflow:ellipsis}.leaflet-rotate-info td:nth-of-type(3){font-family:Monospace}.leaflet-rotate-info th{text-align:left}div.crosshair{position:absolute;width:1px;height:1px;border:1px solid green;z-index:100;left:50%;right:50%}div.pivot,div.pixelorigin{position:absolute;width:1px;height:1px;border:5px solid yellow;border-radius:100%;z-index:1000}div.pivot:before{left:-1.5em;bottom:-2em;position:absolute;content:"PIVOT";color:#ff0}div.pixelorigin:before{left:-1.75em;top:-2em;position:absolute;content:"ORIGIN";color:#ff0}div.panebounds.main{position:absolute;width:800px;height:600px;box-sizing:border-box;border:1px solid red;border-bottom:1px red dashed;border-right:1px red dashed;z-index:100}div.panebounds.rotate{position:absolute;width:800px;height:600px;box-sizing:border-box;border:1px solid purple;border-bottom:1px purple dashed;border-right:1px purple dashed;z-index:100}div.crosshair.c1{left:calc(50% - 5px);top:50%;height:0;width:10px}div.crosshair.c2{left:50%;top:calc(50% - 5px);width:0;height:10px}#rho_input{vertical-align:middle}.panebounds:before{padding:1ch}.panebounds.rotate:before{float:right;content:"ROTATED"}.panebounds.main:before{float:left;content:"MAIN"}</style>'),t._container.insertAdjacentHTML("afterbegin",'<div class="crosshair c1"></div><div class="crosshair c2"></div>'),t._container.insertAdjacentHTML("afterend",`<hr><table class="leaflet-rotate-info"><tr><td><button onclick="L.Rotate._map.setBearing(0);" title="map.setBearing(0);"> 0</button><button onclick="L.Rotate._map.setBearing(15);" title="map.setBearing(15);">15</button><button onclick="L.Rotate._map.setBearing(30);" title="map.setBearing(30);">30</button><button onclick="L.Rotate._map.setBearing(45);" title="map.setBearing(45);">45</button><button onclick="L.Rotate._map.setBearing(60);" title="map.setBearing(60);">60</button><button onclick="L.Rotate._map.setBearing(75);" title="map.setBearing(75);">75</button><button onclick="L.Rotate._map.setBearing(90);" title="map.setBearing(90);">90</button><button onclick="L.Rotate._map.setBearing(180);" title="map.setBearing(180);">180</button></td><td><input title="increase/decrease rotation angle" type="range" min="0" max="360" step="1" value="0" name="rho" id='rho_input' /><span id='rho'></span></td><td><input title="increase/decrease padding bounds" type="range" min="-0.25" max="0" value="0" step="0.01" name="pad" id='pad_input' /></td><td><button onclick="L.Rotate.randomRotateMarkers(L.Rotate._map);" title="markers.setRotation(Math.random() * Math.PI / 30 + 0.1)">Rotate Markers</button><button onclick="L.Rotate.randomMarkers(L.Rotate._map, 100);" title="add 100 random markers to map">Add Markers</button></td></tr><tr><th>LatLng </th><td id='llx'></td><td id='lly'></td><td class='long'>LatLng of mouse pointer</td></tr><tr><th>Rel to pane </th><td id='lyx'></td><td id='lyy'></td><td class='long'>Mouse pointer pixel coords relative to _rotatePane</td></tr><tr><th>Rel to container</th><td id='cnx'></td><td id='cny'></td><td class='long'>Mouse pointer coords relative to map &lt;div&gt;</td></tr><tr><th>Pivot </th><td id='pvx'></td><td id='pvy'></td><td class='long'>Last _rotationPane pivot pixel coords relative to _rotationPane</td></tr><tr><th>Pane offset </th><td id='pox'></td><td id='poy'></td><td class='long'>Pixel offset of _mapPane</td></tr><tr><th>Pixel origin </th><td id='ogx'></td><td id='ogy'></td><td class='long'>Negative pixel coords of the (0,0) CRS point relative to _rotatePane</td></tr><tr><th>Pixel bounds </th><td id='ogbx'></td><td id='ogby'></td><td class='long'>Bounds of the current map view in projected pixel coordinates {min, max}.</td></tr></table>`),t.___rotatePixelOrigin=L.DomUtil.create("div","pixelorigin",t._rotatePane),t.___rotatePivotDot=L.DomUtil.create("div","pivot",t._mapPane),this.___rotateBounds=L.DomUtil.create("div","panebounds rotate",t._rotatePane),this.___mainBounds=L.DomUtil.create("div","panebounds main",t._mapPane),this.updatePaneBounds();var e=document.getElementById("rho_input"),o=document.getElementById("pad_input");e.addEventListener("change",L.Rotate.rotate.bind(this)),e.addEventListener("mousemove",L.Rotate.rotate.bind(this)),o.addEventListener("change",L.Rotate.displayCenter.bind(this)),o.addEventListener("mousemove",L.Rotate.displayCenter.bind(this)),o.addEventListener("change",L.Rotate.displayBounds.bind(this)),o.addEventListener("mousemove",L.Rotate.displayBounds.bind(this)),o.addEventListener("change",L.Rotate.displayLayersBounds.bind(this)),o.addEventListener("mousemove",L.Rotate.displayLayersBounds.bind(this)),o.addEventListener("change",L.Rotate.displayPixelBounds.bind(this)),o.addEventListener("mousemove",L.Rotate.displayPixelBounds.bind(this)),t.on("move rotate zoomend",this.updatePanePos),t.on("resize",this.updatePaneBounds.bind(this)),t.on("rotate zoomend load",this.updatePixelOrigin),t.on("rotate",this.updateRhoInput),t.on("rotate",this.updatePivotDot),t.on("moveend zoomend resetview rotate",this.displayCenter.bind(this)),t.on("moveend zoomend resetview rotate",this.displayBounds.bind(this)),t.on("moveend zoomend resetview rotate",this.displayLayersBounds.bind(this)),t.on("moveend zoomend resetview rotate",this.displayPixelBounds.bind(this)),this.updatePixelOrigin.call(t),this.updateRhoInput.call(t),this.updatePivotDot.call(t),this.displayCenter.call(this),this.displayBounds.call(this),this.displayLayersBounds.call(this),this.displayPixelBounds.call(this),L.Browser.mobile&&t.compassBearing.enable(),t.on("mousemove",this.logMouse),t.on("locationfound",this.resetHeading),n.log&&t.on("click movestart move moveend zoomstart zoomend locationfound locationerror locationfound",this.logEvent)},logEvent:function(t){console.log(t,t.type)},logMouse:function(t){document.getElementById("llx").innerHTML=t.latlng.lng,document.getElementById("lly").innerHTML=t.latlng.lat,document.getElementById("lyx").innerHTML=t.layerPoint.x,document.getElementById("lyy").innerHTML=t.layerPoint.y,document.getElementById("cnx").innerHTML=t.containerPoint.x,document.getElementById("cny").innerHTML=t.containerPoint.y},rotate:function(t){return(t.type!=="mousemove"||t.buttons!==0)&&this._map.setBearing(t.target.valueAsNumber)},updatePaneBounds:function(){const t=this._map.getSize();for(const n of[this.___rotateBounds,this.___mainBounds])n.style.width=t.x+"px",n.style.height=t.y+"px"},updatePixelOrigin:function(){this._pixelOrigin&&(document.getElementById("ogx").innerHTML=this._pixelOrigin.x,document.getElementById("ogy").innerHTML=this._pixelOrigin.y,this.___rotatePixelOrigin.style.left=-this._pixelOrigin.x-1+"px",this.___rotatePixelOrigin.style.top=-this._pixelOrigin.y-1+"px")},updatePivotDot:function(){this._pivot&&(document.getElementById("pvx").innerHTML=this._pivot.x,document.getElementById("pvy").innerHTML=this._pivot.y,this.___rotatePivotDot.style.left=this._pivot.x-1+"px",this.___rotatePivotDot.style.top=this._pivot.y-1+"px")},updatePanePos:function(){var t=this._getMapPanePos();document.getElementById("pox").innerHTML=t.x,document.getElementById("poy").innerHTML=t.y},updateRhoInput:function(){document.getElementById("rho_input").value=this.getBearing()},rotateOneDegree:function(){var t=document.getElementById("rho_input").valueAsNumber++;this.setBearing(t<360?t:0)},randomRotateMarkers:function(t){var n=[],e=[];for(var o in t._layers)t._layers[o]instanceof L.Marker&&(n.push(t._layers[o]),e.push(Math.random()*Math.PI/30+.1));function a(r){for(var i=0;i<n.length;i++)n[i].setRotation(n[i].options.rotation+e[i]);--r&&setTimeout(a.bind(null,r),20)}a(25)},randomMarkers:function(e,n){var e=this._map,o=function(){for(var a=0;a<n;a++){var r=e.getBounds(),i=r.getSouthWest(),s=r.getNorthEast(),d=s.lng-i.lng,l=s.lat-i.lat,c=L.marker(L.latLng(i.lat+l*Math.random(),i.lng+d*Math.random()));L.Rotate.___cluster.addLayer(c)}};this.___cluster?typeof L.markerClusterGroup=="function"&&o():this.___cluster=import("https://unpkg.com/leaflet.markercluster@1.4.1/src/index.js").then(a=>{document.head.insertAdjacentHTML("beforeend",'<link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster@1.4.1/dist/MarkerCluster.css"><link rel="stylesheet" href="https://unpkg.com/leaflet.markercluster@1.4.1/dist/MarkerCluster.Default.css">'),Object.assign(globalThis.L,a),this.___cluster=L.markerClusterGroup({spiderfyOnMaxZoom:!1,showCoverageOnHover:!1,zoomToBoundsOnClick:!1}).on("clusterclick",r=>r.layer.spiderfy()).addTo(e),o()})},displayCenter:function(){var t=this._map;this.___centerMarker&&this.___centerMarker.remove(),this.___centerMarker=L.circleMarker(t.getCenter(),{radius:3,color:"red"}).addTo(t).bindTooltip("<b>center</b>",{direction:"right",permanent:!0}),this.___centerMarker.getTooltip().getElement().setAttribute("tabindex","-1")},displayBounds:function(){var t=this._map;this.___bounds&&this.___bounds.remove();var n=document.getElementById("pad_input").valueAsNumber,e=t.getBounds().pad(n);this.___bounds=L.rectangle(e,{fill:!1,lineCap:"square",color:"yellow",dashArray:"5, 10",weight:2}).addTo(t)},displayLayersBounds:function(){for(const t of["path","polygon","circle"]){if(globalThis[t])this[`___${t}bounds`]&&this[`___${t}bounds`].remove();else continue;let e=globalThis[t].getBounds();this[`___${t}bounds`]=L.featureGroup([L.rectangle(e,{fill:!1,lineCap:"square",color:"yellow",dashArray:"5, 10",weight:1,opacity:.5}),L.marker(e.getNorthWest(),{scale:.5,rotation:-45*L.DomUtil.DEG_TO_RAD,rotateWithView:!0}).bindTooltip("<b>NW</b>",{direction:"center"}),L.marker(e.getNorthEast(),{scale:.5,rotation:45*L.DomUtil.DEG_TO_RAD,rotateWithView:!0}).bindTooltip("<b>NE</b>",{direction:"center"}),L.marker(e.getSouthEast(),{scale:.5,rotation:135*L.DomUtil.DEG_TO_RAD,rotateWithView:!0}).bindTooltip("<b>SE</b>",{direction:"center"}),L.marker(e.getSouthWest(),{scale:.5,rotation:-135*L.DomUtil.DEG_TO_RAD,rotateWithView:!0}).bindTooltip("<b>SW</b>",{direction:"center"})]).addTo(map).eachLayer(o=>{o.getElement&&o.getElement()&&(o.getElement().style.filter="hue-rotate(150deg)")})}},displayPixelBounds:function(){var t=this._map;this.___corners&&this.___corners.remove(),this.___pixelbounds&&this.___pixelbounds.remove();const n=t.getSize(),e=t.getPixelBounds(),o=L.point(0,0),a=L.point(n.x,0),r=L.point(n.x,n.y),i=L.point(0,n.y),s=L.marker(t.containerPointToLatLng(o),{title:"Top Left corner",rotation:-45*L.DomUtil.DEG_TO_RAD}),d=L.marker(t.containerPointToLatLng(a),{title:"Top Right corner",rotation:45*L.DomUtil.DEG_TO_RAD}),l=L.marker(t.containerPointToLatLng(r),{title:"Bottom Right corner",rotation:135*L.DomUtil.DEG_TO_RAD}),c=L.marker(t.containerPointToLatLng(i),{title:"Bottom Left corner",rotation:-135*L.DomUtil.DEG_TO_RAD});this.___corners=L.featureGroup([s,d,l,c]).addTo(t).eachLayer(u=>u.getElement().style.filter="hue-rotate(150deg)"),document.getElementById("ogbx").innerHTML=JSON.stringify(e.min),document.getElementById("ogby").innerHTML=JSON.stringify(e.max)},resetHeading:function(t){t.heading!==null&&this.setBearing(t.heading)},locate:function(){this._map.locate({setView:!0,enableHighAccuracy:!0})}};
@@ -0,0 +1,96 @@
1
+ if (!L.HeatLayer && !globalThis.HeatmapOverlay) {
2
+ throw new Error('Load Leaflet.heat or heatmap.js Leaflet plugin before this adapter.');
3
+ }
4
+
5
+ if (L.HeatLayer) {
6
+ const heatLayerOnAdd = L.HeatLayer.prototype.onAdd;
7
+ const heatLayerOnRemove = L.HeatLayer.prototype.onRemove;
8
+ const heatLayerReset = L.HeatLayer.prototype._reset;
9
+ const heatLayerAnimateZoom = L.HeatLayer.prototype._animateZoom;
10
+
11
+ L.HeatLayer.include({
12
+ onAdd: function(map) {
13
+ heatLayerOnAdd.apply(this, arguments);
14
+ if (map._rotate) {
15
+ map.getPane('norotatePane').appendChild(this._canvas);
16
+ this._reset();
17
+ map.on('rotate', this._reset, this);
18
+ }
19
+ },
20
+
21
+ onRemove: function(map) {
22
+ if (map._rotate) {
23
+ map.off('rotate', this._reset, this);
24
+ map.getPane('overlayPane').appendChild(this._canvas);
25
+ }
26
+ return heatLayerOnRemove.apply(this, arguments);
27
+ },
28
+
29
+ _reset: function() {
30
+ if (!this._map._rotate) {
31
+ return heatLayerReset.apply(this, arguments);
32
+ }
33
+
34
+ const map = this._map;
35
+ const size = map.getSize();
36
+ L.DomUtil.setPosition(this._canvas, map._getMapPanePos().multiplyBy(-1));
37
+
38
+ if (this._heat._width !== size.x) {
39
+ this._canvas.width = this._heat._width = size.x;
40
+ }
41
+ if (this._heat._height !== size.y) {
42
+ this._canvas.height = this._heat._height = size.y;
43
+ }
44
+ this._redraw();
45
+ },
46
+
47
+ _animateZoom: function(event) {
48
+ if (!this._map._rotate) {
49
+ return heatLayerAnimateZoom.apply(this, arguments);
50
+ }
51
+
52
+ const map = this._map;
53
+ const scale = map.getZoomScale(event.zoom);
54
+ const offset = map._getCenterOffset(event.center)
55
+ .rotate(map._bearing)
56
+ .multiplyBy(-scale)
57
+ .subtract(map._getMapPanePos());
58
+ L.DomUtil.setTransform(this._canvas, offset, scale);
59
+ },
60
+ });
61
+ }
62
+
63
+ if (globalThis.HeatmapOverlay) {
64
+ const heatmapOnAdd = globalThis.HeatmapOverlay.prototype.onAdd;
65
+ const heatmapOnRemove = globalThis.HeatmapOverlay.prototype.onRemove;
66
+ const heatmapDraw = globalThis.HeatmapOverlay.prototype._draw;
67
+
68
+ globalThis.HeatmapOverlay.include({
69
+ onAdd: function(map) {
70
+ heatmapOnAdd.apply(this, arguments);
71
+ if (map._rotate) {
72
+ map.getPane('norotatePane').appendChild(this._el);
73
+ this._draw();
74
+ map.on('rotate', this._draw, this);
75
+ }
76
+ },
77
+
78
+ onRemove: function(map) {
79
+ if (map._rotate) {
80
+ map.off('rotate', this._draw, this);
81
+ map.getPane('overlayPane').appendChild(this._el);
82
+ }
83
+ return heatmapOnRemove.apply(this, arguments);
84
+ },
85
+
86
+ _draw: function() {
87
+ const map = this._map;
88
+ if (!map._rotate) {
89
+ return heatmapDraw.apply(this, arguments);
90
+ }
91
+
92
+ L.DomUtil.setPosition(this._el, map._getMapPanePos().multiplyBy(-1));
93
+ this._update();
94
+ },
95
+ });
96
+ }
package/package.json CHANGED
@@ -1,17 +1,15 @@
1
1
  {
2
2
  "name": "leaflet-rotate",
3
- "version": "0.2.8",
3
+ "version": "0.3.0",
4
4
  "description": "A Leaflet plugin that allows to add rotation functionality to map tiles",
5
5
  "type": "module",
6
6
  "main": "dist/leaflet-rotate.js",
7
7
  "browser": "dist/leaflet-rotate.js",
8
8
  "module": "src/index.js",
9
9
  "scripts": {
10
- "test": "uvu . \"(examples|src)[\\\\/].*\\.spec\\.js$\"",
11
- "build": "rollup -c build/rollup.config.js",
12
- "watch": "rollup -c build/rollup.config.js --watch",
13
- "dev": "npm-run-all --print-label --parallel serve watch",
14
- "serve": "http-server",
10
+ "dev": "node build/dev.js",
11
+ "build": "node build/build.js",
12
+ "test": "npm run build && cucumber-js",
15
13
  "version": "npm run build && git add -A dist"
16
14
  },
17
15
  "repository": {
@@ -49,14 +47,9 @@
49
47
  },
50
48
  "homepage": "https://github.com/Raruto/leaflet-rotate#readme",
51
49
  "devDependencies": {
52
- "rollup": "^3.20.6",
53
- "@rollup/plugin-commonjs": "^24.1.0",
54
- "@rollup/plugin-node-resolve": "^15.0.2",
55
- "@rollup/plugin-terser": "^0.4.1",
56
- "http-server": "^14.1.1",
57
- "npm-run-all": "^4.1.5",
58
- "playwright": "^1.32.3",
59
- "uvu": "^0.5.6"
50
+ "@cucumber/cucumber": "^10.3.1",
51
+ "esbuild": "^0.28.2",
52
+ "playwright": "^1.64.1"
60
53
  },
61
54
  "peerDependencies": {
62
55
  "leaflet": "^1.9.3"
@@ -106,7 +106,11 @@ L.Control.Rotate = L.Control.extend({
106
106
  // Compass mode
107
107
  else if (!map.compassBearing.enabled()) {
108
108
  map.touchRotate.disable();
109
- map.compassBearing.enable();
109
+ (
110
+ DeviceOrientationEvent && DeviceOrientationEvent.requestPermission
111
+ ? DeviceOrientationEvent.requestPermission() // iOS compass
112
+ : Promise.resolve('granted') // others
113
+ ).then(state => "granted" === state && map.compassBearing.enable())
110
114
  }
111
115
 
112
116
  // Locked mode
@@ -25,10 +25,22 @@ L.DivOverlay.include({
25
25
  _updatePosition: function() {
26
26
  if (!this._map) { return; }
27
27
  divOverlayProto._updatePosition.apply(this, arguments);
28
- if (this._map && this._map._rotate && this._zoomAnimated) {
28
+ if (this._map._rotate && this._zoomAnimated) {
29
29
  var anchor = this._getAnchor();
30
30
  var pos = L.DomUtil.getPosition(this._container).subtract(anchor);
31
31
  L.DomUtil.setPosition(this._container, this._map.rotatedPointToMapPanePoint(pos).add(anchor));
32
+ } else if (this._map._rotate) {
33
+ var mapPanePoint = this._map.rotatedPointToMapPanePoint(this._map.latLngToLayerPoint(this._latlng)),
34
+ offset = L.point(this.options.offset),
35
+ anchor = this._getAnchor();
36
+
37
+ this._containerLeft = -Math.round(this._containerWidth / 2) + mapPanePoint.x + offset.x + anchor.x;
38
+ this._containerBottom = -(mapPanePoint.y + offset.y + anchor.y);
39
+ this._container.style.left = this._containerLeft + 'px';
40
+ this._container.style.bottom = this._containerBottom + 'px';
41
+ this._container.style.top = '';
42
+ L.DomUtil.setPosition(this._container, new L.Point(0, 0));
43
+ this._container.style.transform = '';
32
44
  }
33
45
 
34
46
  },
@@ -25,6 +25,13 @@ L.Marker.mergeOptions({
25
25
  */
26
26
  rotateWithView: false,
27
27
 
28
+ /**
29
+ * Flip a rotated marker when needed to keep it upright
30
+ *
31
+ * @type {Boolean}
32
+ */
33
+ keepUpright: false,
34
+
28
35
  /**
29
36
  * Scale of the marker icon
30
37
  *
@@ -128,6 +135,13 @@ L.Marker.include({
128
135
  if (this.options.rotateWithView) {
129
136
  bearing += this._map._bearing;
130
137
  }
138
+ if (this.options.keepUpright) {
139
+ bearing = ((bearing % (2 * Math.PI)) + 2 * Math.PI) % (2 * Math.PI);
140
+ if (bearing > Math.PI / 2 && bearing < 3 * Math.PI / 2) {
141
+ bearing += Math.PI;
142
+ }
143
+ bearing %= 2 * Math.PI;
144
+ }
131
145
 
132
146
  /** @TODO use markerProto._setPos */
133
147
  if (this._icon) {
@@ -54,6 +54,10 @@ L.Renderer.include({
54
54
  if (!this._map._rotate) {
55
55
  return rendererProto._updateTransform.apply(this, arguments);
56
56
  }
57
+ if (zoom === this._zoom) {
58
+ // _reset() has already repositioned the renderer at the current zoom.
59
+ return;
60
+ }
57
61
  /**
58
62
  * @FIXME see path._renderer._reset();
59
63
  */
package/src/map/Map.js CHANGED
@@ -10,6 +10,16 @@ L.Map.mergeOptions({ rotate: false, bearing: 0, });
10
10
 
11
11
  L.Map.include({
12
12
 
13
+ _animateZoomNoDelay: function(center, zoom) {
14
+ if (!this._mapPane) { return; }
15
+
16
+ this._animatingZoom = true;
17
+ this._animateToCenter = center;
18
+ this._animateToZoom = zoom;
19
+ this._move(center, zoom, undefined, true);
20
+ this._onZoomTransitionEnd();
21
+ },
22
+
13
23
  /**
14
24
  * @param {(HTMLElement|String)} id html selector
15
25
  * @param {Object} [options={}] leaflet map options
@@ -193,12 +203,62 @@ L.Map.include({
193
203
  * Change map rotation
194
204
  *
195
205
  * @param {number} theta map degrees
206
+ * @param {Object} [options] animation options
207
+ * @param {boolean} [options.animate=false]
208
+ * @param {number} [options.duration=0.25] animation duration in seconds
209
+ * @param {number} [options.easeLinearity=0.5]
196
210
  *
197
211
  * @since leaflet-rotate (v0.1)
198
212
  */
199
- setBearing: function(theta) {
213
+ setBearing: function(theta, options) {
200
214
  if (!L.Browser.any3d || !this._rotate) { return; }
201
215
 
216
+ if (options && options.animate) {
217
+ this._bearingAnimationId = (this._bearingAnimationId || 0) + 1;
218
+ var animationId = this._bearingAnimationId;
219
+ if (this._bearingAnimationFrame) {
220
+ L.Util.cancelAnimFrame(this._bearingAnimationFrame);
221
+ }
222
+
223
+ var startBearing = this.getBearing(),
224
+ endBearing = L.Util.wrapNum(theta, [0, 360]),
225
+ delta = ((endBearing - startBearing + 540) % 360) - 180,
226
+ duration = Math.max(0, options.duration == null ? 0.25 : options.duration) * 1000,
227
+ easeLinearity = Math.max(options.easeLinearity || 0.5, 0.2),
228
+ startTime = performance.now();
229
+
230
+ var animate = function() {
231
+ if (!this._mapPane) {
232
+ delete this._bearingAnimationFrame;
233
+ return;
234
+ }
235
+
236
+ var progress = duration ? Math.min((performance.now() - startTime) / duration, 1) : 1,
237
+ eased = 1 - Math.pow(1 - progress, 1 / easeLinearity);
238
+
239
+ this._setBearing(progress === 1 ? endBearing : startBearing + delta * eased);
240
+ if (this._bearingAnimationId !== animationId) { return; }
241
+ if (progress < 1) {
242
+ this._bearingAnimationFrame = L.Util.requestAnimFrame(animate, this);
243
+ } else {
244
+ delete this._bearingAnimationFrame;
245
+ }
246
+ };
247
+
248
+ this._bearingAnimationFrame = L.Util.requestAnimFrame(animate, this);
249
+ return;
250
+ }
251
+
252
+ this._bearingAnimationId = (this._bearingAnimationId || 0) + 1;
253
+ if (this._bearingAnimationFrame) {
254
+ L.Util.cancelAnimFrame(this._bearingAnimationFrame);
255
+ delete this._bearingAnimationFrame;
256
+ }
257
+
258
+ this._setBearing(theta);
259
+ },
260
+
261
+ _setBearing: function(theta) {
202
262
  var bearing = L.Util.wrapNum(theta, [0, 360]) * L.DomUtil.DEG_TO_RAD,
203
263
  center = this._getPixelCenter(),
204
264
  oldPos = this._getRotatePanePos().rotateFrom(-this._bearing, center),
@@ -322,9 +382,8 @@ L.Map.include({
322
382
  // pixelPoint = this.project(latlng),
323
383
  // pixelBounds = this.getPixelBounds(),
324
384
  // pixelCenter = this.project(this.getCenter()),
325
- rect = this._container.getBoundingClientRect(),
326
385
  pixelPoint = this.latLngToContainerPoint(latlng),
327
- pixelBounds = L.bounds([ L.point(rect), L.point(rect).add(this.getSize()) ]),
386
+ pixelBounds = L.bounds([L.point(0, 0), this.getSize()]),
328
387
  pixelCenter = pixelBounds.getCenter(),
329
388
  //
330
389
  paddedBounds = L.bounds([pixelBounds.min.add(paddingTL), pixelBounds.max.subtract(paddingBR)]),
@@ -52,11 +52,13 @@ L.Map.CompassBearing = L.Handler.extend({
52
52
  * @see https://developer.mozilla.org/en-US/docs/Web/API/Window/orientation
53
53
  */
54
54
  _onDeviceOrientation: function(e) {
55
- var angle = e.webkitCompassHeading || e.alpha;
55
+ var angle = e.webkitCompassHeading != null ? e.webkitCompassHeading : e.alpha;
56
56
  var deviceOrientation = 0;
57
57
 
58
- // Safari iOS
59
- if (!e.absolute && e.webkitCompassHeading) {
58
+ if (!Number.isFinite(angle)) { return; }
59
+
60
+ // Safari iOS
61
+ if (!e.absolute && e.webkitCompassHeading) {
60
62
  angle = 360 - angle;
61
63
  }
62
64
 
@@ -140,6 +140,14 @@ L.Map.TouchGestures = L.Handler.extend({
140
140
  var moveFn = map._move.bind(map, this._center, this._zoom, { pinch: true, round: false }, undefined);
141
141
  this._animRequest = L.Util.requestAnimFrame(moveFn, this, true);
142
142
 
143
+ if (this.zoom) {
144
+ if (map.options.zoomAnimation) {
145
+ map._animateZoomNoDelay(this._center, map._limitZoom(this._zoom));
146
+ } else {
147
+ map._resetView(this._center, map._limitZoom(this._zoom));
148
+ }
149
+ }
150
+
143
151
  L.DomEvent.preventDefault(e);
144
152
  },
145
153
 
@@ -0,0 +1,87 @@
1
+ Feature: Rotating a Leaflet map
2
+ Background:
3
+ Given the example page is open
4
+
5
+ @issue-55 @issue-58
6
+ Scenario: Leaflet heat redraws stay aligned after map rotation
7
+ When I redraw a heat layer after rotating, panning, and zooming
8
+ Then the heat points remain aligned with their coordinates
9
+
10
+ @issue-11
11
+ Scenario: The default rotate control appears only when the map is rotated
12
+ When I rotate a map with the default rotate control
13
+ Then the default rotate control visibility follows the bearing
14
+
15
+ @issue-13
16
+ Scenario: Dragging the rotate control changes the map bearing
17
+ When I drag the rotate control
18
+ Then the map bearing follows the control drag
19
+
20
+ @issue-44
21
+ Scenario: The compass control requests device-orientation permission
22
+ When I activate compass mode on a permission-based device
23
+ Then device-orientation permission is requested
24
+
25
+ @issue-7
26
+ Scenario: Compass headings handle absolute, iOS, and unavailable readings
27
+ When I apply compass heading readings
28
+ Then valid compass readings update bearing and invalid readings are ignored
29
+
30
+ @debugger
31
+ Scenario: Debugger rotation presets and keyboard slider stay synchronized
32
+ When I use the debugger rotation controls
33
+ Then every debugger rotation control matches the map bearing
34
+
35
+ @debugger
36
+ Scenario: Debugger bounds padding updates from the keyboard
37
+ When I change the debugger bounds padding from the keyboard
38
+ Then the displayed bounds use the selected padding
39
+
40
+ @debugger
41
+ Scenario: Debugger geometry follows an offset and resized map
42
+ When I inspect debugger geometry after resizing and moving the map
43
+ Then the debugger reports the current map geometry
44
+
45
+ @example-ui
46
+ Scenario: Example base-layer radios select one layer
47
+ When I switch between the example base layers
48
+ Then exactly the selected base layer is visible
49
+
50
+ @example-ui
51
+ Scenario: Example zoom controls change and restore the zoom
52
+ When I use the example zoom controls
53
+ Then the zoom returns to its starting level
54
+
55
+ @example-ui
56
+ Scenario: Debugger rotates all example markers
57
+ When I rotate the example markers from the debugger
58
+ Then every example marker has a new rotation
59
+
60
+ @example-ui
61
+ Scenario Outline: Example destination buttons fly to the selected place
62
+ When I select "<place>" from the example page
63
+ Then the map settles at "<place>" at zoom 10
64
+
65
+ Examples:
66
+ | place |
67
+ | Kyiv |
68
+ | London |
69
+ | San Francisco |
70
+ | Washington |
71
+ | Trondheim |
72
+
73
+ @example-ui
74
+ Scenario Outline: Example bounds buttons fit the selected layer
75
+ When I fit the "<layer>" bounds from the example page
76
+ Then the map view contains the "<layer>" bounds
77
+
78
+ Examples:
79
+ | layer |
80
+ | circle |
81
+ | polygon |
82
+ | path |
83
+
84
+ @example-ui
85
+ Scenario: Rectangle button resets zoom and fits the rectangle
86
+ When I fit the rectangle from the example page
87
+ Then the rectangle is fitted at zoom 14
@@ -0,0 +1,33 @@
1
+ Feature: Rotating a Leaflet map
2
+ Background:
3
+ Given the example page is open
4
+
5
+ @issue-43
6
+ Scenario: Pinch zoom refreshes paths during the gesture
7
+ When I move a two-finger pinch gesture
8
+ Then the zoom transition ends before the gesture ends
9
+
10
+ @issue-43
11
+ Scenario: Vector paths follow a rotated two-finger gesture
12
+ When I rotate and zoom two fingers over a vector path
13
+ Then the vector path remains aligned during the gesture
14
+
15
+ @issue-56
16
+ Scenario: Dragging a max-bounds marker preserves callback coordinates on a rotated map
17
+ When I drag a constrained marker on a rotated map
18
+ Then its drag callbacks match the marker and pointer coordinates
19
+
20
+ @issue-60
21
+ Scenario: Interactive layers stay aligned while dragging and rotating the map
22
+ When I drag a map with interactive layers while rotated
23
+ Then the marker and vector path stay at their geographic coordinates
24
+
25
+ @issue-59 @issue-52
26
+ Scenario: Focusing an in-bounds point does not pan an offset rotated map
27
+ When I focus a point near the left edge of an offset map
28
+ Then the map center remains unchanged
29
+
30
+ @issue-65
31
+ Scenario: Two-finger rotation can be disabled
32
+ When I disable touch rotation and rotate two fingers
33
+ Then the map bearing remains unchanged
@@ -0,0 +1,58 @@
1
+ Feature: Rotating a Leaflet map
2
+ Background:
3
+ Given the example page is open
4
+
5
+ @issue-4 @issue-35 @issue-62
6
+ Scenario: Rotated road labels stay upright and aligned with the map
7
+ When I rotate a label marker with the view
8
+ Then the label remains upright while following the map bearing
9
+
10
+ @issue-2 @issue-54
11
+ Scenario: GeoJSON in a custom pane rotates with the map
12
+ When I add GeoJSON to a custom pane inside the rotate pane
13
+ Then the custom-pane GeoJSON stays aligned with its coordinates
14
+
15
+ @issue-32
16
+ Scenario: Canvas overlays rotate with the map
17
+ When I render a Canvas vector layer on a rotated map
18
+ Then the Canvas renderer belongs to the rotating pane
19
+
20
+ @issue-57
21
+ Scenario: SVG paths stay aligned near the latitude limit without zoom animation
22
+ When I zoom an SVG path near the latitude limit
23
+ Then the path stays at its geographic coordinate
24
+
25
+ @issue-15 @issue-51
26
+ Scenario: Popup tip remains anchored when zoom animation is disabled
27
+ When I open a popup on a rotated map without zoom animation
28
+ Then the popup tip stays at the marker coordinate
29
+
30
+ @issue-23
31
+ Scenario: Removing the rotate control detaches its map listener
32
+ When I remove the rotate control and change the map bearing
33
+ Then the map rotates without invoking the removed control
34
+
35
+ @issue-3
36
+ Scenario: Markers remain non-draggable unless requested
37
+ When I create default and draggable markers
38
+ Then only the requested marker is draggable
39
+
40
+ @issue-46
41
+ Scenario: Click coordinates match the rotated map location
42
+ When I click a rotated map at a known container point
43
+ Then the click event reports the matching geographic coordinate
44
+
45
+ @issue-18 @issue-59
46
+ Scenario: Focusing an in-bounds marker does not pan the rotated map
47
+ When I focus a marker near the edge of a rotated map
48
+ Then the map center remains unchanged
49
+
50
+ @issue-51
51
+ Scenario: Opening an edge popup pans only enough to keep it visible
52
+ When I open a popup near the edge of a rotated map
53
+ Then the popup stays inside the map without a large jump
54
+
55
+ @issue-52
56
+ Scenario: Opening a popup in a second rotated map does not move it
57
+ When I open a popup in a second rotated map with map listeners
58
+ Then the second map center remains unchanged and its popup stays anchored
@@ -0,0 +1,36 @@
1
+ Feature: Rotating a Leaflet map
2
+ Background:
3
+ Given the example page is open
4
+
5
+ @issue-43
6
+ Scenario: Rotation is enabled
7
+ Then rotation is enabled on the map
8
+
9
+ @issue-19
10
+ Scenario: Bearing survives zoom
11
+ When I set the bearing to 10 and zoom in twice
12
+ Then the bearing remains 10
13
+
14
+ @issue-19
15
+ Scenario: Bearing survives setting the view
16
+ When I set the bearing to 10 and change the view twice
17
+ Then the bearing remains 10
18
+
19
+ @issue-63
20
+ Scenario: Bearing can animate
21
+ When I animate the bearing to 90 degrees
22
+ Then the bearing reaches 90 degrees gradually
23
+
24
+ @issue-25
25
+ Scenario: Fit bounds zoom is stable
26
+ When I fit the same bounds twice
27
+ Then the map zoom is 14 both times
28
+
29
+ @issue-28
30
+ Scenario: Panning inside a marker keeps its bounds stable
31
+ When I pan inside the Trondheim marker
32
+ Then panning inside keeps the expected bounds
33
+
34
+ @issue-34
35
+ Scenario: Draggable marker listeners are not duplicated
36
+ Then the draggable marker has one drag listener and one drag-end listener