leaflet-rotate 0.4.0 → 0.5.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.
- package/README.md +85 -5
- package/dist/leaflet-rotate-src.js +1325 -254
- package/dist/leaflet-rotate-src.js.map +4 -4
- package/dist/leaflet-rotate.js +1 -1
- package/dist/leaflet-rotate.js.map +4 -4
- package/lib/debug.js +62 -88
- package/lib/debug.min.js +16 -1
- package/misc/leaflet-heat-rotate.js +7 -0
- package/package.json +1 -1
- package/src/control/Rotate.js +160 -103
- package/src/dom/DomUtil.js +23 -12
- package/src/dom/Draggable.js +5 -0
- package/src/geo/LatLngBounds.js +5 -0
- package/src/geometry/Bounds.js +5 -0
- package/src/geometry/Point.js +17 -3
- package/src/index.js +4 -1
- package/src/layer/DivOverlay.js +34 -5
- package/src/layer/Popup.js +14 -3
- package/src/layer/Tooltip.js +16 -0
- package/src/layer/marker/Icon.js +12 -0
- package/src/layer/marker/Marker.js +86 -7
- package/src/layer/tile/GridLayer.js +38 -3
- package/src/layer/vector/Renderer.js +88 -34
- package/src/map/Map.js +578 -116
- package/src/map/handler/CompassBearing.js +34 -1
- package/src/map/handler/ContainerMutation.js +18 -1
- package/src/map/handler/ShiftKeyRotate.js +204 -13
- package/src/map/handler/TouchGestures.js +108 -10
- package/src/map/handler/TouchPitch.js +53 -0
- package/src/map/handler/TouchRotate.js +18 -1
- package/src/map/handler/TouchZoom.js +21 -1
- package/test/features/example.feature +8 -4
- package/test/features/gestures.feature +4 -0
- package/test/features/rotation.feature +60 -0
- package/test/index.html +342 -186
- package/test/steps/bearing.steps.js +535 -0
- package/test/steps/example.steps.js +56 -29
- package/test/steps/gestures.steps.js +55 -0
- package/test/support/hooks.js +4 -16
package/lib/debug.js
CHANGED
|
@@ -8,21 +8,31 @@
|
|
|
8
8
|
this._map = map;
|
|
9
9
|
|
|
10
10
|
document.head.insertAdjacentHTML('beforeend', `<style>
|
|
11
|
-
.leaflet-rotate-
|
|
12
|
-
|
|
11
|
+
.leaflet-rotate-tools {
|
|
12
|
+
display: flex;
|
|
13
|
+
flex-wrap: wrap;
|
|
14
|
+
align-items: center;
|
|
15
|
+
gap: 4px 16px;
|
|
16
|
+
margin-bottom: 6px;
|
|
13
17
|
}
|
|
14
|
-
.leaflet-rotate-
|
|
15
|
-
|
|
16
|
-
overflow: hidden;
|
|
17
|
-
/* max-width: 7em; */
|
|
18
|
-
text-overflow: ellipsis;
|
|
19
|
-
/* white-space: nowrap; */
|
|
18
|
+
.leaflet-rotate-tools input, .leaflet-rotate-tools button {
|
|
19
|
+
vertical-align: middle;
|
|
20
20
|
}
|
|
21
|
-
.leaflet-rotate-info
|
|
22
|
-
|
|
21
|
+
.leaflet-rotate-info {
|
|
22
|
+
border-collapse: collapse;
|
|
23
23
|
}
|
|
24
|
-
.leaflet-rotate-info th {
|
|
24
|
+
.leaflet-rotate-info td, .leaflet-rotate-info th {
|
|
25
|
+
padding: 1px 12px 1px 0;
|
|
25
26
|
text-align: left;
|
|
27
|
+
white-space: nowrap;
|
|
28
|
+
vertical-align: top;
|
|
29
|
+
}
|
|
30
|
+
.leaflet-rotate-info td:nth-of-type(1), .leaflet-rotate-info td:nth-of-type(2) {
|
|
31
|
+
font-family: monospace;
|
|
32
|
+
min-width: 12ch;
|
|
33
|
+
}
|
|
34
|
+
.leaflet-rotate-info tr[title] {
|
|
35
|
+
cursor: help;
|
|
26
36
|
}
|
|
27
37
|
div.crosshair {
|
|
28
38
|
position:absolute;
|
|
@@ -115,73 +125,35 @@
|
|
|
115
125
|
`<div class="crosshair c1"></div><div class="crosshair c2"></div>`
|
|
116
126
|
);
|
|
117
127
|
|
|
118
|
-
|
|
128
|
+
const html = `
|
|
129
|
+
<div class="leaflet-rotate-tools">
|
|
130
|
+
<span>Bearing
|
|
131
|
+
${[0, 15, 30, 45, 60, 75, 90, 180].map(b => `<button onclick="L.Rotate._map.setBearing(${b});" title="map.setBearing(${b});">${b}</button>`).join('')}
|
|
132
|
+
<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>
|
|
133
|
+
</span>
|
|
134
|
+
<label>Bounds padding <input title="increase/decrease padding bounds" type="range" min="-0.25" max="0" value="0" step="0.01" name="pad" id='pad_input' /></label>
|
|
135
|
+
<span>Markers
|
|
136
|
+
<button onclick="L.Rotate.randomRotateMarkers(L.Rotate._map);" title="markers.setRotation(Math.random() * Math.PI / 30 + 0.1)">Rotate</button>
|
|
137
|
+
<button onclick="L.Rotate.randomMarkers(L.Rotate._map, 100);" title="add 100 random markers to map">Add 100</button>
|
|
138
|
+
</span>
|
|
139
|
+
</div>
|
|
119
140
|
<table class="leaflet-rotate-info">
|
|
120
|
-
<tr>
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
<
|
|
135
|
-
|
|
136
|
-
</td>
|
|
137
|
-
<td>
|
|
138
|
-
<button onclick="L.Rotate.randomRotateMarkers(L.Rotate._map);" title="markers.setRotation(Math.random() * Math.PI / 30 + 0.1)">Rotate Markers</button>
|
|
139
|
-
<button onclick="L.Rotate.randomMarkers(L.Rotate._map, 100);" title="add 100 random markers to map">Add Markers</button>
|
|
140
|
-
</td>
|
|
141
|
-
</tr>
|
|
142
|
-
<tr>
|
|
143
|
-
<th>LatLng </th>
|
|
144
|
-
<td id='llx'></td>
|
|
145
|
-
<td id='lly'></td>
|
|
146
|
-
<td class='long'>LatLng of mouse pointer</td>
|
|
147
|
-
</tr>
|
|
148
|
-
<tr>
|
|
149
|
-
<th>Rel to pane </th>
|
|
150
|
-
<td id='lyx'></td>
|
|
151
|
-
<td id='lyy'></td>
|
|
152
|
-
<td class='long'>Mouse pointer pixel coords relative to _rotatePane</td>
|
|
153
|
-
</tr>
|
|
154
|
-
<tr>
|
|
155
|
-
<th>Rel to container</th>
|
|
156
|
-
<td id='cnx'></td>
|
|
157
|
-
<td id='cny'></td>
|
|
158
|
-
<td class='long'>Mouse pointer coords relative to map <div></td>
|
|
159
|
-
</tr>
|
|
160
|
-
<tr>
|
|
161
|
-
<th>Pivot </th>
|
|
162
|
-
<td id='pvx'></td>
|
|
163
|
-
<td id='pvy'></td>
|
|
164
|
-
<td class='long'>Last _rotationPane pivot pixel coords relative to _rotationPane</td>
|
|
165
|
-
</tr>
|
|
166
|
-
<tr>
|
|
167
|
-
<th>Pane offset </th>
|
|
168
|
-
<td id='pox'></td>
|
|
169
|
-
<td id='poy'></td>
|
|
170
|
-
<td class='long'>Pixel offset of _mapPane</td>
|
|
171
|
-
</tr>
|
|
172
|
-
<tr>
|
|
173
|
-
<th>Pixel origin </th>
|
|
174
|
-
<td id='ogx'></td>
|
|
175
|
-
<td id='ogy'></td>
|
|
176
|
-
<td class='long'>Negative pixel coords of the (0,0) CRS point relative to _rotatePane</td>
|
|
177
|
-
</tr>
|
|
178
|
-
<tr>
|
|
179
|
-
<th>Pixel bounds </th>
|
|
180
|
-
<td id='ogbx'></td>
|
|
181
|
-
<td id='ogby'></td>
|
|
182
|
-
<td class='long'>Bounds of the current map view in projected pixel coordinates {min, max}.</td>
|
|
183
|
-
</tr>
|
|
184
|
-
</table>`);
|
|
141
|
+
<tr><th></th><th>x / lng</th><th>y / lat</th></tr>
|
|
142
|
+
${[
|
|
143
|
+
['LatLng', 'LatLng of mouse pointer', 'llx', 'lly'],
|
|
144
|
+
['Rel to pane', 'Mouse pointer pixel coords relative to _rotatePane', 'lyx', 'lyy'],
|
|
145
|
+
['Rel to container', 'Mouse pointer coords relative to map <div>', 'cnx', 'cny'],
|
|
146
|
+
['Pivot', 'Last _rotationPane pivot pixel coords relative to _rotationPane', 'pvx', 'pvy'],
|
|
147
|
+
['Pane offset', 'Pixel offset of _mapPane', 'pox', 'poy'],
|
|
148
|
+
['Pixel origin', 'Negative pixel coords of the (0,0) CRS point relative to _rotatePane', 'ogx', 'ogy'],
|
|
149
|
+
['Pixel bounds (min / max)', 'Bounds of the current map view in projected pixel coordinates {min, max}', 'ogbx', 'ogby'],
|
|
150
|
+
].map(([label, title, x, y]) => `<tr title="${title.replace('<', '<').replace('>', '>')}"><th>${label}</th><td id='${x}'></td><td id='${y}'></td></tr>`).join('')}
|
|
151
|
+
</table>`;
|
|
152
|
+
if (options.container) {
|
|
153
|
+
options.container.insertAdjacentHTML('beforeend', `<details open><summary>Debug</summary>${html}</details>`);
|
|
154
|
+
} else {
|
|
155
|
+
map._container.insertAdjacentHTML('afterend', '<hr>' + html);
|
|
156
|
+
}
|
|
185
157
|
|
|
186
158
|
map.___rotatePixelOrigin = L.DomUtil.create('div', 'pixelorigin', map._rotatePane);
|
|
187
159
|
map.___rotatePivotDot = L.DomUtil.create('div', 'pivot', map._mapPane);
|
|
@@ -260,12 +232,13 @@
|
|
|
260
232
|
},
|
|
261
233
|
|
|
262
234
|
logMouse(e) {
|
|
263
|
-
|
|
264
|
-
document.getElementById('
|
|
265
|
-
document.getElementById('
|
|
266
|
-
document.getElementById('
|
|
267
|
-
document.getElementById('
|
|
268
|
-
document.getElementById('
|
|
235
|
+
const fmt = (n, d) => +n.toFixed(d);
|
|
236
|
+
document.getElementById('llx').innerHTML = fmt(e.latlng.lng, 6);
|
|
237
|
+
document.getElementById('lly').innerHTML = fmt(e.latlng.lat, 6);
|
|
238
|
+
document.getElementById('lyx').innerHTML = fmt(e.layerPoint.x, 1);
|
|
239
|
+
document.getElementById('lyy').innerHTML = fmt(e.layerPoint.y, 1);
|
|
240
|
+
document.getElementById('cnx').innerHTML = fmt(e.containerPoint.x, 1);
|
|
241
|
+
document.getElementById('cny').innerHTML = fmt(e.containerPoint.y, 1);
|
|
269
242
|
},
|
|
270
243
|
|
|
271
244
|
/**
|
|
@@ -288,8 +261,8 @@
|
|
|
288
261
|
*/
|
|
289
262
|
updatePixelOrigin() {
|
|
290
263
|
if (this._pixelOrigin) {
|
|
291
|
-
document.getElementById('ogx').innerHTML = this._pixelOrigin.x;
|
|
292
|
-
document.getElementById('ogy').innerHTML = this._pixelOrigin.y;
|
|
264
|
+
document.getElementById('ogx').innerHTML = +this._pixelOrigin.x.toFixed(1);
|
|
265
|
+
document.getElementById('ogy').innerHTML = +this._pixelOrigin.y.toFixed(1);
|
|
293
266
|
this.___rotatePixelOrigin.style.left = (-this._pixelOrigin.x - 1) + 'px';
|
|
294
267
|
this.___rotatePixelOrigin.style.top = (-this._pixelOrigin.y - 1) + 'px';
|
|
295
268
|
}
|
|
@@ -530,8 +503,9 @@
|
|
|
530
503
|
// ]);
|
|
531
504
|
// // paddedSize = paddedBounds.getSize();
|
|
532
505
|
|
|
533
|
-
|
|
534
|
-
document.getElementById('
|
|
506
|
+
const xy = p => `${+p.x.toFixed(1)}, ${+p.y.toFixed(1)}`;
|
|
507
|
+
document.getElementById('ogbx').innerHTML = xy(pixelBounds.min);
|
|
508
|
+
document.getElementById('ogby').innerHTML = xy(pixelBounds.max);
|
|
535
509
|
},
|
|
536
510
|
|
|
537
511
|
resetHeading(e) {
|
|
@@ -551,4 +525,4 @@
|
|
|
551
525
|
}
|
|
552
526
|
|
|
553
527
|
};
|
|
554
|
-
})();
|
|
528
|
+
})();
|
package/lib/debug.min.js
CHANGED
|
@@ -1 +1,16 @@
|
|
|
1
|
-
(function(){const e=Object.isExtensible(globalThis.L)?globalThis.L:Object.assign({},globalThis.L);globalThis.L=e,e.Rotate={debug(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=e.DomUtil.create("div","pixelorigin",t._rotatePane),t.___rotatePivotDot=e.DomUtil.create("div","pivot",t._mapPane),this.___rotateBounds=e.DomUtil.create("div","panebounds rotate",t._rotatePane),this.___mainBounds=e.DomUtil.create("div","panebounds main",t._mapPane),this.updatePaneBounds();const i=document.getElementById("rho_input"),o=document.getElementById("pad_input");if(i.addEventListener("change",e.Rotate.rotate.bind(this)),i.addEventListener("mousemove",e.Rotate.rotate.bind(this)),o.addEventListener("change",e.Rotate.displayCenter.bind(this)),o.addEventListener("mousemove",e.Rotate.displayCenter.bind(this)),o.addEventListener("change",e.Rotate.displayBounds.bind(this)),o.addEventListener("mousemove",e.Rotate.displayBounds.bind(this)),o.addEventListener("change",e.Rotate.displayLayersBounds.bind(this)),o.addEventListener("mousemove",e.Rotate.displayLayersBounds.bind(this)),o.addEventListener("change",e.Rotate.displayPixelBounds.bind(this)),o.addEventListener("mousemove",e.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),e.Browser.mobile&&t.compassBearing.enable(),e.version.startsWith("2.")){const a=t.getContainer();a.addEventListener("mousemove",function(r){const s=a.getBoundingClientRect(),d=new e.Point(r.clientX-s.left-a.clientLeft,r.clientY-s.top-a.clientTop);e.Rotate.logMouse.call(t,{latlng:t.containerPointToLatLng(d),layerPoint:t.containerPointToLayerPoint(d),containerPoint:d})})}else 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(t){console.log(t,t.type)},logMouse(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(t){return(t.type!=="mousemove"||t.buttons!==0)&&this._map.setBearing(t.target.valueAsNumber)},updatePaneBounds(){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(){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(){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(){const t=this._getMapPanePos();document.getElementById("pox").innerHTML=t.x,document.getElementById("poy").innerHTML=t.y},updateRhoInput(){document.getElementById("rho_input").value=this.getBearing()},rotateOneDegree(){const t=document.getElementById("rho_input").valueAsNumber++;this.setBearing(t<360?t:0)},randomRotateMarkers(t){const n=[],i=[];for(const a in t._layers)t._layers[a]instanceof e.Marker&&(n.push(t._layers[a]),i.push(Math.random()*Math.PI/30+.1));function o(a){for(let r=0;r<n.length;r++)n[r].setRotation(n[r].options.rotation+i[r]);--a&&setTimeout(o.bind(null,a),20)}o(25)},randomMarkers(t,n){t=this._map;const i=function(){for(let o=0;o<n;o++){const a=t.getBounds(),r=a.getSouthWest(),s=a.getNorthEast(),d=s.lng-r.lng,l=s.lat-r.lat,c=new e.Marker(new e.LatLng(r.lat+l*Math.random(),r.lng+d*Math.random()));e.Rotate.___cluster.addLayer(c)}};this.___cluster?typeof e.markerClusterGroup=="function"&&i():this.___cluster=import("https://unpkg.com/leaflet.markercluster@1.4.1/src/index.js").then(o=>{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,o),this.___cluster=e.markerClusterGroup({spiderfyOnMaxZoom:!1,showCoverageOnHover:!1,zoomToBoundsOnClick:!1}).on("clusterclick",a=>a.layer.spiderfy()).addTo(t),i()})},displayCenter(){const t=this._map;this.___centerMarker&&this.___centerMarker.remove(),this.___centerMarker=new e.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(){const t=this._map;this.___bounds&&this.___bounds.remove();const n=document.getElementById("pad_input").valueAsNumber,i=t.getBounds().pad(n);this.___bounds=new e.Rectangle(i,{fill:!1,lineCap:"square",color:"yellow",dashArray:"5, 10",weight:2}).addTo(t)},displayLayersBounds(){for(const t of["path","polygon","circle"]){if(globalThis[t])this[`___${t}bounds`]&&this[`___${t}bounds`].remove();else continue;const i=globalThis[t].getBounds();this[`___${t}bounds`]=new e.FeatureGroup([new e.Rectangle(i,{fill:!1,lineCap:"square",color:"yellow",dashArray:"5, 10",weight:1,opacity:.5}),new e.Marker(i.getNorthWest(),{scale:.5,rotation:-45*(Math.PI/180),rotateWithView:!0}).bindTooltip("<b>NW</b>",{direction:"center"}),new e.Marker(i.getNorthEast(),{scale:.5,rotation:45*(Math.PI/180),rotateWithView:!0}).bindTooltip("<b>NE</b>",{direction:"center"}),new e.Marker(i.getSouthEast(),{scale:.5,rotation:135*(Math.PI/180),rotateWithView:!0}).bindTooltip("<b>SE</b>",{direction:"center"}),new e.Marker(i.getSouthWest(),{scale:.5,rotation:-135*(Math.PI/180),rotateWithView:!0}).bindTooltip("<b>SW</b>",{direction:"center"})]).addTo(map).eachLayer(o=>{o.getElement&&o.getElement()&&(o.getElement().style.filter="hue-rotate(150deg)")})}},displayPixelBounds(){const t=this._map;this.___corners&&this.___corners.remove(),this.___pixelbounds&&this.___pixelbounds.remove();const n=t.getSize(),i=t.getPixelBounds(),o=new e.Point(0,0),a=new e.Point(n.x,0),r=new e.Point(n.x,n.y),s=new e.Point(0,n.y),d=new e.Marker(t.containerPointToLatLng(o),{title:"Top Left corner",rotation:-45*(Math.PI/180)}),l=new e.Marker(t.containerPointToLatLng(a),{title:"Top Right corner",rotation:45*(Math.PI/180)}),c=new e.Marker(t.containerPointToLatLng(r),{title:"Bottom Right corner",rotation:135*(Math.PI/180)}),h=new e.Marker(t.containerPointToLatLng(s),{title:"Bottom Left corner",rotation:-135*(Math.PI/180)});this.___corners=new e.FeatureGroup([d,l,c,h]).addTo(t).eachLayer(u=>u.getElement().style.filter="hue-rotate(150deg)"),document.getElementById("ogbx").innerHTML=JSON.stringify(i.min),document.getElementById("ogby").innerHTML=JSON.stringify(i.max)},resetHeading(t){t.heading!==null&&this.setBearing(t.heading)},locate(){this._map.locate({setView:!0,enableHighAccuracy:!0})}}})();
|
|
1
|
+
(function(){const e=Object.isExtensible(globalThis.L)?globalThis.L:Object.assign({},globalThis.L);globalThis.L=e,e.Rotate={debug(t,o={}){this._map=t,document.head.insertAdjacentHTML("beforeend",'<style>.leaflet-rotate-tools{display:flex;flex-wrap:wrap;align-items:center;gap:4px 16px;margin-bottom:6px}.leaflet-rotate-tools input,.leaflet-rotate-tools button{vertical-align:middle}.leaflet-rotate-info{border-collapse:collapse}.leaflet-rotate-info td,.leaflet-rotate-info th{padding:1px 12px 1px 0;text-align:left;white-space:nowrap;vertical-align:top}.leaflet-rotate-info td:nth-of-type(1),.leaflet-rotate-info td:nth-of-type(2){font-family:monospace;min-width:12ch}.leaflet-rotate-info tr[title]{cursor:help}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>');const i=`
|
|
2
|
+
<div class="leaflet-rotate-tools">
|
|
3
|
+
<span>Bearing
|
|
4
|
+
${[0,15,30,45,60,75,90,180].map(n=>`<button onclick="L.Rotate._map.setBearing(${n});" title="map.setBearing(${n});">${n}</button>`).join("")}
|
|
5
|
+
<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>
|
|
6
|
+
</span>
|
|
7
|
+
<label>Bounds padding <input title="increase/decrease padding bounds" type="range" min="-0.25" max="0" value="0" step="0.01" name="pad" id='pad_input' /></label>
|
|
8
|
+
<span>Markers
|
|
9
|
+
<button onclick="L.Rotate.randomRotateMarkers(L.Rotate._map);" title="markers.setRotation(Math.random() * Math.PI / 30 + 0.1)">Rotate</button>
|
|
10
|
+
<button onclick="L.Rotate.randomMarkers(L.Rotate._map, 100);" title="add 100 random markers to map">Add 100</button>
|
|
11
|
+
</span>
|
|
12
|
+
</div>
|
|
13
|
+
<table class="leaflet-rotate-info">
|
|
14
|
+
<tr><th></th><th>x / lng</th><th>y / lat</th></tr>
|
|
15
|
+
${[["LatLng","LatLng of mouse pointer","llx","lly"],["Rel to pane","Mouse pointer pixel coords relative to _rotatePane","lyx","lyy"],["Rel to container","Mouse pointer coords relative to map <div>","cnx","cny"],["Pivot","Last _rotationPane pivot pixel coords relative to _rotationPane","pvx","pvy"],["Pane offset","Pixel offset of _mapPane","pox","poy"],["Pixel origin","Negative pixel coords of the (0,0) CRS point relative to _rotatePane","ogx","ogy"],["Pixel bounds (min / max)","Bounds of the current map view in projected pixel coordinates {min, max}","ogbx","ogby"]].map(([n,s,d,l])=>`<tr title="${s.replace("<","<").replace(">",">")}"><th>${n}</th><td id='${d}'></td><td id='${l}'></td></tr>`).join("")}
|
|
16
|
+
</table>`;o.container?o.container.insertAdjacentHTML("beforeend",`<details open><summary>Debug</summary>${i}</details>`):t._container.insertAdjacentHTML("afterend","<hr>"+i),t.___rotatePixelOrigin=e.DomUtil.create("div","pixelorigin",t._rotatePane),t.___rotatePivotDot=e.DomUtil.create("div","pivot",t._mapPane),this.___rotateBounds=e.DomUtil.create("div","panebounds rotate",t._rotatePane),this.___mainBounds=e.DomUtil.create("div","panebounds main",t._mapPane),this.updatePaneBounds();const r=document.getElementById("rho_input"),a=document.getElementById("pad_input");if(r.addEventListener("change",e.Rotate.rotate.bind(this)),r.addEventListener("mousemove",e.Rotate.rotate.bind(this)),a.addEventListener("change",e.Rotate.displayCenter.bind(this)),a.addEventListener("mousemove",e.Rotate.displayCenter.bind(this)),a.addEventListener("change",e.Rotate.displayBounds.bind(this)),a.addEventListener("mousemove",e.Rotate.displayBounds.bind(this)),a.addEventListener("change",e.Rotate.displayLayersBounds.bind(this)),a.addEventListener("mousemove",e.Rotate.displayLayersBounds.bind(this)),a.addEventListener("change",e.Rotate.displayPixelBounds.bind(this)),a.addEventListener("mousemove",e.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),e.Browser.mobile&&t.compassBearing.enable(),e.version.startsWith("2.")){const n=t.getContainer();n.addEventListener("mousemove",function(s){const d=n.getBoundingClientRect(),l=new e.Point(s.clientX-d.left-n.clientLeft,s.clientY-d.top-n.clientTop);e.Rotate.logMouse.call(t,{latlng:t.containerPointToLatLng(l),layerPoint:t.containerPointToLayerPoint(l),containerPoint:l})})}else t.on("mousemove",this.logMouse);t.on("locationfound",this.resetHeading),o.log&&t.on("click movestart move moveend zoomstart zoomend locationfound locationerror locationfound",this.logEvent)},logEvent(t){console.log(t,t.type)},logMouse(t){const o=(i,r)=>+i.toFixed(r);document.getElementById("llx").innerHTML=o(t.latlng.lng,6),document.getElementById("lly").innerHTML=o(t.latlng.lat,6),document.getElementById("lyx").innerHTML=o(t.layerPoint.x,1),document.getElementById("lyy").innerHTML=o(t.layerPoint.y,1),document.getElementById("cnx").innerHTML=o(t.containerPoint.x,1),document.getElementById("cny").innerHTML=o(t.containerPoint.y,1)},rotate(t){return(t.type!=="mousemove"||t.buttons!==0)&&this._map.setBearing(t.target.valueAsNumber)},updatePaneBounds(){const t=this._map.getSize();for(const o of[this.___rotateBounds,this.___mainBounds])o.style.width=t.x+"px",o.style.height=t.y+"px"},updatePixelOrigin(){this._pixelOrigin&&(document.getElementById("ogx").innerHTML=+this._pixelOrigin.x.toFixed(1),document.getElementById("ogy").innerHTML=+this._pixelOrigin.y.toFixed(1),this.___rotatePixelOrigin.style.left=-this._pixelOrigin.x-1+"px",this.___rotatePixelOrigin.style.top=-this._pixelOrigin.y-1+"px")},updatePivotDot(){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(){const t=this._getMapPanePos();document.getElementById("pox").innerHTML=t.x,document.getElementById("poy").innerHTML=t.y},updateRhoInput(){document.getElementById("rho_input").value=this.getBearing()},rotateOneDegree(){const t=document.getElementById("rho_input").valueAsNumber++;this.setBearing(t<360?t:0)},randomRotateMarkers(t){const o=[],i=[];for(const a in t._layers)t._layers[a]instanceof e.Marker&&(o.push(t._layers[a]),i.push(Math.random()*Math.PI/30+.1));function r(a){for(let n=0;n<o.length;n++)o[n].setRotation(o[n].options.rotation+i[n]);--a&&setTimeout(r.bind(null,a),20)}r(25)},randomMarkers(t,o){t=this._map;const i=function(){for(let r=0;r<o;r++){const a=t.getBounds(),n=a.getSouthWest(),s=a.getNorthEast(),d=s.lng-n.lng,l=s.lat-n.lat,h=new e.Marker(new e.LatLng(n.lat+l*Math.random(),n.lng+d*Math.random()));e.Rotate.___cluster.addLayer(h)}};this.___cluster?typeof e.markerClusterGroup=="function"&&i():this.___cluster=import("https://unpkg.com/leaflet.markercluster@1.4.1/src/index.js").then(r=>{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,r),this.___cluster=e.markerClusterGroup({spiderfyOnMaxZoom:!1,showCoverageOnHover:!1,zoomToBoundsOnClick:!1}).on("clusterclick",a=>a.layer.spiderfy()).addTo(t),i()})},displayCenter(){const t=this._map;this.___centerMarker&&this.___centerMarker.remove(),this.___centerMarker=new e.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(){const t=this._map;this.___bounds&&this.___bounds.remove();const o=document.getElementById("pad_input").valueAsNumber,i=t.getBounds().pad(o);this.___bounds=new e.Rectangle(i,{fill:!1,lineCap:"square",color:"yellow",dashArray:"5, 10",weight:2}).addTo(t)},displayLayersBounds(){for(const t of["path","polygon","circle"]){if(globalThis[t])this[`___${t}bounds`]&&this[`___${t}bounds`].remove();else continue;const i=globalThis[t].getBounds();this[`___${t}bounds`]=new e.FeatureGroup([new e.Rectangle(i,{fill:!1,lineCap:"square",color:"yellow",dashArray:"5, 10",weight:1,opacity:.5}),new e.Marker(i.getNorthWest(),{scale:.5,rotation:-45*(Math.PI/180),rotateWithView:!0}).bindTooltip("<b>NW</b>",{direction:"center"}),new e.Marker(i.getNorthEast(),{scale:.5,rotation:45*(Math.PI/180),rotateWithView:!0}).bindTooltip("<b>NE</b>",{direction:"center"}),new e.Marker(i.getSouthEast(),{scale:.5,rotation:135*(Math.PI/180),rotateWithView:!0}).bindTooltip("<b>SE</b>",{direction:"center"}),new e.Marker(i.getSouthWest(),{scale:.5,rotation:-135*(Math.PI/180),rotateWithView:!0}).bindTooltip("<b>SW</b>",{direction:"center"})]).addTo(map).eachLayer(r=>{r.getElement&&r.getElement()&&(r.getElement().style.filter="hue-rotate(150deg)")})}},displayPixelBounds(){const t=this._map;this.___corners&&this.___corners.remove(),this.___pixelbounds&&this.___pixelbounds.remove();const o=t.getSize(),i=t.getPixelBounds(),r=new e.Point(0,0),a=new e.Point(o.x,0),n=new e.Point(o.x,o.y),s=new e.Point(0,o.y),d=new e.Marker(t.containerPointToLatLng(r),{title:"Top Left corner",rotation:-45*(Math.PI/180)}),l=new e.Marker(t.containerPointToLatLng(a),{title:"Top Right corner",rotation:45*(Math.PI/180)}),h=new e.Marker(t.containerPointToLatLng(n),{title:"Bottom Right corner",rotation:135*(Math.PI/180)}),u=new e.Marker(t.containerPointToLatLng(s),{title:"Bottom Left corner",rotation:-135*(Math.PI/180)});this.___corners=new e.FeatureGroup([d,l,h,u]).addTo(t).eachLayer(c=>c.getElement().style.filter="hue-rotate(150deg)");const p=c=>`${+c.x.toFixed(1)}, ${+c.y.toFixed(1)}`;document.getElementById("ogbx").innerHTML=p(i.min),document.getElementById("ogby").innerHTML=p(i.max)},resetHeading(t){t.heading!==null&&this.setBearing(t.heading)},locate(){this._map.locate({setView:!0,enableHighAccuracy:!0})}}})();
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* For Leaflet.heat (https://github.com/Leaflet/Leaflet.heat) or the heatmap.js
|
|
3
|
+
* Leaflet plugin (https://github.com/pa7/heatmap.js/tree/master/plugins/leaflet-heatmap),
|
|
4
|
+
* load this file after the heatmap plugin. It adapts heat redraw coordinates to
|
|
5
|
+
* the rotated pane.
|
|
6
|
+
*/
|
|
7
|
+
|
|
1
8
|
if (!L.HeatLayer && !globalThis.HeatmapOverlay) {
|
|
2
9
|
throw new Error('Load Leaflet.heat or heatmap.js Leaflet plugin before this adapter.');
|
|
3
10
|
}
|
package/package.json
CHANGED
package/src/control/Rotate.js
CHANGED
|
@@ -1,168 +1,224 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Leaflet control showing the map bearing and pitch, MapLibre-like (`NavigationControl` compass):
|
|
3
3
|
*
|
|
4
|
-
* -
|
|
5
|
-
* -
|
|
6
|
-
* - Follow (rotation follows device orientation, if available)
|
|
4
|
+
* - Click: animate back to north (and to zero pitch when `visualizePitch` is enabled).
|
|
5
|
+
* - Drag: horizontal movement changes the bearing, vertical movement changes the pitch.
|
|
7
6
|
*
|
|
8
|
-
*
|
|
7
|
+
* Touch rotation and compass bearing are configured with the `touchRotate` and `compassBearing`
|
|
8
|
+
* map handlers (eg. `map.compassBearing.enable()`).
|
|
9
|
+
*
|
|
10
|
+
* @class L.Control.Rotate
|
|
11
|
+
*
|
|
12
|
+
* @extends L.Control
|
|
13
|
+
*
|
|
14
|
+
* @property {string} [position='topleft'] Leaflet control position.
|
|
15
|
+
* @property {boolean} [closeOnZeroBearing=true] Hide the control when both bearing and pitch are zero.
|
|
16
|
+
* @property {boolean} [visualizePitch=true] Tilt the icon with the pitch, reset the pitch on click and change it on drag.
|
|
17
|
+
*
|
|
18
|
+
* @since leaflet-rotate (v0.1.0)
|
|
9
19
|
*/
|
|
10
20
|
|
|
11
21
|
L.Control.Rotate = L.Control.extend({
|
|
12
22
|
|
|
13
23
|
options: {
|
|
14
24
|
position: 'topleft',
|
|
15
|
-
closeOnZeroBearing: true
|
|
25
|
+
closeOnZeroBearing: true,
|
|
26
|
+
visualizePitch: true,
|
|
16
27
|
},
|
|
17
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Create the control element and subscribe to map rotation and pitch.
|
|
31
|
+
*
|
|
32
|
+
* @param {L.Map} map
|
|
33
|
+
*
|
|
34
|
+
* @returns {HTMLElement} Control container.
|
|
35
|
+
*
|
|
36
|
+
* @since leaflet-rotate (v0.1.0)
|
|
37
|
+
*/
|
|
18
38
|
onAdd(map) {
|
|
19
39
|
const container = this._container = L.DomUtil.create('div', 'leaflet-control-rotate leaflet-bar');
|
|
20
40
|
|
|
21
|
-
// this.button = L.Control.Zoom.prototype._createButton.call(this, 'R', 'leaflet-control-rotate', 'leaflet-control-rotate', container, this._toggleLock);
|
|
22
|
-
|
|
23
41
|
const arrow = this._arrow = L.DomUtil.create('span', 'leaflet-control-rotate-arrow');
|
|
24
42
|
|
|
25
43
|
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")`;
|
|
26
|
-
arrow.style.cursor = 'grab';
|
|
27
44
|
arrow.style.display = 'block';
|
|
28
45
|
arrow.style.width = '100%';
|
|
29
46
|
arrow.style.height = '100%';
|
|
30
47
|
arrow.style.backgroundRepeat = 'no-repeat';
|
|
31
48
|
arrow.style.backgroundPosition = '50%';
|
|
49
|
+
arrow.style.pointerEvents = 'none';
|
|
32
50
|
|
|
33
|
-
// Copy-pasted from L.Control.Zoom
|
|
34
51
|
const link = this._link = L.DomUtil.create('a', 'leaflet-control-rotate-toggle', container);
|
|
35
52
|
link.appendChild(arrow);
|
|
36
53
|
link.href = '#';
|
|
37
|
-
link.title = '
|
|
54
|
+
link.title = this.options.visualizePitch ? 'Reset bearing and pitch' : 'Reset bearing to north';
|
|
55
|
+
link.setAttribute('role', 'button');
|
|
56
|
+
link.setAttribute('aria-label', link.title);
|
|
57
|
+
link.style.cursor = 'grab';
|
|
58
|
+
link.style.touchAction = 'none';
|
|
59
|
+
|
|
60
|
+
this._onPointerMoveBound = this._onPointerMove.bind(this);
|
|
61
|
+
this._onPointerUpBound = this._onPointerUp.bind(this);
|
|
38
62
|
|
|
39
63
|
L.DomEvent
|
|
40
|
-
.on(link, 'dblclick', L.DomEvent.
|
|
41
|
-
.on(link, 'mousedown', this._handleMouseDown, this)
|
|
64
|
+
.on(link, 'dblclick contextmenu', L.DomEvent.stop)
|
|
42
65
|
.on(link, 'click', L.DomEvent.stop)
|
|
43
|
-
.on(link, 'click', this.
|
|
66
|
+
.on(link, 'click', this._onClick, this)
|
|
44
67
|
.on(link, 'click', this._refocusOnMap, this);
|
|
68
|
+
link.addEventListener('pointerdown', this._onPointerDown.bind(this));
|
|
45
69
|
|
|
70
|
+
map.on('rotate pitch', this._restyle, this);
|
|
71
|
+
this._map = map;
|
|
46
72
|
this._restyle();
|
|
47
73
|
|
|
48
|
-
map.on('rotate', this._restyle, this);
|
|
49
|
-
|
|
50
|
-
// State flag
|
|
51
|
-
this._follow = false;
|
|
52
|
-
this._canFollow = false;
|
|
53
|
-
|
|
54
|
-
if (this.options.closeOnZeroBearing && map.getBearing() === 0) {
|
|
55
|
-
container.style.display = 'none';
|
|
56
|
-
}
|
|
57
|
-
|
|
58
74
|
return container;
|
|
59
75
|
},
|
|
60
|
-
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Remove the map event listener when the control is detached.
|
|
79
|
+
*
|
|
80
|
+
* @param {L.Map} map
|
|
81
|
+
*
|
|
82
|
+
* @since leaflet-rotate (v0.1.0)
|
|
83
|
+
*/
|
|
61
84
|
onRemove(map) {
|
|
62
|
-
|
|
85
|
+
this._onPointerUp();
|
|
86
|
+
map.off('rotate pitch', this._restyle, this);
|
|
63
87
|
},
|
|
64
88
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
.
|
|
89
|
+
/**
|
|
90
|
+
* Reset bearing (and pitch) unless the click ends a drag.
|
|
91
|
+
*
|
|
92
|
+
* @since leaflet-rotate (0.5.1)
|
|
93
|
+
*/
|
|
94
|
+
_onClick() {
|
|
95
|
+
if (this._moved) {
|
|
96
|
+
this._moved = false;
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (!this._map || !this._map._rotate) { return; }
|
|
100
|
+
if (this.options.visualizePitch) {
|
|
101
|
+
this._map.resetNorthPitch();
|
|
102
|
+
} else {
|
|
103
|
+
this._map.resetNorth();
|
|
104
|
+
}
|
|
73
105
|
},
|
|
74
106
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
107
|
+
/**
|
|
108
|
+
* Start dragging the control (mouse, pen or touch).
|
|
109
|
+
*
|
|
110
|
+
* @param {PointerEvent} e
|
|
111
|
+
*
|
|
112
|
+
* @since leaflet-rotate (v0.1.0)
|
|
113
|
+
*/
|
|
114
|
+
_onPointerDown(e) {
|
|
115
|
+
if (!this._map || !this._map._rotate || e.button > 0 || this._pointerId != null) { return; }
|
|
116
|
+
e.preventDefault();
|
|
117
|
+
e.stopPropagation();
|
|
118
|
+
this._pointerId = e.pointerId;
|
|
119
|
+
this._startX = this._lastX = e.clientX;
|
|
120
|
+
this._startY = this._lastY = e.clientY;
|
|
121
|
+
this._moved = false;
|
|
122
|
+
this._link.style.cursor = 'grabbing';
|
|
123
|
+
document.addEventListener('pointermove', this._onPointerMoveBound);
|
|
124
|
+
document.addEventListener('pointerup', this._onPointerUpBound);
|
|
125
|
+
document.addEventListener('pointercancel', this._onPointerUpBound);
|
|
82
126
|
},
|
|
83
127
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
128
|
+
/**
|
|
129
|
+
* Horizontal movement changes the bearing, vertical movement the pitch.
|
|
130
|
+
*
|
|
131
|
+
* @param {PointerEvent} e
|
|
132
|
+
*
|
|
133
|
+
* @since leaflet-rotate (v0.1.0)
|
|
134
|
+
*/
|
|
135
|
+
_onPointerMove(e) {
|
|
136
|
+
if (e.pointerId !== this._pointerId) { return; }
|
|
137
|
+
e.preventDefault();
|
|
138
|
+
if (!this._moved && Math.hypot(e.clientX - this._startX, e.clientY - this._startY) < 3) { return; }
|
|
139
|
+
if (!this._moved) {
|
|
140
|
+
this._moved = true;
|
|
141
|
+
this._map._stopPitchBearingAnimation();
|
|
142
|
+
}
|
|
143
|
+
const map = this._map,
|
|
144
|
+
pitch = this.options.visualizePitch ? map.getPitch() + (this._lastY - e.clientY) * 0.5 : map.getPitch();
|
|
145
|
+
map._setPitchAndBearing(pitch, map.getBearing() + (e.clientX - this._lastX) * 0.8);
|
|
146
|
+
this._lastX = e.clientX;
|
|
147
|
+
this._lastY = e.clientY;
|
|
88
148
|
},
|
|
89
149
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
150
|
+
/**
|
|
151
|
+
* Stop dragging and remove document listeners.
|
|
152
|
+
*
|
|
153
|
+
* @param {PointerEvent} [e]
|
|
154
|
+
*
|
|
155
|
+
* @since leaflet-rotate (v0.1.0)
|
|
156
|
+
*/
|
|
157
|
+
_onPointerUp(e) {
|
|
158
|
+
if (e && e.pointerId !== this._pointerId) { return; }
|
|
159
|
+
document.removeEventListener('pointermove', this._onPointerMoveBound);
|
|
160
|
+
document.removeEventListener('pointerup', this._onPointerUpBound);
|
|
161
|
+
document.removeEventListener('pointercancel', this._onPointerUpBound);
|
|
162
|
+
// a drag ending outside the button produces no click
|
|
163
|
+
if (e && this._moved && !this._link.contains(e.target)) { this._moved = false; }
|
|
164
|
+
delete this._pointerId;
|
|
165
|
+
if (this._link) {
|
|
166
|
+
this._link.style.cursor = 'grab';
|
|
167
|
+
this._restyle();
|
|
93
168
|
}
|
|
169
|
+
},
|
|
94
170
|
|
|
171
|
+
/**
|
|
172
|
+
* Update the icon (bearing and pitch) and the control visibility.
|
|
173
|
+
*
|
|
174
|
+
* @listens L.Map#rotate
|
|
175
|
+
* @listens L.Map#pitch
|
|
176
|
+
*
|
|
177
|
+
* @since leaflet-rotate (v0.1.0)
|
|
178
|
+
*/
|
|
179
|
+
_restyle() {
|
|
95
180
|
const map = this._map;
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Compass mode
|
|
103
|
-
else if (!map.compassBearing.enabled()) {
|
|
104
|
-
map.touchRotate.disable();
|
|
105
|
-
(
|
|
106
|
-
DeviceOrientationEvent && DeviceOrientationEvent.requestPermission
|
|
107
|
-
? DeviceOrientationEvent.requestPermission() // iOS compass
|
|
108
|
-
: Promise.resolve('granted') // others
|
|
109
|
-
).then(state => "granted" === state && map.compassBearing.enable())
|
|
181
|
+
if (!map) { return; }
|
|
182
|
+
if (!map.options.rotate) {
|
|
183
|
+
this._link.classList.add('leaflet-disabled');
|
|
184
|
+
return;
|
|
110
185
|
}
|
|
111
186
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
map.compassBearing.disable();
|
|
115
|
-
map.setBearing(0);
|
|
116
|
-
if (this.options.closeOnZeroBearing) {
|
|
117
|
-
map.touchRotate.enable();
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
this._restyle();
|
|
121
|
-
},
|
|
187
|
+
const bearing = map.getBearing(),
|
|
188
|
+
pitch = map.getPitch();
|
|
122
189
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
this._arrow.style.transform = 'rotate(' + bearing + 'deg)';
|
|
131
|
-
|
|
132
|
-
if (bearing && this.options.closeOnZeroBearing) {
|
|
133
|
-
this._container.style.display = 'block';
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Compass mode
|
|
137
|
-
if (map.compassBearing.enabled()) {
|
|
138
|
-
this._link.style.backgroundColor = 'orange';
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// Touch mode
|
|
142
|
-
else if (map.touchRotate.enabled()) {
|
|
143
|
-
this._link.style.backgroundColor = null;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// Locked mode
|
|
147
|
-
else {
|
|
148
|
-
this._link.style.backgroundColor = 'grey';
|
|
149
|
-
if (0 === bearing && this.options.closeOnZeroBearing) {
|
|
150
|
-
this._container.style.display = 'none';
|
|
151
|
-
}
|
|
152
|
-
}
|
|
190
|
+
this._arrow.style.transform = this.options.visualizePitch && pitch
|
|
191
|
+
? `scale(${1 / Math.sqrt(Math.cos(pitch * Math.PI / 180))}) rotateX(${pitch}deg) rotate(${bearing}deg)`
|
|
192
|
+
: `rotate(${bearing}deg)`;
|
|
193
|
+
|
|
194
|
+
if (this.options.closeOnZeroBearing) {
|
|
195
|
+
const hidden = !bearing && !(this.options.visualizePitch && pitch) && this._pointerId == null;
|
|
196
|
+
this._container.style.display = hidden ? 'none' : 'block';
|
|
153
197
|
}
|
|
154
198
|
},
|
|
155
199
|
|
|
156
200
|
});
|
|
157
|
-
|
|
158
201
|
if (L.control) {
|
|
159
202
|
L.control.rotate = options => new L.Control.Rotate(options);
|
|
160
203
|
}
|
|
161
204
|
|
|
162
205
|
L.Map.mergeOptions({
|
|
206
|
+
/**
|
|
207
|
+
* Add the rotation control automatically. Set to `false` to disable,
|
|
208
|
+
* or pass an options object to configure the control.
|
|
209
|
+
*
|
|
210
|
+
* @type {boolean|Object}
|
|
211
|
+
*
|
|
212
|
+
* @since leaflet-rotate (v0.1.0)
|
|
213
|
+
*/
|
|
163
214
|
rotateControl: true,
|
|
164
215
|
});
|
|
165
216
|
|
|
217
|
+
/**
|
|
218
|
+
* Add the configured rotation control to each newly initialized map.
|
|
219
|
+
*
|
|
220
|
+
* @since leaflet-rotate (v0.1.0)
|
|
221
|
+
*/
|
|
166
222
|
L.Map.addInitHook(function() {
|
|
167
223
|
if (this.options.rotateControl) {
|
|
168
224
|
const options = typeof this.options.rotateControl === 'object' ? this.options.rotateControl : {};
|
|
@@ -170,3 +226,4 @@ L.Map.addInitHook(function() {
|
|
|
170
226
|
this.addControl(this.rotateControl);
|
|
171
227
|
}
|
|
172
228
|
});
|
|
229
|
+
|