1/* 2* Distance ruler for Leaflet 3* by TK5EP 2026/01 4* each click creates a poitn on the map 5* between each point, a box gives distance and azimut 6*/ 7 8class RulerDist { 9 constructor(map, button) { 10 this.map = map; 11 this.button = button; 12 this.isActive = false; 13 14 this.points = []; 15 this.markers = []; 16 this.lines = []; 17 this.tempLine = null; 18 19 this.floatingInfo = document.getElementById("floating-info"); 20 this._defaultCursor = map.getContainer().style.cursor; 21 22 // Bind 23 this.toggle = this.toggle.bind(this); 24 this._onClick = this._onClick.bind(this); 25 this._onMouseMove = this._onMouseMove.bind(this); 26 this._onKeyDown = this._onKeyDown.bind(this); 27 } 28 29 // ---------------- Toggle ---------------- 30 toggle() { 31 if (!this.isActive) { 32 // ON â reset complet 33 this._reset(); 34 this._activate(); 35 } else { 36 // OFF â reset complet 37 this._reset(); 38 this._deactivate(); 39 } 40 } 41 42 _activate() { 43 this.isActive = true; 44 this.map.getContainer().style.cursor = "crosshair"; 45 this.map.on('click', this._onClick); 46 this.map.on('mousemove', this._onMouseMove); 47 window.addEventListener('keydown', this._onKeyDown); 48 } 49 50 _deactivate() { 51 this.isActive = false; 52 this.map.getContainer().style.cursor = this._defaultCursor; 53 this.map.off('click', this._onClick); 54 this.map.off('mousemove', this._onMouseMove); 55 window.removeEventListener('keydown', this._onKeyDown); 56 } 57 58 // ---------------- Reset complet ---------------- 59 _reset() { 60 this.points = []; 61 this.markers.forEach(m => this.map.removeLayer(m)); 62 this.lines.forEach(l => this.map.removeLayer(l)); 63 this.markers = []; 64 this.lines = []; 65 66 if (this.tempLine) this.map.removeLayer(this.tempLine); 67 this.tempLine = null; 68 69 this.floatingInfo.style.display = "none"; 70 } 71 72 // ---------------- Azimut ---------------- 73 _azimuth(p1, p2) { 74 const d2r = Math.PI / 180; 75 const y = Math.sin((p2.lng - p1.lng) * d2r) * Math.cos(p2.lat * d2r); 76 const x = Math.cos(p1.lat * d2r) * Math.sin(p2.lat * d2r) - 77 Math.sin(p1.lat * d2r) * Math.cos(p2.lat * d2r) * 78 Math.cos((p2.lng - p1.lng) * d2r); 79 return (Math.atan2(y, x) * 180 / Math.PI + 360) % 360; 80 } 81 82 // ---------------- Click ---------------- 83 _onClick(e) { 84 const latlng = e.latlng; 85 this.points.push(latlng); 86 87 const marker = L.circleMarker(latlng, { 88 radius: 5, 89 fillColor: '#2ecc71', 90 color: '#fff', 91 weight: 1, 92 fillOpacity: 1, 93 interactive: false 94 }).addTo(this.map); 95 96 this.markers.push(marker); 97 98 if (this.points.length > 1) { 99 const prev = this.points[this.points.length - 2]; 100 101 const line = L.polyline([prev, latlng], { 102 color: '#e74c3c', 103 weight: 1, 104 dashArray: '5,5', 105 interactive: false 106 }).addTo(this.map); 107 108 this.lines.push(line); 109 110 const dist = this.map.distance(prev, latlng) / 1000; 111 const az = this._azimuth(prev, latlng); 112 113 const tooltip = L.tooltip({ 114 permanent: true, 115 direction: 'right', 116 offset: [10, 0], 117 className: 'measure-tooltip' 118 }) 119 .setLatLng(latlng) 120 .setContent(` 121 <div class="measure-line"> 122 Distance : ${dist.toFixed(2)} km<br> 123 Azimut : ${az.toFixed(1)}° 124 </div> 125 `) 126 .addTo(this.map); 127 128 this.markers.push(tooltip); 129 } 130 } 131 132 // ---------------- MouseMove ---------------- 133 _onMouseMove(e) { 134 if (!this.isActive || this.points.length === 0) { 135 this.floatingInfo.style.display = "none"; 136 return; 137 } 138 139 this.floatingInfo.style.display = "block"; 140 this.floatingInfo.style.left = (e.originalEvent.clientX + 15) + "px"; 141 this.floatingInfo.style.top = (e.originalEvent.clientY + 15) + "px"; 142 143 if (this.tempLine) this.map.removeLayer(this.tempLine); 144 145 const last = this.points[this.points.length - 1]; 146 this.tempLine = L.polyline([last, e.latlng], { 147 color: '#2ecc71', 148 weight: 1, 149 dashArray: '4,4', 150 interactive: false 151 }).addTo(this.map); 152 153 const dist = this.map.distance(last, e.latlng) / 1000; 154 const az = this._azimuth(last, e.latlng); 155 156 this.floatingInfo.innerHTML = ` 157 <div class="measure-line"> 158 Distance : ${dist.toFixed(2)} km<br> 159 Azimut : ${az.toFixed(1)}° 160 </div> 161 `; 162 } 163 164 // ---------------- ESC ---------------- 165 _onKeyDown(e) { 166 if (e.key === "Escape" && this.isActive) { 167 if (this.tempLine) this.map.removeLayer(this.tempLine); 168 this.tempLine = null; 169 this.floatingInfo.style.display = "none"; 170 171 this._deactivate(); 172 173 // ð¥ synchronisation bouton 174 if (this.button) this.button.state('off'); 175 } 176 } 177 178}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.