PageSourceSearch

https://www.wanderreitkarte.de/js/editor.js

js wanderreitkarte.de collected 2026-09-24 20:07:37 UTC 17,486 bytes, 619 lines download raw bytes

1/*
2 Copyright (c) 2013 by Nop.
3 Copying or reuse require explicit approval by the author.
4 */
5
6var Line = L.Polyline.extend({
7    normalLine: { color: '#000', weight: 7, opacity: 0.3 },
8    hoverLine: { color: '#000', weight: 7, opacity: 0.6 },
9    leg: null,
10    initialize: function (latlngs, leg) {
11        L.Polyline.prototype.initialize.call(this, latlngs, this.normalLine);
12
13        this.leg = leg;
14
15        this.on('mouseover', this.lineHover)
16        this.on('mouseout', this.lineExit)
17        this.on('mousedown', this.lineSplit)
18    },
19
20    // line event handler
21    lineHover: function (e) {
22        if (!gpxEditor.active) return;
23
24        e.target.setStyle(this.hoverLine);
25    },
26    lineExit: function (e) {
27        if (!gpxEditor.active) return;
28
29        e.target.setStyle(this.normalLine);
30    },
31    lineSplit: function (e) {
32        if (!gpxEditor.active) return;
33
34        gpxEditor.closePopup();
35        const newLeg = this.leg.splitAt(e.latlng);
36        newLeg.mark.dragging._draggable._onDown(e.originalEvent);
37        //L.DomEvent.stopPropagation(e);
38    }
39
40});
41
42var Route = L.Polyline.extend({
43    namedIcon: L.icon({ iconUrl: 'static/namedpoint.png', iconSize: [20, 20] }),
44
45    initialize: function (latlngs, color) {
46
47        L.Polyline.prototype.initialize.call(this, latlngs, {
48            weight: 7,
49            opacity: 0.5,
50            color: color });
51
52        this.on('mouseover', this.lineHover)
53        this.on('mouseout', this.lineExit)
54        this.on('click', this.takeover)
55    },
56
57    // line event handler
58    lineHover: function (e) {
59        if (!gpxEditor.active) return;
60
61        e.target.setStyle( {opacity: 0.8} );
62
63        var ll = this.getLatLngs();
64        if (e.originalEvent.shiftKey)
65            this.start = L.marker( ll[ll.length-1], { icon: this.namedIcon, clickable: false, keyboard:false }).addTo(gpxEditor.background);
66        else
67            this.start = L.marker( ll[0], { icon: this.namedIcon, clickable: false, keyboard:false }).addTo(gpxEditor.background);
68    },
69    lineExit: function (e) {
70        if (!gpxEditor.active) return;
71
72        e.target.setStyle({opacity: 0.5});
73
74        gpxEditor.background.removeLayer( this.start );
75    },
76    takeover: function (e) {
77        if (!gpxEditor.active) return;
78
79        var ll = this.getLatLngs();
80        if (e.originalEvent.shiftKey)
81            for( var i=ll.length-1; i>= 0; i-- ) {
82                gpxEditor.addWaypoint( ll[i].lng, ll[i].lat );
83            }
84        else
85            for( var i=0; i<ll.length; i++ ) {
86                gpxEditor.addWaypoint( ll[i].lng, ll[i].lat );
87            }
88        gpxEditor.background.removeLayer( this );
89        updateTracklength();
90    }
91
92});
93
94var Waypoint = L.Marker.extend({
95    normalIcon: L.icon({ iconUrl: 'static/waypoint.png', iconSize: [13, 13] }),
96    namedIcon: L.icon({ iconUrl: 'static/namedpoint.png', iconSize: [20, 20] }),
97    leg: null,
98    initialize: function (latlng, leg) {
99        L.Marker.prototype.initialize.call(this, latlng, {
100            icon: ( leg.name != null ? this.namedIcon : this.normalIcon ),
101            draggable: true });
102
103        this.leg = leg;
104    }
105});
106
107var Leg = L.Class.extend({
108    next: null,
109
110    line: null,
111    mark: null,
112
113    latlng: null,
114    name: null,
115
116    wasDragged: false,
117
118    // create leg and insert it in linked list
119    initialize: function (latlng) {
120        this.latlng = latlng;
121    },
122    // utility functions
123    findPrev: function () {
124        if (this == gpxEditor.firstLeg)
125            return null;
126
127        var prev = gpxEditor.firstLeg;
128        while (prev.next != null && prev.next != this)
129            prev = prev.next;
130
131        return prev;
132    },
133
134    moved: function (latlng, ended) {
135
136        this.latlng = latlng;
137        this.removeLine();
138        this.addLine();
139
140        var prev = this;
141        if (this != gpxEditor.firstLeg)
142            prev = this.findPrev();
143        if (prev != null) {
144            prev.removeLine();
145            prev.addLine();
146        }
147
148        if (ended)
149            nav.onMarkerChange();
150        updateTracklength();
151    },
152
153    addMarker: function () {
154
155        if (!gpxEditor.active || this.mark != null)
156            return;
157
158        this.mark = new Waypoint(this.latlng, this);
159        this.mark.addTo(map);
160
161        this.mark.on('click', this.markerClicked);
162        //this.mark.on('dragstart', this.markerDragStart);
163        this.mark.on('drag', this.markerDragging);
164        this.mark.on('dragend', this.markerDragged);
165    },
166    removeMarker: function () {
167
168        if (this.mark == null)
169            return;
170
171        map.removeLayer(this.mark);
172        this.mark = null;
173    },
174    addLine: function () {
175
176        // cannot add line without target node
177        if (this.next == null)
178            return;
179
180        this.line = new Line([ this.latlng, this.next.latlng], this).addTo(map);
181    },
182    removeLine: function () {
183
184        if (this.line == null)
185            return;
186
187        map.removeLayer(this.line);
188        this.line = null;
189    },
190
191    add: function (prev) {
192
193        if (gpxEditor.firstLeg == null)
194            gpxEditor.firstLeg = this;
195
196        if (prev != null) {
197            this.next = prev.next;
198            prev.next = this;
199
200            prev.addLine();
201        }
202        if (gpxEditor.lastLeg == prev)
203            gpxEditor.lastLeg = this;
204
205        this.addMarker();
206        if (this.next)
207            this.addLine();
208
209        nav.onMarkerChange();
210        updateTracklength();
211    },
212    remove: function () {
213
214        this.removeMarker();
215        this.removeLine();
216
217        if (gpxEditor.firstLeg == this) {
218            gpxEditor.firstLeg = this.next;
219
220            if (gpxEditor.lastLeg == this)
221                gpxEditor.lastLeg = null;
222        }
223        else {
224            prev = this.findPrev();
225            if (prev != null) {
226                prev.removeLine();
227                prev.next = this.next;
228                prev.addLine();
229
230                if (gpxEditor.lastLeg == this)
231                    gpxEditor.lastLeg = prev;
232            }
233        }
234
235        nav.onMarkerChange();
236        updateTracklength();
237    },
238    enable: function (activate) {
239        if (activate)
240            this.addMarker();
241        else
242            this.removeMarker();
243    },
244    splitAt: function (latlng) {
245
246        this.removeLine();
247
248        var leg = new Leg(latlng, this);
249        leg.add(this);
250
251        nav.onMarkerChange();
252
253        return leg;
254    },
255
256    // marker event handler
257    markerClicked: function (e) {
258        // do not execute click after dragging
259        if (this.wasDragged) {
260            this.wasDragged = false;
261            return;
262        }
263
264        if (e.originalEvent.ctrlKey)
265            this.leg.remove();
266        else {
267            this.leg.showPopup();
268        }
269
270    },
271    showPopup: function () {
272        gpxEditor.popupLeg = this;
273        gpxEditor.popup.setLatLng(this.latlng).openOn(map);
274
275        var dist = nav.getClosestTracklength(this.latlng);
276        if (dist <= 0)
277            dist = gpxEditor.getTracklength(this);
278        var position = document.getElementById('waypoint.position');
279        position.innerHTML = (dist / 1000).toFixed(2);
280
281        // fill in popup data
282        var hasName = document.getElementById('waypoint.hasname');
283        var name = document.getElementById('waypoint.name');
284        if (this.name == null) {
285            hasName.checked = false;
286            name.value = '';
287            name.disabled = true;
288        }
289        else {
290            hasName.checked = true;
291            name.value = this.name;
292            name.disabled = false;
293        }
294    },
295    setNameFromPopup: function () {
296        var hasName = document.getElementById('waypoint.hasname');
297        if (hasName.checked) {
298            var name = document.getElementById('waypoint.name');
299            this.name = name.value;
300        }
301        else
302            this.name = null;
303
304        this.removeMarker();
305        this.addMarker();
306    },
307    /*
308     markerDragStart: function (e) {
309     this.leg.mark.setIcon( this.leg.mark.moveIcon );
310     },
311     */
312    markerDragging: function (e) {
313        gpxEditor.closePopup();
314        this.leg.moved(e.target.getLatLng(), false);
315    },
316    markerDragged: function (e) {
317        this.leg.moved(e.target.getLatLng(), true);
318        //this.leg.mark.setIcon( this.leg.mark.normalIcon );
319        this.wasDragged = true;
320    }
321});
322
323var Editor = L.Class.extend({
324    firstLeg: null,
325    lastLeg: null,
326
327    active: false,
328    background: null,
329
330    popup: null,
331    popupLeg: null,
332
333    initialize: function () {
334        map.on("click", this.addLeg)
335        this.background = L.layerGroup().addTo(map);
336
337        e = document.getElementById('waypoint.popup');
338        this.popup = L.popup().setContent(e);
339    },
340
341    clear: function () {
342        this.closePopup();
343
344        this.background.clearLayers();
345        var leg = this.firstLeg;
346        while (leg != null) {
347            leg.removeLine();
348            leg.removeMarker();
349            leg = leg.next;
350        }
351        this.firstLeg = null;
352        this.lastLeg = null;
353
354        updateTracklength();
355    },
356    addWaypoint: function (lon, lat) {
357        var latlng = L.latLng(lat, lon);
358        var leg = new Leg(latlng);
359        leg.add(this.lastLeg);
360    },
361    addWaypointLL: function (latlng) {
362        var leg = new Leg(latlng);
363        leg.add(this.lastLeg);
364    },
365    // load array of latlngs into editor
366    setRoute: function (waypoints) {
367        this.clear();
368
369        for (var i = 0; i < waypoints.length; i++) {
370            var leg = new Leg(waypoints[i].latlng);
371            leg.name = waypoints[i].name;
372            leg.add(this.lastLeg);
373        }
374
375        updateTracklength();
376    },
377
378    addBackgroundRoute: function( waypoints, color ) {
379        var ll = [];
380        for (var i = 0; i < waypoints.length; i++) {
381            ll.push( waypoints[i].latlng);
382        }
383
384        var route = new Route( ll, color );
385        this.background.addLayer( route );
386    },
387
388    addLeg: function (e) {
389        if (!gpxEditor.active) return;
390
391        this.closePopup();
392        var leg = new Leg(e.latlng);
393        leg.add(gpxEditor.lastLeg);
394    },
395
396    closePopup: function () {
397        if (!this.popupLeg)
398            return;
399
400        map.closePopup(this.popup);
401        this.popupLeg = null;
402    },
403    setNameFromPopup: function () {
404        if (this.popupLeg == null)
405            return;
406
407        this.popupLeg.setNameFromPopup();
408        this.closePopup();
409    },
410    deletePopupWaypoint: function () {
411        if (this.popupLeg == null)
412            return;
413
414        this.popupLeg.remove();
415        this.closePopup();
416    },
417
418    load: function () {
419        var file = document.getElementById('uplfile').files[0];
420
421        if (file) {
422            var reader = new FileReader();
423            reader.onload = function (e) {
424                gpxEditor.routeLoadComplete(e.target.result);
425            };
426            reader.readAsText(file);
427        }
428    },
429    routeLoadComplete: function (text) {
430        var reader = new GpxReader();
431        if (reader.readGPXString(text)) {
432            hideLayer('uploadLayer');
433
434            if (reader.name && reader.name.length > 2) {
435                var saveName = document.getElementById('routename');
436                saveName.value = reader.name;
437            }
438
439            gpxEditor.setRoute(reader.points);
440            //map.fitBounds(reader.area);
441        }
442    },
443
444// build gpx string from route
445    getRouteGPX: function (routename, isRoute) {
446        if (isRoute) {
447            var xmlEl = "rte";
448            var xmlSegOn = "";
449            var xmlSegOff = "";
450        } else {
451            var xmlEl = "trk";
452            var xmlSegOn = "<trkseg>"
453            var xmlSegOff = "</trkseg>"
454        }
455        var xml = '<?xml version="1.0"?>\n'
456                + '<gpx version="1.1"\n'
457                + '  creator="route/track editor on http://' + location.hostname + '"\n'
458                + '  xmlns="http://www.topografix.com/GPX/1/1"\n'
459                + '  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"\n'
460                + '  xsi:schemaLocation="http://www.topografix.com/GPX/1/1\n'
461                + '  http://www.topografix.com/GPX/1/1/gpx.xsd">\n'
462            ;
463
464        // store named waypoints
465        var leg = this.firstLeg;
466        while (leg != null) {
467            if (leg.name != null) {
468                xml = xml
469                    + '<wpt lat="' + leg.latlng.lat.toFixed(5) + '" lon="' + leg.latlng.lng.toFixed(5) + '">\n'
470                    + '\t<name>' + leg.name + '</name>\n'
471                    + '</wpt>\n';
472            }
473            leg = leg.next;
474        }
475
476        var ptBase = parseInt(document.getElementById('ptbase').value);
477        var ptName = "";
478
479        xml = xml
480            + '<' + xmlEl + '>\n'
481            + '\t<name>' + routename + '</name>\n'
482            + xmlSegOn + '\n'
483        ;
484        leg = this.firstLeg;
485        while (leg != null) {
486            xml += '\t<' + xmlEl + 'pt lat="' + leg.latlng.lat.toFixed(5) + '" lon="' + leg.latlng.lng.toFixed(5) + '"';
487            ptName = leg.name;
488            if (ptName == null) {
489                ptBase++;
490                ptName = "I" + ptBase;
491            }
492            xml += '>\n\t\t<name>' + ptName + '</name>\n' + '\t</' + xmlEl + 'pt>\n';
493
494            leg = leg.next;
495        }
496        xml += xmlSegOff + '\n'
497            + '</' + xmlEl + '>\n</gpx>\n';
498
499        return xml;
500    },
501    // send gpx to client
502    save: function () {
503        var routename = document.getElementById('routename').value;
504        var gpx = this.getRouteGPX(routename, document.getElementById('trackoption').value == "route");
505
506        //gpx = gpx.replace(/</g, '&lt;').replace(/>/g, '&gt;');
507
508        var element = document.createElement('a');
509        element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(gpx));
510        element.setAttribute('download', routename + ".gpx");
511
512        element.style.display = 'none';
513        document.body.appendChild(element);
514
515        element.click();
516
517        document.body.removeChild(element);
518    },
519
520    getRoute: function () {
521        var points = [];
522        var leg = this.firstLeg;
523        while (leg != null) {
524            points.push(new GpxPoint(leg.latlng.lat, leg.latlng.lng));
525            leg = leg.next;
526        }
527
528        return points;
529    },
530    getEncodedRoute: function () {
531        var route = "";
532        var leg = this.firstLeg;
533        while (leg != null) {
534            if (route.length > 5)
535                route += "|";
536            //route += leg.latlng.lat.toFixed(5) + ',' + leg.latlng.lng.toFixed(5);
537            route += encodeCoordinate(leg.latlng.lat) + ',' + encodeCoordinate(leg.latlng.lng);
538            leg = leg.next;
539        }
540
541        return route;
542    },
543
544    getTracklength: function (limit) {
545        var dist = 0;
546        var leg = this.firstLeg;
547        while (leg != null && leg.next != null && leg != limit) {
548            dist = dist + leg.latlng.distanceTo(leg.next.latlng);
549            leg = leg.next;
550        }
551
552        return dist;
553    },
554
555    findEditorPointAt: function( distance )
556    {
557        var dist = 0;
558        var leg = this.firstLeg;
559        while (leg != null && leg.next != null) {
560            const legDist = leg.latlng.distanceTo(leg.next.latlng);
561            dist = dist + legDist;
562            if( dist > distance )
563            {
564                const partial = (distance-dist-legDist) / legDist;
565                return L.latLng(
566                    leg.latlng.lat * partial + leg.next.latlng.lat * (1-partial),
567                    leg.latlng.lng * partial + leg.next.latlng.lng * (1-partial)
568                )
569            }
570            leg = leg.next;
571        }
572        return null;
573    },
574
575    isEmpty: function (doAlert) {
576        if (this.firstLeg == null || this.firstLeg == this.lastLeg) {
577            if (doAlert)
578                alert(translations.editor_warn_empty)
579            return true;
580        }
581        return false;
582    },
583
584    enable: function (activate) {
585
586        this.active = activate;
587        this.closePopup();
588
589        var leg = this.firstLeg;
590        while (leg != null) {
591            leg.enable(activate);
592            leg = leg.next;
593        }
594    }
595});
596
597const gpxEditor = new Editor();
598
599let currentTrackLength = 0;
600function updateTracklength(distance) {
601
602    if (distance == null) {
603        if (nav.active && !gpxEditor.isEmpty())
604            return;
605        else
606            distance = gpxEditor.getTracklength();
607    }
608
609    var e = document.getElementById("tracklength");
610    if (distance < 0.01) {
611        e.innerHTML = translations.route_distance + ": --";
612        currentTrackLength = 0;
613    } else {
614        e.innerHTML = translations.route_distance + ": " + (distance / 1000).toFixed(2) + " km";
615        currentTrackLength = distance;
616    }
617}
618
619

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.