leaflet-rotate 0.2.7 → 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/.github/workflows/ci.yml +21 -0
- package/README.md +5 -1
- package/build/build.js +69 -0
- package/build/dev.js +24 -0
- package/build/server.js +104 -0
- package/cucumber.mjs +5 -0
- package/dist/leaflet-rotate-src.js +1380 -1853
- package/dist/leaflet-rotate-src.js.map +7 -1
- package/dist/leaflet-rotate.js +1 -1
- package/dist/leaflet-rotate.js.map +7 -1
- package/lib/debug.js +19 -8
- package/lib/debug.min.js +1 -1
- package/misc/leaflet-heat-rotate.js +96 -0
- package/package.json +7 -14
- package/src/control/Rotate.js +5 -1
- package/src/layer/DivOverlay.js +13 -1
- package/src/layer/marker/Marker.js +16 -2
- package/src/layer/vector/Renderer.js +4 -0
- package/src/map/Map.js +62 -3
- package/src/map/handler/CompassBearing.js +5 -3
- package/src/map/handler/TouchGestures.js +8 -0
- package/test/features/example.feature +87 -0
- package/test/features/gestures.feature +33 -0
- package/test/features/overlays.feature +58 -0
- package/test/features/rotation.feature +36 -0
- package/test/steps/bearing.steps.js +235 -0
- package/test/steps/example.steps.js +363 -0
- package/test/steps/gestures.steps.js +145 -0
- package/test/steps/overlays.steps.js +427 -0
- package/test/support/hooks.js +44 -0
- package/build/rollup.config.js +0 -34
- package/test/setup/http_server.js +0 -86
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 <div></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 <div></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.
|
|
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
|
-
"
|
|
11
|
-
"build": "
|
|
12
|
-
"
|
|
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
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
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"
|
package/src/control/Rotate.js
CHANGED
|
@@ -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
|
-
|
|
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
|
package/src/layer/DivOverlay.js
CHANGED
|
@@ -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
|
|
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
|
*
|
|
@@ -105,12 +112,12 @@ L.Marker.include({
|
|
|
105
112
|
if (this.dragging && this.dragging.enabled() && this._map && this._map._rotate) {
|
|
106
113
|
// L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable
|
|
107
114
|
markerDragProto = markerDragProto || Object.getPrototypeOf(this.dragging);
|
|
115
|
+
this.dragging.disable();
|
|
108
116
|
Object.assign(this.dragging, {
|
|
109
117
|
// _onDragStart: MarkerDrag._onDragStart.bind(this.dragging),
|
|
110
118
|
_onDrag: MarkerDrag._onDrag.bind(this.dragging),
|
|
111
119
|
_onDragEnd: MarkerDrag._onDragEnd.bind(this.dragging),
|
|
112
|
-
})
|
|
113
|
-
this.dragging.disable();
|
|
120
|
+
});
|
|
114
121
|
this.dragging.enable();
|
|
115
122
|
}
|
|
116
123
|
return ret;
|
|
@@ -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([
|
|
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
|
-
|
|
55
|
+
var angle = e.webkitCompassHeading != null ? e.webkitCompassHeading : e.alpha;
|
|
56
56
|
var deviceOrientation = 0;
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
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
|