1import L from 'leaflet'; 2import 'Leaflet.MultiOptionsPolyline'; 3import { MarkerClusterGroup } from 'leaflet.markercluster'; 4 5class Map { 6 constructor(container) { 7 this._color1 = [255, 0, 0]; 8 this._color2 = [255, 255, 0]; 9 this._container = container; 10 this._featureGroup = null; 11 this._markerCluster = null; 12 this._circle = null; 13 this._markers = []; 14 15 L.Polyline.mergeOptions({}); 16 17 delete L.Icon.Default.prototype._getIconUrl; 18 L.Icon.Default.mergeOptions({ 19 iconRetinaUrl: '/assets/img/leaflet/marker-icon-2x.png', 20 iconUrl: '/assets/img/leaflet/marker-icon.png', 21 shadowUrl: '/assets/img/leaflet/marker-shadow.png', 22 }); 23 24 this._iconHike = L.divIcon({ 25 className: 'hike-marker', 26 html: '<span class="inner"><svg class="icon icon-steps"><use xlink:href="#icon-steps"></use></svg></span>', 27 iconSize: [36, 36], 28 iconAnchor: [18, 18], 29 }); 30 31 this._iconHike_hover = L.divIcon({ 32 className: 'hike-marker is-active', 33 html: '<span class="inner"><svg class="icon icon-steps"><use xlink:href="#icon-steps"></use></svg></span>', 34 iconSize: [36, 36], 35 iconAnchor: [18, 18], 36 }); 37 38 this._iconLocation = L.divIcon({ 39 className: 'leaflet-marker-icon leaflet-control-locate-location leaflet-zoom-animated', 40 html: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" version="1.1" viewBox="-12 -12 24 24"><circle r="9" style="stroke:#fff;stroke-width:2;fill:#2A93EE;fill-opacity:1;opacity:1;"></circle></svg>', 41 iconSize: [24, 24], 42 iconAnchor: [12, 12], 43 }); 44 45 this.initiateMap(); 46 } 47 48 initiateMap() { 49 this._map = L.map(this._container, { 50 attributionControl: true, 51 zoomControl: true, 52 gestureHandling: false, 53 zoomSnap: 0.5, 54 zoomDelta: 0.5, 55 }).setView([50, 20], 7); 56 57 const mapyCzUrl = 'https://api.mapy.com/v1/maptiles/outdoor/256{r}/{z}/{x}/{y}?apikey=nLa4fDgSroSqMQsY35VkgI3hA_bXCpacgtdUdDuFPFs'; 58 59 L.tileLayer(mapyCzUrl, { 60 attribution: '© <a href="https://api.mapy.com/copyright" target="_blank">Seznam.cz a.s. a ÄalÅ¡Ã</a>', 61 r: window.devicePixelRatio > 1 ? '@2x' : '', 62 }).addTo(this._map); 63 64 const LogoControl = L.Control.extend({ 65 options: { 66 position: 'bottomleft', 67 }, 68 69 onAdd: function () { 70 const container = L.DomUtil.create('div'); 71 const link = L.DomUtil.create('a', '', container); 72 73 link.setAttribute('href', 'http://mapy.com/'); 74 link.setAttribute('target', '_blank'); 75 link.innerHTML = '<img src="https://api.mapy.com/img/api/logo_green.svg" width="100px" />'; 76 L.DomEvent.disableClickPropagation(link); 77 return container; 78 }, 79 }); 80 81 new LogoControl().addTo(this._map); 82 83 this._featureGroup = L.featureGroup(); 84 this._featureGroup.addTo(this._map); 85 this._markerCluster = new MarkerClusterGroup({ 86 showCoverageOnHover: true, 87 zoomToBoundsOnClick: true, 88 maxClusterRadius: 30, 89 iconCreateFunction: (cluster) => { 90 return L.divIcon({ 91 className: 'cluster-marker', 92 html: '<span class="inner">' + cluster.getChildCount() + '</span>', 93 iconSize: [36, 36], 94 iconAnchor: [18, 18], 95 }); 96 }, 97 }); 98 99 this._map.addLayer(this._markerCluster); 100 } 101 102 clearLayers() { 103 this._markerCluster.clearLayers(); 104 } 105 106 addPolylineFromGeoJson(geoJsonData, pointsData, paddingTopLeft = [20, 20], paddingBottomRight = [20, 20]) { 107 this._featureGroup.clearLayers(); 108 109 let coordinates; 110 if (geoJsonData.hasOwnProperty('routes')) { 111 coordinates = geoJsonData.routes[0].geometry; 112 } else { 113 coordinates = geoJsonData.geometry; 114 } 115 116 const line = coordinates.coordinates.map((item) => { 117 return L.latLng(item[1], item[0], item[2]); 118 }); 119 120 const minAltitude = line.reduce((min, current) => { 121 return current.alt < min.alt ? current : min; 122 }).alt; 123 124 const maxAltitude = line.reduce((max, current) => { 125 return current.alt > max.alt ? current : max; 126 }).alt; 127 128 const polyline1 = L.polyline(line, { className: 'path-shadow' }); 129 130 const polyline2 = L.multiOptionsPolyline(line, { 131 multiOptions: { 132 optionIdxFn: function (latLng) { 133 return latLng.alt; 134 }, 135 options: (altitude) => { 136 return this._getColor(this._color1, this._color2, minAltitude, maxAltitude, altitude, true); 137 }, 138 copyBaseOptions: false, 139 }, 140 }); 141 142 L.geoJSON(pointsData, { 143 onEachFeature: (feature, layer) => { 144 const pop = L.popup({ 145 closeOnClick: false, 146 autoClose: true, 147 minWidth: 20, 148 autoPan: false, 149 }).setContent(feature.properties.popupContent); 150 151 layer.bindPopup(pop); 152 }, 153 pointToLayer: (feature, latlng) => { 154 return L.marker(latlng, { 155 icon: new L.DivIcon({ 156 className: 'custom-marker', 157 html: '<span class="map-poi" style="border-color:rgb(' + 158 this._getColor(this._color1, this._color2, minAltitude, maxAltitude, latlng.alt, false) + 159 ')">' + 160 feature.properties.number + 161 '</span>', 162 iconSize: [20, 20], 163 iconAnchor: [10, 10], 164 }), 165 }); 166 }, 167 coordsToLatLng: (coords) => { 168 return new L.LatLng(coords[1], coords[0], coords[2]); 169 }, 170 filter: (geoJsonFeature) => { 171 return geoJsonFeature.geometry.type !== 'LineString'; 172 }, 173 }).addTo(this._featureGroup); 174 175 this._map.flyToBounds(polyline1.getBounds(), { 176 paddingTopLeft, 177 paddingBottomRight, 178 }); 179 180 setTimeout(() => { 181 polyline1.addTo(this._featureGroup); 182 polyline2.addTo(this._featureGroup); 183 }, 250); 184 } 185 186 removePolylineFromGeoJson() { 187 this._featureGroup.clearLayers(); 188 } 189 190 addUserLocationMarker(coords) { 191 const marker = this.createMarker( 192 coords.longitude, 193 coords.latitude, 194 'location', 195 null, 196 'Tvoja poloha <br>
196(V okolà ' + coords.accuracy + 'm)' 197 ); 198 199 marker.addTo(this._featureGroup); 200 } 201 202 createMarker(lat, lng, iconName = 'default', callback = null, popupText = null) { 203 let icon = L.Icon.Default; 204 if (iconName === 'hike') { 205 icon = this._iconHike; 206 } 207 if (iconName === 'location') { 208 icon = this._iconLocation; 209 } 210 211 const marker = L.marker([lng, lat], { icon }); 212 const popup = L.popup().setContent(popupText); 213 marker.bindPopup(popup); 214 215 marker.off('click'); 216 marker.on('mouseover', function () { 217 this.openPopup(); 218 }); 219 marker.on('mouseout', function () { 220 this.closePopup(); 221 }); 222 223 if (callback !== null) { 224 marker.on('click', (ev) => { 225 callback(this._markers.findIndex((m) => m === ev.target._leaflet_id)); 226 }); 227 } 228 229 return marker; 230 } 231 232 addMarker(lat, lng, iconName = 'default', identifier = null, callback = null) { 233 const marker = this.createMarker(lat, lng, iconName, callback); 234 marker.addTo(this._featureGroup); 235 236 if (identifier) { 237 this._markers[identifier] = this._featureGroup.getLayerId(marker); 238 } 239 } 240 241 addMarkerToCluster(lat, lng, iconName = 'default', identifier = null, callback = null, popupText = null) { 242 const marker = this.createMarker(lat, lng, iconName, callback, popupText); 243 this._markerCluster.addLayer(marker); 244 245 if (identifier) { 246 this._markers[identifier] = this._markerCluster.getLayerId(marker); 247 } 248 } 249 250 fitToMarkerCluster() { 251 this._map.flyToBounds(this._markerCluster.getBounds(), { 252 padding: [20, 20], 253 }); 254 } 255 256 fitToFeatureGroup() { 257 this._map.flyToBounds(this._featureGroup.getBounds(), { 258 padding: [20, 20], 259 }); 260 } 261 262 fitToSlovakia() { 263 const bounds = [[47.7314286000009, 16.8331891], [49.6138161995424, 22.56571]]; 264 this._map.fitBounds(bounds, { 265 padding: [20, 20], 266 }); 267 } 268 269 invalidateSize(timeout = 260) { 270 setTimeout(() => { 271 this._map.invalidateSize(true); 272 }, timeout); 273 } 274 275 focusMarker(identifier) { 276 if (identifier in this._markers) { 277 const marker = this._markerCluster.getLayer(this._markers[identifier]); 278 this._map.flyTo(marker.getLatLng(), 13); 279 marker.setIcon(this._iconHike_hover); 280 } 281 } 282 283 unfocusMarker(identifier) { 284 if (identifier in this._markers) { 285 const marker = this._markerCluster.getLayer(this._markers[identifier]); 286 marker.setIcon(this._iconHike).closePopup(); 287 } 288 } 289 290 bounceMarker(identifier) { 291 if (identifier in this._markers) { 292 const marker = this._markerCluster.getLayer(this._markers[identifier]); 293 window.$(marker._icon).addClass('bouncing'); 294 } 295 } 296 297 unbounceMarker(identifier) { 298 if (identifier in this._markers) { 299 const marker = this._markerCluster.getLayer(this._markers[identifier]); 300 window.$(marker._icon).removeClass('bouncing'); 301 } 302 } 303 304 showCurrentPositionRadius(latitude, longitude, radius) { 305 if (this._circle) { 306 this._circle.removeFrom(this._map); 307 } 308 309 this._circle = L.circle([latitude, longitude], { radius, opacity: 0.5, weight: 1 }); 310 this._circle.addTo(this._map); 311 } 312 313 calculateDistance(lat1, lon1, lat2, lon2) { 314 const toRad = (value) => value * (Math.PI / 180); 315 const R = 6371; 316 const dLat = toRad(lat2 - lat1); 317 const dLon = toRad(lon2 - lon1); 318 319 const a = 320 Math.sin(dLat / 2) * Math.sin(dLat / 2) + 321 Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) * 322 Math.sin(dLon / 2) * Math.sin(dLon / 2); 323 324 const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); 325 return (R * c).toFixed(1); 326 } 327 328 _getColor(color1, color2, min, max, weight, object) { 329 const boundedWeight = weight > max ? max : weight < min ? min : weight; 330 const percent = (max - min) / 100; 331 const current = (boundedWeight - min) / percent / 100; 332 const w1 = current; 333 const w2 = 1 - w1; 334 const rgb = [ 335 Math.round(color1[0] * w1 + color2[0] * w2), 336 Math.round(color1[1] * w1 + color2[1] * w2), 337 Math.round(color1[2] * w1 + color2[2] * w2), 338 ]; 339 340 if (object === true) { 341 return { 342 color: 'rgb(' + rgb[0] + ',' + rgb[1] + ',' + rgb[2] + ')', 343 }; 344 } 345 346 return rgb; 347 } 348} 349 350export default Map;
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.