leaflet-rotate 0.1.4 → 0.2.1

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.
@@ -1,2 +1,1761 @@
1
- !function(t){"function"==typeof define&&define.amd?define(t):t()}((function(){"use strict";const t=L.extend({},L.DomUtil);L.extend(L.DomUtil,{setTransform:function(o,e,i,n,a){var s=e||new L.Point(0,0);if(!n)return e=s._round(),t.setTransform.call(this,o,e,i);s=s.rotateFrom(n,a),o.style[L.DomUtil.TRANSFORM]="translate3d("+s.x+"px,"+s.y+"px,0)"+(i?" scale("+i+")":"")+" rotate("+n+"rad)"},setPosition:function(o,e,i,n){if(!i)return t.setPosition.call(this,o,e);o._leaflet_pos=e,L.Browser.any3d?L.DomUtil.setTransform(o,e,void 0,i,n):(o.style.left=e.x+"px",o.style.top=e.y+"px")},DEG_TO_RAD:Math.PI/180,RAD_TO_DEG:180/Math.PI}),L.Draggable.include({updateMapBearing:function(t){this._mapBearing=t}}),L.extend(L.Point.prototype,{rotate:function(t){if(!t)return this;var o=Math.sin(t),e=Math.cos(t);return new L.Point(this.x*e-this.y*o,this.x*o+this.y*e)},rotateFrom:function(t,o){if(!t)return this;var e=Math.sin(t),i=Math.cos(t),n=o.x,a=o.y,s=this.x-n,r=this.y-a;return new L.Point(s*i-r*e+n,s*e+r*i+a)}});const o=L.extend({},L.DivOverlay.prototype);L.DivOverlay.include({getEvents:function(){return L.extend(o.getEvents.call(this),{rotate:this._updatePosition})},_updatePosition:function(){if(this._map){var t=this._map.latLngToLayerPoint(this._latlng),o=L.point(this.options.offset),e=this._getAnchor();this._zoomAnimated?(this._map._rotate&&(t=this._map.rotatedPointToMapPanePoint(t)),L.DomUtil.setPosition(this._container,t.add(e))):o=o.add(t).add(e);var i=this._containerBottom=-o.y,n=this._containerLeft=-Math.round(this._containerWidth/2)+o.x;this._container.style.bottom=i+"px",this._container.style.left=n+"px"}}});const e=L.extend({},L.Popup.prototype);L.Popup.include({_animateZoom:function(t){this._map._rotate||e._animateZoom.call(this,t);var o=this._map._latLngToNewLayerPoint(this._latlng,t.zoom,t.center),i=this._getAnchor();o=this._map.rotatedPointToMapPanePoint(o),L.DomUtil.setPosition(this._container,o.add(i))},_adjustPan:function(){if(!(!this.options.autoPan||this._map._panAnim&&this._map._panAnim._inProgress)){var t=this._map,o=parseInt(L.DomUtil.getStyle(this._container,"marginBottom"),10)||0,e=this._container.offsetHeight+o,i=this._containerWidth,n=new L.Point(this._containerLeft,-e-this._containerBottom);n._add(L.DomUtil.getPosition(this._container));var a=n._add(this._map._getMapPanePos()),s=L.point(this.options.autoPanPadding),r=L.point(this.options.autoPanPaddingTopLeft||s),h=L.point(this.options.autoPanPaddingBottomRight||s),_=t.getSize(),l=0,c=0;a.x+i+h.x>_.x&&(l=a.x+i-_.x+h.x),a.x-l-r.x<0&&(l=a.x-r.x),a.y+e+h.y>_.y&&(c=a.y+e-_.y+h.y),a.y-c-r.y<0&&(c=a.y-r.y),(l||c)&&t.fire("autopanstart").panBy([l,c])}}});const i=L.extend({},L.Tooltip.prototype);L.Tooltip.include({_updatePosition:function(){if(!this._map._rotate)return i._updatePosition.call(this);var t=this._map.latLngToLayerPoint(this._latlng);t=this._map.rotatedPointToMapPanePoint(t),this._setPosition(t)},_animateZoom:function(t){if(!this._map._rotate)return i._animateZoom.call(this,t);var o=this._map._latLngToNewLayerPoint(this._latlng,t.zoom,t.center);o=this._map.rotatedPointToMapPanePoint(o),this._setPosition(o)}});L.extend({},L.Icon.prototype);var n;L.Icon.include({_setIconStyles:function(t,o){var e=this.options,i=e[o+"Size"];"number"==typeof i&&(i=[i,i]);var n=L.point(i),a=L.point("shadow"===o&&e.shadowAnchor||e.iconAnchor||n&&n.divideBy(2,!0));t.className="leaflet-marker-"+o+" "+(e.className||""),a&&(t.style.marginLeft=-a.x+"px",t.style.marginTop=-a.y+"px",t.style[L.DomUtil.TRANSFORM+"Origin"]=a.x+"px "+a.y+"px 0px"),n&&(t.style.width=n.x+"px",t.style.height=n.y+"px")}});var a={_onDragStart:function(){if(!this._marker._map._rotate)return n._onDragStart.call(this);this._draggable.updateMapBearing(this._marker._map._bearing)},_onDrag:function(t){var o=this._marker,e=o.options.rotation||o.options.rotateWithView,i=o._shadow,n=L.DomUtil.getPosition(o._icon);!e&&i&&L.DomUtil.setPosition(i,n),o._map._rotate&&(n=o._map.mapPanePointToRotatedPoint(n));var a=o._map.layerPointToLatLng(n);o._latlng=a,t.latlng=a,t.oldLatLng=this._oldLatLng,e?o.setLatLng(a):o.fire("move",t),o.fire("drag",t)},_onDragEnd:function(t){this._marker._map._rotate&&this._marker.update(),n._onDragEnd.call(this,t)}};const s=L.extend({},L.Marker.prototype);L.Marker.mergeOptions({rotation:0,rotateWithView:!1}),L.Marker.include({getEvents:function(){return L.extend(s.getEvents.call(this),{rotate:this.update})},onAdd:function(t){s.onAdd.call(this,t),t.on("rotate",this.update,this)},_initInteraction:function(){var t=s._initInteraction.call(this);return this.dragging&&this.dragging.enabled()&&this._map&&this._map._rotate&&(n=n||Object.getPrototypeOf(this.dragging),this.dragging._onDragStart=a._onDragStart.bind(this.dragging),this.dragging._onDrag=a._onDrag.bind(this.dragging),this.dragging._onDragEnd=a._onDragEnd.bind(this.dragging),this.dragging.disable(),this.dragging.enable()),t},_setPos:function(t){this._map._rotate&&(t=this._map.rotatedPointToMapPanePoint(t));var o=this.options.rotation||0;this.options.rotateWithView&&(o+=this._map._bearing),L.DomUtil.setPosition(this._icon,t,o,t),this._shadow&&L.DomUtil.setPosition(this._shadow,t,o,t),this._zIndex=t.y+this.options.zIndexOffset,this._resetZIndex()},_updateZIndex:function(t){if(!this._map._rotate)return s._updateZIndex.call(this,t);this._icon.style.zIndex=Math.round(this._zIndex+t)},setRotation:function(t){this.options.rotation=t,this.update()}});const r=L.extend({},L.GridLayer.prototype);L.GridLayer.include({getEvents:function(){var t=r.getEvents.call(this);return this._map._rotate&&!this.options.updateWhenIdle&&(this._onRotate||(this._onRotate=L.Util.throttle(this._onMoveEnd,this.options.updateInterval,this)),t.rotate=this._onRotate),t},_getTiledPixelBounds:function(t){if(!this._map._rotate)return r._getTiledPixelBounds.call(this,t);var o=this._map,e=o._animatingZoom?Math.max(o._animateToZoom,o.getZoom()):o.getZoom(),i=o.getZoomScale(e,this._tileZoom),n=o.project(t,this._tileZoom).floor(),a=o.getSize(),s=new L.Bounds([o.containerPointToLayerPoint([0,0]).floor(),o.containerPointToLayerPoint([a.x,0]).floor(),o.containerPointToLayerPoint([0,a.y]).floor(),o.containerPointToLayerPoint([a.x,a.y]).floor()]).getSize().divideBy(2*i);return new L.Bounds(n.subtract(s),n.add(s))}});const h=L.extend({},L.Canvas.prototype);L.Canvas.include({onAdd:function(){h.onAdd.call(this),this._map.on("rotate",this._redraw,this)},onRemove:function(){h.onRemove.call(this),this._map.off("rotate",this._redraw,this)},_update:function(){h._update.call(this),this.fire("update")}});const _=L.extend({},L.Renderer.prototype);L.Renderer.include({onAdd:function(){_.onAdd.call(this)},onRemove:function(){_.onRemove.call(this)},_updateTransform:function(t,o){if(!this._map._rotate)return _._updateTransform.call(this,t,o);var e=this._map.getZoomScale(o,this._zoom),i=this._map._latLngToNewLayerPoint(this._topLeft,o,t);L.Browser.any3d?L.DomUtil.setTransform(this._container,i,e):L.DomUtil.setPosition(this._container,i)},_update:function(){if(!this._map._rotate)return _._update.call(this);var t=this.options.padding,o=this._map,e=this._map.getSize(),i=e.multiplyBy(-t),n=e.multiplyBy(1+t),a=new L.Bounds([o.containerPointToLayerPoint([i.x,i.y]).floor(),o.containerPointToLayerPoint([i.x,n.y]).floor(),o.containerPointToLayerPoint([n.x,i.y]).floor(),o.containerPointToLayerPoint([n.x,n.y]).floor()]);this._bounds=a,this._topLeft=this._map.layerPointToLatLng(a.min),this._center=this._map.getCenter(),this._zoom=this._map.getZoom()}});const l=L.extend({},L.SVG.prototype);L.SVG.include({_update:function(){l._update.call(this),this._map._rotate&&this.fire("update")}});const c=L.extend({},L.Map.prototype);L.Map.mergeOptions({rotate:!1,bearing:0}),L.Map.include({initialize:function(t,o){o.rotate&&(this._rotate=!0,this._bearing=0),c.initialize.call(this,t,o),this.options.rotate&&this.setBearing(this.options.bearing)},containerPointToLayerPoint:function(t){return this._rotate?L.point(t).subtract(this._getMapPanePos()).rotateFrom(-this._bearing,this._getRotatePanePos()).subtract(this._getRotatePanePos()):c.containerPointToLayerPoint.call(this,t)},getBounds:function(){if(!this._rotate)return c.getBounds.call(this);var t=this.getSize(),o=this.layerPointToLatLng(this.containerPointToLayerPoint([0,0])),e=this.layerPointToLatLng(this.containerPointToLayerPoint([t.x,0])),i=this.layerPointToLatLng(this.containerPointToLayerPoint([t.x,t.y])),n=this.layerPointToLatLng(this.containerPointToLayerPoint([0,t.y]));return new L.LatLngBounds([o,e,i,n])},layerPointToContainerPoint:function(t){return this._rotate?L.point(t).add(this._getRotatePanePos()).rotateFrom(this._bearing,this._getRotatePanePos()).add(this._getMapPanePos()):c.layerPointToContainerPoint.call(this,t)},setBearing:function(t){if(L.Browser.any3d&&this._rotate){var o=this._getRotatePanePos(),e=this.getSize().divideBy(2);this._pivot=this._getMapPanePos().clone().multiplyBy(-1).add(e),o=o.rotateFrom(-this._bearing,this._pivot),this._bearing=t*L.DomUtil.DEG_TO_RAD,this._rotatePanePos=o.rotateFrom(this._bearing,this._pivot),L.DomUtil.setPosition(this._rotatePane,o,this._bearing,this._pivot),this.fire("rotate")}},getBearing:function(){return this._bearing*L.DomUtil.RAD_TO_DEG},_initPanes:function(){var t=this._panes={};this._paneRenderers={},this._mapPane=this.createPane("mapPane",this._container),L.DomUtil.setPosition(this._mapPane,new L.Point(0,0)),this._rotate?(this._rotatePane=this.createPane("rotatePane",this._mapPane),this._norotatePane=this.createPane("norotatePane",this._mapPane),this.createPane("tilePane",this._rotatePane),this.createPane("overlayPane",this._rotatePane),this.createPane("shadowPane",this._norotatePane),this.createPane("markerPane",this._norotatePane),this.createPane("tooltipPane",this._norotatePane),this.createPane("popupPane",this._norotatePane)):(this.createPane("tilePane"),this.createPane("overlayPane"),this.createPane("shadowPane"),this.createPane("markerPane"),this.createPane("tooltipPane"),this.createPane("popupPane")),this.options.markerZoomAnimation||(L.DomUtil.addClass(t.markerPane,"leaflet-zoom-hide"),L.DomUtil.addClass(t.shadowPane,"leaflet-zoom-hide"))},rotatedPointToMapPanePoint:function(t){return L.point(t).rotate(this._bearing)._add(this._getRotatePanePos())},mapPanePointToRotatedPoint:function(t){return L.point(t)._subtract(this._getRotatePanePos()).rotate(-this._bearing)},_getCenterOffset:function(t){var o=c._getCenterOffset.call(this,t);return this._rotate&&(o=o.rotate(this._bearing)),o},_getRotatePanePos:function(){return this._rotatePanePos||new L.Point(0,0)},_getNewPixelOrigin:function(t,o){var e=this.getSize()._divideBy(2);return this._rotate||c._getNewPixelOrigin.call(this,t,o),this.project(t,o).rotate(this._bearing)._subtract(e)._add(this._getMapPanePos())._add(this._getRotatePanePos()).rotate(-this._bearing)._round()},_handleGeolocationResponse:function(t){var o=t.coords.latitude,e=t.coords.longitude,i=t.coords.heading,n=new L.LatLng(o,e),a=n.toBounds(t.coords.accuracy),s=this._locateOptions;if(s.setView){var r=this.getBoundsZoom(a);this.setView(n,s.maxZoom?Math.min(r,s.maxZoom):r)}var h={latlng:n,bounds:a,timestamp:t.timestamp,heading:i};for(var _ in t.coords)"number"==typeof t.coords[_]&&(h[_]=t.coords[_]);this.fire("locationfound",h)}}),L.Map.CompassBearing=L.Handler.extend({initialize:function(t){window.DeviceOrientationEvent?(this._capable=!0,this._map=t,this._throttled=L.Util.throttle(this._onDeviceOrientation,1e3,this)):this._capable=!1},addHooks:function(){this._capable&&this._map._rotate&&L.DomEvent.on(window,"deviceorientation",this._throttled,this)},removeHooks:function(){this._capable&&this._map._rotate&&L.DomEvent.off(window,"deviceorientation",this._throttled,this)},_onDeviceOrientation:function(t){null!==t.alpha&&this._map.setBearing(t.alpha-window.orientation)}}),L.Map.addInitHook("addHandler","compassBearing",L.Map.CompassBearing),L.Map.mergeOptions({trackContainerMutation:!1}),L.Map.ContainerMutation=L.Handler.extend({addHooks:function(){L.Browser.mutation&&(this._observer||(this._observer=new MutationObserver(L.Util.bind(this._onMutation,this))),this._observer.observe(this._map.getContainer(),{childList:!1,attributes:!0,characterData:!1,subtree:!1,attributeFilter:["style"]}))},removeHooks:function(){L.Browser.mutation&&this._observer.disconnect()},_onMutation:function(){this._map.invalidateSize()}}),L.Map.addInitHook("addHandler","trackContainerMutation",L.Map.ContainerMutation),L.Map.mergeOptions({bounceAtZoomLimits:!0}),L.Map.TouchGestures=L.Handler.extend({initialize:function(t){this._map=t,this.rotate=!!this._map.options.touchRotate,this.zoom=!!this._map.options.touchZoom},addHooks:function(){L.DomEvent.on(this._map._container,"touchstart",this._onTouchStart,this)},removeHooks:function(){L.DomEvent.off(this._map._container,"touchstart",this._onTouchStart,this)},_onTouchStart:function(t){var o=this._map;if(t.touches&&2===t.touches.length&&!o._animatingZoom&&!this._zooming&&!this._rotating){var e=o.mouseEventToContainerPoint(t.touches[0]),i=o.mouseEventToContainerPoint(t.touches[1]),n=e.subtract(i);this._centerPoint=o.getSize()._divideBy(2),this._startLatLng=o.containerPointToLatLng(this._centerPoint),this.zoom?("center"!==o.options.touchZoom&&(this._pinchStartLatLng=o.containerPointToLatLng(e.add(i)._divideBy(2))),this._startDist=e.distanceTo(i),this._startZoom=o.getZoom(),this._zooming=!0):this._zooming=!1,this.rotate?(this._startTheta=Math.atan(n.x/n.y),this._startBearing=o.getBearing(),n.y<0&&(this._startBearing+=180),this._rotating=!0):this._rotating=!1,this._moved=!1,o.stop(),L.DomEvent.on(document,"touchmove",this._onTouchMove,this).on(document,"touchend",this._onTouchEnd,this),L.DomEvent.preventDefault(t)}},_onTouchMove:function(t){if(t.touches&&2===t.touches.length&&(this._zooming||this._rotating)){var o,e=this._map,i=e.mouseEventToContainerPoint(t.touches[0]),n=e.mouseEventToContainerPoint(t.touches[1]),a=i.subtract(n),s=i.distanceTo(n)/this._startDist;if(this._rotating){var r=(Math.atan(a.x/a.y)-this._startTheta)*L.DomUtil.RAD_TO_DEG;a.y<0&&(r+=180),r&&e.setBearing(this._startBearing-r)}if(this._zooming)if(this._zoom=e.getScaleZoom(s,this._startZoom),!e.options.bounceAtZoomLimits&&(this._zoom<e.getMinZoom()&&s<1||this._zoom>e.getMaxZoom()&&s>1)&&(this._zoom=e._limitZoom(this._zoom)),"center"===e.options.touchZoom){if(this._center=this._startLatLng,1===s)return}else{if(o=i._add(n)._divideBy(2)._subtract(this._centerPoint),1===s&&0===o.x&&0===o.y)return;var h=-e.getBearing()*L.DomUtil.DEG_TO_RAD;this._center=e.unproject(e.project(this._pinchStartLatLng).subtract(o.rotate(h)))}this._moved||(e._moveStart(!0),this._moved=!0),L.Util.cancelAnimFrame(this._animRequest);var _=L.bind(e._move,e,this._center,this._zoom,{pinch:!0,round:!1});this._animRequest=L.Util.requestAnimFrame(_,this,!0),L.DomEvent.preventDefault(t)}},_onTouchEnd:function(){this._moved&&this._zooming?(this._zooming=!1,this._rotating=!1,L.Util.cancelAnimFrame(this._animRequest),L.DomEvent.off(document,"touchmove",this._onTouchMove).off(document,"touchend",this._onTouchEnd),this.zoom&&(this._map.options.zoomAnimation?this._map._animateZoom(this._center,this._map._limitZoom(this._zoom),!0,this._map.options.snapZoom):this._map._resetView(this._center,this._map._limitZoom(this._zoom)))):this._zooming=!1}}),L.Map.addInitHook("addHandler","touchGestures",L.Map.TouchGestures),L.Map.mergeOptions({touchRotate:!1}),L.Map.TouchRotate=L.Handler.extend({addHooks:function(){this._map.touchGestures.enable(),this._map.touchGestures.rotate=!0},removeHooks:function(){this._map.touchGestures.rotate=!1}}),L.Map.addInitHook("addHandler","touchRotate",L.Map.TouchRotate),L.Map.mergeOptions({shiftKeyRotate:!0}),L.Map.ShiftKeyRotate=L.Handler.extend({addHooks:function(){L.DomEvent.on(this._map._container,"wheel",this._handleShiftScroll,this),this._map.shiftKeyRotate.rotate=!0},removeHooks:function(){L.DomEvent.off(this._map._container,"wheel",this._handleShiftScroll,this),this._map.shiftKeyRotate.rotate=!1},_handleShiftScroll:function(t){t.shiftKey?(t.preventDefault(),this._map.scrollWheelZoom.disable(),this._map.setBearing(this._map._bearing*L.DomUtil.RAD_TO_DEG+5*Math.sign(t.deltaY))):this._map.scrollWheelZoom.enable()}}),L.Map.addInitHook("addHandler","shiftKeyRotate",L.Map.ShiftKeyRotate),L.Map.addInitHook((function(){this.scrollWheelZoom.enabled()&&this.shiftKeyRotate.enabled()&&(this.scrollWheelZoom.disable(),this.scrollWheelZoom.enable())})),L.Map.mergeOptions({touchZoom:L.Browser.touch&&!L.Browser.android23,bounceAtZoomLimits:!1}),L.Map.TouchZoom=L.Handler.extend({addHooks:function(){L.DomUtil.addClass(this._map._container,"leaflet-touch-zoom"),this._map.touchGestures.enable(),this._map.touchGestures.zoom=!0},removeHooks:function(){L.DomUtil.removeClass(this._map._container,"leaflet-touch-zoom"),this._map.touchGestures.zoom=!1}}),L.Map.addInitHook("addHandler","touchZoom",L.Map.TouchZoom),L.Control.Rotate=L.Control.extend({options:{position:"topleft",closeOnZeroBearing:!0},onAdd:function(t){this._onDeviceOrientation=L.Util.throttle(this._unthrottledOnDeviceOrientation,100,this);var o=this._container=L.DomUtil.create("div","leaflet-control-rotate leaflet-bar"),e=this._arrow=L.DomUtil.create("span","leaflet-control-rotate-arrow");e.style.backgroundImage="url(\"data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 29 29' xmlns='http://www.w3.org/2000/svg' fill='%23333'%3E%3Cpath d='M10.5 14l4-8 4 8h-8z'/%3E%3Cpath d='M10.5 16l4 8 4-8h-8z' fill='%23ccc'/%3E%3C/svg%3E\")",e.style.cursor="grab",e.style.display="block",e.style.width="100%",e.style.height="100%",e.style.backgroundRepeat="no-repeat",e.style.backgroundPosition="50%";var i=this._link=L.DomUtil.create("a","leaflet-control-rotate-toggle",o);return i.appendChild(e),i.href="#",i.title="Rotate map",L.DomEvent.on(i,"dblclick",L.DomEvent.stopPropagation).on(i,"mousedown",this._handleMouseDown,this).on(i,"click",L.DomEvent.stop).on(i,"click",this._cycleState,this).on(i,"click",this._refocusOnMap,this),L.Browser.any3d||L.DomUtil.addClass(i,"leaflet-disabled"),this._restyle(),t.on("rotate",this._restyle.bind(this)),this._follow=!1,this._canFollow=!1,this.options.closeOnZeroBearing&&0===t.getBearing()&&(o.style.display="none"),o},_handleMouseDown:function(t){L.DomEvent.stopPropagation(t),this.dragging=!0,this.dragstartX=t.pageX,this.dragstartY=t.pageY,L.DomEvent.on(document,"mousemove",this._handleMouseDrag,this).on(document,"mouseup",this._handleMouseUp,this)},_handleMouseUp:function(t){L.DomEvent.stopPropagation(t),this.dragging=!1,L.DomEvent.off(document,"mousemove",this._handleMouseDrag,this).off(document,"mouseup",this._handleMouseUp,this)},_handleMouseDrag:function(t){if(this.dragging){var o=t.clientX-this.dragstartX;this._map.setBearing(o)}},_cycleState:function(t){var o=this._map;o&&(o.touchRotate.enabled()||o.compassBearing.enabled()?o.compassBearing.enabled()?(o.compassBearing.disable(),o.setBearing(0),this.options.closeOnZeroBearing&&o.touchRotate.enable()):(o.touchRotate.disable(),o.compassBearing.enable()):o.touchRotate.enable(),this._restyle())},_restyle:function(){if(this._map.options.rotate){var t=this._map,o=t.getBearing();this.options.closeOnZeroBearing&&o&&(this._container.style.display="block");var e="rotate("+o+"deg)";this._arrow.style.transform=e,t.compassBearing.enabled()?this._link.style.backgroundColor="orange":t.touchRotate.enabled()?this._link.style.backgroundColor=null:(this._link.style.backgroundColor="grey",this.options.closeOnZeroBearing&&0===t.getBearing()&&(this._container.style.display="none"))}else L.DomUtil.addClass(this._link,"leaflet-disabled")}}),L.control.rotate=function(t){return new L.Control.Rotate(t)},L.Map.mergeOptions({rotateControl:!0}),L.Map.addInitHook((function(){if(this.options.rotateControl){var t="object"==typeof this.options.rotateControl?this.options.rotateControl:{};this.rotateControl=L.control.rotate(t),this.addControl(this.rotateControl)}}))}));
1
+ (function (factory) {
2
+ typeof define === 'function' && define.amd ? define(factory) :
3
+ factory();
4
+ })((function () { 'use strict';
5
+
6
+ /**
7
+ * @external L.DomUtil
8
+ *
9
+ * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/dom/DomUtil.js
10
+ */
11
+
12
+ const domUtilProto = L.extend({}, L.DomUtil);
13
+
14
+ L.extend(L.DomUtil, {
15
+
16
+ setTransform: function(el, offset, scale, bearing, pivot) {
17
+ var pos = offset || new L.Point(0, 0);
18
+
19
+ if (!bearing) {
20
+ offset = pos._round();
21
+ return domUtilProto.setTransform.apply(this, arguments);
22
+ }
23
+
24
+ pos = pos.rotateFrom(bearing, pivot);
25
+
26
+ el.style[L.DomUtil.TRANSFORM] =
27
+ 'translate3d(' + pos.x + 'px,' + pos.y + 'px' + ',0)' +
28
+ (scale ? ' scale(' + scale + ')' : '') +
29
+ ' rotate(' + bearing + 'rad)';
30
+ },
31
+
32
+ setPosition: function(el, point, bearing, pivot, scale) { // (HTMLElement, Point[, Boolean])
33
+ if (!bearing) {
34
+ return domUtilProto.setPosition.apply(this, arguments);
35
+ }
36
+
37
+ /*eslint-disable */
38
+ el._leaflet_pos = point;
39
+ /*eslint-enable */
40
+
41
+ if (L.Browser.any3d) {
42
+ L.DomUtil.setTransform(el, point, scale, bearing, pivot);
43
+ } else {
44
+ el.style.left = point.x + 'px';
45
+ el.style.top = point.y + 'px';
46
+ }
47
+ },
48
+
49
+ // Constants for rotation
50
+ DEG_TO_RAD: Math.PI / 180,
51
+ RAD_TO_DEG: 180 / Math.PI,
52
+
53
+ });
54
+
55
+ /**
56
+ * @external L.Draggable
57
+ *
58
+ * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/dom/Draggable.js
59
+ */
60
+
61
+ L.Draggable.include({
62
+
63
+ // updateMapBearing: function(mapBearing) {
64
+ // this._mapBearing = mapBearing;
65
+ // },
66
+
67
+ });
68
+
69
+ /**
70
+ * @external L.Point
71
+ *
72
+ * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/geometry/Point.js
73
+ */
74
+
75
+ L.extend(L.Point.prototype, {
76
+
77
+ /**
78
+ * Rotate around (0,0) by applying the 2D rotation matrix:
79
+ *
80
+ * ⎡ x' ⎤ = ⎡ cos θ -sin θ ⎤ ⎡ x ⎤
81
+ * ⎣ y' ⎦ ⎣ sin θ cos θ ⎦ ⎣ y ⎦
82
+ *
83
+ * @param theta must be given in radians.
84
+ */
85
+ rotate: function(theta) {
86
+ return this.rotateFrom(theta, new L.Point(0,0))
87
+ },
88
+
89
+ /**
90
+ * Rotate around (pivot.x, pivot.y) by:
91
+ *
92
+ * 1. subtract (pivot.x, pivot.y)
93
+ * 2. rotate around (0, 0)
94
+ * 3. add (pivot.x, pivot.y) back
95
+ *
96
+ * same as `this.subtract(pivot).rotate(theta).add(pivot)`
97
+ */
98
+ rotateFrom: function(theta, pivot) {
99
+ if (!theta) { return this; }
100
+ var sinTheta = Math.sin(theta);
101
+ var cosTheta = Math.cos(theta);
102
+ var cx = pivot.x,
103
+ cy = pivot.y;
104
+ var x = this.x - cx,
105
+ y = this.y - cy;
106
+
107
+ return new L.Point(
108
+ x * cosTheta - y * sinTheta + cx,
109
+ x * sinTheta + y * cosTheta + cy
110
+ );
111
+ },
112
+
113
+ });
114
+
115
+ /**
116
+ * @external L.DivOverlay
117
+ *
118
+ * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/layer/DivOverlay.js
119
+ */
120
+
121
+ const divOverlayProto = L.extend({}, L.DivOverlay.prototype);
122
+
123
+ L.DivOverlay.include({
124
+
125
+ getEvents: function() {
126
+ return L.extend(divOverlayProto.getEvents.apply(this, arguments), { rotate: this._updatePosition });
127
+ },
128
+
129
+ _updatePosition: function() {
130
+ // 0. update anchor (leaflet v1.9.3)
131
+ divOverlayProto._updatePosition.apply(this, arguments);
132
+ // 1. subtract anchor
133
+ // 2. rotate element
134
+ // 3. restore anchor
135
+ if (this._map && this._map._rotate && this._zoomAnimated) {
136
+ var anchor = this._getAnchor();
137
+ var pos = L.DomUtil.getPosition(this._container).subtract(anchor);
138
+ L.DomUtil.setPosition(this._container, this._map.rotatedPointToMapPanePoint(pos).add(anchor));
139
+ }
140
+
141
+ },
142
+
143
+ });
144
+
145
+ /**
146
+ * @external L.Popup
147
+ *
148
+ * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/layer/Popup.js
149
+ */
150
+
151
+ const popupProto = L.extend({}, L.Popup.prototype);
152
+
153
+ L.Popup.include({
154
+
155
+ _animateZoom: function(e) {
156
+ // 0. update anchor (leaflet v1.9.3)
157
+ popupProto._animateZoom.apply(this, arguments);
158
+ // 1. subtract anchor
159
+ // 2. rotate element
160
+ // 3. restore anchor
161
+ if (this._map && this._map._rotate) {
162
+ var anchor = this._getAnchor();
163
+ var pos = L.DomUtil.getPosition(this._container).subtract(anchor);
164
+ L.DomUtil.setPosition(this._container, this._map.rotatedPointToMapPanePoint(pos).add(anchor));
165
+ }
166
+ },
167
+
168
+ /**
169
+ * Hot fix for L.Popup.mergeOptions({ keepInView: true, });
170
+ *
171
+ * @see https://github.com/fnicollet/Leaflet/pull/21
172
+ */
173
+ _adjustPan: function() {
174
+ if (!this.options.autoPan || (this._map._panAnim && this._map._panAnim._inProgress)) { return; }
175
+
176
+ // We can endlessly recurse if keepInView is set and the view resets.
177
+ // Let's guard against that by exiting early if we're responding to our own autopan.
178
+ if (this._autopanning) {
179
+ this._autopanning = false;
180
+ return;
181
+ }
182
+
183
+ var map = this._map,
184
+ marginBottom = parseInt(L.DomUtil.getStyle(this._container, 'marginBottom'), 10) || 0,
185
+ containerHeight = this._container.offsetHeight + marginBottom,
186
+ containerWidth = this._containerWidth,
187
+ layerPos = new L.Point(this._containerLeft, -containerHeight - this._containerBottom);
188
+
189
+ layerPos._add(L.DomUtil.getPosition(this._container));
190
+
191
+ // var containerPos = map.layerPointToContainerPoint(layerPos);
192
+ /** @TODO use popupProto._adjustPan */
193
+ var containerPos = layerPos._add(this._map._getMapPanePos()),
194
+ padding = L.point(this.options.autoPanPadding),
195
+ paddingTL = L.point(this.options.autoPanPaddingTopLeft || padding),
196
+ paddingBR = L.point(this.options.autoPanPaddingBottomRight || padding),
197
+ size = map.getSize(),
198
+ dx = 0,
199
+ dy = 0;
200
+
201
+ if (containerPos.x + containerWidth + paddingBR.x > size.x) { // right
202
+ dx = containerPos.x + containerWidth - size.x + paddingBR.x;
203
+ }
204
+ if (containerPos.x - dx - paddingTL.x < 0) { // left
205
+ dx = containerPos.x - paddingTL.x;
206
+ }
207
+ if (containerPos.y + containerHeight + paddingBR.y > size.y) { // bottom
208
+ dy = containerPos.y + containerHeight - size.y + paddingBR.y;
209
+ }
210
+ if (containerPos.y - dy - paddingTL.y < 0) { // top
211
+ dy = containerPos.y - paddingTL.y;
212
+ }
213
+
214
+ // @namespace Map
215
+ // @section Popup events
216
+ // @event autopanstart: Event
217
+ // Fired when the map starts autopanning when opening a popup.
218
+ if (dx || dy) {
219
+ // Track that we're autopanning, as this function will be re-ran on moveend
220
+ if (this.options.keepInView) {
221
+ this._autopanning = true;
222
+ }
223
+ map
224
+ .fire('autopanstart')
225
+ .panBy([dx, dy]);
226
+ }
227
+ },
228
+
229
+ });
230
+
231
+ /**
232
+ * @external L.Tooltip
233
+ *
234
+ * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/layer/Tooltip.js
235
+ */
236
+
237
+ const tooltipProto = L.extend({}, L.Tooltip.prototype);
238
+
239
+ L.Tooltip.include({
240
+
241
+ _animateZoom: function(e) {
242
+ if (!this._map._rotate) {
243
+ return tooltipProto._animateZoom.apply(this, arguments);
244
+ }
245
+ var pos = this._map._latLngToNewLayerPoint(this._latlng, e.zoom, e.center);
246
+
247
+ pos = this._map.rotatedPointToMapPanePoint(pos);
248
+ this._setPosition(pos);
249
+ },
250
+
251
+ _updatePosition: function() {
252
+ if (!this._map._rotate) {
253
+ return tooltipProto._updatePosition.apply(this, arguments);
254
+ }
255
+ var pos = this._map.latLngToLayerPoint(this._latlng);
256
+
257
+ pos = this._map.rotatedPointToMapPanePoint(pos);
258
+ this._setPosition(pos);
259
+ },
260
+
261
+ });
262
+
263
+ /**
264
+ * @external L.Icon
265
+ *
266
+ * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/layer/marker/Icon.js
267
+ */
268
+
269
+ L.extend({}, L.Icon.prototype);
270
+
271
+ L.Icon.include({
272
+
273
+ _setIconStyles: function(img, name) {
274
+ var options = this.options;
275
+ var sizeOption = options[name + 'Size'];
276
+
277
+ if (typeof sizeOption === 'number') {
278
+ sizeOption = [sizeOption, sizeOption];
279
+ }
280
+
281
+ var size = L.point(sizeOption),
282
+ anchor = L.point(name === 'shadow' && options.shadowAnchor || options.iconAnchor ||
283
+ size && size.divideBy(2, true));
284
+
285
+ img.className = 'leaflet-marker-' + name + ' ' + (options.className || '');
286
+
287
+ if (anchor) {
288
+ img.style.marginLeft = (-anchor.x) + 'px';
289
+ img.style.marginTop = (-anchor.y) + 'px';
290
+ /** @TODO use iconProto._setIconStyles */
291
+ img.style[L.DomUtil.TRANSFORM + "Origin"] = anchor.x + "px " + anchor.y + "px 0px";
292
+ }
293
+
294
+ if (size) {
295
+ img.style.width = size.x + 'px';
296
+ img.style.height = size.y + 'px';
297
+ }
298
+ },
299
+
300
+ });
301
+
302
+ /**
303
+ * @external L.Marker
304
+ * @external L.Handler.MarkerDrag
305
+ *
306
+ * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/layer/marker/Marker.js
307
+ */
308
+
309
+ const markerProto = L.extend({}, L.Marker.prototype);
310
+
311
+ L.Marker.mergeOptions({
312
+
313
+ // @option rotation: Number = 0
314
+ // Rotation of this marker in rad
315
+ rotation: 0,
316
+
317
+ // @option rotateWithView: Boolean = false
318
+ // Rotate this marker when map rotates
319
+ rotateWithView: false,
320
+
321
+ // @option scale: Number = undefined
322
+ // Scale of the marker icon
323
+ scale: undefined,
324
+
325
+ });
326
+
327
+ var markerDragProto; // retrived at runtime (see below: L.Marker::_initInteraction())
328
+
329
+ var MarkerDrag = {
330
+
331
+ // _onDragStart: function() {
332
+ // if (!this._marker._map._rotate) {
333
+ // return markerDragProto._onDragStart.apply(this, arguments);
334
+ // }
335
+ // this._draggable.updateMapBearing(this._marker._map._bearing);
336
+ // },
337
+
338
+ _onDrag: function(e) {
339
+ var marker = this._marker,
340
+ /** @TODO use markerDragProto._onDrag */
341
+ rotated_marker = marker.options.rotation || marker.options.rotateWithView,
342
+ shadow = marker._shadow,
343
+ iconPos = L.DomUtil.getPosition(marker._icon);
344
+
345
+ /** @TODO use markerDragProto._onDrag */
346
+ // update shadow position
347
+ if (!rotated_marker && shadow) {
348
+ L.DomUtil.setPosition(shadow, iconPos);
349
+ }
350
+
351
+ /** @TODO use markerDragProto._onDrag */
352
+ if (marker._map._rotate) {
353
+ // Reverse calculation from mapPane coordinates to rotatePane coordinates
354
+ iconPos = marker._map.mapPanePointToRotatedPoint(iconPos);
355
+ }
356
+ var latlng = marker._map.layerPointToLatLng(iconPos);
357
+
358
+ marker._latlng = latlng;
359
+ e.latlng = latlng;
360
+ e.oldLatLng = this._oldLatLng;
361
+
362
+ /** @TODO use markerDragProto._onDrag */
363
+ if (rotated_marker) marker.setLatLng(latlng); // use `setLatLng` to presisit rotation. low efficiency
364
+ else marker.fire('move', e); // `setLatLng` will trig 'move' event. we imitate here.
365
+
366
+ // @event drag: Event
367
+ // Fired repeatedly while the user drags the marker.
368
+ marker
369
+ .fire('drag', e);
370
+ },
371
+
372
+ _onDragEnd: function(e) {
373
+ if (this._marker._map._rotate) {
374
+ this._marker.update();
375
+ }
376
+ markerDragProto._onDragEnd.apply(this, arguments);
377
+ },
378
+
379
+ };
380
+
381
+ L.Marker.include({
382
+
383
+ getEvents: function() {
384
+ return L.extend(markerProto.getEvents.apply(this, arguments), { rotate: this.update });
385
+ },
386
+
387
+ onAdd: function(map) {
388
+ markerProto.onAdd.apply(this, arguments);
389
+ map.on('rotate', this.update, this);
390
+ },
391
+
392
+ _initInteraction: function() {
393
+ var ret = markerProto._initInteraction.apply(this, arguments);
394
+ if (this.dragging && this.dragging.enabled() && this._map && this._map._rotate) {
395
+ // L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable
396
+ markerDragProto = markerDragProto || Object.getPrototypeOf(this.dragging);
397
+ Object.assign(this.dragging, {
398
+ // _onDragStart: MarkerDrag._onDragStart.bind(this.dragging),
399
+ _onDrag: MarkerDrag._onDrag.bind(this.dragging),
400
+ _onDragEnd: MarkerDrag._onDragEnd.bind(this.dragging),
401
+ });
402
+ this.dragging.disable();
403
+ this.dragging.enable();
404
+ }
405
+ return ret;
406
+ },
407
+
408
+ _setPos: function(pos) {
409
+
410
+ /** @TODO use markerProto._setPos */
411
+ if (this._map._rotate) {
412
+ pos = this._map.rotatedPointToMapPanePoint(pos);
413
+ }
414
+
415
+ /** @TODO use markerProto._setPos */
416
+ var bearing = this.options.rotation || 0;
417
+ if (this.options.rotateWithView) {
418
+ bearing += this._map._bearing;
419
+ }
420
+
421
+ /** @TODO use markerProto._setPos */
422
+ if (this._icon) {
423
+ L.DomUtil.setPosition(this._icon, pos, bearing, pos, this.options.scale);
424
+ }
425
+
426
+ /** @TODO use markerProto._setPos */
427
+ if (this._shadow) {
428
+ L.DomUtil.setPosition(this._shadow, pos, bearing, pos, this.options.scale);
429
+ }
430
+
431
+ this._zIndex = pos.y + this.options.zIndexOffset;
432
+
433
+ this._resetZIndex();
434
+ },
435
+
436
+ _updateZIndex: function(offset) {
437
+ if (!this._map._rotate) {
438
+ return markerProto._updateZIndex.apply(this, arguments);
439
+ }
440
+ this._icon.style.zIndex = Math.round(this._zIndex + offset);
441
+ },
442
+
443
+ setRotation: function(rotation) {
444
+ this.options.rotation = rotation;
445
+ this.update();
446
+ },
447
+
448
+ });
449
+
450
+ /**
451
+ * @external L.GridLayer
452
+ *
453
+ * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/layer/tile/GridLayer.js
454
+ */
455
+
456
+ const gridLayerProto = L.extend({}, L.GridLayer.prototype);
457
+
458
+ L.GridLayer.include({
459
+
460
+ getEvents: function() {
461
+ var events = gridLayerProto.getEvents.apply(this, arguments);
462
+ if (this._map._rotate && !this.options.updateWhenIdle) {
463
+ if (!this._onRotate) {
464
+ this._onRotate = L.Util.throttle(this._onMoveEnd, this.options.updateInterval, this);
465
+ }
466
+ events.rotate = this._onRotate;
467
+ }
468
+ return events;
469
+ },
470
+
471
+ _getTiledPixelBounds: function(center) {
472
+ if (!this._map._rotate) {
473
+ return gridLayerProto._getTiledPixelBounds.apply(this, arguments);
474
+ }
475
+
476
+ return this._map._getNewPixelBounds(center, this._tileZoom);
477
+ },
478
+
479
+ });
480
+
481
+ /**
482
+ * @external L.Canvas
483
+ *
484
+ * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/layer/vector/Canvas.js
485
+ */
486
+
487
+ L.extend({}, L.Canvas.prototype);
488
+
489
+ L.Canvas.include({
490
+
491
+ // onAdd: function() {
492
+ // canvasProto.onAdd.apply(this, arguments);
493
+ // // When rotating the canvas itself, it is cleared by some weird reason, so redraw.
494
+ // this._map.on('rotate', this._redraw, this);
495
+ // },
496
+
497
+ // onRemove: function() {
498
+ // canvasProto.onRemove.apply(this, arguments);
499
+ // this._map.off('rotate', this._redraw, this);
500
+ // },
501
+
502
+ // _update: function() {
503
+ // canvasProto._update.apply(this, arguments);
504
+ // // Tell paths to redraw themselves
505
+ // this.fire('update')
506
+ // },
507
+
508
+ });
509
+
510
+ /**
511
+ * @external L.Renderer
512
+ *
513
+ * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/layer/vector/Renderer.js
514
+ */
515
+
516
+ const rendererProto = L.extend({}, L.Renderer.prototype);
517
+
518
+ L.Renderer.include({
519
+
520
+ // onAdd: function() {
521
+ // rendererProto.onAdd.apply(this, arguments);
522
+ // // this._map.on('rotate', this._update, this);
523
+ // },
524
+
525
+ // onRemove: function() {
526
+ // rendererProto.onRemove.apply(this, arguments);
527
+ // // this._map.off('rotate', this._update, this);
528
+ // },
529
+
530
+ /**
531
+ * @TODO rechek this changes from leaflet@v1.9.3
532
+ *
533
+ * @see https://github.com/Leaflet/Leaflet/compare/v1.7.0...v1.9.3
534
+ */
535
+ _updateTransform: function(center, zoom) {
536
+ if (!this._map._rotate) {
537
+ return rendererProto._updateTransform.apply(this, arguments);
538
+ }
539
+ var scale = this._map.getZoomScale(zoom, this._zoom),
540
+ offset = this._map._latLngToNewLayerPoint(this._topLeft, zoom, center);
541
+ if (L.Browser.any3d) {
542
+ L.DomUtil.setTransform(this._container, offset, scale);
543
+ } else {
544
+ L.DomUtil.setPosition(this._container, offset);
545
+ }
546
+ },
547
+
548
+ _update: function() {
549
+ if (!this._map._rotate) {
550
+ return rendererProto._update.apply(this, arguments);
551
+ }
552
+ // Update pixel bounds of renderer container (for positioning/sizing/clipping later)
553
+ // Subclasses are responsible of firing the 'update' event.
554
+ this._bounds = this._map._getPaddedPixelBounds(this.options.padding);
555
+ this._topLeft = this._map.layerPointToLatLng(this._bounds.min);
556
+ this._center = this._map.getCenter();
557
+ this._zoom = this._map.getZoom();
558
+ },
559
+
560
+ });
561
+
562
+ /**
563
+ * @external L.SVG
564
+ *
565
+ * @see https://github.com/Leaflet/Leaflet/tree/v1.9.3/src/layer/vector/SVG.js
566
+ */
567
+
568
+ L.extend({}, L.SVG.prototype);
569
+
570
+ L.SVG.include({
571
+
572
+ // _update: function() {
573
+ // svgProto._update.apply(this, arguments);
574
+ // if (this._map._rotate) {
575
+ // this.fire('update');
576
+ // }
577
+ // },
578
+
579
+ });
580
+
581
+ /**
582
+ * @external L.Map
583
+ *
584
+ * @see https://github.com/Leaflet/Leaflet/blob/v1.9.3/src/map/Map.js
585
+ */
586
+
587
+ const mapProto = L.extend({}, L.Map.prototype);
588
+
589
+ L.Map.mergeOptions({ rotate: false, bearing: 0, });
590
+
591
+ L.Map.include({
592
+
593
+ /**
594
+ * @param {(HTMLElement|String)} id html selector
595
+ * @param {Object} [options={}] leaflet map options
596
+ */
597
+ initialize: function(id, options) {
598
+ if (options.rotate) {
599
+ this._rotate = true;
600
+ this._bearing = 0;
601
+ }
602
+ mapProto.initialize.apply(this, arguments);
603
+ if(this.options.rotate){
604
+ this.setBearing(this.options.bearing);
605
+ }
606
+ },
607
+
608
+ /**
609
+ * Given a pixel coordinate relative to the map container,
610
+ * returns the corresponding pixel coordinate relative to
611
+ * the [origin pixel](#map-getpixelorigin).
612
+ *
613
+ * @param {L.Point} point pixel screen coordinates
614
+ * @returns {L.Point} transformed pixel point
615
+ */
616
+ containerPointToLayerPoint: function(point) {
617
+ if (!this._rotate) {
618
+ return mapProto.containerPointToLayerPoint.apply(this, arguments);
619
+ }
620
+ return L.point(point)
621
+ .subtract(this._getMapPanePos())
622
+ .rotateFrom(-this._bearing, this._getRotatePanePos())
623
+ .subtract(this._getRotatePanePos());
624
+ },
625
+
626
+ /**
627
+ * Given a pixel coordinate relative to the [origin pixel](#map-getpixelorigin),
628
+ * returns the corresponding pixel coordinate relative to the map container.
629
+ *
630
+ * @param {L.Point} point pixel screen coordinates
631
+ * @returns {L.Point} transformed pixel point
632
+ */
633
+ layerPointToContainerPoint: function(point) {
634
+ if (!this._rotate) {
635
+ return mapProto.layerPointToContainerPoint.apply(this, arguments);
636
+ }
637
+ return L.point(point)
638
+ .add(this._getRotatePanePos())
639
+ .rotateFrom(this._bearing, this._getRotatePanePos())
640
+ .add(this._getMapPanePos());
641
+ },
642
+
643
+ /**
644
+ * Converts a coordinate from the rotated pane reference system
645
+ * to the reference system of the not rotated map pane.
646
+ *
647
+ * (rotatePane) --> (mapPane)
648
+ * (rotatePane) --> (norotatePane)
649
+ *
650
+ * @param {L.Point} point pixel screen coordinates
651
+ * @returns {L.Point}
652
+ *
653
+ * @since leaflet-rotate (v0.1)
654
+ */
655
+ rotatedPointToMapPanePoint: function(point) {
656
+ return L.point(point)
657
+ .rotate(this._bearing)
658
+ ._add(this._getRotatePanePos());
659
+ },
660
+
661
+ /**
662
+ * Converts a coordinate from the not rotated map pane reference system
663
+ * to the reference system of the rotated pane.
664
+ *
665
+ * (mapPane) --> (rotatePane)
666
+ * (norotatePane) --> (rotatePane)
667
+ *
668
+ * @param {L.Point} point pixel screen coordinates
669
+ *
670
+ * @since leaflet-rotate (v0.1)
671
+ */
672
+ mapPanePointToRotatedPoint: function(point) {
673
+ return L.point(point)
674
+ ._subtract(this._getRotatePanePos())
675
+ .rotate(-this._bearing);
676
+ },
677
+
678
+ /**
679
+ * Given latlng bounds, returns the bounds in projected pixel
680
+ * relative to the map container.
681
+ *
682
+ * @see https://github.com/ronikar/Leaflet/blob/5c480ef959b947c3beed7065425a5a36c486262b/src/map/Map.js#L1114-L1135
683
+ *
684
+ * @param {L.LatLngBounds} bounds
685
+ * @returns {L.Bounds}
686
+ *
687
+ * @since leaflet-rotate (v0.2)
688
+ */
689
+ mapBoundsToContainerBounds: function (bounds) {
690
+ if (!this._rotate && mapProto.mapBoundsToContainerBounds) {
691
+ return mapProto.mapBoundsToContainerBounds.apply(this, arguments);
692
+ }
693
+ const nw = this.latLngToContainerPoint(bounds.getNorthWest()),
694
+ ne = this.latLngToContainerPoint(bounds.getNorthEast()),
695
+ sw = this.latLngToContainerPoint(bounds.getSouthWest()),
696
+ se = this.latLngToContainerPoint(bounds.getSouthEast());
697
+ return L.bounds([
698
+ L.point(Math.min(nw.x, ne.x, se.x, sw.x), Math.min(nw.y, ne.y, se.y, sw.y)), // [ minX, minY ]
699
+ L.point(Math.max(nw.x, ne.x, se.x, sw.x), Math.max(nw.y, ne.y, se.y, sw.y)) // [ maxX, maxY ]
700
+ ]);
701
+ },
702
+
703
+ /**
704
+ * Returns geographical bounds visible in the current map view
705
+ *
706
+ * @TODO find out if map bounds calculated by `L.Map::getBounds()`
707
+ * function should match the `rotatePane` or `norotatePane` bounds
708
+ *
709
+ * @see https://github.com/fnicollet/Leaflet/issues/7
710
+ *
711
+ * @returns {L.LatLngBounds}
712
+ */
713
+ getBounds: function() {
714
+ if (!this._rotate) {
715
+ return mapProto.getBounds.apply(this, arguments);
716
+ }
717
+
718
+ // SEE: https://github.com/fnicollet/Leaflet/pull/22
719
+ //
720
+ // var bounds = this.getPixelBounds(),
721
+ // sw = this.unproject(bounds.getBottomLeft()),
722
+ // ne = this.unproject(bounds.getTopRight());
723
+ // return new LatLngBounds(sw, ne);
724
+ //
725
+
726
+ // LatLngBounds' constructor automatically
727
+ // extends the bounds to fit the passed points
728
+ var size = this.getSize();
729
+ return new L.LatLngBounds([
730
+ this.containerPointToLatLng([0, 0]), // topleft
731
+ this.containerPointToLatLng([size.x, 0]), // topright
732
+ this.containerPointToLatLng([size.x, size.y]), // bottomright
733
+ this.containerPointToLatLng([0, size.y]), // bottomleft
734
+ ]);
735
+ },
736
+
737
+ /**
738
+ * Returns the bounds of the current map view in projected pixel
739
+ * coordinates (sometimes useful in layer and overlay implementations).
740
+ *
741
+ * @TODO find out if map bounds calculated by `L.Map::getPixelBounds()`
742
+ * function should match the `rotatePane` or `norotatePane` bounds
743
+ *
744
+ * @see https://github.com/fnicollet/Leaflet/issues/7
745
+ *
746
+ * @returns {L.Bounds}
747
+ */
748
+ // getPixelBounds(center, zoom) {
749
+ // // const topLeftPoint = map.containerPointToLayerPoint(this._getTopLeftPoint());
750
+ // const topLeftPoint = this._getTopLeftPoint(center, zoom);
751
+ // return new L.Bounds(topLeftPoint, topLeftPoint.add(this.getSize()));
752
+ // },
753
+
754
+ /**
755
+ * Change map rotation
756
+ *
757
+ * @param {number} theta map degrees
758
+ *
759
+ * @since leaflet-rotate (v0.1)
760
+ */
761
+ setBearing: function(theta) {
762
+ if (!L.Browser.any3d || !this._rotate) { return; }
763
+
764
+ var bearing = L.Util.wrapNum(theta, [0, 360]) * L.DomUtil.DEG_TO_RAD,
765
+ center = this._getPixelCenter(),
766
+ oldPos = this._getRotatePanePos().rotateFrom(-this._bearing, center),
767
+ newPos = oldPos.rotateFrom(bearing, center);
768
+
769
+ // CSS transform
770
+ L.DomUtil.setPosition(this._rotatePane, oldPos, bearing, center);
771
+
772
+ this._pivot = center;
773
+ this._bearing = bearing;
774
+ this._rotatePanePos = newPos;
775
+
776
+ this.fire('rotate');
777
+ },
778
+
779
+ /**
780
+ * Get current map rotation
781
+ *
782
+ * @returns {number} theta map degrees
783
+ *
784
+ * @since leaflet-rotate (v0.1)
785
+ */
786
+ getBearing: function() {
787
+ return this._bearing * L.DomUtil.RAD_TO_DEG;
788
+ },
789
+
790
+ /**
791
+ * Creates a new [map pane](#map-pane) with the given name if it doesn't
792
+ * exist already, then returns it. The pane is created as a child of
793
+ * `container`, or as a child of the main map pane if not set.
794
+ *
795
+ * @param {String} name leaflet pane
796
+ * @param {HTMLElement} [container] parent element
797
+ * @returns {HTMLElement} pane container
798
+ */
799
+ // createPane: function(name, container) {
800
+ // if (!this._rotate || name == 'mapPane') {
801
+ // return mapProto.createPane.apply(this, arguments);
802
+ // }
803
+ // // init "rotatePane"
804
+ // if (!this._rotatePane) {
805
+ // // this._pivot = this.getSize().divideBy(2);
806
+ // this._rotatePane = mapProto.createPane.call(this, 'rotatePane', this._mapPane);
807
+ // L.DomUtil.setPosition(this._rotatePane, new L.Point(0, 0), this._bearing, this._pivot);
808
+ // }
809
+ // return mapProto.createPane.call(this, name, container || this._rotatePane);
810
+ // },
811
+
812
+ /**
813
+ * Panes are DOM elements used to control the ordering of layers on
814
+ * the map. You can access panes with [`map.getPane`](#map-getpane)
815
+ * or [`map.getPanes`](#map-getpanes) methods. New panes can be created
816
+ * with the [`map.createPane`](#map-createpane) method.
817
+ *
818
+ * Every map has the following default panes that differ only in zIndex:
819
+ *
820
+ * - mapPane [HTMLElement = 'auto'] - Pane that contains all other map panes
821
+ * - tilePane [HTMLElement = 2] - Pane for tile layers
822
+ * - overlayPane [HTMLElement = 4] - Pane for overlays like polylines and polygons
823
+ * - shadowPane [HTMLElement = 5] - Pane for overlay shadows (e.g. marker shadows)
824
+ * - markerPane [HTMLElement = 6] - Pane for marker icons
825
+ * - tooltipPane [HTMLElement = 650] - Pane for tooltips.
826
+ * - popupPane [HTMLElement = 700] - Pane for popups.
827
+ */
828
+ _initPanes: function() {
829
+ var panes = this._panes = {};
830
+ this._paneRenderers = {};
831
+
832
+ this._mapPane = this.createPane('mapPane', this._container);
833
+ L.DomUtil.setPosition(this._mapPane, new L.Point(0, 0));
834
+
835
+ if (this._rotate) {
836
+ this._rotatePane = this.createPane('rotatePane', this._mapPane);
837
+ this._norotatePane = this.createPane('norotatePane', this._mapPane);
838
+ // rotatePane
839
+ this.createPane('tilePane', this._rotatePane);
840
+ this.createPane('overlayPane', this._rotatePane);
841
+ // norotatePane
842
+ this.createPane('shadowPane', this._norotatePane);
843
+ this.createPane('markerPane', this._norotatePane);
844
+ this.createPane('tooltipPane', this._norotatePane);
845
+ this.createPane('popupPane', this._norotatePane);
846
+ } else {
847
+ this.createPane('tilePane');
848
+ this.createPane('overlayPane');
849
+ this.createPane('shadowPane');
850
+ this.createPane('markerPane');
851
+ this.createPane('tooltipPane');
852
+ this.createPane('popupPane');
853
+ }
854
+
855
+ if (!this.options.markerZoomAnimation) {
856
+ L.DomUtil.addClass(panes.markerPane, 'leaflet-zoom-hide');
857
+ L.DomUtil.addClass(panes.shadowPane, 'leaflet-zoom-hide');
858
+ }
859
+ },
860
+
861
+ /**
862
+ * Pans the map the minimum amount to make the `latlng` visible. Use
863
+ * padding options to fit the display to more restricted bounds.
864
+ * If `latlng` is already within the (optionally padded) display bounds,
865
+ * the map will not be panned.
866
+ *
867
+ * @see https://github.com/Raruto/leaflet-rotate/issues/18
868
+ *
869
+ * @param {L.LatLng} latlng coordinates
870
+ * @param {Object} [options={}] padding options
871
+ *
872
+ * @returns {L.Map} current map instance
873
+ */
874
+ panInside(latlng, options) {
875
+ options = options || {};
876
+
877
+ const paddingTL = L.point(options.paddingTopLeft || options.padding || [0, 0]),
878
+ paddingBR = L.point(options.paddingBottomRight || options.padding || [0, 0]),
879
+ /** @TODO use mapProto.panInside */
880
+ // pixelPoint = this.project(latlng),
881
+ // pixelBounds = this.getPixelBounds(),
882
+ // pixelCenter = this.project(this.getCenter()),
883
+ rect = this._container.getBoundingClientRect(),
884
+ pixelPoint = this.latLngToContainerPoint(latlng),
885
+ pixelBounds = L.bounds([ L.point(rect), L.point(rect).add(this.getSize()) ]),
886
+ pixelCenter = pixelBounds.getCenter(),
887
+ //
888
+ paddedBounds = L.bounds([pixelBounds.min.add(paddingTL), pixelBounds.max.subtract(paddingBR)]),
889
+ paddedSize = paddedBounds.getSize();
890
+
891
+ if (!paddedBounds.contains(pixelPoint)) {
892
+ this._enforcingBounds = true;
893
+ const centerOffset = pixelPoint.subtract(paddedBounds.getCenter());
894
+ const offset = paddedBounds.extend(pixelPoint).getSize().subtract(paddedSize);
895
+ pixelCenter.x += centerOffset.x < 0 ? -offset.x : offset.x;
896
+ pixelCenter.y += centerOffset.y < 0 ? -offset.y : offset.y;
897
+ /** @TODO use mapProto.panInside */
898
+ // this.panTo(this.unproject(pixelCenter), options);
899
+ this.panTo(this.containerPointToLatLng(pixelCenter), options);
900
+ //
901
+ this._enforcingBounds = false;
902
+ }
903
+ return this;
904
+ },
905
+
906
+ /**
907
+ * Pans the map to the closest view that would lie inside the given bounds
908
+ * (if it's not already), controlling the animation using the options specific,
909
+ * if any.
910
+ *
911
+ * @TODO check if map bounds calculated by `L.Map::panInsideBounds()`
912
+ * function should match the `rotatePane` or `norotatePane` bounds
913
+ *
914
+ * @see https://github.com/fnicollet/Leaflet/issues/7
915
+ *
916
+ * @param {L.LatLngBounds} bounds coordinates
917
+ * @param {Object} [options] pan options
918
+ * @returns {L.Map} current map instance
919
+ */
920
+ // panInsideBounds: function (bounds, options) {
921
+ // this._enforcingBounds = true;
922
+ // var center = this.getCenter(),
923
+ // newCenter = this._limitCenter(center, this._zoom, L.latLngBounds(bounds));
924
+ //
925
+ // if (!center.equals(newCenter)) {
926
+ // this.panTo(newCenter, options);
927
+ // }
928
+ //
929
+ // this._enforcingBounds = false;
930
+ // return this;
931
+ // },
932
+
933
+ // adjust center for view to get inside bounds
934
+ // _limitCenter(center, zoom, bounds) {
935
+ //
936
+ // if (!bounds) { return center; }
937
+ //
938
+ // const centerPoint = this.project(center, zoom),
939
+ // viewHalf = this.getSize().divideBy(2),
940
+ // viewBounds = new Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)),
941
+ // offset = this._getBoundsOffset(viewBounds, bounds, zoom);
942
+ //
943
+ // // If offset is less than a pixel, ignore.
944
+ // // This prevents unstable projections from getting into
945
+ // // an infinite loop of tiny offsets.
946
+ // if (Math.abs(offset.x) <= 1 && Math.abs(offset.y) <= 1) {
947
+ // return center;
948
+ // }
949
+ //
950
+ // return this.unproject(centerPoint.add(offset), zoom);
951
+ // },
952
+
953
+ // @method flyToBounds(bounds: LatLngBounds, options?: fitBounds options): this
954
+ // Sets the view of the map with a smooth animation like [`flyTo`](#map-flyto),
955
+ // but takes a bounds parameter like [`fitBounds`](#map-fitbounds).
956
+ // flyToBounds(bounds, options) {
957
+ // const target = this._getBoundsCenterZoom(bounds, options);
958
+ // return this.flyTo(target.center, target.zoom, options);
959
+ // },
960
+
961
+ // _getBoundsCenterZoom(bounds, options) {
962
+ //
963
+ // options = options || {};
964
+ // bounds = bounds.getBounds ? bounds.getBounds() : toLatLngBounds(bounds);
965
+ //
966
+ // const paddingTL = L.point(options.paddingTopLeft || options.padding || [0, 0]),
967
+ // paddingBR = L.point(options.paddingBottomRight || options.padding || [0, 0]);
968
+ //
969
+ // let zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR));
970
+ //
971
+ // zoom = (typeof options.maxZoom === 'number') ? Math.min(options.maxZoom, zoom) : zoom;
972
+ //
973
+ // if (zoom === Infinity) {
974
+ // return { center: bounds.getCenter(), zoom };
975
+ // }
976
+ //
977
+ // return { center, zoom };
978
+ //
979
+ // },
980
+
981
+ /**
982
+ * Returns the maximum zoom level on which the given bounds fit to the map
983
+ * view in its entirety. If `inside` (optional) is set to `true`, the method
984
+ * instead returns the minimum zoom level on which the map view fits into
985
+ * the given bounds in its entirety.
986
+ *
987
+ * @param {L.LatLngBounds} bounds
988
+ * @param {Boolean} [inside=false]
989
+ * @param {L.Point} [padding=[0,0]]
990
+ *
991
+ * @returns {Number} zoom level
992
+ */
993
+ getBoundsZoom(bounds, inside, padding) {
994
+ if (!this._rotate || Math.abs(this._bearing).toFixed(1) < 0.1) {
995
+ return mapProto.getBoundsZoom.apply(this, arguments);
996
+ }
997
+
998
+ bounds = L.latLngBounds(bounds);
999
+ padding = L.point(padding || [0, 0]);
1000
+
1001
+ let zoom = this.getZoom() || 0;
1002
+ const min = this.getMinZoom(),
1003
+ max = this.getMaxZoom(),
1004
+ /** @TODO use mapProto.getBoundsZoom */
1005
+ // nw = bounds.getNorthWest(),
1006
+ // se = bounds.getSouthEast(),
1007
+ // size = this.getSize().subtract(padding),
1008
+ // boundsSize = L.bounds(this.project(se, zoom), this.project(nw, zoom)).getSize(),
1009
+ size = this.getSize().subtract(padding),
1010
+ boundsSize = this.mapBoundsToContainerBounds(bounds).getSize(),
1011
+ snap = this.options.zoomSnap,
1012
+ scalex = size.x / boundsSize.x,
1013
+ scaley = size.y / boundsSize.y,
1014
+ scale = inside ? Math.max(scalex, scaley) : Math.min(scalex, scaley);
1015
+
1016
+ zoom = this.getScaleZoom(scale, zoom);
1017
+
1018
+ if (snap) {
1019
+ zoom = Math.round(zoom / (snap / 100)) * (snap / 100); // don't jump if within 1% of a snap level
1020
+ zoom = inside ? Math.ceil(zoom / snap) * snap : Math.floor(zoom / snap) * snap;
1021
+ }
1022
+
1023
+ return Math.max(min, Math.min(max, zoom));
1024
+ },
1025
+
1026
+ /**
1027
+ * Layer point of the current center
1028
+ *
1029
+ * @returns {L.Point} layer center
1030
+ */
1031
+ // _getCenterLayerPoint: function () {
1032
+ // return this.containerPointToLayerPoint(this.getSize()._divideBy(2));
1033
+ // },
1034
+
1035
+ /**
1036
+ * Offset of the specified place to the current center in pixels
1037
+ *
1038
+ * @param {L.LatLng} latlng map coordinates
1039
+ */
1040
+ _getCenterOffset: function(latlng) {
1041
+ var centerOffset = mapProto._getCenterOffset.apply(this, arguments);
1042
+ if (this._rotate) {
1043
+ centerOffset = centerOffset.rotate(this._bearing);
1044
+ }
1045
+ return centerOffset;
1046
+ },
1047
+
1048
+ /**
1049
+ * @since leaflet-rotate (v0.1)
1050
+ */
1051
+ _getRotatePanePos: function() {
1052
+ return this._rotatePanePos || new L.Point(0, 0);
1053
+ // return L.DomUtil.getPosition(this._rotatePane) || new L.Point(0, 0);
1054
+ },
1055
+
1056
+ _getNewPixelOrigin: function(center, zoom) {
1057
+ var viewHalf = this.getSize()._divideBy(2);
1058
+ if (!this._rotate) {
1059
+ return mapProto._getNewPixelOrigin.apply(this, arguments);
1060
+ }
1061
+ return this.project(center, zoom)
1062
+ .rotate(this._bearing)
1063
+ ._subtract(viewHalf)
1064
+ ._add(this._getMapPanePos())
1065
+ ._add(this._getRotatePanePos())
1066
+ .rotate(-this._bearing)
1067
+ ._round();
1068
+ },
1069
+
1070
+ /**
1071
+ * @since leaflet-rotate (v0.2)
1072
+ *
1073
+ * @see src\layer\tile\GridLayer::_getTiledPixelBounds()
1074
+ */
1075
+ _getNewPixelBounds: function(center, zoom) {
1076
+ center = center || this.getCenter();
1077
+ zoom = zoom || this.getZoom();
1078
+ if (!this._rotate && mapProto._getNewPixelBounds) {
1079
+ return mapProto._getNewPixelBounds.apply(this, arguments);
1080
+ }
1081
+ var mapZoom = this._animatingZoom ? Math.max(this._animateToZoom, this.getZoom()) : this.getZoom(),
1082
+ scale = this.getZoomScale(mapZoom, zoom),
1083
+ pixelCenter = this.project(center, zoom).floor(),
1084
+ size = this.getSize(),
1085
+ halfSize = new L.Bounds([
1086
+ this.containerPointToLayerPoint([0, 0]).floor(),
1087
+ this.containerPointToLayerPoint([size.x, 0]).floor(),
1088
+ this.containerPointToLayerPoint([0, size.y]).floor(),
1089
+ this.containerPointToLayerPoint([size.x, size.y]).floor()
1090
+ ]).getSize().divideBy(scale * 2);
1091
+
1092
+ return new L.Bounds(pixelCenter.subtract(halfSize), pixelCenter.add(halfSize));
1093
+ },
1094
+
1095
+ /**
1096
+ * @since leaflet-rotate (v0.2)
1097
+ *
1098
+ * @return {L.Point} map pivot point (center)
1099
+ */
1100
+ _getPixelCenter: function() {
1101
+ if (!this._rotate && mapProto._getPixelCenter) {
1102
+ return mapProto._getPixelCenter.apply(this, arguments);
1103
+ }
1104
+ return this.getSize()._divideBy(2)._subtract(this._getMapPanePos());
1105
+ },
1106
+
1107
+ /**
1108
+ * @since leaflet-rotate (v0.2)
1109
+ *
1110
+ * @see src\layer\vector\Renderer::_update()
1111
+ */
1112
+ _getPaddedPixelBounds: function(padding) {
1113
+ if (!this._rotate && mapProto._getPaddedPixelBounds) {
1114
+ return mapProto._getPaddedPixelBounds.apply(this, arguments);
1115
+ }
1116
+ var p = padding,
1117
+ size = this.getSize(),
1118
+ padMin = size.multiplyBy(-p),
1119
+ padMax = size.multiplyBy(1 + p);
1120
+ //min = this.containerPointToLayerPoint(size.multiplyBy(-p)).round();
1121
+
1122
+ return new L.Bounds([
1123
+ this.containerPointToLayerPoint([padMin.x, padMin.y]).floor(),
1124
+ this.containerPointToLayerPoint([padMin.x, padMax.y]).floor(),
1125
+ this.containerPointToLayerPoint([padMax.x, padMin.y]).floor(),
1126
+ this.containerPointToLayerPoint([padMax.x, padMax.y]).floor()
1127
+ ]);
1128
+ },
1129
+
1130
+ _handleGeolocationResponse: function(pos) {
1131
+ if (!this._container._leaflet_id) { return; }
1132
+
1133
+ var lat = pos.coords.latitude,
1134
+ lng = pos.coords.longitude,
1135
+ /** @TODO use mapProto._handleGeolocationResponse */
1136
+ hdg = pos.coords.heading,
1137
+ latlng = new L.LatLng(lat, lng),
1138
+ bounds = latlng.toBounds(pos.coords.accuracy),
1139
+ options = this._locateOptions;
1140
+
1141
+ if (options.setView) {
1142
+ var zoom = this.getBoundsZoom(bounds);
1143
+ this.setView(latlng, options.maxZoom ? Math.min(zoom, options.maxZoom) : zoom);
1144
+ }
1145
+
1146
+ var data = {
1147
+ latlng: latlng,
1148
+ bounds: bounds,
1149
+ timestamp: pos.timestamp,
1150
+ /** @TODO use mapProto._handleGeolocationResponse */
1151
+ heading: hdg
1152
+ };
1153
+
1154
+ for (var i in pos.coords) {
1155
+ if (typeof pos.coords[i] === 'number') {
1156
+ data[i] = pos.coords[i];
1157
+ }
1158
+ }
1159
+
1160
+ // @event locationfound: LocationEvent
1161
+ // Fired when geolocation (using the [`locate`](#map-locate) method)
1162
+ // went successfully.
1163
+ this.fire('locationfound', data);
1164
+ },
1165
+
1166
+ /**
1167
+ * @see https://github.com/ronikar/Leaflet/blob/5c480ef959b947c3beed7065425a5a36c486262b/src/geo/LatLngBounds.js#L253-L264
1168
+ *
1169
+ * @param {L.Bounds} points
1170
+ * @returns {L.Bounds}
1171
+ */
1172
+ // toCircumscribedBounds(points) {
1173
+ // var minX = points.reduce(function (pv, v) { return Math.min(pv, v.x); }, points[0].x),
1174
+ // maxX = points.reduce(function (pv, v) { return Math.max(pv, v.x); }, points[0].x),
1175
+ // minY = points.reduce(function (pv, v) { return Math.min(pv, v.y); }, points[0].y),
1176
+ // maxY = points.reduce(function (pv, v) { return Math.max(pv, v.y); }, points[0].y);
1177
+ //
1178
+ // return L.bounds(L.point(minX, minY), L.point(maxX, maxY));
1179
+ // },
1180
+
1181
+ });
1182
+
1183
+ /**
1184
+ * Rotates the map according to a smartphone's compass.
1185
+ *
1186
+ * @typedef L.Map.CompassBearing
1187
+ */
1188
+
1189
+ L.Map.CompassBearing = L.Handler.extend({
1190
+
1191
+ initialize: function(map) {
1192
+ this._map = map;
1193
+ this._throttled = L.Util.throttle(this._onDeviceOrientation, 1000, this);
1194
+ },
1195
+
1196
+ addHooks: function() {
1197
+ if (this._map._rotate && window.DeviceOrientationEvent) {
1198
+ L.DomEvent.on(window, 'deviceorientation', this._throttled, this);
1199
+ }
1200
+ },
1201
+
1202
+ removeHooks: function() {
1203
+ if (this._map._rotate && window.DeviceOrientationEvent) {
1204
+ L.DomEvent.off(window, 'deviceorientation', this._throttled, this);
1205
+ }
1206
+ },
1207
+
1208
+ _onDeviceOrientation: function(event) {
1209
+ if (event.alpha !== null && window.orientation !== undefined) {
1210
+ this._map.setBearing(event.alpha - window.orientation);
1211
+ }
1212
+ },
1213
+
1214
+ });
1215
+
1216
+ // @section Handlers
1217
+ // @property compassBearing: Handler
1218
+ // Compass bearing handler.
1219
+ L.Map.addInitHook('addHandler', 'compassBearing', L.Map.CompassBearing);
1220
+
1221
+ /**
1222
+ * Triggers `invalidateResize` when the map's DOM container mutates.
1223
+ *
1224
+ * @typedef L.Map.ContainerMutation
1225
+ */
1226
+
1227
+ // @namespace Map
1228
+ // @section Interaction Options
1229
+ L.Map.mergeOptions({
1230
+
1231
+ // @option trackContainerMutation: Boolean = false
1232
+ // Whether the map uses [mutation observers](https://developer.mozilla.org/docs/Web/API/MutationObserver)
1233
+ // to detect changes in its container and trigger `invalidateSize`. Disabled
1234
+ // by default due to support not being available in all web browsers.
1235
+ trackContainerMutation: false
1236
+
1237
+ });
1238
+
1239
+ L.Map.ContainerMutation = L.Handler.extend({
1240
+
1241
+ addHooks: function() {
1242
+ /** @TODO check again this property. Will likely be added in future releases (leaflet > v1.9.3) */
1243
+ // if (!L.Browser.mutation) {
1244
+ // return;
1245
+ // }
1246
+
1247
+ if (!this._observer) {
1248
+ this._observer = new MutationObserver(L.Util.bind(this._onMutation, this));
1249
+ }
1250
+
1251
+ this._observer.observe(this._map.getContainer(), {
1252
+ childList: false,
1253
+ attributes: true,
1254
+ characterData: false,
1255
+ subtree: false,
1256
+ attributeFilter: ['style']
1257
+ });
1258
+ },
1259
+
1260
+ removeHooks: function() {
1261
+ /** @TODO check again this property. Will likely be added in future releases (leaflet > v1.9.3) */
1262
+ // if (!L.Browser.mutation) {
1263
+ // return;
1264
+ // }
1265
+ this._observer.disconnect();
1266
+ },
1267
+
1268
+ _onMutation: function() {
1269
+ this._map.invalidateSize();
1270
+ },
1271
+
1272
+ });
1273
+
1274
+ // @section Handlers
1275
+ // @property containerMutation: Handler
1276
+ // Container mutation handler (disabled unless [`trackContainerMutation`](#map-trackcontainermutation) is set).
1277
+ L.Map.addInitHook('addHandler', 'trackContainerMutation', L.Map.ContainerMutation);
1278
+
1279
+ /**
1280
+ * TouchGestures is both TouchZoom plus TouchRotate
1281
+ *
1282
+ * @see https://github.com/fnicollet/Leaflet/commit/a77af51a6b10f308d1b9a16552091d1d0aee8834
1283
+ * @see https://github.com/Leaflet/Leaflet/blob/v1.9.3/src/map/handler/Map.TouchZoom.js
1284
+ *
1285
+ * @typedef L.Map.TouchGestures
1286
+ */
1287
+
1288
+ // @namespace Map
1289
+ // @section Interaction Options
1290
+ L.Map.mergeOptions({
1291
+
1292
+ // @option bounceAtZoomLimits: Boolean = true
1293
+ // Set it to false if you don't want the map to zoom beyond min/max zoom
1294
+ // and then bounce back when pinch-zooming.
1295
+ bounceAtZoomLimits: true,
1296
+
1297
+ });
1298
+
1299
+ L.Map.TouchGestures = L.Handler.extend({
1300
+
1301
+ initialize: function(map) {
1302
+ this._map = map;
1303
+ this.rotate = !!this._map.options.touchRotate;
1304
+ this.zoom = !!this._map.options.touchZoom;
1305
+ },
1306
+
1307
+ addHooks: function() {
1308
+ L.DomEvent.on(this._map._container, 'touchstart', this._onTouchStart, this);
1309
+ },
1310
+
1311
+ removeHooks: function() {
1312
+ L.DomEvent.off(this._map._container, 'touchstart', this._onTouchStart, this);
1313
+ },
1314
+
1315
+ _onTouchStart: function(e) {
1316
+ var map = this._map;
1317
+
1318
+ if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming || this._rotating) { return; }
1319
+
1320
+ var p1 = map.mouseEventToContainerPoint(e.touches[0]),
1321
+ p2 = map.mouseEventToContainerPoint(e.touches[1]),
1322
+ vector = p1.subtract(p2);
1323
+
1324
+ this._centerPoint = map.getSize()._divideBy(2);
1325
+ this._startLatLng = map.containerPointToLatLng(this._centerPoint);
1326
+
1327
+ if (this.zoom) {
1328
+ if (map.options.touchZoom !== 'center') {
1329
+ this._pinchStartLatLng = map.containerPointToLatLng(p1.add(p2)._divideBy(2));
1330
+ }
1331
+ this._startDist = p1.distanceTo(p2);
1332
+ this._startZoom = map.getZoom();
1333
+ this._zooming = true;
1334
+ } else {
1335
+ this._zooming = false;
1336
+ }
1337
+
1338
+ if (this.rotate) {
1339
+ this._startTheta = Math.atan(vector.x / vector.y);
1340
+ this._startBearing = map.getBearing();
1341
+ if (vector.y < 0) { this._startBearing += 180; }
1342
+ this._rotating = true;
1343
+ } else {
1344
+ this._rotating = false;
1345
+ }
1346
+
1347
+ this._moved = false;
1348
+
1349
+ map._stop();
1350
+
1351
+ L.DomEvent
1352
+ .on(document, 'touchmove', this._onTouchMove, this)
1353
+ .on(document, 'touchend touchcancel', this._onTouchEnd, this);
1354
+
1355
+ L.DomEvent.preventDefault(e);
1356
+ },
1357
+
1358
+ _onTouchMove: function(e) {
1359
+ if (!e.touches || e.touches.length !== 2 || !(this._zooming || this._rotating)) { return; }
1360
+
1361
+ var map = this._map,
1362
+ p1 = map.mouseEventToContainerPoint(e.touches[0]),
1363
+ p2 = map.mouseEventToContainerPoint(e.touches[1]),
1364
+ vector = p1.subtract(p2),
1365
+ scale = p1.distanceTo(p2) / this._startDist,
1366
+ delta;
1367
+
1368
+ if (this._rotating) {
1369
+ var theta = Math.atan(vector.x / vector.y);
1370
+ var bearingDelta = (theta - this._startTheta) * L.DomUtil.RAD_TO_DEG;
1371
+ if (vector.y < 0) { bearingDelta += 180; }
1372
+ if (bearingDelta) {
1373
+ /// TODO: The pivot should be the last touch point, but zoomAnimation manages to
1374
+ /// overwrite the rotate pane position. Maybe related to #3529.
1375
+ map.setBearing(this._startBearing - bearingDelta);
1376
+ }
1377
+ }
1378
+
1379
+ if (this._zooming) {
1380
+ this._zoom = map.getScaleZoom(scale, this._startZoom);
1381
+
1382
+ if (!map.options.bounceAtZoomLimits && (
1383
+ (this._zoom < map.getMinZoom() && scale < 1) ||
1384
+ (this._zoom > map.getMaxZoom() && scale > 1))) {
1385
+ this._zoom = map._limitZoom(this._zoom);
1386
+ }
1387
+
1388
+ if (map.options.touchZoom === 'center') {
1389
+ this._center = this._startLatLng;
1390
+ if (scale === 1) { return; }
1391
+ } else {
1392
+ // Get delta from pinch to center, so centerLatLng is delta applied to initial pinchLatLng
1393
+ delta = p1._add(p2)._divideBy(2)._subtract(this._centerPoint);
1394
+ if (scale === 1 && delta.x === 0 && delta.y === 0) { return; }
1395
+
1396
+ var alpha = -map.getBearing() * L.DomUtil.DEG_TO_RAD;
1397
+
1398
+ this._center = map.unproject(map.project(this._pinchStartLatLng).subtract(delta.rotate(alpha)));
1399
+ }
1400
+
1401
+ }
1402
+
1403
+ if (!this._moved) {
1404
+ map._moveStart(true, false);
1405
+ this._moved = true;
1406
+ }
1407
+
1408
+ L.Util.cancelAnimFrame(this._animRequest);
1409
+
1410
+ var moveFn = map._move.bind(map, this._center, this._zoom, { pinch: true, round: false }, undefined);
1411
+ this._animRequest = L.Util.requestAnimFrame(moveFn, this, true);
1412
+
1413
+ L.DomEvent.preventDefault(e);
1414
+ },
1415
+
1416
+ _onTouchEnd: function() {
1417
+ if (!this._moved || !(this._zooming || this._rotating)) {
1418
+ this._zooming = false;
1419
+ return;
1420
+ }
1421
+
1422
+ this._zooming = false;
1423
+ this._rotating = false;
1424
+ L.Util.cancelAnimFrame(this._animRequest);
1425
+
1426
+ L.DomEvent
1427
+ .off(document, 'touchmove', this._onTouchMove, this)
1428
+ .off(document, 'touchend touchcancel', this._onTouchEnd, this);
1429
+
1430
+ if (this.zoom) {
1431
+ // Pinch updates GridLayers' levels only when zoomSnap is off, so zoomSnap becomes noUpdate.
1432
+ if (this._map.options.zoomAnimation) {
1433
+ this._map._animateZoom(this._center, this._map._limitZoom(this._zoom), true, this._map.options.zoomSnap);
1434
+ } else {
1435
+ this._map._resetView(this._center, this._map._limitZoom(this._zoom));
1436
+ }
1437
+ }
1438
+ },
1439
+
1440
+ });
1441
+
1442
+ // @section Handlers
1443
+ // @property touchGestures: Handler
1444
+ // Touch gestures handler.
1445
+ L.Map.addInitHook('addHandler', 'touchGestures', L.Map.TouchGestures);
1446
+
1447
+ /**
1448
+ * Rotates the map on two-finger (touch devices).
1449
+ *
1450
+ * @typedef L.Map.TouchRotate
1451
+ */
1452
+
1453
+ // @namespace Map
1454
+ // @section Interaction Options
1455
+ L.Map.mergeOptions({
1456
+
1457
+ // @section Touch interaction options
1458
+ // @option touchRotate: Boolean|String = *
1459
+ // Whether the map can be rotated with a two-finger rotation gesture
1460
+ touchRotate: false,
1461
+
1462
+ });
1463
+
1464
+ L.Map.TouchRotate = L.Handler.extend({
1465
+
1466
+ addHooks: function() {
1467
+ this._map.touchGestures.enable();
1468
+ this._map.touchGestures.rotate = true;
1469
+ },
1470
+
1471
+ removeHooks: function() {
1472
+ this._map.touchGestures.rotate = false;
1473
+ },
1474
+
1475
+ });
1476
+
1477
+ // @section Handlers
1478
+ // @property touchZoom: Handler
1479
+ // Touch rotate handler.
1480
+ L.Map.addInitHook('addHandler', 'touchRotate', L.Map.TouchRotate);
1481
+
1482
+ /**
1483
+ * Rotates the map on shift key + mouseheel scrolling (desktop).
1484
+ *
1485
+ * @typedef L.Map.ShiftKeyRotate
1486
+ */
1487
+
1488
+ // @namespace Map
1489
+ // @section Interaction Options
1490
+ L.Map.mergeOptions({
1491
+
1492
+ // @section ShiftKey interaction options
1493
+ // @option shiftKeyRotate: Boolean|String = *
1494
+ // Whether the map can be rotated with a shit-wheel rotation
1495
+ shiftKeyRotate: true,
1496
+
1497
+ });
1498
+
1499
+ L.Map.ShiftKeyRotate = L.Handler.extend({
1500
+
1501
+ addHooks: function() {
1502
+ L.DomEvent.on(this._map._container, "wheel", this._handleShiftScroll, this);
1503
+ // this._map.shiftKeyRotate.enable();
1504
+ this._map.shiftKeyRotate.rotate = true;
1505
+ },
1506
+
1507
+ removeHooks: function() {
1508
+ L.DomEvent.off(this._map._container, "wheel", this._handleShiftScroll, this);
1509
+ this._map.shiftKeyRotate.rotate = false;
1510
+ },
1511
+
1512
+ _handleShiftScroll: function(e) {
1513
+ if (e.shiftKey) {
1514
+ e.preventDefault();
1515
+ this._map.scrollWheelZoom.disable();
1516
+ this._map.setBearing((this._map._bearing * L.DomUtil.RAD_TO_DEG) + Math.sign(e.deltaY) * 5);
1517
+ } else {
1518
+ this._map.scrollWheelZoom.enable();
1519
+ }
1520
+ },
1521
+
1522
+ });
1523
+
1524
+ // @section Handlers
1525
+ // @property touchZoom: Handler
1526
+ // Touch rotate handler.
1527
+ L.Map.addInitHook('addHandler', 'shiftKeyRotate', L.Map.ShiftKeyRotate);
1528
+
1529
+ // decrease "scrollWheelZoom" handler priority over "shiftKeyRotate" handler
1530
+ L.Map.addInitHook(function() {
1531
+ if (this.scrollWheelZoom.enabled() && this.shiftKeyRotate.enabled()) {
1532
+ this.scrollWheelZoom.disable();
1533
+ this.scrollWheelZoom.enable();
1534
+ }
1535
+ });
1536
+
1537
+ /**
1538
+ * Adds pinch zoom rotation on mobile browsers
1539
+ *
1540
+ * @see https://github.com/Leaflet/Leaflet/blob/v1.9.3/src/map/handler/Map.TouchZoom.js
1541
+ *
1542
+ * @external L.Map.TouchZoom
1543
+ */
1544
+
1545
+ // @namespace Map
1546
+ // @section Interaction Options
1547
+ L.Map.mergeOptions({
1548
+
1549
+ // @section Touch interaction options
1550
+ // @option touchZoom: Boolean|String = *
1551
+ // Whether the map can be zoomed by touch-dragging with two fingers. If
1552
+ // passed `'center'`, it will zoom to the center of the view regardless of
1553
+ // where the touch events (fingers) were. Enabled for touch-capable web
1554
+ // browsers.
1555
+ touchZoom: L.Browser.touch,
1556
+
1557
+ /**
1558
+ * @TODO check if this is a duplicate of `L.Map.TouchGestures::bounceAtZoomLimits`
1559
+ */
1560
+
1561
+ // @option bounceAtZoomLimits: Boolean = true
1562
+ // Set it to false if you don't want the map to zoom beyond min/max zoom
1563
+ // and then bounce back when pinch-zooming.
1564
+ bounceAtZoomLimits: false,
1565
+
1566
+ });
1567
+
1568
+ L.Map.TouchZoom = L.Handler.extend({
1569
+
1570
+ addHooks: function() {
1571
+ L.DomUtil.addClass(this._map._container, 'leaflet-touch-zoom');
1572
+ this._map.touchGestures.enable();
1573
+ this._map.touchGestures.zoom = true;
1574
+ },
1575
+
1576
+ removeHooks: function() {
1577
+ L.DomUtil.removeClass(this._map._container, 'leaflet-touch-zoom');
1578
+ this._map.touchGestures.zoom = false;
1579
+ },
1580
+
1581
+ });
1582
+
1583
+ // @section Handlers
1584
+ // @property touchZoom: Handler
1585
+ // Touch zoom handler.
1586
+ L.Map.addInitHook('addHandler', 'touchZoom', L.Map.TouchZoom);
1587
+
1588
+ /**
1589
+ * A tri-state control for map rotation, states are:
1590
+ *
1591
+ * - Locked (default)
1592
+ * - Unlocked (user can pinch-rotate)
1593
+ * - Follow (rotation follows device orientation, if available)
1594
+ *
1595
+ * @typedef L.Control.Rotate
1596
+ */
1597
+
1598
+ L.Control.Rotate = L.Control.extend({
1599
+
1600
+ options: {
1601
+ position: 'topleft',
1602
+ closeOnZeroBearing: true
1603
+ },
1604
+
1605
+ onAdd: function(map) {
1606
+ this._onDeviceOrientation = L.Util.throttle(this._unthrottledOnDeviceOrientation, 100, this);
1607
+
1608
+ var container = this._container = L.DomUtil.create('div', 'leaflet-control-rotate leaflet-bar');
1609
+
1610
+ // this.button = L.Control.Zoom.prototype._createButton.call(this, 'R', 'leaflet-control-rotate', 'leaflet-control-rotate', container, this._toggleLock);
1611
+
1612
+ var arrow = this._arrow = L.DomUtil.create('span', 'leaflet-control-rotate-arrow');
1613
+
1614
+ arrow.style.backgroundImage = `url("data:image/svg+xml;charset=utf-8,%3Csvg width='29' height='29' viewBox='0 0 29 29' xmlns='http://www.w3.org/2000/svg' fill='%23333'%3E%3Cpath d='M10.5 14l4-8 4 8h-8z'/%3E%3Cpath d='M10.5 16l4 8 4-8h-8z' fill='%23ccc'/%3E%3C/svg%3E")`;
1615
+ arrow.style.cursor = 'grab';
1616
+ arrow.style.display = 'block';
1617
+ arrow.style.width = '100%';
1618
+ arrow.style.height = '100%';
1619
+ arrow.style.backgroundRepeat = 'no-repeat';
1620
+ arrow.style.backgroundPosition = '50%';
1621
+
1622
+ // Copy-pasted from L.Control.Zoom
1623
+ var link = this._link = L.DomUtil.create('a', 'leaflet-control-rotate-toggle', container);
1624
+ link.appendChild(arrow);
1625
+ link.href = '#';
1626
+ link.title = 'Rotate map';
1627
+ // link.draggable = false;
1628
+
1629
+ L.DomEvent
1630
+ .on(link, 'dblclick', L.DomEvent.stopPropagation)
1631
+ .on(link, 'mousedown', this._handleMouseDown, this)
1632
+ .on(link, 'click', L.DomEvent.stop)
1633
+ .on(link, 'click', this._cycleState, this)
1634
+ .on(link, 'click', this._refocusOnMap, this);
1635
+
1636
+ if (!L.Browser.any3d) {
1637
+ L.DomUtil.addClass(link, 'leaflet-disabled');
1638
+ }
1639
+
1640
+ this._restyle();
1641
+
1642
+ map.on('rotate', this._restyle.bind(this));
1643
+
1644
+ // State flag
1645
+ this._follow = false;
1646
+ this._canFollow = false;
1647
+
1648
+ if (this.options.closeOnZeroBearing && map.getBearing() === 0) {
1649
+ container.style.display = 'none';
1650
+ }
1651
+
1652
+ return container;
1653
+ },
1654
+
1655
+ _handleMouseDown: function(e) {
1656
+ L.DomEvent.stop(e); // L.DomEvent.stopPropagation(e);
1657
+ this.dragging = true;
1658
+ this.dragstartX = e.pageX;
1659
+ this.dragstartY = e.pageY;
1660
+ L.DomEvent
1661
+ .on(document, 'mousemove', this._handleMouseDrag, this)
1662
+ .on(document, 'mouseup', this._handleMouseUp, this);
1663
+ },
1664
+
1665
+ _handleMouseUp: function(e) {
1666
+ L.DomEvent.stop(e); // L.DomEvent.stopPropagation(e);
1667
+ this.dragging = false;
1668
+
1669
+ L.DomEvent
1670
+ .off(document, 'mousemove', this._handleMouseDrag, this)
1671
+ .off(document, 'mouseup', this._handleMouseUp, this);
1672
+ },
1673
+
1674
+ _handleMouseDrag: function(e) {
1675
+ if (!this.dragging) { return; }
1676
+ var deltaX = e.clientX - this.dragstartX;
1677
+ this._map.setBearing(deltaX);
1678
+ },
1679
+
1680
+ _cycleState: function(ev) {
1681
+ var map = this._map;
1682
+
1683
+ if (!map) { return; }
1684
+
1685
+ if (!map.touchRotate.enabled() && !map.compassBearing.enabled()) {
1686
+ // Go from disabled to touch
1687
+ map.touchRotate.enable();
1688
+
1689
+ // console.log('state is now: touch rotate');
1690
+ } else {
1691
+
1692
+ if (!map.compassBearing.enabled()) {
1693
+ // Go from touch to compass
1694
+ map.touchRotate.disable();
1695
+ map.compassBearing.enable();
1696
+
1697
+ // console.log('state is now: compass');
1698
+
1699
+ // It is possible that compass is not supported. If so,
1700
+ // the hangler will automatically go from compass to disabled.
1701
+ } else {
1702
+ // Go from compass to disabled
1703
+ map.compassBearing.disable();
1704
+
1705
+ // console.log('state is now: locked');
1706
+
1707
+ map.setBearing(0);
1708
+ if (this.options.closeOnZeroBearing) {
1709
+ map.touchRotate.enable();
1710
+ }
1711
+ }
1712
+ }
1713
+ this._restyle();
1714
+ },
1715
+
1716
+ _restyle: function() {
1717
+ if (this._map.options.rotate) {
1718
+ var map = this._map;
1719
+ var bearing = map.getBearing();
1720
+ if (this.options.closeOnZeroBearing && bearing) {
1721
+ this._container.style.display = 'block';
1722
+ }
1723
+
1724
+ var cssTransform = 'rotate(' + bearing + 'deg)';
1725
+ this._arrow.style.transform = cssTransform;
1726
+
1727
+ if (map.compassBearing.enabled()) {
1728
+ this._link.style.backgroundColor = 'orange';
1729
+ } else if (map.touchRotate.enabled()) {
1730
+ this._link.style.backgroundColor = null;
1731
+ } else {
1732
+ this._link.style.backgroundColor = 'grey';
1733
+ if (this.options.closeOnZeroBearing && map.getBearing() === 0) {
1734
+ this._container.style.display = 'none';
1735
+ }
1736
+ }
1737
+ } else {
1738
+ L.DomUtil.addClass(this._link, 'leaflet-disabled');
1739
+ }
1740
+ },
1741
+
1742
+ });
1743
+
1744
+ L.control.rotate = function(options) {
1745
+ return new L.Control.Rotate(options);
1746
+ };
1747
+
1748
+ L.Map.mergeOptions({
1749
+ rotateControl: true,
1750
+ });
1751
+
1752
+ L.Map.addInitHook(function() {
1753
+ if (this.options.rotateControl) {
1754
+ var options = typeof this.options.rotateControl === 'object' ? this.options.rotateControl : {};
1755
+ this.rotateControl = L.control.rotate(options);
1756
+ this.addControl(this.rotateControl);
1757
+ }
1758
+ });
1759
+
1760
+ }));
2
1761
  //# sourceMappingURL=leaflet-rotate.js.map