PageSourceSearch

https://www.hikemates.com/assets/pages/search/map-wJ9OfzW.js

js hikemates.com collected 2026-10-08 23:51:32 UTC 11,753 bytes, 350 lines download raw bytes

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.