UNPKG

leaflet-rotate

Version:

A Leaflet plugin that allows to add rotation functionality to map tiles

1 lines 14.5 kB
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})}};