@raruto/leaflet-elevation 2.0.6 → 2.1.0
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 +1 -1
- package/dist/leaflet-elevation.js +1634 -1682
- package/dist/leaflet-elevation.js.map +1 -1
- package/dist/leaflet-elevation.min.js +1 -1
- package/dist/leaflet-elevation.min.js.map +1 -1
- package/package.json +3 -5
- package/src/control.js +77 -74
- package/src/index.js +0 -12
|
@@ -1,1688 +1,1640 @@
|
|
|
1
1
|
(function (factory) {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
typeof define === 'function' && define.amd ? define(factory) :
|
|
3
|
+
factory();
|
|
4
4
|
})((function () { 'use strict';
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
*
|
|
1639
|
-
* This file is free software: you may copy, redistribute and/or modify it
|
|
1640
|
-
* under the terms of the GNU General Public License as published by the
|
|
1641
|
-
* Free Software Foundation, either version 2 of the License, or (at your
|
|
1642
|
-
* option) any later version.
|
|
1643
|
-
*
|
|
1644
|
-
* This file is distributed in the hope that it will be useful, but
|
|
1645
|
-
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
1646
|
-
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
1647
|
-
* General Public License for more details.
|
|
1648
|
-
*
|
|
1649
|
-
* You should have received a copy of the GNU General Public License
|
|
1650
|
-
* along with this program. If not, see .
|
|
1651
|
-
*
|
|
1652
|
-
* This file incorporates work covered by the following copyright and
|
|
1653
|
-
* permission notice:
|
|
1654
|
-
*
|
|
1655
|
-
* Copyright (c) 2013-2016, MIT License, Felix “MrMufflon” Bache
|
|
1656
|
-
*
|
|
1657
|
-
* Permission to use, copy, modify, and/or distribute this software
|
|
1658
|
-
* for any purpose with or without fee is hereby granted, provided
|
|
1659
|
-
* that the above copyright notice and this permission notice appear
|
|
1660
|
-
* in all copies.
|
|
1661
|
-
*
|
|
1662
|
-
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
|
1663
|
-
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
|
1664
|
-
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
|
1665
|
-
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
|
|
1666
|
-
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
|
1667
|
-
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
|
1668
|
-
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
|
1669
|
-
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
1670
|
-
*/
|
|
1671
|
-
|
|
1672
|
-
Elevation.Utils = _;
|
|
1673
|
-
|
|
1674
|
-
/* Temporary fix for empty values evaluated as false (leaflet-i18n v0.3.1) */
|
|
1675
|
-
(function(){
|
|
1676
|
-
let proto = L.i18n.bind({});
|
|
1677
|
-
L.i18n = L._ = (string, data) => {
|
|
1678
|
-
if (L.locale && L.locales[L.locale] && L.locales[L.locale][string] == "") {
|
|
1679
|
-
L.locales[L.locale][string] = "\u200B";
|
|
1680
|
-
}
|
|
1681
|
-
return proto.call(null, string, data);
|
|
1682
|
-
};
|
|
1683
|
-
})();
|
|
1684
|
-
|
|
1685
|
-
L.control.elevation = (options) => new Elevation(options);
|
|
6
|
+
/**
|
|
7
|
+
* TODO: exget computed styles of theese values from actual "CSS vars"
|
|
8
|
+
**/
|
|
9
|
+
const Colors = {
|
|
10
|
+
'lightblue': { area: '#3366CC', alpha: 0.45, stroke: '#3366CC' },
|
|
11
|
+
'magenta' : { area: '#FF005E' },
|
|
12
|
+
'yellow' : { area: '#FF0' },
|
|
13
|
+
'purple' : { area: '#732C7B' },
|
|
14
|
+
'steelblue': { area: '#4682B4' },
|
|
15
|
+
'red' : { area: '#F00' },
|
|
16
|
+
'lime' : { area: '#9CC222', line: '#566B13' }
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const SEC = 1000;
|
|
20
|
+
const MIN = SEC * 60;
|
|
21
|
+
const HOUR = MIN * 60;
|
|
22
|
+
const DAY = HOUR * 24;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Convert a time (millis) to a human readable duration string (%Dd %H:%M'%S")
|
|
26
|
+
*/
|
|
27
|
+
function formatTime(t) {
|
|
28
|
+
let d = Math.floor(t / DAY);
|
|
29
|
+
let h = Math.floor( (t - d * DAY) / HOUR);
|
|
30
|
+
let m = Math.floor( (t - d * DAY - h * HOUR) / MIN);
|
|
31
|
+
let s = Math.round( (t - d * DAY - h * HOUR - m * MIN) / SEC);
|
|
32
|
+
if ( s === 60 ) { m++; s = 0; }
|
|
33
|
+
if ( m === 60 ) { h++; m = 0; }
|
|
34
|
+
if ( h === 24 ) { d++; h = 0; }
|
|
35
|
+
return (d ? d + "d " : '') + h.toString().padStart(2, 0) + ':' + m.toString().padStart(2, 0) + "'" + s.toString().padStart(2, 0) + '"';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Convert a time (millis) to human readable date string (dd-mm-yyyy hh:mm:ss)
|
|
40
|
+
*/
|
|
41
|
+
function formatDate(format) {
|
|
42
|
+
if (!format) {
|
|
43
|
+
return (time) => (new Date(time)).toLocaleString().replaceAll('/', '-').replaceAll(',', ' ');
|
|
44
|
+
} else if (format == 'time') {
|
|
45
|
+
return (time) => (new Date(time)).toLocaleTimeString();
|
|
46
|
+
} else if (format == 'date') {
|
|
47
|
+
return (time) => (new Date(time)).toLocaleDateString();
|
|
48
|
+
}
|
|
49
|
+
return (time) => format(time);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Generate download data event.
|
|
54
|
+
*/
|
|
55
|
+
function saveFile(dataURI, fileName) {
|
|
56
|
+
let a = create('a', '', { href: dataURI, target: '_new', download: fileName || "", style: "display:none;" });
|
|
57
|
+
let b = document.body;
|
|
58
|
+
b.appendChild(a);
|
|
59
|
+
a.click();
|
|
60
|
+
b.removeChild(a);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Convert SVG Path into Path2D and then update canvas
|
|
66
|
+
*/
|
|
67
|
+
function drawCanvas(ctx, path) {
|
|
68
|
+
path.classed('canvas-path', true);
|
|
69
|
+
|
|
70
|
+
ctx.beginPath();
|
|
71
|
+
ctx.moveTo(0, 0);
|
|
72
|
+
let p = new Path2D(path.attr('d'));
|
|
73
|
+
|
|
74
|
+
ctx.strokeStyle = path.attr('stroke');
|
|
75
|
+
ctx.fillStyle = path.attr('fill');
|
|
76
|
+
ctx.lineWidth = 1.25;
|
|
77
|
+
ctx.globalCompositeOperation = 'source-over';
|
|
78
|
+
|
|
79
|
+
// stroke opacity
|
|
80
|
+
ctx.globalAlpha = path.attr('stroke-opacity') || 0.3;
|
|
81
|
+
ctx.stroke(p);
|
|
82
|
+
|
|
83
|
+
// fill opacity
|
|
84
|
+
ctx.globalAlpha = path.attr('fill-opacity') || 0.45;
|
|
85
|
+
ctx.fill(p);
|
|
86
|
+
|
|
87
|
+
ctx.globalAlpha = 1;
|
|
88
|
+
|
|
89
|
+
ctx.closePath();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Loop and extract GPX Extensions handled by "@tmcw/toGeoJSON" (eg. "coordinateProperties" > "times")
|
|
94
|
+
*/
|
|
95
|
+
function coordPropsToMeta(coordProps, name, parser) {
|
|
96
|
+
return coordProps && (({props, point, id, isMulti }) => {
|
|
97
|
+
if (props) {
|
|
98
|
+
for (const key of coordProps) {
|
|
99
|
+
if (key in props) {
|
|
100
|
+
point.meta[name] = (parser || parseInt).call(this, (isMulti ? props[key][isMulti] : props[key]), id);
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Extract numeric property (id) from GeoJSON object
|
|
110
|
+
*/
|
|
111
|
+
const parseInt = (property, id) => window.parseInt((typeof property === 'object' ? property[id] : property));
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Extract datetime property (id) from GeoJSON object
|
|
115
|
+
*/
|
|
116
|
+
const parseDate = (property, id) => new Date(Date.parse((typeof property === 'object' ? property[id] : property)));
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* A little bit shorter than L.DomUtil
|
|
120
|
+
*/
|
|
121
|
+
const addClass = (n, str) => n && str.split(" ").every(s => s && L.DomUtil.addClass(n, s));
|
|
122
|
+
const removeClass = (n, str) => n && str.split(" ").every(s => s && L.DomUtil.removeClass(n, s));
|
|
123
|
+
const toggleClass = (n, str, cond) => (cond ? addClass : removeClass)(n, str);
|
|
124
|
+
const replaceClass = (n, rem, add) => (rem && removeClass(n, rem)) || (add && addClass(n, add));
|
|
125
|
+
const style = (n, k, v) => (typeof v === "undefined" && L.DomUtil.getStyle(n, k)) || n.style.setProperty(k, v);
|
|
126
|
+
const toggleStyle = (n, k, v, cond) => style(n, k, cond ? v : '');
|
|
127
|
+
const setAttributes = (n, attrs) => { for (let k in attrs) { n.setAttribute(k, attrs[k]); } };
|
|
128
|
+
const toggleEvent = (el, e, fn, cond) => el[cond ? 'on' : 'off'](e, fn);
|
|
129
|
+
const create = (tag, str, attrs, n) => { let elem = L.DomUtil.create(tag, str || ""); if (attrs) setAttributes(elem, attrs); if (n) append(n, elem); return elem; };
|
|
130
|
+
const append = (n, c) => n.appendChild(c);
|
|
131
|
+
const insert = (n, c, pos) => n.insertAdjacentElement(pos, c);
|
|
132
|
+
const select = (str, n) => (n || document).querySelector(str);
|
|
133
|
+
const each = (obj, fn) => { for (let i in obj) fn(obj[i], i); };
|
|
134
|
+
const randomId = () => Math.random().toString(36).substr(2, 9);
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* TODO: use generators instead? (ie. "yield")
|
|
138
|
+
*/
|
|
139
|
+
const iMax = (iVal, max) => (iVal > max ? iVal : max) || -Infinity;
|
|
140
|
+
const iMin = (iVal, min) => (iVal < min ? iVal : min) || +Infinity;
|
|
141
|
+
const iAvg = (iVal, avg) => (iVal + (avg || 0)) / 2.0;
|
|
142
|
+
const iSum = (iVal, sum) => iVal + (sum || 0);
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Alias for some leaflet core functions
|
|
146
|
+
*/
|
|
147
|
+
const { on, off } = L.DomEvent;
|
|
148
|
+
const { throttle, wrapNum } = L.Util;
|
|
149
|
+
const { hasClass } = L.DomUtil;
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Limit floating point precision
|
|
153
|
+
*/
|
|
154
|
+
const round = L.Util.formatNum;
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Limit a number between min / max values
|
|
158
|
+
*/
|
|
159
|
+
const clamp = (val, range) => range ? (val < range[0] ? range[0] : val > range[1] ? range[1] : val) : val;
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Limit a delta difference between two values
|
|
163
|
+
*/
|
|
164
|
+
const wrapDelta = (curr, prev, deltaMax) => Math.abs(curr - prev) > deltaMax ? prev + deltaMax * Math.sign(curr - prev) : curr;
|
|
165
|
+
|
|
166
|
+
var _ = /*#__PURE__*/Object.freeze({
|
|
167
|
+
__proto__: null,
|
|
168
|
+
Colors: Colors,
|
|
169
|
+
formatTime: formatTime,
|
|
170
|
+
formatDate: formatDate,
|
|
171
|
+
saveFile: saveFile,
|
|
172
|
+
drawCanvas: drawCanvas,
|
|
173
|
+
coordPropsToMeta: coordPropsToMeta,
|
|
174
|
+
parseInt: parseInt,
|
|
175
|
+
parseDate: parseDate,
|
|
176
|
+
addClass: addClass,
|
|
177
|
+
removeClass: removeClass,
|
|
178
|
+
toggleClass: toggleClass,
|
|
179
|
+
replaceClass: replaceClass,
|
|
180
|
+
style: style,
|
|
181
|
+
toggleStyle: toggleStyle,
|
|
182
|
+
setAttributes: setAttributes,
|
|
183
|
+
toggleEvent: toggleEvent,
|
|
184
|
+
create: create,
|
|
185
|
+
append: append,
|
|
186
|
+
insert: insert,
|
|
187
|
+
select: select,
|
|
188
|
+
each: each,
|
|
189
|
+
randomId: randomId,
|
|
190
|
+
iMax: iMax,
|
|
191
|
+
iMin: iMin,
|
|
192
|
+
iAvg: iAvg,
|
|
193
|
+
iSum: iSum,
|
|
194
|
+
on: on,
|
|
195
|
+
off: off,
|
|
196
|
+
throttle: throttle,
|
|
197
|
+
wrapNum: wrapNum,
|
|
198
|
+
hasClass: hasClass,
|
|
199
|
+
round: round,
|
|
200
|
+
clamp: clamp,
|
|
201
|
+
wrapDelta: wrapDelta
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
var Options = {
|
|
205
|
+
autofitBounds: true,
|
|
206
|
+
autohide: false,
|
|
207
|
+
autohideMarker: true,
|
|
208
|
+
almostover: true,
|
|
209
|
+
altitude: true,
|
|
210
|
+
closeBtn: true,
|
|
211
|
+
collapsed: false,
|
|
212
|
+
detached: true,
|
|
213
|
+
distance: true,
|
|
214
|
+
distanceMarkers: { lazy: true, distance: true, direction: true },
|
|
215
|
+
dragging: !L.Browser.mobile,
|
|
216
|
+
downloadLink: 'link',
|
|
217
|
+
elevationDiv: "#elevation-div",
|
|
218
|
+
followMarker: true,
|
|
219
|
+
imperial: false,
|
|
220
|
+
legend: true,
|
|
221
|
+
handlers: [],
|
|
222
|
+
hotline: 'elevation',
|
|
223
|
+
marker: 'elevation-line',
|
|
224
|
+
markerIcon: L.divIcon({
|
|
225
|
+
className: 'elevation-position-marker',
|
|
226
|
+
html: '<i class="elevation-position-icon"></i>',
|
|
227
|
+
iconSize: [32, 32],
|
|
228
|
+
iconAnchor: [16, 16],
|
|
229
|
+
}),
|
|
230
|
+
position: "topright",
|
|
231
|
+
polyline: {
|
|
232
|
+
className: 'elevation-polyline',
|
|
233
|
+
color: '#000',
|
|
234
|
+
opacity: 0.75,
|
|
235
|
+
weight: 5,
|
|
236
|
+
lineCap: 'round'
|
|
237
|
+
},
|
|
238
|
+
polylineSegments: {
|
|
239
|
+
className: 'elevation-polyline-segments',
|
|
240
|
+
color: '#F00',
|
|
241
|
+
interactive: false,
|
|
242
|
+
},
|
|
243
|
+
preferCanvas: false,
|
|
244
|
+
reverseCoords: false,
|
|
245
|
+
ruler: true,
|
|
246
|
+
theme: "lightblue-theme",
|
|
247
|
+
summary: 'inline',
|
|
248
|
+
slope: false,
|
|
249
|
+
speed: false,
|
|
250
|
+
time: false,
|
|
251
|
+
timeFactor: 3600,
|
|
252
|
+
timestamps: false,
|
|
253
|
+
trkStart: L.circleMarker([0,0], { className: 'start-marker', radius: 6, weight: 2, color: '#fff', fillColor: '#00d800', fillOpacity: 1, interactive: false }),
|
|
254
|
+
trkEnd: L.circleMarker([0,0], { className: 'end-marker', radius: 6, weight: 2, color: '#fff', fillColor: '#ff0606', fillOpacity: 1, interactive: false }),
|
|
255
|
+
waypoints: true,
|
|
256
|
+
wptIcons: {
|
|
257
|
+
'': L.divIcon({
|
|
258
|
+
className: 'elevation-waypoint-marker',
|
|
259
|
+
html: '<i class="elevation-waypoint-icon default"></i>',
|
|
260
|
+
iconSize: [30, 30],
|
|
261
|
+
iconAnchor: [8, 30],
|
|
262
|
+
}),
|
|
263
|
+
},
|
|
264
|
+
wptLabels: true,
|
|
265
|
+
xAttr: "dist",
|
|
266
|
+
xLabel: "km",
|
|
267
|
+
yAttr: "z",
|
|
268
|
+
yLabel: "m",
|
|
269
|
+
zFollow: false,
|
|
270
|
+
zooming: !L.Browser.Mobile,
|
|
271
|
+
|
|
272
|
+
// Quite uncommon and undocumented options
|
|
273
|
+
margins: { top: 30, right: 30, bottom: 30, left: 40 },
|
|
274
|
+
height: (screen.height * 0.3) || 200,
|
|
275
|
+
width: (screen.width * 0.6) || 600,
|
|
276
|
+
xTicks: undefined,
|
|
277
|
+
yTicks: undefined,
|
|
278
|
+
|
|
279
|
+
decimalsX: 2,
|
|
280
|
+
decimalsY: 0,
|
|
281
|
+
forceAxisBounds: false,
|
|
282
|
+
interpolation: "curveLinear",
|
|
283
|
+
skipNullZCoords: false,
|
|
284
|
+
yAxisMax: undefined,
|
|
285
|
+
yAxisMin: undefined,
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
// "leaflet-i18n" fallback
|
|
289
|
+
if (!L._ || !L.i18n) {
|
|
290
|
+
L._ = L.i18n = (string, data) => string;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const Elevation = L.Control.Elevation = L.Control.extend({
|
|
294
|
+
|
|
295
|
+
includes: L.Evented ? L.Evented.prototype : L.Mixin.Events,
|
|
296
|
+
|
|
297
|
+
options: Options,
|
|
298
|
+
__mileFactor: 0.621371, // 1 km = (0.621371 mi)
|
|
299
|
+
__footFactor: 3.28084, // 1 m = (3.28084 ft)
|
|
300
|
+
__D3: 'https://unpkg.com/d3@6.5.0/dist/d3.min.js',
|
|
301
|
+
__TOGEOJSON: 'https://unpkg.com/@tmcw/togeojson@4.6.0/dist/togeojson.umd.js',
|
|
302
|
+
__LGEOMUTIL: 'https://unpkg.com/leaflet-geometryutil@0.9.3/src/leaflet.geometryutil.js',
|
|
303
|
+
__LALMOSTOVER: 'https://unpkg.com/leaflet-almostover@1.0.1/src/leaflet.almostover.js',
|
|
304
|
+
__LHOTLINE: '../libs/leaflet-hotline.min.js',
|
|
305
|
+
__LDISTANCEM: '../libs/leaflet-distance-marker.min.js',
|
|
306
|
+
__LCHART: '../src/components/chart.js',
|
|
307
|
+
__LMARKER: '../src/components/marker.js',
|
|
308
|
+
__LSUMMARY: '../src/components/summary.js',
|
|
309
|
+
__modulesFolder: '../src/handlers/',
|
|
310
|
+
|
|
311
|
+
/*
|
|
312
|
+
* Add data to the diagram either from GPX or GeoJSON and update the axis domain and data
|
|
313
|
+
*/
|
|
314
|
+
addData: function(d, layer) {
|
|
315
|
+
this.import(this.__D3)
|
|
316
|
+
.then(() => {
|
|
317
|
+
layer = layer ?? (d.on && d);
|
|
318
|
+
this._addData(d);
|
|
319
|
+
this._addLayer(layer);
|
|
320
|
+
this._fireEvt("eledata_added", { data: d, layer: layer, track_info: this.track_info });
|
|
321
|
+
});
|
|
322
|
+
},
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Adds the control to the given map.
|
|
326
|
+
*/
|
|
327
|
+
addTo: function(map) {
|
|
328
|
+
if (this.options.detached) {
|
|
329
|
+
let parent = select(this.options.elevationDiv);
|
|
330
|
+
let eleDiv = this.onAdd(map);
|
|
331
|
+
parent ? append(parent, eleDiv) : insert(map.getContainer(), eleDiv, 'afterend');
|
|
332
|
+
} else {
|
|
333
|
+
L.Control.prototype.addTo.call(this, map);
|
|
334
|
+
}
|
|
335
|
+
return this;
|
|
336
|
+
},
|
|
337
|
+
|
|
338
|
+
/*
|
|
339
|
+
* Reset data and display
|
|
340
|
+
*/
|
|
341
|
+
clear: function() {
|
|
342
|
+
if (this._marker) this._marker.remove();
|
|
343
|
+
if (this._chart) this._clearChart();
|
|
344
|
+
if (this._layers) this._clearLayers(this._layers);
|
|
345
|
+
if (this._markers) this._clearLayers(this._markers);
|
|
346
|
+
if (this._circleMarkers) this._circleMarkers.remove();
|
|
347
|
+
if (this._hotline) this._clearLayers(this._hotline);
|
|
348
|
+
|
|
349
|
+
this._data = [];
|
|
350
|
+
this.track_info = {};
|
|
351
|
+
|
|
352
|
+
this._fireEvt("eledata_clear");
|
|
353
|
+
|
|
354
|
+
this._updateChart();
|
|
355
|
+
},
|
|
356
|
+
|
|
357
|
+
_clearChart: function() {
|
|
358
|
+
if (this._events && this._events.elechart_updated) {
|
|
359
|
+
this._events.elechart_updated.forEach(({fn, ctx}) => this.off('elechart_updated', fn, ctx));
|
|
360
|
+
}
|
|
361
|
+
if (this._chart && this._chart._container) {
|
|
362
|
+
this._chart._container.selectAll('g.point .point').remove();
|
|
363
|
+
this._chart.clear();
|
|
364
|
+
}
|
|
365
|
+
},
|
|
366
|
+
|
|
367
|
+
_clearLayers: function(l) {
|
|
368
|
+
l = l || this._layers;
|
|
369
|
+
if (l && l.eachLayer) {
|
|
370
|
+
l.eachLayer(f => f.remove());
|
|
371
|
+
l.clearLayers();
|
|
372
|
+
}
|
|
373
|
+
},
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* TODO: Create a base class to handle custom data attributes (heart rate, cadence, temperature, ...)
|
|
377
|
+
*
|
|
378
|
+
* @link https://leafletjs.com/examples/extending/extending-3-controls.html#handlers
|
|
379
|
+
*/
|
|
380
|
+
// addHandler: function (name, HandlerClass) {
|
|
381
|
+
// if (HandlerClass) {
|
|
382
|
+
// let handler = this[name] = new HandlerClass(this);
|
|
383
|
+
// this.handlers.push(handler);
|
|
384
|
+
// if (this.options[name]) {
|
|
385
|
+
// handler.enable();
|
|
386
|
+
// }
|
|
387
|
+
// }
|
|
388
|
+
// return this;
|
|
389
|
+
// },
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Disable chart brushing.
|
|
393
|
+
*/
|
|
394
|
+
disableBrush: function() {
|
|
395
|
+
this._chart._brushEnabled = false;
|
|
396
|
+
this._resetDrag();
|
|
397
|
+
},
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Enable chart brushing.
|
|
401
|
+
*/
|
|
402
|
+
enableBrush: function() {
|
|
403
|
+
this._chart._brushEnabled = true;
|
|
404
|
+
},
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Disable chart zooming.
|
|
408
|
+
*/
|
|
409
|
+
disableZoom: function() {
|
|
410
|
+
this._chart._zoomEnabled = false;
|
|
411
|
+
this._chart._resetZoom();
|
|
412
|
+
},
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Enable chart zooming.
|
|
416
|
+
*/
|
|
417
|
+
enableZoom: function() {
|
|
418
|
+
this._chart._zoomEnabled = true;
|
|
419
|
+
},
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Sets a map view that contains the given geographical bounds.
|
|
423
|
+
*/
|
|
424
|
+
fitBounds: function(bounds) {
|
|
425
|
+
bounds = bounds || this.getBounds();
|
|
426
|
+
if (this._map && bounds.isValid()) this._map.fitBounds(bounds);
|
|
427
|
+
},
|
|
428
|
+
|
|
429
|
+
getBounds: function(data) {
|
|
430
|
+
return L.latLngBounds((data || this._data).map((d) => d.latlng));
|
|
431
|
+
},
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Get default zoom level (followMarker: true).
|
|
435
|
+
*/
|
|
436
|
+
getZFollow: function() {
|
|
437
|
+
return this.options.zFollow;
|
|
438
|
+
},
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Hide current elevation chart profile.
|
|
442
|
+
*/
|
|
443
|
+
hide: function() {
|
|
444
|
+
style(this._container, "display", "none");
|
|
445
|
+
},
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Initialize chart control "options" and "container".
|
|
449
|
+
*/
|
|
450
|
+
initialize: function(opts) {
|
|
451
|
+
|
|
452
|
+
opts = L.setOptions(this, opts);
|
|
453
|
+
|
|
454
|
+
this._data = [];
|
|
455
|
+
this._layers = L.featureGroup();
|
|
456
|
+
this._markers = L.featureGroup();
|
|
457
|
+
this._hotline = L.featureGroup();
|
|
458
|
+
this._circleMarkers = L.featureGroup();
|
|
459
|
+
this._markedSegments = L.polyline([]);
|
|
460
|
+
this._start = (opts.trkStart || Options.trkStart);
|
|
461
|
+
this._end = (opts.trkEnd || Options.trkEnd);
|
|
462
|
+
this._chartEnabled = true;
|
|
463
|
+
this._yCoordMax = -Infinity;
|
|
464
|
+
this.track_info = {};
|
|
465
|
+
// this.handlers = [];
|
|
466
|
+
|
|
467
|
+
if (opts.followMarker) this._setMapView = throttle(this._setMapView, 300, this);
|
|
468
|
+
if (opts.legend) opts.margins.bottom += 30;
|
|
469
|
+
if (opts.theme) opts.polylineSegments.className += ' ' + opts.theme;
|
|
470
|
+
if (opts.wptIcons === true) opts.wptIcons = Options.wptIcons;
|
|
471
|
+
if (opts.distanceMarkers === true) opts.distanceMarkers = Options.distanceMarkers;
|
|
472
|
+
if (opts.trkStart) this._start.addTo(this._circleMarkers);
|
|
473
|
+
if (opts.trkEnd) this._end.addTo(this._circleMarkers);
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
this._markedSegments.setStyle(opts.polylineSegments);
|
|
477
|
+
|
|
478
|
+
// Leaflet canvas renderer colors
|
|
479
|
+
L.extend(Colors, opts.colors || {});
|
|
480
|
+
|
|
481
|
+
// Various stuff
|
|
482
|
+
this._fixCanvasPaths();
|
|
483
|
+
this._fixTooltipSize();
|
|
484
|
+
|
|
485
|
+
},
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* Javascript scripts downloader (lazy loader)
|
|
489
|
+
*/
|
|
490
|
+
import: function(src, condition) {
|
|
491
|
+
switch(src) {
|
|
492
|
+
case this.__D3: condition = typeof d3 !== 'object'; break;
|
|
493
|
+
case this.__TOGEOJSON: condition = typeof toGeoJSON !== 'object'; break;
|
|
494
|
+
case this.__LGEOMUTIL: condition = typeof L.GeometryUtil !== 'object'; break;
|
|
495
|
+
case this.__LALMOSTOVER: condition = typeof L.Handler.AlmostOver !== 'function'; break;
|
|
496
|
+
case this.__LDISTANCEM: condition = typeof L.DistanceMarkers !== 'function'; break;
|
|
497
|
+
}
|
|
498
|
+
return condition !== false ? import(src) : Promise.resolve();
|
|
499
|
+
},
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Load elevation data (GPX, GeoJSON, KML or TCX).
|
|
503
|
+
*/
|
|
504
|
+
load: function(data) {
|
|
505
|
+
this._parseFromString(data).then( geojson => geojson ? this._loadLayer(geojson) : this._loadFile(data));
|
|
506
|
+
},
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* Create container DOM element and related event listeners.
|
|
510
|
+
* Called on control.addTo(map).
|
|
511
|
+
*/
|
|
512
|
+
onAdd: function(map) {
|
|
513
|
+
this._map = map;
|
|
514
|
+
|
|
515
|
+
let container = this._container = create("div", "elevation-control " + this.options.theme + " " + (this.options.detached ? 'elevation-detached' : 'leaflet-control'), this.options.detached ? { id: 'elevation-' + randomId() } : {});
|
|
516
|
+
|
|
517
|
+
if (!this.eleDiv) this.eleDiv = container;
|
|
518
|
+
|
|
519
|
+
let modules = [
|
|
520
|
+
"Distance",
|
|
521
|
+
"Time",
|
|
522
|
+
"Altitude",
|
|
523
|
+
"Slope",
|
|
524
|
+
"Speed",
|
|
525
|
+
"Acceleration",
|
|
526
|
+
...this.options.handlers
|
|
527
|
+
].filter(m => false !== this.options[typeof m == "string" && m.toLowerCase()]);
|
|
528
|
+
|
|
529
|
+
this._loadModules(modules).then(() => { // Inject here required modules (data handlers)
|
|
530
|
+
this._initChart(container);
|
|
531
|
+
this._initButton(container);
|
|
532
|
+
this._initSummary(container);
|
|
533
|
+
this._initMarker(map);
|
|
534
|
+
this._initLayer(map);
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
this.fire('add');
|
|
538
|
+
|
|
539
|
+
return container;
|
|
540
|
+
},
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* Clean up control code and related event listeners.
|
|
544
|
+
* Called on control.remove().
|
|
545
|
+
*/
|
|
546
|
+
onRemove: function(map) {
|
|
547
|
+
this._container = null;
|
|
548
|
+
|
|
549
|
+
map
|
|
550
|
+
.off('zoom viewreset zoomanim', this._hideMarker, this)
|
|
551
|
+
.off('resize', this._resetView, this)
|
|
552
|
+
.off('resize', this._resizeChart, this)
|
|
553
|
+
.off('mousedown', this._resetDrag, this);
|
|
554
|
+
|
|
555
|
+
off(map.getContainer(), 'mousewheel', this._resetDrag, this);
|
|
556
|
+
off(map.getContainer(), 'touchstart', this._resetDrag, this);
|
|
557
|
+
off(document, 'keydown', this._onKeyDown, this);
|
|
558
|
+
|
|
559
|
+
this
|
|
560
|
+
.off('eledata_added eledata_loaded', this._updateChart, this)
|
|
561
|
+
.off('eledata_added eledata_loaded', this._updateSummary, this);
|
|
562
|
+
|
|
563
|
+
this.fire('remove');
|
|
564
|
+
},
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Redraws the chart control. Sometimes useful after screen resize.
|
|
568
|
+
*/
|
|
569
|
+
redraw: function() {
|
|
570
|
+
this._resizeChart();
|
|
571
|
+
},
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* Set default zoom level (followMarker: true).
|
|
575
|
+
*/
|
|
576
|
+
setZFollow: function(zoom) {
|
|
577
|
+
this.options.zFollow = zoom;
|
|
578
|
+
},
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* Hide current elevation chart profile.
|
|
582
|
+
*/
|
|
583
|
+
show: function() {
|
|
584
|
+
style(this._container, "display", "block");
|
|
585
|
+
},
|
|
586
|
+
|
|
587
|
+
/*
|
|
588
|
+
* Parsing data either from GPX or GeoJSON and update the diagram data
|
|
589
|
+
*/
|
|
590
|
+
_addData: function(d) {
|
|
591
|
+
if (!d) {
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// Standard GeoJSON
|
|
596
|
+
if (d.type === "FeatureCollection" ) {
|
|
597
|
+
return each(d.features, feature => this._addData(feature));
|
|
598
|
+
} else if (d.type === "Feature") {
|
|
599
|
+
let geom = d.geometry;
|
|
600
|
+
if (geom) {
|
|
601
|
+
switch (geom.type) {
|
|
602
|
+
case 'LineString': return this._addGeoJSONData(geom.coordinates, d.properties);
|
|
603
|
+
case 'MultiLineString': return each(geom.coordinates, (coords, i) => this._addGeoJSONData(coords, d.properties, i));
|
|
604
|
+
case 'Point':
|
|
605
|
+
default: return console.warn('Unsopperted GeoJSON feature geometry type:' + geom.type);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// Fallback for leaflet layers (eg. L.Gpx)
|
|
611
|
+
if (d._latlngs) {
|
|
612
|
+
return this._addGeoJSONData(d._latlngs, d.feature && d.feature.properties);
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
},
|
|
616
|
+
|
|
617
|
+
/*
|
|
618
|
+
* Parsing of GeoJSON data lines and their elevation in z-coordinate
|
|
619
|
+
*/
|
|
620
|
+
_addGeoJSONData: function(coords, properties, nestingLevel) {
|
|
621
|
+
|
|
622
|
+
// "coordinateProperties" property is generated inside "@tmcw/toGeoJSON"
|
|
623
|
+
let props = (properties && properties.coordinateProperties) || properties;
|
|
624
|
+
|
|
625
|
+
coords.forEach((point, i) => {
|
|
626
|
+
|
|
627
|
+
// GARMIN_EXTENSIONS = ["hr", "cad", "atemp", "wtemp", "depth", "course", "bearing"];
|
|
628
|
+
point.meta = point.meta ?? { time: null, ele: null };
|
|
629
|
+
|
|
630
|
+
point.prev = (attr) => (attr ? this._data[i > 0 ? i - 1 : 0][attr] : this._data[i > 0 ? i - 1 : 0]);
|
|
631
|
+
|
|
632
|
+
this.fire("elepoint_init", { point: point, props: props, id: i, isMulti: nestingLevel });
|
|
633
|
+
|
|
634
|
+
this._addPoint(
|
|
635
|
+
point.lat ?? point[1],
|
|
636
|
+
point.lng ?? point[0],
|
|
637
|
+
point.alt ?? point.meta.ele ?? point[2]
|
|
638
|
+
);
|
|
639
|
+
|
|
640
|
+
this.fire("elepoint_added", { point: point, index: this._data.length - 1 });
|
|
641
|
+
|
|
642
|
+
if (this._yCoordMax < this._data[this._data.length - 1][this.options.yAttr]) this._yCoordMax = this._data[this._data.length - 1][this.options.yAttr];
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
this.fire("eletrack_added", { coords: coords, index: this._data.length - 1 });
|
|
646
|
+
},
|
|
647
|
+
|
|
648
|
+
/*
|
|
649
|
+
* Parse and push a single (x, y, z) point to current elevation profile.
|
|
650
|
+
*/
|
|
651
|
+
_addPoint: function(x, y, z) {
|
|
652
|
+
if (this.options.reverseCoords) {
|
|
653
|
+
[x, y] = [y, x];
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
this._data.push({
|
|
657
|
+
x: x,
|
|
658
|
+
y: y,
|
|
659
|
+
z: z,
|
|
660
|
+
latlng: L.latLng(x, y, z)
|
|
661
|
+
});
|
|
662
|
+
|
|
663
|
+
this.fire("eledata_updated", { index: this._data.length - 1 });
|
|
664
|
+
},
|
|
665
|
+
|
|
666
|
+
_addLayer: function(layer) {
|
|
667
|
+
if (layer) this._layers.addLayer(layer);
|
|
668
|
+
// Postpone adding the distance markers (lazy: true)
|
|
669
|
+
if (layer && this.options.distanceMarkers && this.options.distanceMarkers.lazy) {
|
|
670
|
+
layer.on('add remove', ({target, type}) => L.DistanceMarkers && target instanceof L.Polyline && target[type + 'DistanceMarkers']());
|
|
671
|
+
}
|
|
672
|
+
return layer;
|
|
673
|
+
},
|
|
674
|
+
|
|
675
|
+
_addMarker: function(marker) {
|
|
676
|
+
if (marker) this._markers.addLayer(marker);
|
|
677
|
+
return marker;
|
|
678
|
+
},
|
|
679
|
+
|
|
680
|
+
/**
|
|
681
|
+
* Initialize "L.AlmostOver" integration
|
|
682
|
+
*/
|
|
683
|
+
_initAlmostOverHandler: function(map, layer) {
|
|
684
|
+
return (map && this.options.almostOver && !L.Browser.mobile) ? Promise.all([
|
|
685
|
+
this.import(this.__LGEOMUTIL),
|
|
686
|
+
this.import(this.__LALMOSTOVER)
|
|
687
|
+
]).then(() => {
|
|
688
|
+
map.addHandler('almostOver', L.Handler.AlmostOver);
|
|
689
|
+
if (L.GeometryUtil && map.almostOver && map.almostOver.enabled()) {
|
|
690
|
+
map.almostOver.addLayer(layer);
|
|
691
|
+
map
|
|
692
|
+
.on('almost:move', (e) => this._onMouseMoveLayer(e))
|
|
693
|
+
.on('almost:out', (e) => this._onMouseOut(e));
|
|
694
|
+
}
|
|
695
|
+
}) : Promise.resolve();
|
|
696
|
+
},
|
|
697
|
+
|
|
698
|
+
/**
|
|
699
|
+
* Initialize "L.DistanceMarkers" integration
|
|
700
|
+
*/
|
|
701
|
+
_initDistanceMarkers: function() {
|
|
702
|
+
return this.options.distanceMarkers ? Promise.all([this.import(this.__LGEOMUTIL), this.import(this.__LDISTANCEM)]) : Promise.resolve();
|
|
703
|
+
},
|
|
704
|
+
|
|
705
|
+
_initHotLine: function(layer) {
|
|
706
|
+
let prop = typeof this.options.hotline == 'string' ? this.options.hotline : 'elevation';
|
|
707
|
+
return this.options.hotline ? this.import(this.__LHOTLINE)
|
|
708
|
+
.then(() => {
|
|
709
|
+
layer.eachLayer((trkseg) => {
|
|
710
|
+
if(trkseg.feature.geometry.type != "Point") {
|
|
711
|
+
let geo = L.geoJson(trkseg.toGeoJSON(), { coordsToLatLng: (coords) => L.latLng(coords[0], coords[1], coords[2])});
|
|
712
|
+
let line = L.hotline(geo.toGeoJSON().features[0].geometry.coordinates, {
|
|
713
|
+
min: isFinite(this.track_info[prop + '_min']) ? this.track_info[prop + '_min'] : 0,
|
|
714
|
+
max: isFinite(this.track_info[prop + '_max']) ? this.track_info[prop + '_max'] : 1,
|
|
715
|
+
palette: {
|
|
716
|
+
0.0: '#008800',
|
|
717
|
+
0.5: '#ffff00',
|
|
718
|
+
1.0: '#ff0000'
|
|
719
|
+
},
|
|
720
|
+
weight: 5,
|
|
721
|
+
outlineColor: '#000000',
|
|
722
|
+
outlineWidth: 1
|
|
723
|
+
}).addTo(this._hotline);
|
|
724
|
+
let alpha = trkseg.options.style && trkseg.options.style.opacity || 1;
|
|
725
|
+
trkseg.on('add remove', ({type}) => {
|
|
726
|
+
trkseg.setStyle({opacity: (type == 'add' ? 0 : alpha)});
|
|
727
|
+
line[(type == 'add' ? 'addTo' : 'removeFrom')](trkseg._map);
|
|
728
|
+
if (line._renderer) line._renderer._container.parentElement.insertBefore(line._renderer._container, line._renderer._container.parentElement.firstChild);
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
});
|
|
732
|
+
}) : Promise.resolve();
|
|
733
|
+
},
|
|
734
|
+
|
|
735
|
+
/**
|
|
736
|
+
* Initialize "L.AlmostOver" and "L.DistanceMarkers"
|
|
737
|
+
*/
|
|
738
|
+
_initMapIntegrations: function(layer) {
|
|
739
|
+
let map = this._map;
|
|
740
|
+
if (map) {
|
|
741
|
+
if (this._data.length) {
|
|
742
|
+
this._start.setLatLng(this._data[0].latlng);
|
|
743
|
+
this._end.setLatLng(this._data[this._data.length -1].latlng);
|
|
744
|
+
}
|
|
745
|
+
Promise.all([
|
|
746
|
+
this._initHotLine(layer),
|
|
747
|
+
this._initAlmostOverHandler(map, layer),
|
|
748
|
+
this._initDistanceMarkers(),
|
|
749
|
+
]).then(() => {
|
|
750
|
+
if (this.options.polyline) {
|
|
751
|
+
layer.addTo(map);
|
|
752
|
+
this._circleMarkers.addTo(map);
|
|
753
|
+
}
|
|
754
|
+
if (this.options.autofitBounds) {
|
|
755
|
+
this.fitBounds(layer.getBounds());
|
|
756
|
+
}
|
|
757
|
+
map.invalidateSize();
|
|
758
|
+
});
|
|
759
|
+
} else {
|
|
760
|
+
this.once('add', () => this._initMapIntegrations(layer));
|
|
761
|
+
}
|
|
762
|
+
},
|
|
763
|
+
|
|
764
|
+
/*
|
|
765
|
+
* Collapse current chart control.
|
|
766
|
+
*/
|
|
767
|
+
_collapse: function() {
|
|
768
|
+
replaceClass(this._container, 'elevation-expanded', 'elevation-collapsed');
|
|
769
|
+
if (this._map) this._map.invalidateSize();
|
|
770
|
+
},
|
|
771
|
+
|
|
772
|
+
/*
|
|
773
|
+
* Expand current chart control.
|
|
774
|
+
*/
|
|
775
|
+
_expand: function() {
|
|
776
|
+
replaceClass(this._container, 'elevation-collapsed', 'elevation-expanded');
|
|
777
|
+
if (this._map) this._map.invalidateSize();
|
|
778
|
+
},
|
|
779
|
+
|
|
780
|
+
/**
|
|
781
|
+
* Add some basic colors to leaflet canvas renderer (preferCanvas: true).
|
|
782
|
+
*/
|
|
783
|
+
_fixCanvasPaths: function() {
|
|
784
|
+
let oldProto = L.Canvas.prototype._fillStroke;
|
|
785
|
+
let control = this;
|
|
786
|
+
|
|
787
|
+
let theme = this.options.theme.split(' ')[0].replace('-theme', '');
|
|
788
|
+
let color = Colors[theme] || {};
|
|
789
|
+
|
|
790
|
+
L.Canvas.include({
|
|
791
|
+
_fillStroke: function(ctx, layer) {
|
|
792
|
+
if (control._layers.hasLayer(layer)) {
|
|
793
|
+
|
|
794
|
+
let options = layer.options;
|
|
795
|
+
|
|
796
|
+
options.color = color.line || color.area || theme;
|
|
797
|
+
options.stroke = !!options.color;
|
|
798
|
+
|
|
799
|
+
oldProto.call(this, ctx, layer);
|
|
800
|
+
|
|
801
|
+
if (options.stroke && options.weight !== 0) {
|
|
802
|
+
let oldVal = ctx.globalCompositeOperation || 'source-over';
|
|
803
|
+
ctx.globalCompositeOperation = 'destination-over';
|
|
804
|
+
ctx.strokeStyle = color.outline || '#FFF';
|
|
805
|
+
ctx.lineWidth = options.weight * 1.75;
|
|
806
|
+
ctx.stroke();
|
|
807
|
+
ctx.globalCompositeOperation = oldVal;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
} else {
|
|
811
|
+
oldProto.call(this, ctx, layer);
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
});
|
|
815
|
+
},
|
|
816
|
+
|
|
817
|
+
/**
|
|
818
|
+
* Partial fix for initial tooltip size
|
|
819
|
+
*
|
|
820
|
+
* @link https://github.com/Raruto/leaflet-elevation/issues/81#issuecomment-713477050
|
|
821
|
+
*/
|
|
822
|
+
_fixTooltipSize: function() {
|
|
823
|
+
this.on('elechart_init', () =>
|
|
824
|
+
this.once('elechart_change elechart_hover', ({data, xCoord}) => {
|
|
825
|
+
if (this._chartEnabled) {
|
|
826
|
+
this._chart._showDiagramIndicator(data, xCoord);
|
|
827
|
+
this._chart._showDiagramIndicator(data, xCoord);
|
|
828
|
+
}
|
|
829
|
+
this._updateMarker(data);
|
|
830
|
+
})
|
|
831
|
+
);
|
|
832
|
+
},
|
|
833
|
+
|
|
834
|
+
/*
|
|
835
|
+
* Finds a data entry for the given LatLng
|
|
836
|
+
*/
|
|
837
|
+
_findItemForLatLng: function(latlng) {
|
|
838
|
+
return this._data[this._chart._findIndexForLatLng(latlng)];
|
|
839
|
+
},
|
|
840
|
+
|
|
841
|
+
/*
|
|
842
|
+
* Finds a data entry for the given xDiagCoord
|
|
843
|
+
*/
|
|
844
|
+
_findItemForX: function(x) {
|
|
845
|
+
return this._data[this._chart._findIndexForXCoord(x)];
|
|
846
|
+
},
|
|
847
|
+
|
|
848
|
+
/**
|
|
849
|
+
* Fires an event of the specified type.
|
|
850
|
+
*/
|
|
851
|
+
_fireEvt: function(type, data, propagate) {
|
|
852
|
+
if (this.fire) this.fire(type, data, propagate);
|
|
853
|
+
if (this._map) this._map.fire(type, data, propagate);
|
|
854
|
+
},
|
|
855
|
+
|
|
856
|
+
/*
|
|
857
|
+
* Hides the position/height indicator marker drawn onto the map
|
|
858
|
+
*/
|
|
859
|
+
_hideMarker: function() {
|
|
860
|
+
if (this.options.autohideMarker) {
|
|
861
|
+
this._marker.remove();
|
|
862
|
+
}
|
|
863
|
+
},
|
|
864
|
+
|
|
865
|
+
/**
|
|
866
|
+
* Generate "svg" chart (DOM element).
|
|
867
|
+
*/
|
|
868
|
+
_initChart: function(container) {
|
|
869
|
+
let opts = this.options;
|
|
870
|
+
|
|
871
|
+
if (opts.detached) {
|
|
872
|
+
let { offsetWidth, offsetHeight} = this.eleDiv;
|
|
873
|
+
if (offsetWidth > 0) opts.width = offsetWidth;
|
|
874
|
+
if (offsetHeight > 20) opts.height = offsetHeight - 20; // 20 = horizontal scrollbar size.
|
|
875
|
+
} else {
|
|
876
|
+
let { clientWidth } = this._map.getContainer();
|
|
877
|
+
opts._maxWidth = opts._maxWidth > opts.width ? opts._maxWidth : opts.width;
|
|
878
|
+
this._container.style.maxWidth = opts._maxWidth + 'px';
|
|
879
|
+
if (opts._maxWidth > clientWidth) opts.width = clientWidth - 30;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
Promise.all([
|
|
883
|
+
this.import(this.__D3),
|
|
884
|
+
this.import(this.__LCHART)
|
|
885
|
+
]).then((m) => {
|
|
886
|
+
|
|
887
|
+
let chart = this._chart = new (m[1] || Elevation).Chart(opts, this);
|
|
888
|
+
|
|
889
|
+
this._x = this._chart._x;
|
|
890
|
+
this._y = this._chart._y;
|
|
891
|
+
|
|
892
|
+
d3
|
|
893
|
+
.select(container)
|
|
894
|
+
.call(chart.render());
|
|
895
|
+
|
|
896
|
+
chart
|
|
897
|
+
.on('reset_drag', this._hideMarker, this)
|
|
898
|
+
.on('mouse_enter', this._onMouseEnter, this)
|
|
899
|
+
.on('dragged', this._onDragEnd, this)
|
|
900
|
+
.on('mouse_move', this._onMouseMove, this)
|
|
901
|
+
.on('mouse_out', this._onMouseOut, this)
|
|
902
|
+
.on('ruler_filter', this._onRulerFilter, this)
|
|
903
|
+
.on('zoom', this._updateChart, this)
|
|
904
|
+
.on('elepath_toggle', this._onToggleChart, this)
|
|
905
|
+
.on('margins_updated', this._resizeChart, this);
|
|
906
|
+
|
|
907
|
+
|
|
908
|
+
this.fire("elechart_init");
|
|
909
|
+
|
|
910
|
+
map
|
|
911
|
+
.on('zoom viewreset zoomanim', this._hideMarker, this)
|
|
912
|
+
.on('resize', this._resetView, this)
|
|
913
|
+
.on('resize', this._resizeChart, this)
|
|
914
|
+
.on('rotate', this._rotateMarker, this)
|
|
915
|
+
.on('mousedown', this._resetDrag, this);
|
|
916
|
+
|
|
917
|
+
on(map.getContainer(), 'mousewheel', this._resetDrag, this);
|
|
918
|
+
on(map.getContainer(), 'touchstart', this._resetDrag, this);
|
|
919
|
+
on(document, 'keydown', this._onKeyDown, this);
|
|
920
|
+
|
|
921
|
+
this
|
|
922
|
+
.on('eledata_added eledata_loaded', this._updateChart, this)
|
|
923
|
+
.on('eledata_added eledata_loaded', this._updateSummary, this);
|
|
924
|
+
|
|
925
|
+
this._updateChart();
|
|
926
|
+
this._updateSummary();
|
|
927
|
+
});
|
|
928
|
+
|
|
929
|
+
|
|
930
|
+
},
|
|
931
|
+
|
|
932
|
+
_initLayer: function() {
|
|
933
|
+
this._layers
|
|
934
|
+
.on('layeradd layerremove', ({layer, type}) => {
|
|
935
|
+
let node = layer.getElement && layer.getElement();
|
|
936
|
+
toggleClass(node, this.options.polyline.className + ' ' + this.options.theme, type == 'layeradd');
|
|
937
|
+
toggleEvent(layer, "mousemove", this._onMouseMoveLayer.bind(this), type == 'layeradd');
|
|
938
|
+
toggleEvent(layer, "mouseout", this._onMouseOut.bind(this), type == 'layeradd');
|
|
939
|
+
});
|
|
940
|
+
},
|
|
941
|
+
|
|
942
|
+
_initMarker: function(map) {
|
|
943
|
+
let pane = map.getPane('elevationPane');
|
|
944
|
+
if (!pane) {
|
|
945
|
+
pane = this._pane = map.createPane('elevationPane', map.getPane('norotatePane') || map.getPane('mapPane'));
|
|
946
|
+
pane.style.zIndex = 625; // This pane is above markers but below popups.
|
|
947
|
+
pane.style.pointerEvents = 'none';
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
if (this._renderer) this._renderer.remove();
|
|
951
|
+
this._renderer = L.svg({ pane: "elevationPane" }).addTo(this._map); // default leaflet svg renderer
|
|
952
|
+
|
|
953
|
+
Promise.all([
|
|
954
|
+
this.import(this.__D3),
|
|
955
|
+
this.import(this.__LMARKER)
|
|
956
|
+
]).then((m) => {
|
|
957
|
+
this._marker = new (m[1] || Elevation).Marker(this.options, this);
|
|
958
|
+
this.fire("elechart_marker");
|
|
959
|
+
});
|
|
960
|
+
},
|
|
961
|
+
|
|
962
|
+
/**
|
|
963
|
+
* Inspired by L.Control.Layers
|
|
964
|
+
*/
|
|
965
|
+
_initButton: function(container) {
|
|
966
|
+
L.DomEvent
|
|
967
|
+
.disableClickPropagation(container)
|
|
968
|
+
.disableScrollPropagation(container);
|
|
969
|
+
|
|
970
|
+
this.options.collapsed ? this._collapse() : this._expand();
|
|
971
|
+
|
|
972
|
+
if (this.options.autohide) {
|
|
973
|
+
on(container, 'mouseover', this._expand, this);
|
|
974
|
+
on(container, 'mouseout', this._collapse, this);
|
|
975
|
+
this._map.on('click', this._collapse, this);
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
if (this.options.closeBtn) {
|
|
979
|
+
let link = this._button = create('a', "elevation-toggle-icon", { href: '#', title: L._('Elevation'), }, container);
|
|
980
|
+
on(link, 'click', L.DomEvent.stop);
|
|
981
|
+
on(link, 'click', this._toggle, this);
|
|
982
|
+
on(link, 'focus', this._toggle, this);
|
|
983
|
+
}
|
|
984
|
+
},
|
|
985
|
+
|
|
986
|
+
_initSummary: function(container) {
|
|
987
|
+
this.import(this.__LSUMMARY).then((m)=>{
|
|
988
|
+
this._summary = new (m || Elevation).Summary({ summary: this.options.summary }, this);
|
|
989
|
+
|
|
990
|
+
this.on('elechart_init', () => {
|
|
991
|
+
d3.select(container).call(this._summary.render());
|
|
992
|
+
});
|
|
993
|
+
});
|
|
994
|
+
},
|
|
995
|
+
|
|
996
|
+
/**
|
|
997
|
+
* Retrieve data from a remote url (HTTP).
|
|
998
|
+
*/
|
|
999
|
+
_loadFile: function(url) {
|
|
1000
|
+
fetch(url)
|
|
1001
|
+
.then((response) => response.text())
|
|
1002
|
+
.then((data) => {
|
|
1003
|
+
this._downloadURL = url; // TODO: handle multiple urls?
|
|
1004
|
+
this._parseFromString(data)
|
|
1005
|
+
.then( geojson => geojson && this._loadLayer(geojson));
|
|
1006
|
+
}).catch((err) => console.warn(err));
|
|
1007
|
+
},
|
|
1008
|
+
|
|
1009
|
+
/**
|
|
1010
|
+
* Dynamically import only required javascript modules (code splitting)
|
|
1011
|
+
*/
|
|
1012
|
+
_loadModules: function(handlers) {
|
|
1013
|
+
// First map known classnames (eg. "Altitude" --> L.Control.Elevation.Altitude)
|
|
1014
|
+
handlers = handlers.map((h) => typeof h === 'string' && typeof Elevation[h] !== "undefined" ? Elevation[h] : h);
|
|
1015
|
+
// Then load optional classes and custom imports (eg. "Cadence" --> import('../src/handlers/cadence.js'))
|
|
1016
|
+
return Promise.
|
|
1017
|
+
all(
|
|
1018
|
+
handlers
|
|
1019
|
+
.filter(h => typeof h === 'string' || h instanceof Promise)
|
|
1020
|
+
.map(file => file instanceof Promise ? file : this.import(this.__modulesFolder + file.toLowerCase() + '.js'))
|
|
1021
|
+
)
|
|
1022
|
+
.then((m) => {
|
|
1023
|
+
each(m, (exported) => {
|
|
1024
|
+
let fn = Object.keys(exported)[0];
|
|
1025
|
+
if (fn && typeof Elevation[fn] === "undefined" ) {
|
|
1026
|
+
handlers[fn] = Elevation[fn] = exported[fn];
|
|
1027
|
+
|
|
1028
|
+
}
|
|
1029
|
+
});
|
|
1030
|
+
each(handlers, h => this._registerHandler(h));
|
|
1031
|
+
});
|
|
1032
|
+
},
|
|
1033
|
+
|
|
1034
|
+
/**
|
|
1035
|
+
* Simple GeoJSON data loader (L.GeoJSON).
|
|
1036
|
+
*/
|
|
1037
|
+
_loadLayer: function(geojson) {
|
|
1038
|
+
let { polyline, theme, waypoints, wptIcons, wptLabels, distanceMarkers } = this.options;
|
|
1039
|
+
let style = L.extend({}, polyline);
|
|
1040
|
+
|
|
1041
|
+
if (theme) {
|
|
1042
|
+
style.className += ' ' + theme;
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
if (geojson.name) {
|
|
1046
|
+
this.track_info.name = geojson.name;
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
let layer = L.geoJson(geojson, {
|
|
1050
|
+
distanceMarkers: distanceMarkers,
|
|
1051
|
+
style: style,
|
|
1052
|
+
pointToLayer: (feature, latlng) => {
|
|
1053
|
+
if (waypoints) {
|
|
1054
|
+
let { desc, name, sym } = feature.properties;
|
|
1055
|
+
desc = desc || '';
|
|
1056
|
+
name = name || '';
|
|
1057
|
+
// Handle chart waypoints (dots)
|
|
1058
|
+
if ([true, 'dots'].includes(waypoints)) {
|
|
1059
|
+
this._registerCheckPoint({
|
|
1060
|
+
latlng: latlng,
|
|
1061
|
+
label : ([true, 'dots'].includes(wptLabels) ? name : '')
|
|
1062
|
+
});
|
|
1063
|
+
}
|
|
1064
|
+
// Handle map waypoints (markers)
|
|
1065
|
+
if ([true, 'markers'].includes(waypoints) && wptIcons != false) {
|
|
1066
|
+
return this._registerMarker({
|
|
1067
|
+
latlng : latlng,
|
|
1068
|
+
sym : (sym ?? name).replace(' ', '-').replace('"', '').replace("'", '').toLowerCase(),
|
|
1069
|
+
content: [true, 'markers'].includes(wptLabels) && (name || desc) && decodeURI("<b>" + name + "</b>" + (desc.length > 0 ? '<br>' + desc : ''))
|
|
1070
|
+
});
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
},
|
|
1074
|
+
onEachFeature: (feature, layer) => feature.geometry && feature.geometry.type != 'Point' && this.addData(feature, layer),
|
|
1075
|
+
});
|
|
1076
|
+
|
|
1077
|
+
this.import(this.__D3).then(() => {
|
|
1078
|
+
this._initMapIntegrations(layer);
|
|
1079
|
+
this._fireEvt("eledata_loaded", { data: geojson, layer: layer, name: this.track_info.name, track_info: this.track_info });
|
|
1080
|
+
});
|
|
1081
|
+
|
|
1082
|
+
return layer;
|
|
1083
|
+
},
|
|
1084
|
+
|
|
1085
|
+
_onDragEnd: function({ dragstart, dragend}) {
|
|
1086
|
+
this._hideMarker();
|
|
1087
|
+
this.fitBounds(L.latLngBounds([dragstart.latlng, dragend.latlng]));
|
|
1088
|
+
|
|
1089
|
+
this.fire("elechart_dragged");
|
|
1090
|
+
},
|
|
1091
|
+
|
|
1092
|
+
_onKeyDown: function({key}) {
|
|
1093
|
+
if(!this.options.detached && key === "Escape"){
|
|
1094
|
+
this._collapse();
|
|
1095
|
+
} },
|
|
1096
|
+
|
|
1097
|
+
/**
|
|
1098
|
+
* Trigger mouseenter event.
|
|
1099
|
+
*/
|
|
1100
|
+
_onMouseEnter: function() {
|
|
1101
|
+
this.fire('elechart_enter');
|
|
1102
|
+
},
|
|
1103
|
+
|
|
1104
|
+
/*
|
|
1105
|
+
* Handles the moueseover the chart and displays distance and altitude level.
|
|
1106
|
+
*/
|
|
1107
|
+
_onMouseMove: function({xCoord}) {
|
|
1108
|
+
if (this._chartEnabled && this._data.length) {
|
|
1109
|
+
let item = this._findItemForX(xCoord);
|
|
1110
|
+
if (item) {
|
|
1111
|
+
if (this._chartEnabled) this._chart._showDiagramIndicator(item, xCoord);
|
|
1112
|
+
|
|
1113
|
+
this._updateMarker(item);
|
|
1114
|
+
this._setMapView(item);
|
|
1115
|
+
|
|
1116
|
+
if (this._map) {
|
|
1117
|
+
addClass(this._map.getContainer(), 'elechart-hover');
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
this.fire("elechart_change", { data: item, xCoord: xCoord });
|
|
1121
|
+
this.fire("elechart_hover", { data: item, xCoord: xCoord });
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
},
|
|
1125
|
+
|
|
1126
|
+
/*
|
|
1127
|
+
* Handles mouseover events of the data layers on the map.
|
|
1128
|
+
*/
|
|
1129
|
+
_onMouseMoveLayer: function({latlng}) {
|
|
1130
|
+
if (this._data.length) {
|
|
1131
|
+
let item = this._findItemForLatLng(latlng);
|
|
1132
|
+
if (item) {
|
|
1133
|
+
let xCoord = item.xDiagCoord;
|
|
1134
|
+
|
|
1135
|
+
if (this._chartEnabled) this._chart._showDiagramIndicator(item, xCoord);
|
|
1136
|
+
|
|
1137
|
+
this._updateMarker(item);
|
|
1138
|
+
|
|
1139
|
+
this.fire("elechart_change", { data: item, xCoord: xCoord });
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
},
|
|
1143
|
+
|
|
1144
|
+
/*
|
|
1145
|
+
* Handles the moueseout over the chart.
|
|
1146
|
+
*/
|
|
1147
|
+
_onMouseOut: function() {
|
|
1148
|
+
if (!this.options.detached) {
|
|
1149
|
+
this._hideMarker();
|
|
1150
|
+
this._chart._hideDiagramIndicator();
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
if (this._map) {
|
|
1154
|
+
removeClass(this._map.getContainer(), 'elechart-hover');
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
this.fire("elechart_leave");
|
|
1158
|
+
},
|
|
1159
|
+
|
|
1160
|
+
/**
|
|
1161
|
+
* Handles the drag event over the ruler filter.
|
|
1162
|
+
*/
|
|
1163
|
+
_onRulerFilter: function({coords}) {
|
|
1164
|
+
this._updateMapSegments(coords);
|
|
1165
|
+
},
|
|
1166
|
+
|
|
1167
|
+
/**
|
|
1168
|
+
* Toggle chart data on legend click
|
|
1169
|
+
*/
|
|
1170
|
+
_onToggleChart: function({ name, enabled }) {
|
|
1171
|
+
|
|
1172
|
+
this._chartEnabled = this._chart._hasActiveLayers();
|
|
1173
|
+
|
|
1174
|
+
// toggle layer visibility on empty chart
|
|
1175
|
+
this._layers.eachLayer(layer => toggleClass(layer.getElement && layer.getElement(), this.options.polyline.className + ' ' + this.options.theme, this._chartEnabled));
|
|
1176
|
+
|
|
1177
|
+
// toggle option value (eg. altitude = { 'disabled' || 'enabled' })
|
|
1178
|
+
this.options[name] = !enabled && this.options[name] == 'disabled' ? 'enabled' : 'disabled';
|
|
1179
|
+
|
|
1180
|
+
// remove marker on empty chart
|
|
1181
|
+
if (!this._chartEnabled) {
|
|
1182
|
+
this._chart._hideDiagramIndicator();
|
|
1183
|
+
this._marker.remove();
|
|
1184
|
+
}
|
|
1185
|
+
},
|
|
1186
|
+
|
|
1187
|
+
/**
|
|
1188
|
+
* Simple GeoJSON Parser
|
|
1189
|
+
*/
|
|
1190
|
+
_parseFromGeoJSONString: function(data) {
|
|
1191
|
+
try {
|
|
1192
|
+
return JSON.parse(data);
|
|
1193
|
+
} catch (e) { }
|
|
1194
|
+
},
|
|
1195
|
+
|
|
1196
|
+
/**
|
|
1197
|
+
* Attempt to parse raw response data (GeoJSON or XML > GeoJSON)
|
|
1198
|
+
*/
|
|
1199
|
+
_parseFromString: function(data) {
|
|
1200
|
+
return new Promise(resolve =>
|
|
1201
|
+
this.import(this.__TOGEOJSON).then(() => {
|
|
1202
|
+
let geojson;
|
|
1203
|
+
try {
|
|
1204
|
+
geojson = this._parseFromXMLString(data.trim());
|
|
1205
|
+
} catch (e) {
|
|
1206
|
+
geojson = this._parseFromGeoJSONString(data.toString());
|
|
1207
|
+
}
|
|
1208
|
+
if (geojson) {
|
|
1209
|
+
geojson.name = geojson.name || (this._downloadURL || '').split('/').pop().split('#')[0].split('?')[0];
|
|
1210
|
+
}
|
|
1211
|
+
resolve(geojson);
|
|
1212
|
+
})
|
|
1213
|
+
);
|
|
1214
|
+
},
|
|
1215
|
+
|
|
1216
|
+
/**
|
|
1217
|
+
* Simple XML Parser (GPX, KML, TCX)
|
|
1218
|
+
*/
|
|
1219
|
+
_parseFromXMLString: function(data) {
|
|
1220
|
+
if (data.indexOf("<") != 0) {
|
|
1221
|
+
throw 'Invalid XML';
|
|
1222
|
+
}
|
|
1223
|
+
let xml = (new DOMParser()).parseFromString(data, "text/xml");
|
|
1224
|
+
let type = xml.documentElement.tagName.toLowerCase(); // "kml" or "gpx"
|
|
1225
|
+
let name = xml.getElementsByTagName('name');
|
|
1226
|
+
if (xml.getElementsByTagName('parsererror').length) {
|
|
1227
|
+
throw 'Invalid XML';
|
|
1228
|
+
}
|
|
1229
|
+
if (!(type in toGeoJSON)) {
|
|
1230
|
+
type = xml.documentElement.tagName == "TrainingCenterDatabase" ? 'tcx' : 'gpx';
|
|
1231
|
+
}
|
|
1232
|
+
let geojson = toGeoJSON[type](xml);
|
|
1233
|
+
geojson.name = name.length > 0 ? (Array.from(name).find(tag => tag.parentElement.tagName == "trk") ?? name[0]).textContent : '';
|
|
1234
|
+
return geojson;
|
|
1235
|
+
},
|
|
1236
|
+
|
|
1237
|
+
/**
|
|
1238
|
+
* Add chart profile to diagram
|
|
1239
|
+
*/
|
|
1240
|
+
_registerAreaPath: function(props) {
|
|
1241
|
+
this.on("elechart_init", () => this._chart._registerAreaPath(props));
|
|
1242
|
+
},
|
|
1243
|
+
|
|
1244
|
+
/**
|
|
1245
|
+
* Add chart grid to diagram
|
|
1246
|
+
*/
|
|
1247
|
+
_registerAxisGrid: function(props) {
|
|
1248
|
+
this.on("elechart_axis", () => this._chart._registerAxisGrid(props));
|
|
1249
|
+
},
|
|
1250
|
+
|
|
1251
|
+
/**
|
|
1252
|
+
* Add chart axis to diagram
|
|
1253
|
+
*/
|
|
1254
|
+
_registerAxisScale: function(props) {
|
|
1255
|
+
this.on("elechart_axis", () => this._chart._registerAxisScale(props));
|
|
1256
|
+
},
|
|
1257
|
+
|
|
1258
|
+
/**
|
|
1259
|
+
* Add a point of interest over the diagram
|
|
1260
|
+
*/
|
|
1261
|
+
_registerCheckPoint: function(props) {
|
|
1262
|
+
this.on("elechart_updated", () => this._chart._registerCheckPoint(props));
|
|
1263
|
+
},
|
|
1264
|
+
|
|
1265
|
+
|
|
1266
|
+
/**
|
|
1267
|
+
* Base handler for iterative track statistics (dist, time, z, slope, speed, acceleration, ...)
|
|
1268
|
+
*/
|
|
1269
|
+
_registerDataAttribute: function(props) {
|
|
1270
|
+
|
|
1271
|
+
// parse of "coordinateProperties" for later usage
|
|
1272
|
+
if (props.coordPropsToMeta) {
|
|
1273
|
+
this.on("elepoint_init", (e) => props.coordPropsToMeta.call(this, e));
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
// prevent excessive variabile instanstations
|
|
1277
|
+
let i, curr, prev, attr;
|
|
1278
|
+
|
|
1279
|
+
// save here a reference to last used point
|
|
1280
|
+
let lastValid = null;
|
|
1281
|
+
|
|
1282
|
+
// iteration
|
|
1283
|
+
this.on("elepoint_added", ({index, point}) => {
|
|
1284
|
+
i = index;
|
|
1285
|
+
|
|
1286
|
+
prev = curr ?? this._data[i]; // same as: this._data[i > 0 ? i - 1 : i]
|
|
1287
|
+
curr = this._data[i];
|
|
1288
|
+
attr = props.attr || props.name;
|
|
1289
|
+
|
|
1290
|
+
// check and fix missing data on last added point
|
|
1291
|
+
if (props.skipNull === false) {
|
|
1292
|
+
let curr = this._data[i][attr];
|
|
1293
|
+
if(i > 0) {
|
|
1294
|
+
let prev = this._data[i - 1][attr];
|
|
1295
|
+
if (isNaN(prev)) {
|
|
1296
|
+
if (!isNaN(lastValid) && !isNaN(curr)) {
|
|
1297
|
+
prev = (lastValid + curr) / 2;
|
|
1298
|
+
} else if (!isNaN(lastValid)) {
|
|
1299
|
+
prev = lastValid;
|
|
1300
|
+
} else if (!isNaN(curr)) {
|
|
1301
|
+
prev = curr;
|
|
1302
|
+
}
|
|
1303
|
+
if (!isNaN(prev)) return this._data.splice(i - 1, 1);
|
|
1304
|
+
this._data[i - 1][attr] = prev;
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
// update reference to last used point (ie. if it has data)
|
|
1308
|
+
if (!isNaN(curr)) {
|
|
1309
|
+
lastValid = curr;
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
// retrieve point value
|
|
1314
|
+
curr[attr] = props.pointToAttr.call(this, point, i);
|
|
1315
|
+
|
|
1316
|
+
// update "track_info" stats (min, max, avg, ...)
|
|
1317
|
+
if (props.stats) {
|
|
1318
|
+
for (const key in props.stats) {
|
|
1319
|
+
let sname = (props.statsName || attr) + (key != '' ? '_' : '');
|
|
1320
|
+
this.track_info[sname + key] = props.stats[key].call(this, curr[attr], this.track_info[sname + key]);
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
// Limit "crazy" delta values.
|
|
1325
|
+
if (props.deltaMax) {
|
|
1326
|
+
curr[attr] =wrapDelta(curr[attr], prev[attr], props.deltaMax);
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
// Range of acceptable values.
|
|
1330
|
+
if (props.clampRange) {
|
|
1331
|
+
curr[attr] = clamp(curr[attr], props.clampRange);
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
// Limit floating point precision.
|
|
1335
|
+
if (!isNaN(props.decimals)) {
|
|
1336
|
+
curr[attr] = round(curr[attr], props.decimals);
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
// update here some mixins (eg. complex "track_info" stuff)
|
|
1340
|
+
if(props.onPointAdded) props.onPointAdded.call(this, curr[attr], i, point);
|
|
1341
|
+
});
|
|
1342
|
+
},
|
|
1343
|
+
|
|
1344
|
+
/**
|
|
1345
|
+
* Parse a module definition and attach related function listeners
|
|
1346
|
+
*/
|
|
1347
|
+
_registerHandler: function(props) {
|
|
1348
|
+
|
|
1349
|
+
// eg. L.Control.Altitude
|
|
1350
|
+
if (typeof props === "function") {
|
|
1351
|
+
return this._registerHandler(props.call(this));
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
let {
|
|
1355
|
+
name,
|
|
1356
|
+
attr,
|
|
1357
|
+
required,
|
|
1358
|
+
skipNull,
|
|
1359
|
+
deltaMax,
|
|
1360
|
+
clampRange,
|
|
1361
|
+
decimals,
|
|
1362
|
+
meta,
|
|
1363
|
+
unit,
|
|
1364
|
+
coordinateProperties,
|
|
1365
|
+
coordPropsToMeta: coordPropsToMeta$1,
|
|
1366
|
+
pointToAttr,
|
|
1367
|
+
onPointAdded,
|
|
1368
|
+
stats,
|
|
1369
|
+
statsName,
|
|
1370
|
+
grid,
|
|
1371
|
+
scale,
|
|
1372
|
+
path,
|
|
1373
|
+
tooltip,
|
|
1374
|
+
summary
|
|
1375
|
+
} = props;
|
|
1376
|
+
|
|
1377
|
+
// eg. "altitude" == true
|
|
1378
|
+
if (this.options[name] || required) {
|
|
1379
|
+
|
|
1380
|
+
this._registerDataAttribute({
|
|
1381
|
+
name,
|
|
1382
|
+
attr,
|
|
1383
|
+
skipNull,
|
|
1384
|
+
deltaMax,
|
|
1385
|
+
clampRange,
|
|
1386
|
+
decimals,
|
|
1387
|
+
coordPropsToMeta: coordPropsToMeta(coordinateProperties, meta || name, coordPropsToMeta$1),
|
|
1388
|
+
pointToAttr,
|
|
1389
|
+
onPointAdded,
|
|
1390
|
+
stats,
|
|
1391
|
+
statsName,
|
|
1392
|
+
});
|
|
1393
|
+
|
|
1394
|
+
if (grid) {
|
|
1395
|
+
this._registerAxisGrid(L.extend({ name }, grid));
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
if (this.options[name] !== "summary") {
|
|
1399
|
+
if (scale) this._registerAxisScale(L.extend({ name, label: unit }, scale));
|
|
1400
|
+
if (path) this._registerAreaPath(L.extend({ name }, path));
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
if (tooltip || props.tooltips) {
|
|
1404
|
+
each([tooltip, ...(props.tooltips || [])], t => t && this._registerTooltip(L.extend({ name }, t)));
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
if (summary) {
|
|
1408
|
+
each(summary, (s, k) => summary[k] = L.extend({ unit }, s));
|
|
1409
|
+
this._registerSummary(summary);
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
},
|
|
1413
|
+
|
|
1414
|
+
_registerMarker({latlng, sym, content}) {
|
|
1415
|
+
let { wptIcons } = this.options;
|
|
1416
|
+
// generate and cache appropriate icon symbol
|
|
1417
|
+
if (!wptIcons.hasOwnProperty(sym)) {
|
|
1418
|
+
wptIcons[sym] = L.divIcon(L.extend({}, wptIcons[""].options, { html: '<i class="elevation-waypoint-icon ' + sym + '"></i>' } ));
|
|
1419
|
+
}
|
|
1420
|
+
let marker = L.marker(latlng, { icon: wptIcons[sym] });
|
|
1421
|
+
if (content) {
|
|
1422
|
+
marker.bindPopup(content, { className: 'elevation-popup', keepInView: true }).openPopup();
|
|
1423
|
+
marker.bindTooltip(content, { className: 'elevation-tooltip', direction: 'auto', sticky: true, opacity: 1 }).openTooltip();
|
|
1424
|
+
}
|
|
1425
|
+
return this._addMarker(marker)
|
|
1426
|
+
},
|
|
1427
|
+
|
|
1428
|
+
/**
|
|
1429
|
+
* Add chart or marker tooltip info
|
|
1430
|
+
*/
|
|
1431
|
+
_registerTooltip: function(props) {
|
|
1432
|
+
props.chart && this.on("elechart_init", () => this._chart._registerTooltip(L.extend({}, props, { value: props.chart })));
|
|
1433
|
+
props.marker && this.on("elechart_marker", () => this._marker._registerTooltip(L.extend({}, props, { value: props.marker })));
|
|
1434
|
+
},
|
|
1435
|
+
|
|
1436
|
+
/**
|
|
1437
|
+
* Add summary info to diagram
|
|
1438
|
+
*/
|
|
1439
|
+
_registerSummary: function(props) {
|
|
1440
|
+
this.on('elechart_summary', () => this._summary._registerSummary(props));
|
|
1441
|
+
},
|
|
1442
|
+
|
|
1443
|
+
/*
|
|
1444
|
+
* Removes the drag rectangle and zoms back to the total extent of the data.
|
|
1445
|
+
*/
|
|
1446
|
+
_resetDrag: function() {
|
|
1447
|
+
this._chart._resetDrag();
|
|
1448
|
+
this._hideMarker();
|
|
1449
|
+
},
|
|
1450
|
+
|
|
1451
|
+
/**
|
|
1452
|
+
* Resets drag, marker and bounds.
|
|
1453
|
+
*/
|
|
1454
|
+
_resetView: function() {
|
|
1455
|
+
if (this._map && this._map._isFullscreen) return;
|
|
1456
|
+
this._resetDrag();
|
|
1457
|
+
this._hideMarker();
|
|
1458
|
+
if (this.options.autofitBounds) {
|
|
1459
|
+
this.fitBounds();
|
|
1460
|
+
}
|
|
1461
|
+
},
|
|
1462
|
+
|
|
1463
|
+
/**
|
|
1464
|
+
* Hacky way for handling chart resize. Deletes it and redraw chart.
|
|
1465
|
+
*/
|
|
1466
|
+
_resizeChart: function() {
|
|
1467
|
+
if (this._container && style(this._container, "display") != "none") {
|
|
1468
|
+
let opts = this.options;
|
|
1469
|
+
let newWidth = opts.detached ? (this.eleDiv || this._container).offsetWidth : clamp(opts._maxWidth, [0, this._map.getContainer().clientWidth - 30]);
|
|
1470
|
+
if (newWidth) {
|
|
1471
|
+
opts.width = newWidth;
|
|
1472
|
+
if (this._chart && this._chart._chart) {
|
|
1473
|
+
this._chart._chart._resize(opts);
|
|
1474
|
+
this._updateChart();
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
this._updateMapSegments();
|
|
1478
|
+
}
|
|
1479
|
+
},
|
|
1480
|
+
|
|
1481
|
+
/**
|
|
1482
|
+
* Collapse or Expand chart control.
|
|
1483
|
+
*/
|
|
1484
|
+
_toggle: function() {
|
|
1485
|
+
hasClass(this._container, "elevation-expanded") ? this._collapse() : this._expand();
|
|
1486
|
+
},
|
|
1487
|
+
|
|
1488
|
+
/**
|
|
1489
|
+
* Update map center and zoom (followMarker: true)
|
|
1490
|
+
*/
|
|
1491
|
+
_setMapView: function(item) {
|
|
1492
|
+
if (this._map && this.options.followMarker) {
|
|
1493
|
+
let zoom = this._map.getZoom();
|
|
1494
|
+
let z = this.options.zFollow;
|
|
1495
|
+
if (typeof z === "number") {
|
|
1496
|
+
this._map.setView(item.latlng, (zoom < z ? z : zoom), { animate: true, duration: 0.25 });
|
|
1497
|
+
} else if (!this._map.getBounds().contains(item.latlng)) {
|
|
1498
|
+
this._map.setView(item.latlng, zoom, { animate: true, duration: 0.25 });
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
},
|
|
1502
|
+
|
|
1503
|
+
/**
|
|
1504
|
+
* Calculates [x, y] domain and then update chart.
|
|
1505
|
+
*/
|
|
1506
|
+
_updateChart: function() {
|
|
1507
|
+
if (this._chart && this._container) {
|
|
1508
|
+
this.fire("elechart_axis");
|
|
1509
|
+
|
|
1510
|
+
this._chart.update({ data: this._data, options: this.options });
|
|
1511
|
+
|
|
1512
|
+
this._x = this._chart._x;
|
|
1513
|
+
this._y = this._chart._y;
|
|
1514
|
+
|
|
1515
|
+
this.fire('elechart_updated');
|
|
1516
|
+
}
|
|
1517
|
+
},
|
|
1518
|
+
|
|
1519
|
+
/*
|
|
1520
|
+
* Update the position/height indicator marker drawn onto the map
|
|
1521
|
+
*/
|
|
1522
|
+
_updateMarker: function(item) {
|
|
1523
|
+
if (this._marker) {
|
|
1524
|
+
this._marker.update({
|
|
1525
|
+
map : this._map,
|
|
1526
|
+
item : item,
|
|
1527
|
+
yCoordMax : this._yCoordMax || 0,
|
|
1528
|
+
options : this.options
|
|
1529
|
+
});
|
|
1530
|
+
}
|
|
1531
|
+
},
|
|
1532
|
+
|
|
1533
|
+
/**
|
|
1534
|
+
* Fix marker rotation on rotated maps
|
|
1535
|
+
*/
|
|
1536
|
+
_rotateMarker: function() {
|
|
1537
|
+
if (this._marker) {
|
|
1538
|
+
this._marker.update();
|
|
1539
|
+
}
|
|
1540
|
+
},
|
|
1541
|
+
|
|
1542
|
+
/**
|
|
1543
|
+
* Highlight track segments on the map.
|
|
1544
|
+
*/
|
|
1545
|
+
_updateMapSegments: function(coords) {
|
|
1546
|
+
this._markedSegments.setLatLngs(coords || []);
|
|
1547
|
+
if (coords && this._map && !this._map.hasLayer(this._markedSegments)) {
|
|
1548
|
+
this._markedSegments.addTo(this._map);
|
|
1549
|
+
}
|
|
1550
|
+
},
|
|
1551
|
+
|
|
1552
|
+
/**
|
|
1553
|
+
* Update chart summary.
|
|
1554
|
+
*/
|
|
1555
|
+
_updateSummary: function() {
|
|
1556
|
+
if (this._summary) {
|
|
1557
|
+
this._summary.reset();
|
|
1558
|
+
if (this.options.summary) {
|
|
1559
|
+
this.fire("elechart_summary");
|
|
1560
|
+
this._summary.update();
|
|
1561
|
+
}
|
|
1562
|
+
if (this.options.downloadLink && this._downloadURL) { // TODO: generate dynamically file content instead of using static file urls.
|
|
1563
|
+
this._summary._container.innerHTML += '<span class="download"><a href="#">' + L._('Download') + '</a></span>';
|
|
1564
|
+
select('.download a', this._summary._container).onclick = (e) => {
|
|
1565
|
+
e.preventDefault();
|
|
1566
|
+
let event = { downloadLink: this.options.downloadLink, confirm: saveFile.bind(this, this._downloadURL) };
|
|
1567
|
+
if (this.options.downloadLink == 'modal' && typeof CustomEvent === "function") {
|
|
1568
|
+
document.dispatchEvent(new CustomEvent("eletrack_download", { detail: event }));
|
|
1569
|
+
} else if (this.options.downloadLink == 'link' || this.options.downloadLink === true) {
|
|
1570
|
+
event.confirm();
|
|
1571
|
+
}
|
|
1572
|
+
this.fire('eletrack_download', event);
|
|
1573
|
+
};
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1576
|
+
},
|
|
1577
|
+
|
|
1578
|
+
|
|
1579
|
+
/**
|
|
1580
|
+
* Calculates chart width.
|
|
1581
|
+
*/
|
|
1582
|
+
_width: function() {
|
|
1583
|
+
if (this._chart) return this._chart._width();
|
|
1584
|
+
const { width, margins } = this.options;
|
|
1585
|
+
return width - margins.left - margins.right;
|
|
1586
|
+
},
|
|
1587
|
+
|
|
1588
|
+
/**
|
|
1589
|
+
* Calculates chart height.
|
|
1590
|
+
*/
|
|
1591
|
+
_height: function() {
|
|
1592
|
+
if (this._chart) return this._chart._height();
|
|
1593
|
+
const { height, margins } = this.options;
|
|
1594
|
+
return height - margins.top - margins.bottom;
|
|
1595
|
+
},
|
|
1596
|
+
|
|
1597
|
+
});
|
|
1598
|
+
|
|
1599
|
+
/*
|
|
1600
|
+
* Copyright (c) 2019, GPL-3.0+ Project, Raruto
|
|
1601
|
+
*
|
|
1602
|
+
* This file is free software: you may copy, redistribute and/or modify it
|
|
1603
|
+
* under the terms of the GNU General Public License as published by the
|
|
1604
|
+
* Free Software Foundation, either version 2 of the License, or (at your
|
|
1605
|
+
* option) any later version.
|
|
1606
|
+
*
|
|
1607
|
+
* This file is distributed in the hope that it will be useful, but
|
|
1608
|
+
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
1609
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
1610
|
+
* General Public License for more details.
|
|
1611
|
+
*
|
|
1612
|
+
* You should have received a copy of the GNU General Public License
|
|
1613
|
+
* along with this program. If not, see .
|
|
1614
|
+
*
|
|
1615
|
+
* This file incorporates work covered by the following copyright and
|
|
1616
|
+
* permission notice:
|
|
1617
|
+
*
|
|
1618
|
+
* Copyright (c) 2013-2016, MIT License, Felix “MrMufflon” Bache
|
|
1619
|
+
*
|
|
1620
|
+
* Permission to use, copy, modify, and/or distribute this software
|
|
1621
|
+
* for any purpose with or without fee is hereby granted, provided
|
|
1622
|
+
* that the above copyright notice and this permission notice appear
|
|
1623
|
+
* in all copies.
|
|
1624
|
+
*
|
|
1625
|
+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
|
1626
|
+
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
|
1627
|
+
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
|
1628
|
+
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
|
|
1629
|
+
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
|
1630
|
+
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
|
|
1631
|
+
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
|
1632
|
+
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
1633
|
+
*/
|
|
1634
|
+
|
|
1635
|
+
Elevation.Utils = _;
|
|
1636
|
+
|
|
1637
|
+
L.control.elevation = (options) => new Elevation(options);
|
|
1686
1638
|
|
|
1687
1639
|
}));
|
|
1688
1640
|
//# sourceMappingURL=leaflet-elevation.js.map
|