129 Commits
Author SHA1 Message Date
vcoppe 497ac120ec progress 2026-09-23 19:53:39 +02:00
vcoppe a486016d96 progress 2026-09-21 23:49:57 +02:00
vcoppe cc5f5653ef progress 2026-09-20 13:58:27 +02:00
vcoppe 20222c450d parsing continued 2026-09-18 09:50:04 +02:00
vcoppe 69049e1345 parsing start 2026-09-18 09:03:51 +02:00
vcoppe ed6264444f wasm first steps 2026-07-14 18:36:30 +02:00
vcoppe 86f4fc3394 New Crowdin updates (#355)
* New translations map-controls.mdx (Chinese Simplified)

[ci skip]

* New translations en.json (Chinese Simplified)

[ci skip]

* New translations elevation.mdx (Chinese Simplified)

[ci skip]

* New translations settings.mdx (Dutch)

[ci skip]

* New translations en.json (Romanian)

[ci skip]

* New translations en.json (Chinese Traditional, Hong Kong)

[ci skip]

* New translations en.json (Chinese Simplified)

[ci skip]

* New translations en.json (Chinese Traditional, Hong Kong)

[ci skip]

* New translations en.json (Chinese Traditional, Hong Kong)

[ci skip]

* New translations en.json (Chinese Simplified)

[ci skip]

* New translations en.json (Chinese Traditional, Hong Kong)

[ci skip]

* New translations en.json (Chinese Simplified)

[ci skip]

* New translations en.json (Chinese Traditional, Hong Kong)

[ci skip]

* New translations settings.mdx (Chinese Traditional, Hong Kong)

[ci skip]

* New translations routing.mdx (Chinese Traditional, Hong Kong)

[ci skip]

* New translations faq.mdx (Chinese Traditional, Hong Kong)

[ci skip]

* New translations en.json (Chinese Traditional, Hong Kong)

[ci skip]

* New translations edit.mdx (Chinese Traditional, Hong Kong)

[ci skip]

* New translations en.json (Chinese Traditional, Hong Kong)

[ci skip]

* New translations edit.mdx (Chinese Traditional, Hong Kong)

[ci skip]

* New translations edit.mdx (Chinese Simplified)

[ci skip]

* New translations files-and-stats.mdx (Chinese Traditional, Hong Kong)

[ci skip]

* New translations getting-started.mdx (Chinese Traditional, Hong Kong)

[ci skip]

* New translations file.mdx (Chinese Traditional, Hong Kong)

[ci skip]

* New translations en.json (Chinese Simplified)

[ci skip]

* New translations en.json (Chinese Simplified)

[ci skip]
2026-06-18 23:07:16 +02:00
vcoppe 565589fc94 improve railway station query 2026-06-18 23:06:38 +02:00
vcoppe 4ef92e5abd update readme 2026-06-06 16:43:38 +02:00
vcoppe 7f8751fa38 turn wikipedia tags into links, closes #354 2026-06-03 19:05:15 +02:00
vcoppe befcba00b4 hopefully more reliable initial fitBounds 2026-06-03 18:48:07 +02:00
vcoppe 26e4b637a2 New Crowdin updates (#353)
* New translations en.json (Dutch)

[ci skip]

* New translations map-controls.mdx (Dutch)

[ci skip]

* New translations elevation.mdx (Dutch)

[ci skip]
2026-05-31 15:25:59 +02:00
vcoppe 75e3bc6f93 use self-hosted overpass api 2026-05-31 15:25:10 +02:00
vcoppe 70bb125076 change contour line labels for all compatible styles 2026-05-29 08:35:11 +02:00
vcoppe e0a4fc186a New Crowdin updates (#351)
* New translations map-controls.mdx (Chinese Simplified)

[ci skip]

* New translations en.json (Dutch)

[ci skip]

* New translations en.json (Romanian)

[ci skip]

* New translations en.json (French)

[ci skip]

* New translations en.json (Spanish)

[ci skip]

* New translations en.json (Belarusian)

[ci skip]

* New translations en.json (Catalan)

[ci skip]

* New translations en.json (Czech)

[ci skip]

* New translations en.json (Danish)

[ci skip]

* New translations en.json (German)

[ci skip]

* New translations en.json (Greek)

[ci skip]

* New translations en.json (Basque)

[ci skip]

* New translations en.json (Finnish)

[ci skip]

* New translations en.json (Hebrew)

[ci skip]

* New translations en.json (Hungarian)

[ci skip]

* New translations en.json (Italian)

[ci skip]

* New translations en.json (Korean)

[ci skip]

* New translations en.json (Lithuanian)

[ci skip]

* New translations en.json (Norwegian)

[ci skip]

* New translations en.json (Polish)

[ci skip]

* New translations en.json (Portuguese)

[ci skip]

* New translations en.json (Russian)

[ci skip]

* New translations en.json (Swedish)

[ci skip]

* New translations en.json (Turkish)

[ci skip]

* New translations en.json (Indonesian)

[ci skip]

* New translations map-controls.mdx (Chinese Simplified)

[ci skip]

* New translations en.json (Ukrainian)

[ci skip]

* New translations en.json (Chinese Simplified)

[ci skip]

* New translations en.json (Vietnamese)

[ci skip]

* New translations en.json (Portuguese, Brazilian)

[ci skip]

* New translations en.json (Thai)

[ci skip]

* New translations en.json (Latvian)

[ci skip]

* New translations en.json (Chinese Traditional, Hong Kong)

[ci skip]

* New translations en.json (Serbian (Latin))

[ci skip]

* New translations map-controls.mdx (Romanian)

[ci skip]

* New translations map-controls.mdx (French)

[ci skip]

* New translations map-controls.mdx (Spanish)

[ci skip]

* New translations map-controls.mdx (Belarusian)

[ci skip]

* New translations map-controls.mdx (Catalan)

[ci skip]

* New translations map-controls.mdx (Czech)

[ci skip]

* New translations map-controls.mdx (Danish)

[ci skip]

* New translations map-controls.mdx (German)

[ci skip]

* New translations map-controls.mdx (Greek)

[ci skip]

* New translations map-controls.mdx (Basque)

[ci skip]

* New translations map-controls.mdx (Finnish)

[ci skip]

* New translations map-controls.mdx (Hebrew)

[ci skip]

* New translations map-controls.mdx (Hungarian)

[ci skip]

* New translations map-controls.mdx (Italian)

[ci skip]

* New translations map-controls.mdx (Korean)

[ci skip]

* New translations map-controls.mdx (Lithuanian)

[ci skip]

* New translations map-controls.mdx (Dutch)

[ci skip]

* New translations map-controls.mdx (Norwegian)

[ci skip]

* New translations map-controls.mdx (Polish)

[ci skip]

* New translations map-controls.mdx (Portuguese)

[ci skip]

* New translations map-controls.mdx (Russian)

[ci skip]

* New translations map-controls.mdx (Swedish)

[ci skip]

* New translations map-controls.mdx (Turkish)

[ci skip]

* New translations map-controls.mdx (Ukrainian)

[ci skip]

* New translations map-controls.mdx (Vietnamese)

[ci skip]

* New translations map-controls.mdx (Portuguese, Brazilian)

[ci skip]

* New translations map-controls.mdx (Indonesian)

[ci skip]

* New translations map-controls.mdx (Thai)

[ci skip]

* New translations map-controls.mdx (Latvian)

[ci skip]

* New translations map-controls.mdx (Chinese Traditional, Hong Kong)

[ci skip]

* New translations map-controls.mdx (Serbian (Latin))

[ci skip]

* New translations elevation.mdx (Romanian)

[ci skip]

* New translations elevation.mdx (French)

[ci skip]

* New translations elevation.mdx (Spanish)

[ci skip]

* New translations elevation.mdx (Belarusian)

[ci skip]

* New translations elevation.mdx (Catalan)

[ci skip]

* New translations elevation.mdx (Czech)

[ci skip]

* New translations elevation.mdx (Danish)

[ci skip]

* New translations elevation.mdx (German)

[ci skip]

* New translations elevation.mdx (Greek)

[ci skip]

* New translations elevation.mdx (Basque)

[ci skip]

* New translations elevation.mdx (Finnish)

[ci skip]

* New translations elevation.mdx (Hebrew)

[ci skip]

* New translations elevation.mdx (Hungarian)

[ci skip]

* New translations elevation.mdx (Italian)

[ci skip]

* New translations elevation.mdx (Korean)

[ci skip]

* New translations elevation.mdx (Lithuanian)

[ci skip]

* New translations elevation.mdx (Dutch)

[ci skip]

* New translations elevation.mdx (Norwegian)

[ci skip]

* New translations elevation.mdx (Polish)

[ci skip]

* New translations elevation.mdx (Portuguese)

[ci skip]

* New translations elevation.mdx (Russian)

[ci skip]

* New translations elevation.mdx (Swedish)

[ci skip]

* New translations elevation.mdx (Turkish)

[ci skip]

* New translations elevation.mdx (Ukrainian)

[ci skip]

* New translations elevation.mdx (Chinese Simplified)

[ci skip]

* New translations elevation.mdx (Vietnamese)

[ci skip]

* New translations elevation.mdx (Portuguese, Brazilian)

[ci skip]

* New translations elevation.mdx (Indonesian)

[ci skip]

* New translations elevation.mdx (Thai)

[ci skip]

* New translations elevation.mdx (Latvian)

[ci skip]

* New translations elevation.mdx (Chinese Traditional, Hong Kong)

[ci skip]

* New translations elevation.mdx (Serbian (Latin))

[ci skip]

* New translations en.json (French)

[ci skip]

* New translations en.json (Spanish)

[ci skip]

* New translations map-controls.mdx (Spanish)

[ci skip]

* New translations elevation.mdx (Spanish)

[ci skip]

* New translations en.json (Indonesian)

[ci skip]

* New translations en.json (Dutch)

[ci skip]

* New translations en.json (Romanian)

[ci skip]

* New translations en.json (French)

[ci skip]

* New translations en.json (Spanish)

[ci skip]

* New translations en.json (Belarusian)

[ci skip]

* New translations en.json (Catalan)

[ci skip]

* New translations en.json (Czech)

[ci skip]

* New translations en.json (Danish)

[ci skip]

* New translations en.json (German)

[ci skip]

* New translations en.json (Greek)

[ci skip]

* New translations en.json (Basque)

[ci skip]

* New translations en.json (Finnish)

[ci skip]

* New translations en.json (Hebrew)

[ci skip]

* New translations en.json (Hungarian)

[ci skip]

* New translations en.json (Italian)

[ci skip]

* New translations en.json (Korean)

[ci skip]

* New translations en.json (Lithuanian)

[ci skip]

* New translations en.json (Norwegian)

[ci skip]

* New translations en.json (Polish)

[ci skip]

* New translations en.json (Portuguese)

[ci skip]

* New translations en.json (Russian)

[ci skip]

* New translations en.json (Swedish)

[ci skip]

* New translations en.json (Turkish)

[ci skip]

* New translations en.json (Ukrainian)

[ci skip]

* New translations en.json (Chinese Simplified)

[ci skip]

* New translations en.json (Vietnamese)

[ci skip]

* New translations en.json (Portuguese, Brazilian)

[ci skip]

* New translations en.json (Thai)

[ci skip]

* New translations en.json (Latvian)

[ci skip]

* New translations en.json (Chinese Traditional, Hong Kong)

[ci skip]

* New translations en.json (Serbian (Latin))

[ci skip]
2026-05-28 19:29:10 +02:00
vcoppe 6a9d30e8ea localization of OpenMapTiles styles 2026-05-28 19:15:01 +02:00
vcoppe 1e6a12eeda update styles 2026-05-28 18:58:49 +02:00
vcoppe 31912da76f use same font for markers 2026-05-27 22:03:53 +02:00
vcoppe fe8896e870 get styles from github 2026-05-27 20:17:49 +02:00
vcoppe c99517572e update images 2026-05-27 19:40:09 +02:00
vcoppe 0b7f30a7c4 remove maptiler layers 2026-05-27 18:52:15 +02:00
vcoppe fcfaf043c4 fix map localization 2026-05-26 06:28:32 +02:00
vcoppe 0393a330a6 fix loading initial style 2026-05-26 06:18:00 +02:00
vcoppe 2fdb58bc7d switch contour lines based on units 2026-05-25 16:50:28 +02:00
vcoppe c758bda1a9 update tiles endpoint 2026-05-25 15:48:23 +02:00
vcoppe 88e301e2a2 use maptiler satellite 2026-05-25 14:45:03 +02:00
vcoppe 161c664e08 borders 2026-05-25 11:26:49 +02:00
vcoppe a0408ec798 revert rivers 2026-05-25 11:00:55 +02:00
vcoppe 480d5586b9 inverse font color and halo 2026-05-25 10:58:23 +02:00
vcoppe 7b49018593 remove casings 2026-05-25 10:44:53 +02:00
vcoppe 4b447a9b9d revert some of the changes 2026-05-25 10:40:38 +02:00
vcoppe 443feb2bfa road opacities 2026-05-25 10:29:29 +02:00
vcoppe 8710101a78 remove filled areas 2026-05-25 09:59:54 +02:00
vcoppe 2c20148e64 start satellite style 2026-05-24 13:49:43 +02:00
vcoppe ffc0e84788 slightly bigger paths 2026-05-24 00:23:40 +02:00
vcoppe 2d2004e447 small color change 2026-05-24 00:16:55 +02:00
vcoppe 4a7cfc113d vector terrain 2026-05-24 00:13:26 +02:00
vcoppe f03ad5a0e9 colored track casing 2026-05-24 00:04:09 +02:00
vcoppe 7f5b83f9d8 only use surface for coloring 2026-05-23 20:13:47 +02:00
vcoppe 5a8f93e225 different colors depending on surface 2026-05-23 20:10:21 +02:00
vcoppe 668b0f6b23 clearer path names 2026-05-23 19:59:12 +02:00
vcoppe fc1eb5a408 highlight trails 2026-05-23 19:52:04 +02:00
vcoppe 561c7e22b3 first tweaks 2026-05-23 19:17:37 +02:00
vcoppe ce067d4ff0 add custom layer base from openfreemap (osm liberty) 2026-05-23 19:14:59 +02:00
vcoppe 7361b54255 only apply minify to selection 2026-05-21 20:27:22 +02:00
vcoppe 9e2334ca83 New Crowdin updates (#336)
* New translations en.json (Dutch)

* New translations view.mdx (Dutch)

* New translations en.json (Spanish)

* New translations view.mdx (Spanish)

* New translations view.mdx (Dutch)

* New translations en.json (Chinese Simplified)

* New translations view.mdx (Chinese Simplified)

* New translations map-controls.mdx (Chinese Simplified)

* New translations files-and-stats.mdx (Serbian (Latin))

* New translations edit.mdx (Serbian (Latin))

* New translations elevation.mdx (Serbian (Latin))

* New translations en.json (Serbian (Latin))

* New translations files-and-stats.mdx (Serbian (Latin))

* New translations edit.mdx (Serbian (Latin))

* New translations file.mdx (Serbian (Latin))

* New translations settings.mdx (Serbian (Latin))

* New translations view.mdx (Serbian (Latin))

* New translations clean.mdx (Serbian (Latin))

* New translations extract.mdx (Serbian (Latin))

* New translations merge.mdx (Serbian (Latin))

* New translations poi.mdx (Serbian (Latin))

* New translations routing.mdx (Serbian (Latin))

* New translations scissors.mdx (Serbian (Latin))

* New translations time.mdx (Serbian (Latin))

* New translations elevation.mdx (Serbian (Latin))

* New translations minify.mdx (Serbian (Latin))

* New translations routing.mdx (Serbian (Latin))

* New translations faq.mdx (Serbian (Latin))

* New translations integration.mdx (Serbian (Latin))

* New translations files-and-stats.mdx (Serbian (Latin))

* New translations integration.mdx (Serbian (Latin))

* New translations map-controls.mdx (Serbian (Latin))

* New translations menu.mdx (Serbian (Latin))

* New translations edit.mdx (Serbian (Latin))

* New translations file.mdx (Serbian (Latin))

* New translations view.mdx (Serbian (Latin))

* New translations toolbar.mdx (Serbian (Latin))

* New translations routing.mdx (Serbian (Latin))

* New translations scissors.mdx (Serbian (Latin))

* New translations gpx.mdx (Serbian (Latin))

* New translations integration.mdx (Serbian (Latin))

* New translations en.json (Russian)

* New translations elevation.mdx (Dutch)

[ci skip]

* New translations map-controls.mdx (Dutch)

[ci skip]

* New translations elevation.mdx (Dutch)

[ci skip]

* New translations files-and-stats.mdx (Russian)

[ci skip]

* New translations view.mdx (Russian)

[ci skip]

* New translations clean.mdx (Norwegian)

[ci skip]

* New translations clean.mdx (Norwegian)

[ci skip]

* New translations en.json (Norwegian)

[ci skip]

* New translations poi.mdx (Norwegian)

[ci skip]

* New translations en.json (Indonesian)

[ci skip]

* New translations en.json (Dutch)

[ci skip]
2026-05-21 20:20:49 +02:00
vcoppe 08d1f1d5e4 New Crowdin updates (#334)
* New translations en.json (Basque)

* New translations en.json (Dutch)

* New translations en.json (Catalan)

* New translations en.json (Romanian)

* New translations en.json (French)

* New translations en.json (Spanish)

* New translations en.json (Belarusian)

* New translations en.json (Czech)

* New translations en.json (Danish)

* New translations en.json (German)

* New translations en.json (Greek)

* New translations en.json (Finnish)

* New translations en.json (Hebrew)

* New translations en.json (Hungarian)

* New translations en.json (Italian)

* New translations en.json (Korean)

* New translations en.json (Lithuanian)

* New translations en.json (Norwegian)

* New translations en.json (Polish)

* New translations en.json (Portuguese)

* New translations en.json (Russian)

* New translations en.json (Swedish)

* New translations en.json (Turkish)

* New translations en.json (Ukrainian)

* New translations en.json (Chinese Simplified)

* New translations en.json (Vietnamese)

* New translations en.json (Portuguese, Brazilian)

* New translations en.json (Indonesian)

* New translations en.json (Thai)

* New translations en.json (Latvian)

* New translations en.json (Chinese Traditional, Hong Kong)

* New translations en.json (Serbian (Latin))

* New translations view.mdx (Romanian)

* New translations view.mdx (French)

* New translations view.mdx (Spanish)

* New translations view.mdx (Belarusian)

* New translations view.mdx (Catalan)

* New translations view.mdx (Czech)

* New translations view.mdx (Danish)

* New translations view.mdx (German)

* New translations view.mdx (Greek)

* New translations view.mdx (Basque)

* New translations view.mdx (Finnish)

* New translations view.mdx (Hebrew)

* New translations view.mdx (Hungarian)

* New translations view.mdx (Italian)

* New translations view.mdx (Korean)

* New translations view.mdx (Lithuanian)

* New translations view.mdx (Dutch)

* New translations view.mdx (Norwegian)

* New translations view.mdx (Polish)

* New translations view.mdx (Portuguese)

* New translations view.mdx (Russian)

* New translations view.mdx (Swedish)

* New translations view.mdx (Turkish)

* New translations view.mdx (Ukrainian)

* New translations view.mdx (Chinese Simplified)

* New translations view.mdx (Vietnamese)

* New translations view.mdx (Portuguese, Brazilian)

* New translations view.mdx (Indonesian)

* New translations view.mdx (Thai)

* New translations view.mdx (Latvian)

* New translations view.mdx (Chinese Traditional, Hong Kong)

* New translations view.mdx (Serbian (Latin))

* New translations en.json (French)
2026-04-19 19:03:58 +02:00
vcoppe f3bf4f0096 add minimize icon 2026-04-19 18:29:38 +02:00
vcoppe b4094d0a5b slightly resize routing controls 2026-04-19 18:20:01 +02:00
JCarrasco 36122b4ac5 Add fullscreen view mode (#324)
Closes #301
2026-04-19 18:12:02 +02:00
vcoppe dd9aba3adb fine tuning 2026-04-19 16:49:32 +02:00
vcoppe bd40fbae74 fix routing controls on mobile 2026-04-19 16:34:06 +02:00
vcoppe 690cbc49cc avoid querying features when dragging 2026-04-19 16:31:50 +02:00
vcoppe 36b16ddeef catch errors when fetching styles and add fallback one 2026-04-19 14:47:19 +02:00
vcoppe 16b8988fa7 fix layer filtering, must allow unknown intermediary keys 2026-04-17 22:10:30 +02:00
Pablo Ovelleiro Corral 40f97b7c35 Fix: overlays bikerouterGravel, cyclOSMlite, mapterhornHillshade, openRailwayMap cannot be toggled in Layer settings (#329) 2026-04-17 20:07:51 +02:00
vcoppe 54b3113480 New Crowdin updates (#326)
* New translations en.json (Spanish)

* New translations merge.mdx (Spanish)

* New translations elevation.mdx (Spanish)

* New translations en.json (Chinese Traditional, Hong Kong)

* New translations en.json (Spanish)

* New translations integration.mdx (Spanish)

* New translations merge.mdx (Spanish)

* New translations integration.mdx (Spanish)

* New translations map-controls.mdx (Spanish)
2026-04-17 20:00:51 +02:00
vcoppe 7e9140492a fix max zoom 2026-04-09 21:10:45 +02:00
vcoppe 79c0aed54f New Crowdin updates (#323)
* New translations en.json (Basque)

* New translations en.json (Dutch)

* New translations en.json (Catalan)

* New translations en.json (Romanian)

* New translations en.json (French)

* New translations en.json (Spanish)

* New translations en.json (Belarusian)

* New translations en.json (Czech)

* New translations en.json (Danish)

* New translations en.json (German)

* New translations en.json (Greek)

* New translations en.json (Finnish)

* New translations en.json (Hebrew)

* New translations en.json (Hungarian)

* New translations en.json (Italian)

* New translations en.json (Korean)

* New translations en.json (Lithuanian)

* New translations en.json (Norwegian)

* New translations en.json (Polish)

* New translations en.json (Portuguese)

* New translations en.json (Russian)

* New translations en.json (Swedish)

* New translations en.json (Turkish)

* New translations en.json (Ukrainian)

* New translations en.json (Chinese Simplified)

* New translations en.json (Vietnamese)

* New translations en.json (Portuguese, Brazilian)

* New translations en.json (Indonesian)

* New translations en.json (Thai)

* New translations en.json (Latvian)

* New translations en.json (Chinese Traditional, Hong Kong)

* New translations en.json (Serbian (Latin))
2026-04-09 20:57:02 +02:00
vcoppe cb5a74de00 add esri satellite 2026-04-09 19:53:53 +02:00
vcoppe 31f25f346a New Crowdin updates (#322)
* New translations en.json (Ukrainian)

* New translations en.json (Ukrainian)

* New translations files-and-stats.mdx (Ukrainian)

* New translations getting-started.mdx (Ukrainian)

* New translations edit.mdx (Ukrainian)

* New translations view.mdx (Ukrainian)

* New translations en.json (Basque)
2026-04-09 19:33:42 +02:00
vcoppe b3a11125a5 adapt temporary anchor layer 2026-04-07 22:19:03 +02:00
vcoppe 71cdc03da5 file specific routing controls layers 2026-04-07 22:15:17 +02:00
vcoppe 694e73a677 reinit shadcn-svelte, fixes #318 2026-04-07 22:01:58 +02:00
vcoppe 5aaacccef9 update components 2026-04-06 18:22:01 +02:00
vcoppe f2bf043900 fix button size 2026-04-06 15:05:47 +02:00
vcoppe ba251fe407 update date picker with language 2026-04-06 15:04:13 +02:00
vcoppe 421aa9dc69 remove now unused package 2026-04-06 14:26:19 +02:00
vcoppe b4a7f1353b New Crowdin updates (#316)
* New translations integration.mdx (Chinese Simplified)

* New translations merge.mdx (Chinese Simplified)

* New translations elevation.mdx (Chinese Simplified)

* New translations en.json (Chinese Simplified)

* New translations file.mdx (Ukrainian)

* New translations en.json (German)

* New translations integration.mdx (German)

* New translations map-controls.mdx (German)

* New translations file.mdx (German)

* New translations merge.mdx (German)

* New translations elevation.mdx (German)
2026-04-04 23:14:44 +02:00
vcoppe 30de3c6db5 update motorcycle profile 2026-04-04 20:10:53 +02:00
vcoppe 8aafa26238 fix for safari 2026-04-03 08:47:40 +02:00
vcoppe 5768391305 set default value for new layers 2026-04-03 08:06:09 +02:00
vcoppe 1d204bacf2 fix threshold 2026-04-02 22:35:36 +02:00
vcoppe 4d1d5d48c0 avoid layout shift 2026-04-02 20:23:27 +02:00
vcoppe 5a6321535e New translations settings.mdx (Greek) (#315) 2026-04-02 18:58:58 +02:00
vcoppe e077de9f48 remove obsolete files 2026-04-02 18:47:59 +02:00
vcoppe 7586f03998 New Crowdin updates (#307)
* New translations en.json (Swedish)

* New translations en.json (Turkish)

* New translations en.json (Ukrainian)

* New translations en.json (Chinese Simplified)

* New translations en.json (Vietnamese)

* New translations en.json (Portuguese, Brazilian)

* New translations en.json (Indonesian)

* New translations en.json (Thai)

* New translations en.json (Latvian)

* New translations en.json (Chinese Traditional, Hong Kong)

* New translations en.json (Serbian (Latin))

* New translations funding.mdx (Dutch)

* New translations en.json (Dutch)

* New translations en.json (Spanish)

* New translations en.json (Basque)

* New translations en.json (Dutch)

* New translations en.json (Catalan)

* New translations en.json (Romanian)

* New translations en.json (French)

* New translations en.json (Spanish)

* New translations en.json (Belarusian)

* New translations en.json (Czech)

* New translations en.json (Danish)

* New translations en.json (German)

* New translations en.json (Greek)

* New translations en.json (Finnish)

* New translations en.json (Hebrew)

* New translations en.json (Hungarian)

* New translations en.json (Italian)

* New translations en.json (Korean)

* New translations en.json (Lithuanian)

* New translations en.json (Norwegian)

* New translations en.json (Polish)

* New translations en.json (Portuguese)

* New translations en.json (Russian)

* New translations en.json (Swedish)

* New translations en.json (Turkish)

* New translations en.json (Ukrainian)

* New translations en.json (Chinese Simplified)

* New translations en.json (Vietnamese)

* New translations en.json (Portuguese, Brazilian)

* New translations en.json (Indonesian)

* New translations en.json (Thai)

* New translations en.json (Latvian)

* New translations en.json (Chinese Traditional, Hong Kong)

* New translations en.json (Serbian (Latin))

* New translations files-and-stats.mdx (Romanian)

* New translations files-and-stats.mdx (French)

* New translations files-and-stats.mdx (Spanish)

* New translations files-and-stats.mdx (Belarusian)

* New translations files-and-stats.mdx (Catalan)

* New translations files-and-stats.mdx (Czech)

* New translations files-and-stats.mdx (Danish)

* New translations files-and-stats.mdx (German)

* New translations files-and-stats.mdx (Greek)

* New translations files-and-stats.mdx (Basque)

* New translations files-and-stats.mdx (Finnish)

* New translations files-and-stats.mdx (Hebrew)

* New translations files-and-stats.mdx (Hungarian)

* New translations files-and-stats.mdx (Italian)

* New translations files-and-stats.mdx (Korean)

* New translations files-and-stats.mdx (Lithuanian)

* New translations files-and-stats.mdx (Dutch)

* New translations files-and-stats.mdx (Norwegian)

* New translations files-and-stats.mdx (Polish)

* New translations files-and-stats.mdx (Portuguese)

* New translations files-and-stats.mdx (Russian)

* New translations files-and-stats.mdx (Swedish)

* New translations files-and-stats.mdx (Turkish)

* New translations files-and-stats.mdx (Ukrainian)

* New translations files-and-stats.mdx (Chinese Simplified)

* New translations files-and-stats.mdx (Vietnamese)

* New translations files-and-stats.mdx (Portuguese, Brazilian)

* New translations files-and-stats.mdx (Indonesian)

* New translations files-and-stats.mdx (Thai)

* New translations files-and-stats.mdx (Latvian)

* New translations files-and-stats.mdx (Chinese Traditional, Hong Kong)

* New translations files-and-stats.mdx (Serbian (Latin))

* New translations integration.mdx (Romanian)

* New translations integration.mdx (French)

* New translations integration.mdx (Spanish)

* New translations integration.mdx (Belarusian)

* New translations integration.mdx (Catalan)

* New translations integration.mdx (Czech)

* New translations integration.mdx (Danish)

* New translations integration.mdx (German)

* New translations integration.mdx (Greek)

* New translations integration.mdx (Basque)

* New translations integration.mdx (Finnish)

* New translations integration.mdx (Hebrew)

* New translations integration.mdx (Hungarian)

* New translations integration.mdx (Italian)

* New translations integration.mdx (Korean)

* New translations integration.mdx (Lithuanian)

* New translations integration.mdx (Dutch)

* New translations integration.mdx (Norwegian)

* New translations integration.mdx (Polish)

* New translations integration.mdx (Portuguese)

* New translations integration.mdx (Russian)

* New translations integration.mdx (Swedish)

* New translations integration.mdx (Turkish)

* New translations integration.mdx (Ukrainian)

* New translations integration.mdx (Chinese Simplified)

* New translations integration.mdx (Vietnamese)

* New translations integration.mdx (Portuguese, Brazilian)

* New translations integration.mdx (Indonesian)

* New translations integration.mdx (Thai)

* New translations integration.mdx (Latvian)

* New translations integration.mdx (Chinese Traditional, Hong Kong)

* New translations integration.mdx (Serbian (Latin))

* New translations merge.mdx (Romanian)

* New translations merge.mdx (French)

* New translations merge.mdx (Spanish)

* New translations merge.mdx (Belarusian)

* New translations merge.mdx (Catalan)

* New translations merge.mdx (Czech)

* New translations merge.mdx (Danish)

* New translations merge.mdx (German)

* New translations merge.mdx (Greek)

* New translations merge.mdx (Basque)

* New translations merge.mdx (Finnish)

* New translations merge.mdx (Hebrew)

* New translations merge.mdx (Hungarian)

* New translations merge.mdx (Italian)

* New translations merge.mdx (Korean)

* New translations merge.mdx (Lithuanian)

* New translations merge.mdx (Dutch)

* New translations merge.mdx (Norwegian)

* New translations merge.mdx (Polish)

* New translations merge.mdx (Portuguese)

* New translations merge.mdx (Russian)

* New translations merge.mdx (Swedish)

* New translations merge.mdx (Turkish)

* New translations merge.mdx (Ukrainian)

* New translations merge.mdx (Chinese Simplified)

* New translations merge.mdx (Vietnamese)

* New translations merge.mdx (Portuguese, Brazilian)

* New translations merge.mdx (Indonesian)

* New translations merge.mdx (Thai)

* New translations merge.mdx (Latvian)

* New translations merge.mdx (Chinese Traditional, Hong Kong)

* New translations merge.mdx (Serbian (Latin))

* New translations map-controls.mdx (Romanian)

* New translations map-controls.mdx (French)

* New translations map-controls.mdx (Spanish)

* New translations map-controls.mdx (Belarusian)

* New translations map-controls.mdx (Catalan)

* New translations map-controls.mdx (Czech)

* New translations map-controls.mdx (Danish)

* New translations map-controls.mdx (German)

* New translations map-controls.mdx (Greek)

* New translations map-controls.mdx (Basque)

* New translations map-controls.mdx (Finnish)

* New translations map-controls.mdx (Hebrew)

* New translations map-controls.mdx (Hungarian)

* New translations map-controls.mdx (Italian)

* New translations map-controls.mdx (Korean)

* New translations map-controls.mdx (Lithuanian)

* New translations map-controls.mdx (Dutch)

* New translations map-controls.mdx (Norwegian)

* New translations map-controls.mdx (Polish)

* New translations map-controls.mdx (Portuguese)

* New translations map-controls.mdx (Russian)

* New translations map-controls.mdx (Swedish)

* New translations map-controls.mdx (Turkish)

* New translations map-controls.mdx (Ukrainian)

* New translations map-controls.mdx (Chinese Simplified)

* New translations map-controls.mdx (Vietnamese)

* New translations map-controls.mdx (Portuguese, Brazilian)

* New translations map-controls.mdx (Indonesian)

* New translations map-controls.mdx (Thai)

* New translations map-controls.mdx (Latvian)

* New translations map-controls.mdx (Chinese Traditional, Hong Kong)

* New translations map-controls.mdx (Serbian (Latin))

* New translations en.json (French)

* New translations integration.mdx (French)

* New translations map-controls.mdx (French)

* New translations merge.mdx (French)

* New translations elevation.mdx (French)

* New translations en.json (Basque)

* New translations en.json (Dutch)

* New translations en.json (Catalan)

* New translations en.json (Romanian)

* New translations en.json (French)

* New translations en.json (Spanish)

* New translations en.json (Belarusian)

* New translations en.json (Czech)

* New translations en.json (Danish)

* New translations en.json (German)

* New translations en.json (Greek)

* New translations en.json (Finnish)

* New translations en.json (Hebrew)

* New translations en.json (Hungarian)

* New translations en.json (Italian)

* New translations en.json (Korean)

* New translations en.json (Lithuanian)

* New translations en.json (Norwegian)

* New translations en.json (Polish)

* New translations en.json (Portuguese)

* New translations en.json (Russian)

* New translations en.json (Swedish)

* New translations en.json (Turkish)

* New translations en.json (Ukrainian)

* New translations en.json (Chinese Simplified)

* New translations en.json (Vietnamese)

* New translations en.json (Portuguese, Brazilian)

* New translations en.json (Indonesian)

* New translations en.json (Thai)

* New translations en.json (Latvian)

* New translations en.json (Chinese Traditional, Hong Kong)

* New translations en.json (Serbian (Latin))

* New translations integration.mdx (Dutch)

* New translations map-controls.mdx (Spanish)

* New translations map-controls.mdx (Dutch)

* New translations merge.mdx (Dutch)

* New translations en.json (Basque)

* New translations en.json (Dutch)

* New translations en.json (Catalan)

* New translations en.json (Romanian)

* New translations en.json (French)

* New translations en.json (Spanish)

* New translations en.json (Belarusian)

* New translations en.json (Czech)

* New translations en.json (Danish)

* New translations en.json (German)

* New translations en.json (Greek)

* New translations en.json (Finnish)

* New translations en.json (Hebrew)

* New translations en.json (Hungarian)

* New translations en.json (Italian)

* New translations en.json (Korean)

* New translations en.json (Lithuanian)

* New translations en.json (Norwegian)

* New translations en.json (Polish)

* New translations en.json (Portuguese)

* New translations en.json (Russian)

* New translations en.json (Swedish)

* New translations en.json (Turkish)

* New translations en.json (Ukrainian)

* New translations en.json (Chinese Simplified)

* New translations en.json (Vietnamese)

* New translations en.json (Portuguese, Brazilian)

* New translations en.json (Indonesian)

* New translations en.json (Thai)

* New translations en.json (Latvian)

* New translations en.json (Chinese Traditional, Hong Kong)

* New translations en.json (Serbian (Latin))

* New translations en.json (Dutch)

* New translations en.json (French)

* New translations en.json (Spanish)
2026-04-02 18:43:41 +02:00
vcoppe af8c22dcda redirect to app from image 2026-04-02 18:41:49 +02:00
vcoppe 3dce5dc617 improve filtering to always show layers in the same order 2026-04-01 09:01:01 +02:00
vcoppe 84b90e1026 remove unused import 2026-04-01 08:36:33 +02:00
vcoppe d507586eed fix small shift 2026-04-01 08:33:17 +02:00
vcoppe 57afaedf83 rephrasing 2026-03-29 23:22:36 +02:00
vcoppe 48063b9066 allow line breaks in buttons 2026-03-29 23:06:59 +02:00
vcoppe 452d356599 rephrase 2026-03-29 22:56:48 +02:00
vcoppe 25eda8041e slight rephrasing 2026-03-29 22:38:31 +02:00
vcoppe ae4d5356eb fix color 2026-03-29 22:31:57 +02:00
vcoppe 3343bb906e format for crowdin 2026-03-29 20:34:52 +02:00
vcoppe 7b17900160 simplify styling 2026-03-29 20:21:52 +02:00
vcoppe d5f1fe1c7b finish homepage 2026-03-29 20:04:38 +02:00
vcoppe 553f73f992 change break point for centering 2026-03-29 15:14:21 +02:00
vcoppe c8cedf2e2c simplify illustration 2026-03-29 14:06:59 +02:00
vcoppe a751817847 max size for docs 2026-03-28 19:41:44 +01:00
vcoppe d1ef12db8d work in progress 2026-03-28 19:31:52 +01:00
vcoppe 43d73edf29 small fix 2026-03-28 17:12:43 +01:00
vcoppe 5a0b8c376c small ui changes 2026-03-28 13:03:25 +01:00
vcoppe 9743fd460e update embedding instructions 2026-03-28 12:09:31 +01:00
vcoppe f70f92a176 change note type 2026-03-28 11:42:11 +01:00
vcoppe 1a4175446c add missing instructions 2026-03-28 11:40:27 +01:00
vcoppe ed6dfab4c1 fix embedding spacing 2026-03-28 11:32:25 +01:00
vcoppe 6a6e1105c0 update images 2026-03-28 11:32:05 +01:00
vcoppe 1677fe254b move theme button and search bar 2026-03-28 08:41:30 +01:00
vcoppe 02efe708c2 update maplibre 2026-03-27 21:47:17 +01:00
vcoppe 7dc834f506 small ui fixes 2026-03-27 21:32:33 +01:00
vcoppe 57c4958ff2 refresh routing controls on style load 2026-03-27 21:23:51 +01:00
vcoppe 03e59a8cce change default basemap 2026-03-27 19:43:01 +01:00
vcoppe f3d18f09a0 refresh markers on style load 2026-03-27 19:21:25 +01:00
vcoppe c1dbd984e6 fix validator 2026-03-27 19:07:48 +01:00
vcoppe e4f227221d small detail 2026-03-27 18:49:32 +01:00
vcoppe 34139974aa change 3D shortcut 2026-03-27 18:49:20 +01:00
vcoppe 408b2422e6 add missing value 2026-03-27 18:35:25 +01:00
vcoppe b59cb9e200 Merge branch 'maplibre' into dev 2026-03-27 18:31:23 +01:00
vcoppe bd5cb65d0f switch ko-fi to open collective 2026-03-25 21:53:10 +01:00
vcoppe 4cfe487af0 fix typo 2026-03-18 18:35:33 +01:00
vcoppe 4da2e39e32 Merge branch 'graphhopper' into dev
migrate to graphhopper
2026-03-18 18:28:05 +01:00
vcoppe 5ff11a32c9 update readme 2026-03-15 17:00:03 +01:00
vcoppe 01a7ec916e remove console log 2026-03-07 15:59:08 +01:00
vcoppe dd94a7d613 catch graphhopper exceptions 2026-03-07 15:57:58 +01:00
vcoppe 089b88c62d update graphhopper url 2026-03-07 15:30:22 +01:00
vcoppe 9c6e03f4a8 improve layer stacking 2026-01-30 21:30:37 +01:00
vcoppe 2a4dfe010e improve color management 2026-01-30 21:17:59 +01:00
vcoppe f42a916c25 remove unused parameter 2026-01-30 21:17:11 +01:00
vcoppe 772b810fa8 simplify initialization 2026-01-30 21:16:56 +01:00
vcoppe 4d4d10d5c2 small UI tweaks 2026-01-30 21:16:32 +01:00
vcoppe 0e4c7dbe64 New translations en.json (Chinese Simplified) (#306) 2026-01-30 21:02:21 +01:00
vcoppe a01ca79a82 finer-grained road access 2026-01-18 15:23:39 +01:00
vcoppe c91baf7c83 switch gravel to graphhopper 2026-01-17 11:58:47 +01:00
vcoppe 5062de8ddf Merge branch 'dev' into graphhopper 2026-01-17 11:42:30 +01:00
vcoppe 9ca46b9d35 small fix 2025-12-24 17:21:26 +01:00
vcoppe 7c2e24bbc4 draft support for graphhopper 2025-12-23 16:49:47 +01:00
626 changed files with 17012 additions and 8182 deletions
+1 -1
View File
@@ -1 +1 @@
ko_fi: gpxstudio open_collective: gpxstudio
+5 -4
View File
@@ -5,7 +5,7 @@
[**gpx.studio**](https://gpx.studio) is an online tool for creating and editing GPX files. [**gpx.studio**](https://gpx.studio) is an online tool for creating and editing GPX files.
![gpx.studio screenshot](website/src/lib/assets/img/docs/getting-started/interface.png) ![gpx.studio screenshot](website/src/lib/assets/img/docs/getting-started/interface.webp)
This repository contains the source code of the website. This repository contains the source code of the website.
@@ -69,9 +69,10 @@ This project has been made possible thanks to the following open source projects
- [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) — fast GPX file parsing - [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) — fast GPX file parsing
- [SortableJS](https://github.com/SortableJS/Sortable) — creating a sortable file tree - [SortableJS](https://github.com/SortableJS/Sortable) — creating a sortable file tree
- Mapping: - Mapping:
- [MapLibre GL JS](https://github.com/maplibre/maplibre-gl-js) — beautiful and fast interactive maps - [MapLibre GL JS](https://github.com/maplibre/maplibre-gl-js) — beautiful and fast interactive map rendering
- [brouter](https://github.com/abrensch/brouter) — routing engine - [GraphHopper](https://github.com/graphhopper/graphhopper) — powerful routing engine
- [OpenStreetMap](https://www.openstreetmap.org) — map data used by most of the map layers, and by the routing engine - [OpenStreetMap](https://www.openstreetmap.org) — open map data used by most of the map layers, and by the routing engine
- [Mapterhorn](https://github.com/mapterhorn/mapterhorn) — high-quality open terrain data used by some map layers (including for 3D), and by the routing engine
- Search: - Search:
- [DocSearch](https://github.com/algolia/docsearch) — search engine for the documentation - [DocSearch](https://github.com/algolia/docsearch) — search engine for the documentation
+2
View File
@@ -0,0 +1,2 @@
target/
pkg/
+367
View File
@@ -0,0 +1,367 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "android_system_properties"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc"
dependencies = [
"libc",
]
[[package]]
name = "autocfg"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
[[package]]
name = "bumpalo"
version = "3.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
[[package]]
name = "cc"
version = "1.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3eb0f42d6c360dc3f8a821f6bf2fdea7f72bfd36b3076eb0e6d1e9e0752fff4"
dependencies = [
"find-msvc-tools",
"shlex",
]
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "chrono"
version = "0.4.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327"
dependencies = [
"iana-time-zone",
"js-sys",
"num-traits",
"wasm-bindgen",
"windows-link",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "find-msvc-tools"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e0f1c7c3a72c66fd80abe965175f7523475c0489a87d3ff9d6e8c87d87a9d2d"
[[package]]
name = "futures-core"
version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e"
[[package]]
name = "futures-task"
version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd"
[[package]]
name = "futures-util"
version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc"
dependencies = [
"futures-core",
"futures-task",
"pin-project-lite",
"slab",
]
[[package]]
name = "getrandom"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
dependencies = [
"cfg-if",
"libc",
"r-efi",
]
[[package]]
name = "gpx-rs"
version = "0.1.0"
dependencies = [
"chrono",
"quick-xml",
"uuid",
"wasm-bindgen",
]
[[package]]
name = "iana-time-zone"
version = "0.1.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"log",
"wasm-bindgen",
"windows-core",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]]
name = "js-sys"
version = "0.3.103"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102"
dependencies = [
"cfg-if",
"futures-util",
"wasm-bindgen",
]
[[package]]
name = "libc"
version = "0.2.189"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
[[package]]
name = "log"
version = "0.4.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6"
[[package]]
name = "memchr"
version = "2.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
[[package]]
name = "pin-project-lite"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
[[package]]
name = "proc-macro2"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quick-xml"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41b1177fdf999d2321d3fb46ff47159d9c1fb9ad66a4879f8c50a0b504615e9b"
dependencies = [
"memchr",
]
[[package]]
name = "quote"
version = "1.0.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368"
dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rustversion"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
[[package]]
name = "shlex"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
[[package]]
name = "slab"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
[[package]]
name = "syn"
version = "2.0.118"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "uuid"
version = "1.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ef6dac1e96601b4fb3acccccff2139741fcb757cb9a36089bf5be91cfb285ce"
dependencies = [
"getrandom",
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4"
dependencies = [
"cfg-if",
"once_cell",
"rustversion",
"wasm-bindgen-macro",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e"
dependencies = [
"bumpalo",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24"
dependencies = [
"unicode-ident",
]
[[package]]
name = "windows-core"
version = "0.62.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
dependencies = [
"windows-implement",
"windows-interface",
"windows-link",
"windows-result",
"windows-strings",
]
[[package]]
name = "windows-implement"
version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-interface"
version = "0.59.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-result"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-strings"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
dependencies = [
"windows-link",
]
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "gpx-rs"
version = "0.1.0"
edition = "2024"
[lib]
crate-type = ["cdylib"]
[dependencies]
chrono = "0.4.45"
quick-xml = "0.42.0"
uuid = {version = "1.26.1", features = ["v4", "js"]}
wasm-bindgen = "0.2"
+274
View File
@@ -0,0 +1,274 @@
<?xml version="1.0" encoding="UTF-8"?>
<gpx xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.topografix.com/GPX/1/1"
xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd
http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd
http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd
http://www.topografix.com/GPX/gpx_style/0/2 http://www.topografix.com/GPX/gpx_style/0/2/gpx_style.xsd"
xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1"
xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3"
xmlns:gpx_style="http://www.topografix.com/GPX/gpx_style/0/2" version="1.1"
creator="https://gpx.studio">
<metadata>
<name>simple</name>
<desc>description</desc>
<author>
<name>gpx.studio</name>
<link href="https://gpx.studio">
<text>link text</text>
</link>
</author>
</metadata>
<trk>
<name>track name</name>
<cmt>track comment</cmt>
<desc>track description</desc>
<src>track source</src>
<link href="https://gpx.studio">
<text>track link text</text>
</link>
<type>Cycling</type>
<trkseg>
<trkpt lat="50.790867" lon="4.404968">
<ele>109.0</ele>
</trkpt>
<trkpt lat="50.790714" lon="4.405036">
<ele>110.8</ele>
</trkpt>
<trkpt lat="50.790336" lon="4.405259">
<ele>110.3</ele>
</trkpt>
<trkpt lat="50.790165" lon="4.405331">
<ele>110.0</ele>
</trkpt>
<trkpt lat="50.790008" lon="4.405359">
<ele>110.3</ele>
</trkpt>
<trkpt lat="50.789818" lon="4.405359">
<ele>109.3</ele>
</trkpt>
<trkpt lat="50.789409" lon="4.40534">
<ele>107.0</ele>
</trkpt>
<trkpt lat="50.789105" lon="4.405411">
<ele>106.0</ele>
</trkpt>
<trkpt lat="50.788799" lon="4.405527">
<ele>108.5</ele>
</trkpt>
<trkpt lat="50.788645" lon="4.405606">
<ele>109.8</ele>
</trkpt>
<trkpt lat="50.7885" lon="4.405711">
<ele>110.8</ele>
</trkpt>
<trkpt lat="50.78822" lon="4.405959">
<ele>112.0</ele>
</trkpt>
<trkpt lat="50.787956" lon="4.406092">
<ele>112.8</ele>
</trkpt>
<trkpt lat="50.787814" lon="4.406143">
<ele>113.5</ele>
</trkpt>
<trkpt lat="50.787674" lon="4.406177">
<ele>114.3</ele>
</trkpt>
<trkpt lat="50.787451" lon="4.406199">
<ele>115.3</ele>
</trkpt>
<trkpt lat="50.787297" lon="4.406177">
<ele>114.8</ele>
</trkpt>
<trkpt lat="50.78716" lon="4.406098">
<ele>114.3</ele>
</trkpt>
<trkpt lat="50.787045" lon="4.405984">
<ele>114.3</ele>
</trkpt>
<trkpt lat="50.786683" lon="4.405653">
<ele>114.5</ele>
</trkpt>
<trkpt lat="50.786538" lon="4.405543">
<ele>115.0</ele>
</trkpt>
<trkpt lat="50.78635" lon="4.405441">
<ele>115.8</ele>
</trkpt>
<trkpt lat="50.786275" lon="4.40542">
<ele>115.8</ele>
</trkpt>
<trkpt lat="50.786182" lon="4.405435">
<ele>116.0</ele>
</trkpt>
<trkpt lat="50.786121" lon="4.405475">
<ele>115.8</ele>
</trkpt>
<trkpt lat="50.786042" lon="4.405558">
<ele>115.5</ele>
</trkpt>
<trkpt lat="50.785821" lon="4.405925">
<ele>114.5</ele>
</trkpt>
<trkpt lat="50.785672" lon="4.406119">
<ele>112.5</ele>
</trkpt>
<trkpt lat="50.785516" lon="4.406256">
<ele>110.8</ele>
</trkpt>
<trkpt lat="50.785384" lon="4.406364">
<ele>109.0</ele>
</trkpt>
<trkpt lat="50.785126" lon="4.406475">
<ele>106.3</ele>
</trkpt>
<trkpt lat="50.784697" lon="4.406537">
<ele>104.3</ele>
</trkpt>
<trkpt lat="50.784591" lon="4.40657">
<ele>104.0</ele>
</trkpt>
<trkpt lat="50.784507" lon="4.406612">
<ele>103.8</ele>
</trkpt>
<trkpt lat="50.784435" lon="4.40669">
<ele>103.3</ele>
</trkpt>
<trkpt lat="50.784209" lon="4.407148">
<ele>103.5</ele>
</trkpt>
<trkpt lat="50.784162" lon="4.407257">
<ele>103.8</ele>
</trkpt>
<trkpt lat="50.784077" lon="4.407372">
<ele>104.8</ele>
</trkpt>
<trkpt lat="50.784006" lon="4.407435">
<ele>105.8</ele>
</trkpt>
<trkpt lat="50.783924" lon="4.407471">
<ele>106.8</ele>
</trkpt>
<trkpt lat="50.783837" lon="4.407486">
<ele>107.8</ele>
</trkpt>
<trkpt lat="50.783771" lon="4.407472">
<ele>108.5</ele>
</trkpt>
<trkpt lat="50.783697" lon="4.407428">
<ele>109.3</ele>
</trkpt>
<trkpt lat="50.783626" lon="4.407363">
<ele>110.0</ele>
</trkpt>
<trkpt lat="50.783548" lon="4.407274">
<ele>110.5</ele>
</trkpt>
<trkpt lat="50.783458" lon="4.407134">
<ele>110.8</ele>
</trkpt>
<trkpt lat="50.783123" lon="4.406435">
<ele>111.8</ele>
</trkpt>
<trkpt lat="50.782982" lon="4.406168">
<ele>112.8</ele>
</trkpt>
<trkpt lat="50.782871" lon="4.406044">
<ele>113.3</ele>
</trkpt>
<trkpt lat="50.78279" lon="4.406021">
<ele>113.5</ele>
</trkpt>
<trkpt lat="50.782714" lon="4.406018">
<ele>113.5</ele>
</trkpt>
<trkpt lat="50.782607" lon="4.406047">
<ele>113.8</ele>
</trkpt>
<trkpt lat="50.782405" lon="4.406194">
<ele>114.8</ele>
</trkpt>
<trkpt lat="50.782175" lon="4.406413">
<ele>115.8</ele>
</trkpt>
<trkpt lat="50.781749" lon="4.407018">
<ele>118.5</ele>
</trkpt>
<trkpt lat="50.781654" lon="4.407316">
<ele>119.5</ele>
</trkpt>
<trkpt lat="50.781563" lon="4.407764">
<ele>121.3</ele>
</trkpt>
<trkpt lat="50.781487" lon="4.407984">
<ele>122.0</ele>
</trkpt>
<trkpt lat="50.781422" lon="4.408216">
<ele>122.8</ele>
</trkpt>
<trkpt lat="50.781395" lon="4.408508">
<ele>123.5</ele>
</trkpt>
<trkpt lat="50.781399" lon="4.409114">
<ele>126.3</ele>
</trkpt>
<trkpt lat="50.781367" lon="4.409428">
<ele>128.0</ele>
</trkpt>
<trkpt lat="50.781286" lon="4.409607">
<ele>129.0</ele>
</trkpt>
<trkpt lat="50.78116" lon="4.409789">
<ele>130.0</ele>
</trkpt>
<trkpt lat="50.780804" lon="4.409993">
<ele>130.8</ele>
</trkpt>
<trkpt lat="50.780389" lon="4.410334">
<ele>131.8</ele>
</trkpt>
<trkpt lat="50.780232" lon="4.410563">
<ele>132.3</ele>
</trkpt>
<trkpt lat="50.780094" lon="4.410827">
<ele>132.8</ele>
</trkpt>
<trkpt lat="50.779723" lon="4.411582">
<ele>135.8</ele>
</trkpt>
<trkpt lat="50.779591" lon="4.411791">
<ele>135.5</ele>
</trkpt>
<trkpt lat="50.779125" lon="4.412435">
<ele>132.5</ele>
</trkpt>
<trkpt lat="50.778676" lon="4.412979">
<ele>134.0</ele>
</trkpt>
<trkpt lat="50.778194" lon="4.413466">
<ele>136.8</ele>
</trkpt>
<trkpt lat="50.777427" lon="4.414302">
<ele>137.5</ele>
</trkpt>
<trkpt lat="50.777165" lon="4.414736">
<ele>137.3</ele>
</trkpt>
<trkpt lat="50.776927" lon="4.415201">
<ele>137.5</ele>
</trkpt>
<trkpt lat="50.776778" lon="4.415613">
<ele>137.3</ele>
</trkpt>
<trkpt lat="50.776553" lon="4.416425">
<ele>134.8</ele>
</trkpt>
<trkpt lat="50.776326" lon="4.417304">
<ele>132.3</ele>
</trkpt>
<trkpt lat="50.776129" lon="4.418383">
<ele>129.5</ele>
</trkpt>
</trkseg>
</trk>
</gpx>
+660
View File
@@ -0,0 +1,660 @@
<?xml version="1.0" encoding="UTF-8"?>
<gpx xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.topografix.com/GPX/1/1" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd http://www.topografix.com/GPX/gpx_style/0/2 http://www.topografix.com/GPX/gpx_style/0/2/gpx_style.xsd"
xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1"
xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3"
xmlns:gpx_style="http://www.topografix.com/GPX/gpx_style/0/2" version="1.1" creator="https://gpx.studio">
<metadata>
<name>with_cad</name>
<author>
<name>gpx.studio</name>
<link href="https://gpx.studio"></link>
</author>
</metadata>
<trk>
<name>with_cad</name>
<type>Cycling</type>
<trkseg>
<trkpt lat="50.790867" lon="4.404968">
<ele>109.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.790714" lon="4.405036">
<ele>110.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.790336" lon="4.405259">
<ele>110.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.790165" lon="4.405331">
<ele>110.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.790008" lon="4.405359">
<ele>110.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.789818" lon="4.405359">
<ele>109.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.789409" lon="4.40534">
<ele>107.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.789105" lon="4.405411">
<ele>106.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.788799" lon="4.405527">
<ele>108.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.788645" lon="4.405606">
<ele>109.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.7885" lon="4.405711">
<ele>110.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.78822" lon="4.405959">
<ele>112.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.787956" lon="4.406092">
<ele>112.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.787814" lon="4.406143">
<ele>113.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.787674" lon="4.406177">
<ele>114.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.787451" lon="4.406199">
<ele>115.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.787297" lon="4.406177">
<ele>114.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.78716" lon="4.406098">
<ele>114.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.787045" lon="4.405984">
<ele>114.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.786683" lon="4.405653">
<ele>114.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.786538" lon="4.405543">
<ele>115.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.78635" lon="4.405441">
<ele>115.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.786275" lon="4.40542">
<ele>115.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.786182" lon="4.405435">
<ele>116.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.786121" lon="4.405475">
<ele>115.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.786042" lon="4.405558">
<ele>115.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.785821" lon="4.405925">
<ele>114.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.785672" lon="4.406119">
<ele>112.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.785516" lon="4.406256">
<ele>110.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.785384" lon="4.406364">
<ele>109.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.785126" lon="4.406475">
<ele>106.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.784697" lon="4.406537">
<ele>104.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.784591" lon="4.40657">
<ele>104.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.784507" lon="4.406612">
<ele>103.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.784435" lon="4.40669">
<ele>103.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.784209" lon="4.407148">
<ele>103.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.784162" lon="4.407257">
<ele>103.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.784077" lon="4.407372">
<ele>104.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.784006" lon="4.407435">
<ele>105.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.783924" lon="4.407471">
<ele>106.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.783837" lon="4.407486">
<ele>107.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.783771" lon="4.407472">
<ele>108.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.783697" lon="4.407428">
<ele>109.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.783626" lon="4.407363">
<ele>110.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.783548" lon="4.407274">
<ele>110.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.783458" lon="4.407134">
<ele>110.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.783123" lon="4.406435">
<ele>111.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.782982" lon="4.406168">
<ele>112.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.782871" lon="4.406044">
<ele>113.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.78279" lon="4.406021">
<ele>113.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.782714" lon="4.406018">
<ele>113.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.782607" lon="4.406047">
<ele>113.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.782405" lon="4.406194">
<ele>114.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.782175" lon="4.406413">
<ele>115.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.781749" lon="4.407018">
<ele>118.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.781654" lon="4.407316">
<ele>119.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.781563" lon="4.407764">
<ele>121.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.781487" lon="4.407984">
<ele>122.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.781422" lon="4.408216">
<ele>122.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.781395" lon="4.408508">
<ele>123.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.781399" lon="4.409114">
<ele>126.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.781367" lon="4.409428">
<ele>128.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.781286" lon="4.409607">
<ele>129.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.78116" lon="4.409789">
<ele>130.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.780804" lon="4.409993">
<ele>130.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.780389" lon="4.410334">
<ele>131.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.780232" lon="4.410563">
<ele>132.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.780094" lon="4.410827">
<ele>132.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.779723" lon="4.411582">
<ele>135.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.779591" lon="4.411791">
<ele>135.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.779125" lon="4.412435">
<ele>132.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.778676" lon="4.412979">
<ele>134.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.778194" lon="4.413466">
<ele>136.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.777427" lon="4.414302">
<ele>137.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.777165" lon="4.414736">
<ele>137.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.776927" lon="4.415201">
<ele>137.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.776778" lon="4.415613">
<ele>137.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.776553" lon="4.416425">
<ele>134.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.776326" lon="4.417304">
<ele>132.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>80</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.776129" lon="4.418383">
<ele>129.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:cad>90</gpxtpx:cad>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
</trkseg>
</trk>
</gpx>
+660
View File
@@ -0,0 +1,660 @@
<?xml version="1.0" encoding="UTF-8"?>
<gpx xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.topografix.com/GPX/1/1" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd http://www.topografix.com/GPX/gpx_style/0/2 http://www.topografix.com/GPX/gpx_style/0/2/gpx_style.xsd"
xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1"
xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3"
xmlns:gpx_style="http://www.topografix.com/GPX/gpx_style/0/2" version="1.1" creator="https://gpx.studio">
<metadata>
<name>with_hr</name>
<author>
<name>gpx.studio</name>
<link href="https://gpx.studio"></link>
</author>
</metadata>
<trk>
<name>with_hr</name>
<type>Cycling</type>
<trkseg>
<trkpt lat="50.790867" lon="4.404968">
<ele>109.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.790714" lon="4.405036">
<ele>110.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.790336" lon="4.405259">
<ele>110.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.790165" lon="4.405331">
<ele>110.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.790008" lon="4.405359">
<ele>110.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.789818" lon="4.405359">
<ele>109.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.789409" lon="4.40534">
<ele>107.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.789105" lon="4.405411">
<ele>106.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.788799" lon="4.405527">
<ele>108.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.788645" lon="4.405606">
<ele>109.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.7885" lon="4.405711">
<ele>110.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.78822" lon="4.405959">
<ele>112.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.787956" lon="4.406092">
<ele>112.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.787814" lon="4.406143">
<ele>113.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.787674" lon="4.406177">
<ele>114.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.787451" lon="4.406199">
<ele>115.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.787297" lon="4.406177">
<ele>114.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.78716" lon="4.406098">
<ele>114.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.787045" lon="4.405984">
<ele>114.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.786683" lon="4.405653">
<ele>114.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.786538" lon="4.405543">
<ele>115.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.78635" lon="4.405441">
<ele>115.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.786275" lon="4.40542">
<ele>115.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.786182" lon="4.405435">
<ele>116.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.786121" lon="4.405475">
<ele>115.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.786042" lon="4.405558">
<ele>115.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.785821" lon="4.405925">
<ele>114.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.785672" lon="4.406119">
<ele>112.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.785516" lon="4.406256">
<ele>110.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.785384" lon="4.406364">
<ele>109.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.785126" lon="4.406475">
<ele>106.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.784697" lon="4.406537">
<ele>104.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.784591" lon="4.40657">
<ele>104.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.784507" lon="4.406612">
<ele>103.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.784435" lon="4.40669">
<ele>103.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.784209" lon="4.407148">
<ele>103.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.784162" lon="4.407257">
<ele>103.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.784077" lon="4.407372">
<ele>104.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.784006" lon="4.407435">
<ele>105.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.783924" lon="4.407471">
<ele>106.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.783837" lon="4.407486">
<ele>107.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.783771" lon="4.407472">
<ele>108.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.783697" lon="4.407428">
<ele>109.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.783626" lon="4.407363">
<ele>110.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.783548" lon="4.407274">
<ele>110.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.783458" lon="4.407134">
<ele>110.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.783123" lon="4.406435">
<ele>111.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.782982" lon="4.406168">
<ele>112.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.782871" lon="4.406044">
<ele>113.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.78279" lon="4.406021">
<ele>113.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.782714" lon="4.406018">
<ele>113.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.782607" lon="4.406047">
<ele>113.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.782405" lon="4.406194">
<ele>114.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.782175" lon="4.406413">
<ele>115.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.781749" lon="4.407018">
<ele>118.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.781654" lon="4.407316">
<ele>119.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.781563" lon="4.407764">
<ele>121.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.781487" lon="4.407984">
<ele>122.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.781422" lon="4.408216">
<ele>122.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.781395" lon="4.408508">
<ele>123.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.781399" lon="4.409114">
<ele>126.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.781367" lon="4.409428">
<ele>128.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.781286" lon="4.409607">
<ele>129.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.78116" lon="4.409789">
<ele>130.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.780804" lon="4.409993">
<ele>130.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.780389" lon="4.410334">
<ele>131.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.780232" lon="4.410563">
<ele>132.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.780094" lon="4.410827">
<ele>132.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.779723" lon="4.411582">
<ele>135.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.779591" lon="4.411791">
<ele>135.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.779125" lon="4.412435">
<ele>132.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.778676" lon="4.412979">
<ele>134.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.778194" lon="4.413466">
<ele>136.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.777427" lon="4.414302">
<ele>137.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.777165" lon="4.414736">
<ele>137.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.776927" lon="4.415201">
<ele>137.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.776778" lon="4.415613">
<ele>137.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.776553" lon="4.416425">
<ele>134.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.776326" lon="4.417304">
<ele>132.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>150</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.776129" lon="4.418383">
<ele>129.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:hr>160</gpxtpx:hr>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
</trkseg>
</trk>
</gpx>
+500
View File
@@ -0,0 +1,500 @@
<?xml version="1.0" encoding="UTF-8"?>
<gpx xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.topografix.com/GPX/1/1" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd http://www.topografix.com/GPX/gpx_style/0/2 http://www.topografix.com/GPX/gpx_style/0/2/gpx_style.xsd"
xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1"
xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3"
xmlns:gpx_style="http://www.topografix.com/GPX/gpx_style/0/2" version="1.1" creator="https://gpx.studio">
<metadata>
<name>with_power</name>
<author>
<name>gpx.studio</name>
<link href="https://gpx.studio"></link>
</author>
</metadata>
<trk>
<name>with_power</name>
<type>Cycling</type>
<trkseg>
<trkpt lat="50.790867" lon="4.404968">
<ele>109.0</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.790714" lon="4.405036">
<ele>110.8</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.790336" lon="4.405259">
<ele>110.3</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.790165" lon="4.405331">
<ele>110.0</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.790008" lon="4.405359">
<ele>110.3</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.789818" lon="4.405359">
<ele>109.3</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.789409" lon="4.40534">
<ele>107.0</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.789105" lon="4.405411">
<ele>106.0</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.788799" lon="4.405527">
<ele>108.5</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.788645" lon="4.405606">
<ele>109.8</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.7885" lon="4.405711">
<ele>110.8</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.78822" lon="4.405959">
<ele>112.0</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.787956" lon="4.406092">
<ele>112.8</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.787814" lon="4.406143">
<ele>113.5</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.787674" lon="4.406177">
<ele>114.3</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.787451" lon="4.406199">
<ele>115.3</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.787297" lon="4.406177">
<ele>114.8</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.78716" lon="4.406098">
<ele>114.3</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.787045" lon="4.405984">
<ele>114.3</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.786683" lon="4.405653">
<ele>114.5</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.786538" lon="4.405543">
<ele>115.0</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.78635" lon="4.405441">
<ele>115.8</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.786275" lon="4.40542">
<ele>115.8</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.786182" lon="4.405435">
<ele>116.0</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.786121" lon="4.405475">
<ele>115.8</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.786042" lon="4.405558">
<ele>115.5</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.785821" lon="4.405925">
<ele>114.5</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.785672" lon="4.406119">
<ele>112.5</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.785516" lon="4.406256">
<ele>110.8</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.785384" lon="4.406364">
<ele>109.0</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.785126" lon="4.406475">
<ele>106.3</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.784697" lon="4.406537">
<ele>104.3</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.784591" lon="4.40657">
<ele>104.0</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.784507" lon="4.406612">
<ele>103.8</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.784435" lon="4.40669">
<ele>103.3</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.784209" lon="4.407148">
<ele>103.5</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.784162" lon="4.407257">
<ele>103.8</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.784077" lon="4.407372">
<ele>104.8</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.784006" lon="4.407435">
<ele>105.8</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.783924" lon="4.407471">
<ele>106.8</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.783837" lon="4.407486">
<ele>107.8</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.783771" lon="4.407472">
<ele>108.5</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.783697" lon="4.407428">
<ele>109.3</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.783626" lon="4.407363">
<ele>110.0</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.783548" lon="4.407274">
<ele>110.5</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.783458" lon="4.407134">
<ele>110.8</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.783123" lon="4.406435">
<ele>111.8</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.782982" lon="4.406168">
<ele>112.8</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.782871" lon="4.406044">
<ele>113.3</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.78279" lon="4.406021">
<ele>113.5</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.782714" lon="4.406018">
<ele>113.5</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.782607" lon="4.406047">
<ele>113.8</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.782405" lon="4.406194">
<ele>114.8</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.782175" lon="4.406413">
<ele>115.8</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.781749" lon="4.407018">
<ele>118.5</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.781654" lon="4.407316">
<ele>119.5</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.781563" lon="4.407764">
<ele>121.3</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.781487" lon="4.407984">
<ele>122.0</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.781422" lon="4.408216">
<ele>122.8</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.781395" lon="4.408508">
<ele>123.5</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.781399" lon="4.409114">
<ele>126.3</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.781367" lon="4.409428">
<ele>128.0</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.781286" lon="4.409607">
<ele>129.0</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.78116" lon="4.409789">
<ele>130.0</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.780804" lon="4.409993">
<ele>130.8</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.780389" lon="4.410334">
<ele>131.8</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.780232" lon="4.410563">
<ele>132.3</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.780094" lon="4.410827">
<ele>132.8</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.779723" lon="4.411582">
<ele>135.8</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.779591" lon="4.411791">
<ele>135.5</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.779125" lon="4.412435">
<ele>132.5</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.778676" lon="4.412979">
<ele>134.0</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.778194" lon="4.413466">
<ele>136.8</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.777427" lon="4.414302">
<ele>137.5</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.777165" lon="4.414736">
<ele>137.3</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.776927" lon="4.415201">
<ele>137.5</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.776778" lon="4.415613">
<ele>137.3</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.776553" lon="4.416425">
<ele>134.8</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.776326" lon="4.417304">
<ele>132.3</ele>
<extensions>
<power>200</power>
</extensions>
</trkpt>
<trkpt lat="50.776129" lon="4.418383">
<ele>129.5</ele>
<extensions>
<power>210</power>
</extensions>
</trkpt>
</trkseg>
</trk>
</gpx>
+665
View File
@@ -0,0 +1,665 @@
<?xml version="1.0" encoding="UTF-8"?>
<gpx xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.topografix.com/GPX/1/1"
xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd
http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd
http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd
http://www.topografix.com/GPX/gpx_style/0/2 http://www.topografix.com/GPX/gpx_style/0/2/gpx_style.xsd"
xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1"
xmlns:gpxpx="http://www.garmin.com/xmlschemas/GpxExtensions/v3"
xmlns:gpx_style="http://www.topografix.com/GPX/gpx_style/0/2" version="1.1"
creator="https://gpx.studio">
<metadata>
<name>with_power</name>
<author>
<name>gpx.studio</name>
<link href="https://gpx.studio"></link>
</author>
</metadata>
<trk>
<name>with_power</name>
<type>Cycling</type>
<trkseg>
<trkpt lat="50.790867" lon="4.404968">
<ele>109.0</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.790714" lon="4.405036">
<ele>110.8</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.790336" lon="4.405259">
<ele>110.3</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.790165" lon="4.405331">
<ele>110.0</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.790008" lon="4.405359">
<ele>110.3</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.789818" lon="4.405359">
<ele>109.3</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.789409" lon="4.40534">
<ele>107.0</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.789105" lon="4.405411">
<ele>106.0</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.788799" lon="4.405527">
<ele>108.5</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.788645" lon="4.405606">
<ele>109.8</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.7885" lon="4.405711">
<ele>110.8</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.78822" lon="4.405959">
<ele>112.0</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.787956" lon="4.406092">
<ele>112.8</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.787814" lon="4.406143">
<ele>113.5</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.787674" lon="4.406177">
<ele>114.3</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.787451" lon="4.406199">
<ele>115.3</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.787297" lon="4.406177">
<ele>114.8</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.78716" lon="4.406098">
<ele>114.3</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.787045" lon="4.405984">
<ele>114.3</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.786683" lon="4.405653">
<ele>114.5</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.786538" lon="4.405543">
<ele>115.0</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.78635" lon="4.405441">
<ele>115.8</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.786275" lon="4.40542">
<ele>115.8</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.786182" lon="4.405435">
<ele>116.0</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.786121" lon="4.405475">
<ele>115.8</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.786042" lon="4.405558">
<ele>115.5</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.785821" lon="4.405925">
<ele>114.5</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.785672" lon="4.406119">
<ele>112.5</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.785516" lon="4.406256">
<ele>110.8</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.785384" lon="4.406364">
<ele>109.0</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.785126" lon="4.406475">
<ele>106.3</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.784697" lon="4.406537">
<ele>104.3</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.784591" lon="4.40657">
<ele>104.0</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.784507" lon="4.406612">
<ele>103.8</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.784435" lon="4.40669">
<ele>103.3</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.784209" lon="4.407148">
<ele>103.5</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.784162" lon="4.407257">
<ele>103.8</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.784077" lon="4.407372">
<ele>104.8</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.784006" lon="4.407435">
<ele>105.8</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.783924" lon="4.407471">
<ele>106.8</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.783837" lon="4.407486">
<ele>107.8</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.783771" lon="4.407472">
<ele>108.5</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.783697" lon="4.407428">
<ele>109.3</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.783626" lon="4.407363">
<ele>110.0</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.783548" lon="4.407274">
<ele>110.5</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.783458" lon="4.407134">
<ele>110.8</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.783123" lon="4.406435">
<ele>111.8</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.782982" lon="4.406168">
<ele>112.8</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.782871" lon="4.406044">
<ele>113.3</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.78279" lon="4.406021">
<ele>113.5</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.782714" lon="4.406018">
<ele>113.5</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.782607" lon="4.406047">
<ele>113.8</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.782405" lon="4.406194">
<ele>114.8</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.782175" lon="4.406413">
<ele>115.8</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.781749" lon="4.407018">
<ele>118.5</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.781654" lon="4.407316">
<ele>119.5</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.781563" lon="4.407764">
<ele>121.3</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.781487" lon="4.407984">
<ele>122.0</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.781422" lon="4.408216">
<ele>122.8</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.781395" lon="4.408508">
<ele>123.5</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.781399" lon="4.409114">
<ele>126.3</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.781367" lon="4.409428">
<ele>128.0</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.781286" lon="4.409607">
<ele>129.0</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.78116" lon="4.409789">
<ele>130.0</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.780804" lon="4.409993">
<ele>130.8</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.780389" lon="4.410334">
<ele>131.8</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.780232" lon="4.410563">
<ele>132.3</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.780094" lon="4.410827">
<ele>132.8</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.779723" lon="4.411582">
<ele>135.8</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.779591" lon="4.411791">
<ele>135.5</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.779125" lon="4.412435">
<ele>132.5</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.778676" lon="4.412979">
<ele>134.0</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.778194" lon="4.413466">
<ele>136.8</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.777427" lon="4.414302">
<ele>137.5</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.777165" lon="4.414736">
<ele>137.3</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.776927" lon="4.415201">
<ele>137.5</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.776778" lon="4.415613">
<ele>137.3</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.776553" lon="4.416425">
<ele>134.8</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.776326" lon="4.417304">
<ele>132.3</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>200</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
<trkpt lat="50.776129" lon="4.418383">
<ele>129.5</ele>
<extensions>
<gpxpx:PowerExtension>
<gpxpx:PowerInWatts>210</gpxpx:PowerInWatts>
</gpxpx:PowerExtension>
</extensions>
</trkpt>
</trkseg>
</trk>
</gpx>
+253
View File
@@ -0,0 +1,253 @@
<?xml version="1.0" encoding="UTF-8"?>
<gpx xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.topografix.com/GPX/1/1" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd http://www.topografix.com/GPX/gpx_style/0/2 http://www.topografix.com/GPX/gpx_style/0/2/gpx_style.xsd"
xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1"
xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3"
xmlns:gpx_style="http://www.topografix.com/GPX/gpx_style/0/2" version="1.1" creator="https://gpx.studio">
<metadata>
<name>with_routes</name>
<author>
<name>gpx.studio</name>
<link href="https://gpx.studio"></link>
</author>
</metadata>
<rte>
<name>route 1</name>
<type>Cycling</type>
<rtept lat="50.790867" lon="4.404968">
<ele>109.0</ele>
</rtept>
<rtept lat="50.790714" lon="4.405036">
<ele>110.8</ele>
</rtept>
<rtept lat="50.790336" lon="4.405259">
<ele>110.3</ele>
</rtept>
<rtept lat="50.790165" lon="4.405331">
<ele>110.0</ele>
</rtept>
<rtept lat="50.790008" lon="4.405359">
<ele>110.3</ele>
</rtept>
<rtept lat="50.789818" lon="4.405359">
<ele>109.3</ele>
</rtept>
<rtept lat="50.789409" lon="4.40534">
<ele>107.0</ele>
</rtept>
<rtept lat="50.789105" lon="4.405411">
<ele>106.0</ele>
</rtept>
<rtept lat="50.788799" lon="4.405527">
<ele>108.5</ele>
</rtept>
<rtept lat="50.788645" lon="4.405606">
<ele>109.8</ele>
</rtept>
<rtept lat="50.7885" lon="4.405711">
<ele>110.8</ele>
</rtept>
<rtept lat="50.78822" lon="4.405959">
<ele>112.0</ele>
</rtept>
<rtept lat="50.787956" lon="4.406092">
<ele>112.8</ele>
</rtept>
<rtept lat="50.787814" lon="4.406143">
<ele>113.5</ele>
</rtept>
<rtept lat="50.787674" lon="4.406177">
<ele>114.3</ele>
</rtept>
<rtept lat="50.787451" lon="4.406199">
<ele>115.3</ele>
</rtept>
<rtept lat="50.787297" lon="4.406177">
<ele>114.8</ele>
</rtept>
<rtept lat="50.78716" lon="4.406098">
<ele>114.3</ele>
</rtept>
<rtept lat="50.787045" lon="4.405984">
<ele>114.3</ele>
</rtept>
<rtept lat="50.786683" lon="4.405653">
<ele>114.5</ele>
</rtept>
<rtept lat="50.786538" lon="4.405543">
<ele>115.0</ele>
</rtept>
<rtept lat="50.78635" lon="4.405441">
<ele>115.8</ele>
</rtept>
<rtept lat="50.786275" lon="4.40542">
<ele>115.8</ele>
</rtept>
<rtept lat="50.786182" lon="4.405435">
<ele>116.0</ele>
</rtept>
<rtept lat="50.786121" lon="4.405475">
<ele>115.8</ele>
</rtept>
<rtept lat="50.786042" lon="4.405558">
<ele>115.5</ele>
</rtept>
<rtept lat="50.785821" lon="4.405925">
<ele>114.5</ele>
</rtept>
<rtept lat="50.785672" lon="4.406119">
<ele>112.5</ele>
</rtept>
<rtept lat="50.785516" lon="4.406256">
<ele>110.8</ele>
</rtept>
<rtept lat="50.785384" lon="4.406364">
<ele>109.0</ele>
</rtept>
<rtept lat="50.785126" lon="4.406475">
<ele>106.3</ele>
</rtept>
<rtept lat="50.784697" lon="4.406537">
<ele>104.3</ele>
</rtept>
<rtept lat="50.784591" lon="4.40657">
<ele>104.0</ele>
</rtept>
<rtept lat="50.784507" lon="4.406612">
<ele>103.8</ele>
</rtept>
<rtept lat="50.784435" lon="4.40669">
<ele>103.3</ele>
</rtept>
<rtept lat="50.784209" lon="4.407148">
<ele>103.5</ele>
</rtept>
<rtept lat="50.784162" lon="4.407257">
<ele>103.8</ele>
</rtept>
<rtept lat="50.784077" lon="4.407372">
<ele>104.8</ele>
</rtept>
<rtept lat="50.784006" lon="4.407435">
<ele>105.8</ele>
</rtept>
<rtept lat="50.783924" lon="4.407471">
<ele>106.8</ele>
</rtept>
<rtept lat="50.783837" lon="4.407486">
<ele>107.8</ele>
</rtept>
<rtept lat="50.783771" lon="4.407472">
<ele>108.5</ele>
</rtept>
<rtept lat="50.783697" lon="4.407428">
<ele>109.3</ele>
</rtept>
<rtept lat="50.783626" lon="4.407363">
<ele>110.0</ele>
</rtept>
<rtept lat="50.783548" lon="4.407274">
<ele>110.5</ele>
</rtept>
<rtept lat="50.783458" lon="4.407134">
<ele>110.8</ele>
</rtept>
<rtept lat="50.783123" lon="4.406435">
<ele>111.8</ele>
</rtept>
<rtept lat="50.782982" lon="4.406168">
<ele>112.8</ele>
</rtept>
<rtept lat="50.782871" lon="4.406044">
<ele>113.3</ele>
</rtept>
</rte>
<rte>
<name>route 2</name>
<type>Cycling</type>
<rtept lat="50.782212" lon="4.406377">
<ele>115.5</ele>
</rtept>
<rtept lat="50.782175" lon="4.406413">
<ele>115.8</ele>
</rtept>
<rtept lat="50.781749" lon="4.407018">
<ele>118.5</ele>
</rtept>
<rtept lat="50.781654" lon="4.407316">
<ele>119.5</ele>
</rtept>
<rtept lat="50.781563" lon="4.407764">
<ele>121.3</ele>
</rtept>
<rtept lat="50.781487" lon="4.407984">
<ele>122.0</ele>
</rtept>
<rtept lat="50.781422" lon="4.408216">
<ele>122.8</ele>
</rtept>
<rtept lat="50.781395" lon="4.408508">
<ele>123.5</ele>
</rtept>
<rtept lat="50.781399" lon="4.409114">
<ele>126.3</ele>
</rtept>
<rtept lat="50.781367" lon="4.409428">
<ele>128.0</ele>
</rtept>
<rtept lat="50.781286" lon="4.409607">
<ele>129.0</ele>
</rtept>
<rtept lat="50.78116" lon="4.409789">
<ele>130.0</ele>
</rtept>
<rtept lat="50.780804" lon="4.409993">
<ele>130.8</ele>
</rtept>
<rtept lat="50.780389" lon="4.410334">
<ele>131.8</ele>
</rtept>
<rtept lat="50.780232" lon="4.410563">
<ele>132.3</ele>
</rtept>
<rtept lat="50.780094" lon="4.410827">
<ele>132.8</ele>
</rtept>
<rtept lat="50.779723" lon="4.411582">
<ele>135.8</ele>
</rtept>
<rtept lat="50.779591" lon="4.411791">
<ele>135.5</ele>
</rtept>
<rtept lat="50.779125" lon="4.412435">
<ele>132.5</ele>
</rtept>
<rtept lat="50.778676" lon="4.412979">
<ele>134.0</ele>
</rtept>
<rtept lat="50.778194" lon="4.413466">
<ele>136.8</ele>
</rtept>
<rtept lat="50.777427" lon="4.414302">
<ele>137.5</ele>
</rtept>
<rtept lat="50.777165" lon="4.414736">
<ele>137.3</ele>
</rtept>
<rtept lat="50.776927" lon="4.415201">
<ele>137.5</ele>
</rtept>
<rtept lat="50.776778" lon="4.415613">
<ele>137.3</ele>
</rtept>
<rtept lat="50.776553" lon="4.416425">
<ele>134.8</ele>
</rtept>
<rtept lat="50.776326" lon="4.417304">
<ele>132.3</ele>
</rtept>
<rtept lat="50.776129" lon="4.418383">
<ele>129.5</ele>
</rtept>
</rte>
</gpx>
+253
View File
@@ -0,0 +1,253 @@
<?xml version="1.0" encoding="UTF-8"?>
<gpx xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.topografix.com/GPX/1/1" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd http://www.topografix.com/GPX/gpx_style/0/2 http://www.topografix.com/GPX/gpx_style/0/2/gpx_style.xsd"
xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1"
xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3"
xmlns:gpx_style="http://www.topografix.com/GPX/gpx_style/0/2" version="1.1" creator="https://gpx.studio">
<metadata>
<name>with_segments</name>
<author>
<name>gpx.studio</name>
<link href="https://gpx.studio"></link>
</author>
</metadata>
<trk>
<name>with_segments</name>
<type>Cycling</type>
<trkseg>
<trkpt lat="50.790867" lon="4.404968">
<ele>109.0</ele>
</trkpt>
<trkpt lat="50.790714" lon="4.405036">
<ele>110.8</ele>
</trkpt>
<trkpt lat="50.790336" lon="4.405259">
<ele>110.3</ele>
</trkpt>
<trkpt lat="50.790165" lon="4.405331">
<ele>110.0</ele>
</trkpt>
<trkpt lat="50.790008" lon="4.405359">
<ele>110.3</ele>
</trkpt>
<trkpt lat="50.789818" lon="4.405359">
<ele>109.3</ele>
</trkpt>
<trkpt lat="50.789409" lon="4.40534">
<ele>107.0</ele>
</trkpt>
<trkpt lat="50.789105" lon="4.405411">
<ele>106.0</ele>
</trkpt>
<trkpt lat="50.788799" lon="4.405527">
<ele>108.5</ele>
</trkpt>
<trkpt lat="50.788645" lon="4.405606">
<ele>109.8</ele>
</trkpt>
<trkpt lat="50.7885" lon="4.405711">
<ele>110.8</ele>
</trkpt>
<trkpt lat="50.78822" lon="4.405959">
<ele>112.0</ele>
</trkpt>
<trkpt lat="50.787956" lon="4.406092">
<ele>112.8</ele>
</trkpt>
<trkpt lat="50.787814" lon="4.406143">
<ele>113.5</ele>
</trkpt>
<trkpt lat="50.787674" lon="4.406177">
<ele>114.3</ele>
</trkpt>
<trkpt lat="50.787451" lon="4.406199">
<ele>115.3</ele>
</trkpt>
<trkpt lat="50.787297" lon="4.406177">
<ele>114.8</ele>
</trkpt>
<trkpt lat="50.78716" lon="4.406098">
<ele>114.3</ele>
</trkpt>
<trkpt lat="50.787045" lon="4.405984">
<ele>114.3</ele>
</trkpt>
<trkpt lat="50.786683" lon="4.405653">
<ele>114.5</ele>
</trkpt>
<trkpt lat="50.786538" lon="4.405543">
<ele>115.0</ele>
</trkpt>
<trkpt lat="50.78635" lon="4.405441">
<ele>115.8</ele>
</trkpt>
<trkpt lat="50.786275" lon="4.40542">
<ele>115.8</ele>
</trkpt>
<trkpt lat="50.786182" lon="4.405435">
<ele>116.0</ele>
</trkpt>
<trkpt lat="50.786121" lon="4.405475">
<ele>115.8</ele>
</trkpt>
<trkpt lat="50.786042" lon="4.405558">
<ele>115.5</ele>
</trkpt>
<trkpt lat="50.785821" lon="4.405925">
<ele>114.5</ele>
</trkpt>
<trkpt lat="50.785672" lon="4.406119">
<ele>112.5</ele>
</trkpt>
<trkpt lat="50.785516" lon="4.406256">
<ele>110.8</ele>
</trkpt>
<trkpt lat="50.785384" lon="4.406364">
<ele>109.0</ele>
</trkpt>
<trkpt lat="50.785126" lon="4.406475">
<ele>106.3</ele>
</trkpt>
<trkpt lat="50.784697" lon="4.406537">
<ele>104.3</ele>
</trkpt>
<trkpt lat="50.784591" lon="4.40657">
<ele>104.0</ele>
</trkpt>
<trkpt lat="50.784507" lon="4.406612">
<ele>103.8</ele>
</trkpt>
<trkpt lat="50.784435" lon="4.40669">
<ele>103.3</ele>
</trkpt>
<trkpt lat="50.784209" lon="4.407148">
<ele>103.5</ele>
</trkpt>
<trkpt lat="50.784162" lon="4.407257">
<ele>103.8</ele>
</trkpt>
<trkpt lat="50.784077" lon="4.407372">
<ele>104.8</ele>
</trkpt>
<trkpt lat="50.784006" lon="4.407435">
<ele>105.8</ele>
</trkpt>
<trkpt lat="50.783924" lon="4.407471">
<ele>106.8</ele>
</trkpt>
<trkpt lat="50.783837" lon="4.407486">
<ele>107.8</ele>
</trkpt>
<trkpt lat="50.783771" lon="4.407472">
<ele>108.5</ele>
</trkpt>
<trkpt lat="50.783697" lon="4.407428">
<ele>109.3</ele>
</trkpt>
<trkpt lat="50.783626" lon="4.407363">
<ele>110.0</ele>
</trkpt>
<trkpt lat="50.783548" lon="4.407274">
<ele>110.5</ele>
</trkpt>
<trkpt lat="50.783458" lon="4.407134">
<ele>110.8</ele>
</trkpt>
<trkpt lat="50.783123" lon="4.406435">
<ele>111.8</ele>
</trkpt>
<trkpt lat="50.782982" lon="4.406168">
<ele>112.8</ele>
</trkpt>
<trkpt lat="50.782871" lon="4.406044">
<ele>113.3</ele>
</trkpt>
</trkseg>
<trkseg>
<trkpt lat="50.782212" lon="4.406377">
<ele>115.5</ele>
</trkpt>
<trkpt lat="50.782175" lon="4.406413">
<ele>115.8</ele>
</trkpt>
<trkpt lat="50.781749" lon="4.407018">
<ele>118.5</ele>
</trkpt>
<trkpt lat="50.781654" lon="4.407316">
<ele>119.5</ele>
</trkpt>
<trkpt lat="50.781563" lon="4.407764">
<ele>121.3</ele>
</trkpt>
<trkpt lat="50.781487" lon="4.407984">
<ele>122.0</ele>
</trkpt>
<trkpt lat="50.781422" lon="4.408216">
<ele>122.8</ele>
</trkpt>
<trkpt lat="50.781395" lon="4.408508">
<ele>123.5</ele>
</trkpt>
<trkpt lat="50.781399" lon="4.409114">
<ele>126.3</ele>
</trkpt>
<trkpt lat="50.781367" lon="4.409428">
<ele>128.0</ele>
</trkpt>
<trkpt lat="50.781286" lon="4.409607">
<ele>129.0</ele>
</trkpt>
<trkpt lat="50.78116" lon="4.409789">
<ele>130.0</ele>
</trkpt>
<trkpt lat="50.780804" lon="4.409993">
<ele>130.8</ele>
</trkpt>
<trkpt lat="50.780389" lon="4.410334">
<ele>131.8</ele>
</trkpt>
<trkpt lat="50.780232" lon="4.410563">
<ele>132.3</ele>
</trkpt>
<trkpt lat="50.780094" lon="4.410827">
<ele>132.8</ele>
</trkpt>
<trkpt lat="50.779723" lon="4.411582">
<ele>135.8</ele>
</trkpt>
<trkpt lat="50.779591" lon="4.411791">
<ele>135.5</ele>
</trkpt>
<trkpt lat="50.779125" lon="4.412435">
<ele>132.5</ele>
</trkpt>
<trkpt lat="50.778676" lon="4.412979">
<ele>134.0</ele>
</trkpt>
<trkpt lat="50.778194" lon="4.413466">
<ele>136.8</ele>
</trkpt>
<trkpt lat="50.777427" lon="4.414302">
<ele>137.5</ele>
</trkpt>
<trkpt lat="50.777165" lon="4.414736">
<ele>137.3</ele>
</trkpt>
<trkpt lat="50.776927" lon="4.415201">
<ele>137.5</ele>
</trkpt>
<trkpt lat="50.776778" lon="4.415613">
<ele>137.3</ele>
</trkpt>
<trkpt lat="50.776553" lon="4.416425">
<ele>134.8</ele>
</trkpt>
<trkpt lat="50.776326" lon="4.417304">
<ele>132.3</ele>
</trkpt>
<trkpt lat="50.776129" lon="4.418383">
<ele>129.5</ele>
</trkpt>
</trkseg>
</trk>
</gpx>
+267
View File
@@ -0,0 +1,267 @@
<?xml version="1.0" encoding="UTF-8"?>
<gpx xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.topografix.com/GPX/1/1" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd http://www.topografix.com/GPX/gpx_style/0/2 http://www.topografix.com/GPX/gpx_style/0/2/gpx_style.xsd"
xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1"
xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3"
xmlns:gpx_style="http://www.topografix.com/GPX/gpx_style/0/2" version="1.1" creator="https://gpx.studio">
<metadata>
<name>with_style</name>
<author>
<name>gpx.studio</name>
<link href="https://gpx.studio"></link>
</author>
</metadata>
<trk>
<name>with_style</name>
<type>Cycling</type>
<extensions>
<gpx_style:line>
<gpx_style:color>2d3ee9</gpx_style:color>
<gpx_style:opacity>0.5</gpx_style:opacity>
<gpx_style:width>6</gpx_style:width>
</gpx_style:line>
</extensions>
<trkseg>
<trkpt lat="50.790867" lon="4.404968">
<ele>109.0</ele>
</trkpt>
<trkpt lat="50.790714" lon="4.405036">
<ele>110.8</ele>
</trkpt>
<trkpt lat="50.790336" lon="4.405259">
<ele>110.3</ele>
</trkpt>
<trkpt lat="50.790165" lon="4.405331">
<ele>110.0</ele>
</trkpt>
<trkpt lat="50.790008" lon="4.405359">
<ele>110.3</ele>
</trkpt>
<trkpt lat="50.789818" lon="4.405359">
<ele>109.3</ele>
</trkpt>
<trkpt lat="50.789409" lon="4.40534">
<ele>107.0</ele>
</trkpt>
<trkpt lat="50.789105" lon="4.405411">
<ele>106.0</ele>
</trkpt>
<trkpt lat="50.788799" lon="4.405527">
<ele>108.5</ele>
</trkpt>
<trkpt lat="50.788645" lon="4.405606">
<ele>109.8</ele>
</trkpt>
<trkpt lat="50.7885" lon="4.405711">
<ele>110.8</ele>
</trkpt>
<trkpt lat="50.78822" lon="4.405959">
<ele>112.0</ele>
</trkpt>
<trkpt lat="50.787956" lon="4.406092">
<ele>112.8</ele>
</trkpt>
<trkpt lat="50.787814" lon="4.406143">
<ele>113.5</ele>
</trkpt>
<trkpt lat="50.787674" lon="4.406177">
<ele>114.3</ele>
</trkpt>
<trkpt lat="50.787451" lon="4.406199">
<ele>115.3</ele>
</trkpt>
<trkpt lat="50.787297" lon="4.406177">
<ele>114.8</ele>
</trkpt>
<trkpt lat="50.78716" lon="4.406098">
<ele>114.3</ele>
</trkpt>
<trkpt lat="50.787045" lon="4.405984">
<ele>114.3</ele>
</trkpt>
<trkpt lat="50.786683" lon="4.405653">
<ele>114.5</ele>
</trkpt>
<trkpt lat="50.786538" lon="4.405543">
<ele>115.0</ele>
</trkpt>
<trkpt lat="50.78635" lon="4.405441">
<ele>115.8</ele>
</trkpt>
<trkpt lat="50.786275" lon="4.40542">
<ele>115.8</ele>
</trkpt>
<trkpt lat="50.786182" lon="4.405435">
<ele>116.0</ele>
</trkpt>
<trkpt lat="50.786121" lon="4.405475">
<ele>115.8</ele>
</trkpt>
<trkpt lat="50.786042" lon="4.405558">
<ele>115.5</ele>
</trkpt>
<trkpt lat="50.785821" lon="4.405925">
<ele>114.5</ele>
</trkpt>
<trkpt lat="50.785672" lon="4.406119">
<ele>112.5</ele>
</trkpt>
<trkpt lat="50.785516" lon="4.406256">
<ele>110.8</ele>
</trkpt>
<trkpt lat="50.785384" lon="4.406364">
<ele>109.0</ele>
</trkpt>
<trkpt lat="50.785126" lon="4.406475">
<ele>106.3</ele>
</trkpt>
<trkpt lat="50.784697" lon="4.406537">
<ele>104.3</ele>
</trkpt>
<trkpt lat="50.784591" lon="4.40657">
<ele>104.0</ele>
</trkpt>
<trkpt lat="50.784507" lon="4.406612">
<ele>103.8</ele>
</trkpt>
<trkpt lat="50.784435" lon="4.40669">
<ele>103.3</ele>
</trkpt>
<trkpt lat="50.784209" lon="4.407148">
<ele>103.5</ele>
</trkpt>
<trkpt lat="50.784162" lon="4.407257">
<ele>103.8</ele>
</trkpt>
<trkpt lat="50.784077" lon="4.407372">
<ele>104.8</ele>
</trkpt>
<trkpt lat="50.784006" lon="4.407435">
<ele>105.8</ele>
</trkpt>
<trkpt lat="50.783924" lon="4.407471">
<ele>106.8</ele>
</trkpt>
<trkpt lat="50.783837" lon="4.407486">
<ele>107.8</ele>
</trkpt>
<trkpt lat="50.783771" lon="4.407472">
<ele>108.5</ele>
</trkpt>
<trkpt lat="50.783697" lon="4.407428">
<ele>109.3</ele>
</trkpt>
<trkpt lat="50.783626" lon="4.407363">
<ele>110.0</ele>
</trkpt>
<trkpt lat="50.783548" lon="4.407274">
<ele>110.5</ele>
</trkpt>
<trkpt lat="50.783458" lon="4.407134">
<ele>110.8</ele>
</trkpt>
<trkpt lat="50.783123" lon="4.406435">
<ele>111.8</ele>
</trkpt>
<trkpt lat="50.782982" lon="4.406168">
<ele>112.8</ele>
</trkpt>
<trkpt lat="50.782871" lon="4.406044">
<ele>113.3</ele>
</trkpt>
<trkpt lat="50.78279" lon="4.406021">
<ele>113.5</ele>
</trkpt>
<trkpt lat="50.782714" lon="4.406018">
<ele>113.5</ele>
</trkpt>
<trkpt lat="50.782607" lon="4.406047">
<ele>113.8</ele>
</trkpt>
<trkpt lat="50.782405" lon="4.406194">
<ele>114.8</ele>
</trkpt>
<trkpt lat="50.782175" lon="4.406413">
<ele>115.8</ele>
</trkpt>
<trkpt lat="50.781749" lon="4.407018">
<ele>118.5</ele>
</trkpt>
<trkpt lat="50.781654" lon="4.407316">
<ele>119.5</ele>
</trkpt>
<trkpt lat="50.781563" lon="4.407764">
<ele>121.3</ele>
</trkpt>
<trkpt lat="50.781487" lon="4.407984">
<ele>122.0</ele>
</trkpt>
<trkpt lat="50.781422" lon="4.408216">
<ele>122.8</ele>
</trkpt>
<trkpt lat="50.781395" lon="4.408508">
<ele>123.5</ele>
</trkpt>
<trkpt lat="50.781399" lon="4.409114">
<ele>126.3</ele>
</trkpt>
<trkpt lat="50.781367" lon="4.409428">
<ele>128.0</ele>
</trkpt>
<trkpt lat="50.781286" lon="4.409607">
<ele>129.0</ele>
</trkpt>
<trkpt lat="50.78116" lon="4.409789">
<ele>130.0</ele>
</trkpt>
<trkpt lat="50.780804" lon="4.409993">
<ele>130.8</ele>
</trkpt>
<trkpt lat="50.780389" lon="4.410334">
<ele>131.8</ele>
</trkpt>
<trkpt lat="50.780232" lon="4.410563">
<ele>132.3</ele>
</trkpt>
<trkpt lat="50.780094" lon="4.410827">
<ele>132.8</ele>
</trkpt>
<trkpt lat="50.779723" lon="4.411582">
<ele>135.8</ele>
</trkpt>
<trkpt lat="50.779591" lon="4.411791">
<ele>135.5</ele>
</trkpt>
<trkpt lat="50.779125" lon="4.412435">
<ele>132.5</ele>
</trkpt>
<trkpt lat="50.778676" lon="4.412979">
<ele>134.0</ele>
</trkpt>
<trkpt lat="50.778194" lon="4.413466">
<ele>136.8</ele>
</trkpt>
<trkpt lat="50.777427" lon="4.414302">
<ele>137.5</ele>
</trkpt>
<trkpt lat="50.777165" lon="4.414736">
<ele>137.3</ele>
</trkpt>
<trkpt lat="50.776927" lon="4.415201">
<ele>137.5</ele>
</trkpt>
<trkpt lat="50.776778" lon="4.415613">
<ele>137.3</ele>
</trkpt>
<trkpt lat="50.776553" lon="4.416425">
<ele>134.8</ele>
</trkpt>
<trkpt lat="50.776326" lon="4.417304">
<ele>132.3</ele>
</trkpt>
<trkpt lat="50.776129" lon="4.418383">
<ele>129.5</ele>
</trkpt>
</trkseg>
</trk>
</gpx>
+820
View File
@@ -0,0 +1,820 @@
<?xml version="1.0" encoding="UTF-8"?>
<gpx xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.topografix.com/GPX/1/1" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd http://www.topografix.com/GPX/gpx_style/0/2 http://www.topografix.com/GPX/gpx_style/0/2/gpx_style.xsd"
xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1"
xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3"
xmlns:gpx_style="http://www.topografix.com/GPX/gpx_style/0/2" version="1.1" creator="https://gpx.studio">
<metadata>
<name>with_surface</name>
<author>
<name>gpx.studio</name>
<link href="https://gpx.studio"></link>
</author>
</metadata>
<trk>
<name>with_surface</name>
<type>Cycling</type>
<trkseg>
<trkpt lat="50.790867" lon="4.404968">
<ele>109.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.790714" lon="4.405036">
<ele>110.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.790336" lon="4.405259">
<ele>110.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.790165" lon="4.405331">
<ele>110.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.790008" lon="4.405359">
<ele>110.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.789818" lon="4.405359">
<ele>109.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.789409" lon="4.40534">
<ele>107.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.789105" lon="4.405411">
<ele>106.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.788799" lon="4.405527">
<ele>108.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.788645" lon="4.405606">
<ele>109.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.7885" lon="4.405711">
<ele>110.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.78822" lon="4.405959">
<ele>112.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.787956" lon="4.406092">
<ele>112.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.787814" lon="4.406143">
<ele>113.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.787674" lon="4.406177">
<ele>114.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.787451" lon="4.406199">
<ele>115.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.787297" lon="4.406177">
<ele>114.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.78716" lon="4.406098">
<ele>114.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.787045" lon="4.405984">
<ele>114.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.786683" lon="4.405653">
<ele>114.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.786538" lon="4.405543">
<ele>115.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.78635" lon="4.405441">
<ele>115.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.786275" lon="4.40542">
<ele>115.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.786182" lon="4.405435">
<ele>116.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.786121" lon="4.405475">
<ele>115.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.786042" lon="4.405558">
<ele>115.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.785821" lon="4.405925">
<ele>114.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.785672" lon="4.406119">
<ele>112.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.785516" lon="4.406256">
<ele>110.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.785384" lon="4.406364">
<ele>109.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.785126" lon="4.406475">
<ele>106.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.784697" lon="4.406537">
<ele>104.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.784591" lon="4.40657">
<ele>104.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.784507" lon="4.406612">
<ele>103.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.784435" lon="4.40669">
<ele>103.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.784209" lon="4.407148">
<ele>103.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.784162" lon="4.407257">
<ele>103.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.784077" lon="4.407372">
<ele>104.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.784006" lon="4.407435">
<ele>105.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.783924" lon="4.407471">
<ele>106.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.783837" lon="4.407486">
<ele>107.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.783771" lon="4.407472">
<ele>108.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.783697" lon="4.407428">
<ele>109.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.783626" lon="4.407363">
<ele>110.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.783548" lon="4.407274">
<ele>110.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.783458" lon="4.407134">
<ele>110.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.783123" lon="4.406435">
<ele>111.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.782982" lon="4.406168">
<ele>112.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.782871" lon="4.406044">
<ele>113.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.78279" lon="4.406021">
<ele>113.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.782714" lon="4.406018">
<ele>113.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.782607" lon="4.406047">
<ele>113.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.782405" lon="4.406194">
<ele>114.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.782175" lon="4.406413">
<ele>115.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.781749" lon="4.407018">
<ele>118.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.781654" lon="4.407316">
<ele>119.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.781563" lon="4.407764">
<ele>121.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.781487" lon="4.407984">
<ele>122.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.781422" lon="4.408216">
<ele>122.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.781395" lon="4.408508">
<ele>123.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.781399" lon="4.409114">
<ele>126.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.781367" lon="4.409428">
<ele>128.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.781286" lon="4.409607">
<ele>129.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.78116" lon="4.409789">
<ele>130.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.780804" lon="4.409993">
<ele>130.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.780389" lon="4.410334">
<ele>131.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.780232" lon="4.410563">
<ele>132.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.780094" lon="4.410827">
<ele>132.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.779723" lon="4.411582">
<ele>135.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.779591" lon="4.411791">
<ele>135.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.779125" lon="4.412435">
<ele>132.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.778676" lon="4.412979">
<ele>134.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.778194" lon="4.413466">
<ele>136.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.777427" lon="4.414302">
<ele>137.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.777165" lon="4.414736">
<ele>137.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.776927" lon="4.415201">
<ele>137.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.776778" lon="4.415613">
<ele>137.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.776553" lon="4.416425">
<ele>134.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.776326" lon="4.417304">
<ele>132.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>asphalt</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.776129" lon="4.418383">
<ele>129.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:Extensions>
<surface>cobblestone</surface>
</gpxtpx:Extensions>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
</trkseg>
</trk>
</gpx>
+660
View File
@@ -0,0 +1,660 @@
<?xml version="1.0" encoding="UTF-8"?>
<gpx xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.topografix.com/GPX/1/1" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd http://www.topografix.com/GPX/gpx_style/0/2 http://www.topografix.com/GPX/gpx_style/0/2/gpx_style.xsd"
xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1"
xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3"
xmlns:gpx_style="http://www.topografix.com/GPX/gpx_style/0/2" version="1.1" creator="https://gpx.studio">
<metadata>
<name>with_temp</name>
<author>
<name>gpx.studio</name>
<link href="https://gpx.studio"></link>
</author>
</metadata>
<trk>
<name>with_temp</name>
<type>Cycling</type>
<trkseg>
<trkpt lat="50.790867" lon="4.404968">
<ele>109.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.790714" lon="4.405036">
<ele>110.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.790336" lon="4.405259">
<ele>110.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.790165" lon="4.405331">
<ele>110.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.790008" lon="4.405359">
<ele>110.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.789818" lon="4.405359">
<ele>109.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.789409" lon="4.40534">
<ele>107.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.789105" lon="4.405411">
<ele>106.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.788799" lon="4.405527">
<ele>108.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.788645" lon="4.405606">
<ele>109.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.7885" lon="4.405711">
<ele>110.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.78822" lon="4.405959">
<ele>112.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.787956" lon="4.406092">
<ele>112.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.787814" lon="4.406143">
<ele>113.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.787674" lon="4.406177">
<ele>114.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.787451" lon="4.406199">
<ele>115.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.787297" lon="4.406177">
<ele>114.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.78716" lon="4.406098">
<ele>114.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.787045" lon="4.405984">
<ele>114.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.786683" lon="4.405653">
<ele>114.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.786538" lon="4.405543">
<ele>115.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.78635" lon="4.405441">
<ele>115.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.786275" lon="4.40542">
<ele>115.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.786182" lon="4.405435">
<ele>116.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.786121" lon="4.405475">
<ele>115.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.786042" lon="4.405558">
<ele>115.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.785821" lon="4.405925">
<ele>114.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.785672" lon="4.406119">
<ele>112.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.785516" lon="4.406256">
<ele>110.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.785384" lon="4.406364">
<ele>109.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.785126" lon="4.406475">
<ele>106.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.784697" lon="4.406537">
<ele>104.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.784591" lon="4.40657">
<ele>104.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.784507" lon="4.406612">
<ele>103.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.784435" lon="4.40669">
<ele>103.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.784209" lon="4.407148">
<ele>103.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.784162" lon="4.407257">
<ele>103.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.784077" lon="4.407372">
<ele>104.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.784006" lon="4.407435">
<ele>105.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.783924" lon="4.407471">
<ele>106.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.783837" lon="4.407486">
<ele>107.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.783771" lon="4.407472">
<ele>108.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.783697" lon="4.407428">
<ele>109.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.783626" lon="4.407363">
<ele>110.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.783548" lon="4.407274">
<ele>110.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.783458" lon="4.407134">
<ele>110.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.783123" lon="4.406435">
<ele>111.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.782982" lon="4.406168">
<ele>112.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.782871" lon="4.406044">
<ele>113.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.78279" lon="4.406021">
<ele>113.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.782714" lon="4.406018">
<ele>113.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.782607" lon="4.406047">
<ele>113.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.782405" lon="4.406194">
<ele>114.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.782175" lon="4.406413">
<ele>115.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.781749" lon="4.407018">
<ele>118.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.781654" lon="4.407316">
<ele>119.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.781563" lon="4.407764">
<ele>121.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.781487" lon="4.407984">
<ele>122.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.781422" lon="4.408216">
<ele>122.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.781395" lon="4.408508">
<ele>123.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.781399" lon="4.409114">
<ele>126.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.781367" lon="4.409428">
<ele>128.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.781286" lon="4.409607">
<ele>129.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.78116" lon="4.409789">
<ele>130.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.780804" lon="4.409993">
<ele>130.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.780389" lon="4.410334">
<ele>131.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.780232" lon="4.410563">
<ele>132.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.780094" lon="4.410827">
<ele>132.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.779723" lon="4.411582">
<ele>135.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.779591" lon="4.411791">
<ele>135.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.779125" lon="4.412435">
<ele>132.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.778676" lon="4.412979">
<ele>134.0</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.778194" lon="4.413466">
<ele>136.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.777427" lon="4.414302">
<ele>137.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.777165" lon="4.414736">
<ele>137.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.776927" lon="4.415201">
<ele>137.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.776778" lon="4.415613">
<ele>137.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.776553" lon="4.416425">
<ele>134.8</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.776326" lon="4.417304">
<ele>132.3</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>21</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
<trkpt lat="50.776129" lon="4.418383">
<ele>129.5</ele>
<extensions>
<gpxtpx:TrackPointExtension>
<gpxtpx:atemp>22</gpxtpx:atemp>
</gpxtpx:TrackPointExtension>
</extensions>
</trkpt>
</trkseg>
</trk>
</gpx>
+340
View File
@@ -0,0 +1,340 @@
<?xml version="1.0" encoding="UTF-8"?>
<gpx xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.topografix.com/GPX/1/1" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd http://www.topografix.com/GPX/gpx_style/0/2 http://www.topografix.com/GPX/gpx_style/0/2/gpx_style.xsd"
xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1"
xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3"
xmlns:gpx_style="http://www.topografix.com/GPX/gpx_style/0/2" version="1.1" creator="https://gpx.studio">
<metadata>
<name>with_time</name>
<author>
<name>gpx.studio</name>
<link href="https://gpx.studio"></link>
</author>
</metadata>
<trk>
<name>with_time</name>
<type>Cycling</type>
<trkseg>
<trkpt lat="50.790867" lon="4.404968">
<ele>109.0</ele>
<time>2023-12-31T23:00:00.000Z</time>
</trkpt>
<trkpt lat="50.790714" lon="4.405036">
<ele>110.8</ele>
<time>2023-12-31T23:00:03.180Z</time>
</trkpt>
<trkpt lat="50.790336" lon="4.405259">
<ele>110.3</ele>
<time>2023-12-31T23:00:11.254Z</time>
</trkpt>
<trkpt lat="50.790165" lon="4.405331">
<ele>110.0</ele>
<time>2023-12-31T23:00:14.795Z</time>
</trkpt>
<trkpt lat="50.790008" lon="4.405359">
<ele>110.3</ele>
<time>2023-12-31T23:00:17.957Z</time>
</trkpt>
<trkpt lat="50.789818" lon="4.405359">
<ele>109.3</ele>
<time>2023-12-31T23:00:21.759Z</time>
</trkpt>
<trkpt lat="50.789409" lon="4.40534">
<ele>107.0</ele>
<time>2023-12-31T23:00:29.948Z</time>
</trkpt>
<trkpt lat="50.789105" lon="4.405411">
<ele>106.0</ele>
<time>2023-12-31T23:00:36.098Z</time>
</trkpt>
<trkpt lat="50.788799" lon="4.405527">
<ele>108.5</ele>
<time>2023-12-31T23:00:42.396Z</time>
</trkpt>
<trkpt lat="50.788645" lon="4.405606">
<ele>109.8</ele>
<time>2023-12-31T23:00:45.636Z</time>
</trkpt>
<trkpt lat="50.7885" lon="4.405711">
<ele>110.8</ele>
<time>2023-12-31T23:00:48.827Z</time>
</trkpt>
<trkpt lat="50.78822" lon="4.405959">
<ele>112.0</ele>
<time>2023-12-31T23:00:55.249Z</time>
</trkpt>
<trkpt lat="50.787956" lon="4.406092">
<ele>112.8</ele>
<time>2023-12-31T23:01:00.794Z</time>
</trkpt>
<trkpt lat="50.787814" lon="4.406143">
<ele>113.5</ele>
<time>2023-12-31T23:01:03.708Z</time>
</trkpt>
<trkpt lat="50.787674" lon="4.406177">
<ele>114.3</ele>
<time>2023-12-31T23:01:06.542Z</time>
</trkpt>
<trkpt lat="50.787451" lon="4.406199">
<ele>115.3</ele>
<time>2023-12-31T23:01:11.014Z</time>
</trkpt>
<trkpt lat="50.787297" lon="4.406177">
<ele>114.8</ele>
<time>2023-12-31T23:01:14.108Z</time>
</trkpt>
<trkpt lat="50.78716" lon="4.406098">
<ele>114.3</ele>
<time>2023-12-31T23:01:17.026Z</time>
</trkpt>
<trkpt lat="50.787045" lon="4.405984">
<ele>114.3</ele>
<time>2023-12-31T23:01:19.742Z</time>
</trkpt>
<trkpt lat="50.786683" lon="4.405653">
<ele>114.5</ele>
<time>2023-12-31T23:01:28.110Z</time>
</trkpt>
<trkpt lat="50.786538" lon="4.405543">
<ele>115.0</ele>
<time>2023-12-31T23:01:31.328Z</time>
</trkpt>
<trkpt lat="50.78635" lon="4.405441">
<ele>115.8</ele>
<time>2023-12-31T23:01:35.306Z</time>
</trkpt>
<trkpt lat="50.786275" lon="4.40542">
<ele>115.8</ele>
<time>2023-12-31T23:01:36.830Z</time>
</trkpt>
<trkpt lat="50.786182" lon="4.405435">
<ele>116.0</ele>
<time>2023-12-31T23:01:38.701Z</time>
</trkpt>
<trkpt lat="50.786121" lon="4.405475">
<ele>115.8</ele>
<time>2023-12-31T23:01:40.022Z</time>
</trkpt>
<trkpt lat="50.786042" lon="4.405558">
<ele>115.5</ele>
<time>2023-12-31T23:01:41.920Z</time>
</trkpt>
<trkpt lat="50.785821" lon="4.405925">
<ele>114.5</ele>
<time>2023-12-31T23:01:48.333Z</time>
</trkpt>
<trkpt lat="50.785672" lon="4.406119">
<ele>112.5</ele>
<time>2023-12-31T23:01:52.195Z</time>
</trkpt>
<trkpt lat="50.785516" lon="4.406256">
<ele>110.8</ele>
<time>2023-12-31T23:01:55.766Z</time>
</trkpt>
<trkpt lat="50.785384" lon="4.406364">
<ele>109.0</ele>
<time>2023-12-31T23:01:58.740Z</time>
</trkpt>
<trkpt lat="50.785126" lon="4.406475">
<ele>106.3</ele>
<time>2023-12-31T23:02:04.091Z</time>
</trkpt>
<trkpt lat="50.784697" lon="4.406537">
<ele>104.3</ele>
<time>2023-12-31T23:02:12.713Z</time>
</trkpt>
<trkpt lat="50.784591" lon="4.40657">
<ele>104.0</ele>
<time>2023-12-31T23:02:14.875Z</time>
</trkpt>
<trkpt lat="50.784507" lon="4.406612">
<ele>103.8</ele>
<time>2023-12-31T23:02:16.638Z</time>
</trkpt>
<trkpt lat="50.784435" lon="4.40669">
<ele>103.3</ele>
<time>2023-12-31T23:02:18.384Z</time>
</trkpt>
<trkpt lat="50.784209" lon="4.407148">
<ele>103.5</ele>
<time>2023-12-31T23:02:25.735Z</time>
</trkpt>
<trkpt lat="50.784162" lon="4.407257">
<ele>103.8</ele>
<time>2023-12-31T23:02:27.404Z</time>
</trkpt>
<trkpt lat="50.784077" lon="4.407372">
<ele>104.8</ele>
<time>2023-12-31T23:02:29.642Z</time>
</trkpt>
<trkpt lat="50.784006" lon="4.407435">
<ele>105.8</ele>
<time>2023-12-31T23:02:31.271Z</time>
</trkpt>
<trkpt lat="50.783924" lon="4.407471">
<ele>106.8</ele>
<time>2023-12-31T23:02:32.974Z</time>
</trkpt>
<trkpt lat="50.783837" lon="4.407486">
<ele>107.8</ele>
<time>2023-12-31T23:02:34.725Z</time>
</trkpt>
<trkpt lat="50.783771" lon="4.407472">
<ele>108.5</ele>
<time>2023-12-31T23:02:36.057Z</time>
</trkpt>
<trkpt lat="50.783697" lon="4.407428">
<ele>109.3</ele>
<time>2023-12-31T23:02:37.639Z</time>
</trkpt>
<trkpt lat="50.783626" lon="4.407363">
<ele>110.0</ele>
<time>2023-12-31T23:02:39.280Z</time>
</trkpt>
<trkpt lat="50.783548" lon="4.407274">
<ele>110.5</ele>
<time>2023-12-31T23:02:41.205Z</time>
</trkpt>
<trkpt lat="50.783458" lon="4.407134">
<ele>110.8</ele>
<time>2023-12-31T23:02:43.731Z</time>
</trkpt>
<trkpt lat="50.783123" lon="4.406435">
<ele>111.8</ele>
<time>2023-12-31T23:02:54.830Z</time>
</trkpt>
<trkpt lat="50.782982" lon="4.406168">
<ele>112.8</ele>
<time>2023-12-31T23:02:59.232Z</time>
</trkpt>
<trkpt lat="50.782871" lon="4.406044">
<ele>113.3</ele>
<time>2023-12-31T23:03:01.951Z</time>
</trkpt>
<trkpt lat="50.78279" lon="4.406021">
<ele>113.5</ele>
<time>2023-12-31T23:03:03.598Z</time>
</trkpt>
<trkpt lat="50.782714" lon="4.406018">
<ele>113.5</ele>
<time>2023-12-31T23:03:05.119Z</time>
</trkpt>
<trkpt lat="50.782607" lon="4.406047">
<ele>113.8</ele>
<time>2023-12-31T23:03:07.291Z</time>
</trkpt>
<trkpt lat="50.782405" lon="4.406194">
<ele>114.8</ele>
<time>2023-12-31T23:03:11.741Z</time>
</trkpt>
<trkpt lat="50.782175" lon="4.406413">
<ele>115.8</ele>
<time>2023-12-31T23:03:17.114Z</time>
</trkpt>
<trkpt lat="50.781749" lon="4.407018">
<ele>118.5</ele>
<time>2023-12-31T23:03:28.573Z</time>
</trkpt>
<trkpt lat="50.781654" lon="4.407316">
<ele>119.5</ele>
<time>2023-12-31T23:03:32.796Z</time>
</trkpt>
<trkpt lat="50.781563" lon="4.407764">
<ele>121.3</ele>
<time>2023-12-31T23:03:38.750Z</time>
</trkpt>
<trkpt lat="50.781487" lon="4.407984">
<ele>122.0</ele>
<time>2023-12-31T23:03:41.922Z</time>
</trkpt>
<trkpt lat="50.781422" lon="4.408216">
<ele>122.8</ele>
<time>2023-12-31T23:03:45.133Z</time>
</trkpt>
<trkpt lat="50.781395" lon="4.408508">
<ele>123.5</ele>
<time>2023-12-31T23:03:48.867Z</time>
</trkpt>
<trkpt lat="50.781399" lon="4.409114">
<ele>126.3</ele>
<time>2023-12-31T23:03:56.536Z</time>
</trkpt>
<trkpt lat="50.781367" lon="4.409428">
<ele>128.0</ele>
<time>2023-12-31T23:04:00.561Z</time>
</trkpt>
<trkpt lat="50.781286" lon="4.409607">
<ele>129.0</ele>
<time>2023-12-31T23:04:03.346Z</time>
</trkpt>
<trkpt lat="50.78116" lon="4.409789">
<ele>130.0</ele>
<time>2023-12-31T23:04:06.761Z</time>
</trkpt>
<trkpt lat="50.780804" lon="4.409993">
<ele>130.8</ele>
<time>2023-12-31T23:04:14.339Z</time>
</trkpt>
<trkpt lat="50.780389" lon="4.410334">
<ele>131.8</ele>
<time>2023-12-31T23:04:23.699Z</time>
</trkpt>
<trkpt lat="50.780232" lon="4.410563">
<ele>132.3</ele>
<time>2023-12-31T23:04:27.973Z</time>
</trkpt>
<trkpt lat="50.780094" lon="4.410827">
<ele>132.8</ele>
<time>2023-12-31T23:04:32.307Z</time>
</trkpt>
<trkpt lat="50.779723" lon="4.411582">
<ele>135.8</ele>
<time>2023-12-31T23:04:44.408Z</time>
</trkpt>
<trkpt lat="50.779591" lon="4.411791">
<ele>135.5</ele>
<time>2023-12-31T23:04:48.146Z</time>
</trkpt>
<trkpt lat="50.779125" lon="4.412435">
<ele>132.5</ele>
<time>2023-12-31T23:05:00.532Z</time>
</trkpt>
<trkpt lat="50.778676" lon="4.412979">
<ele>134.0</ele>
<time>2023-12-31T23:05:11.852Z</time>
</trkpt>
<trkpt lat="50.778194" lon="4.413466">
<ele>136.8</ele>
<time>2023-12-31T23:05:23.300Z</time>
</trkpt>
<trkpt lat="50.777427" lon="4.414302">
<ele>137.5</ele>
<time>2023-12-31T23:05:41.944Z</time>
</trkpt>
<trkpt lat="50.777165" lon="4.414736">
<ele>137.3</ele>
<time>2023-12-31T23:05:49.538Z</time>
</trkpt>
<trkpt lat="50.776927" lon="4.415201">
<ele>137.5</ele>
<time>2023-12-31T23:05:57.109Z</time>
</trkpt>
<trkpt lat="50.776778" lon="4.415613">
<ele>137.3</ele>
<time>2023-12-31T23:06:03.116Z</time>
</trkpt>
<trkpt lat="50.776553" lon="4.416425">
<ele>134.8</ele>
<time>2023-12-31T23:06:14.336Z</time>
</trkpt>
<trkpt lat="50.776326" lon="4.417304">
<ele>132.3</ele>
<time>2023-12-31T23:06:26.353Z</time>
</trkpt>
<trkpt lat="50.776129" lon="4.418383">
<ele>129.5</ele>
<time>2023-12-31T23:06:40.567Z</time>
</trkpt>
</trkseg>
</trk>
</gpx>
+257
View File
@@ -0,0 +1,257 @@
<?xml version="1.0" encoding="UTF-8"?>
<gpx xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.topografix.com/GPX/1/1" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd http://www.topografix.com/GPX/gpx_style/0/2 http://www.topografix.com/GPX/gpx_style/0/2/gpx_style.xsd"
xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1"
xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3"
xmlns:gpx_style="http://www.topografix.com/GPX/gpx_style/0/2" version="1.1" creator="https://gpx.studio">
<metadata>
<name>with_tracks</name>
<author>
<name>gpx.studio</name>
<link href="https://gpx.studio"></link>
</author>
</metadata>
<trk>
<name>track 1</name>
<type>Cycling</type>
<trkseg>
<trkpt lat="50.790867" lon="4.404968">
<ele>109.0</ele>
</trkpt>
<trkpt lat="50.790714" lon="4.405036">
<ele>110.8</ele>
</trkpt>
<trkpt lat="50.790336" lon="4.405259">
<ele>110.3</ele>
</trkpt>
<trkpt lat="50.790165" lon="4.405331">
<ele>110.0</ele>
</trkpt>
<trkpt lat="50.790008" lon="4.405359">
<ele>110.3</ele>
</trkpt>
<trkpt lat="50.789818" lon="4.405359">
<ele>109.3</ele>
</trkpt>
<trkpt lat="50.789409" lon="4.40534">
<ele>107.0</ele>
</trkpt>
<trkpt lat="50.789105" lon="4.405411">
<ele>106.0</ele>
</trkpt>
<trkpt lat="50.788799" lon="4.405527">
<ele>108.5</ele>
</trkpt>
<trkpt lat="50.788645" lon="4.405606">
<ele>109.8</ele>
</trkpt>
<trkpt lat="50.7885" lon="4.405711">
<ele>110.8</ele>
</trkpt>
<trkpt lat="50.78822" lon="4.405959">
<ele>112.0</ele>
</trkpt>
<trkpt lat="50.787956" lon="4.406092">
<ele>112.8</ele>
</trkpt>
<trkpt lat="50.787814" lon="4.406143">
<ele>113.5</ele>
</trkpt>
<trkpt lat="50.787674" lon="4.406177">
<ele>114.3</ele>
</trkpt>
<trkpt lat="50.787451" lon="4.406199">
<ele>115.3</ele>
</trkpt>
<trkpt lat="50.787297" lon="4.406177">
<ele>114.8</ele>
</trkpt>
<trkpt lat="50.78716" lon="4.406098">
<ele>114.3</ele>
</trkpt>
<trkpt lat="50.787045" lon="4.405984">
<ele>114.3</ele>
</trkpt>
<trkpt lat="50.786683" lon="4.405653">
<ele>114.5</ele>
</trkpt>
<trkpt lat="50.786538" lon="4.405543">
<ele>115.0</ele>
</trkpt>
<trkpt lat="50.78635" lon="4.405441">
<ele>115.8</ele>
</trkpt>
<trkpt lat="50.786275" lon="4.40542">
<ele>115.8</ele>
</trkpt>
<trkpt lat="50.786182" lon="4.405435">
<ele>116.0</ele>
</trkpt>
<trkpt lat="50.786121" lon="4.405475">
<ele>115.8</ele>
</trkpt>
<trkpt lat="50.786042" lon="4.405558">
<ele>115.5</ele>
</trkpt>
<trkpt lat="50.785821" lon="4.405925">
<ele>114.5</ele>
</trkpt>
<trkpt lat="50.785672" lon="4.406119">
<ele>112.5</ele>
</trkpt>
<trkpt lat="50.785516" lon="4.406256">
<ele>110.8</ele>
</trkpt>
<trkpt lat="50.785384" lon="4.406364">
<ele>109.0</ele>
</trkpt>
<trkpt lat="50.785126" lon="4.406475">
<ele>106.3</ele>
</trkpt>
<trkpt lat="50.784697" lon="4.406537">
<ele>104.3</ele>
</trkpt>
<trkpt lat="50.784591" lon="4.40657">
<ele>104.0</ele>
</trkpt>
<trkpt lat="50.784507" lon="4.406612">
<ele>103.8</ele>
</trkpt>
<trkpt lat="50.784435" lon="4.40669">
<ele>103.3</ele>
</trkpt>
<trkpt lat="50.784209" lon="4.407148">
<ele>103.5</ele>
</trkpt>
<trkpt lat="50.784162" lon="4.407257">
<ele>103.8</ele>
</trkpt>
<trkpt lat="50.784077" lon="4.407372">
<ele>104.8</ele>
</trkpt>
<trkpt lat="50.784006" lon="4.407435">
<ele>105.8</ele>
</trkpt>
<trkpt lat="50.783924" lon="4.407471">
<ele>106.8</ele>
</trkpt>
<trkpt lat="50.783837" lon="4.407486">
<ele>107.8</ele>
</trkpt>
<trkpt lat="50.783771" lon="4.407472">
<ele>108.5</ele>
</trkpt>
<trkpt lat="50.783697" lon="4.407428">
<ele>109.3</ele>
</trkpt>
<trkpt lat="50.783626" lon="4.407363">
<ele>110.0</ele>
</trkpt>
<trkpt lat="50.783548" lon="4.407274">
<ele>110.5</ele>
</trkpt>
<trkpt lat="50.783458" lon="4.407134">
<ele>110.8</ele>
</trkpt>
<trkpt lat="50.783123" lon="4.406435">
<ele>111.8</ele>
</trkpt>
<trkpt lat="50.782982" lon="4.406168">
<ele>112.8</ele>
</trkpt>
<trkpt lat="50.782871" lon="4.406044">
<ele>113.3</ele>
</trkpt>
</trkseg>
</trk>
<trk>
<name>track 2</name>
<type>Cycling</type>
<trkseg>
<trkpt lat="50.782212" lon="4.406377">
<ele>115.5</ele>
</trkpt>
<trkpt lat="50.782175" lon="4.406413">
<ele>115.8</ele>
</trkpt>
<trkpt lat="50.781749" lon="4.407018">
<ele>118.5</ele>
</trkpt>
<trkpt lat="50.781654" lon="4.407316">
<ele>119.5</ele>
</trkpt>
<trkpt lat="50.781563" lon="4.407764">
<ele>121.3</ele>
</trkpt>
<trkpt lat="50.781487" lon="4.407984">
<ele>122.0</ele>
</trkpt>
<trkpt lat="50.781422" lon="4.408216">
<ele>122.8</ele>
</trkpt>
<trkpt lat="50.781395" lon="4.408508">
<ele>123.5</ele>
</trkpt>
<trkpt lat="50.781399" lon="4.409114">
<ele>126.3</ele>
</trkpt>
<trkpt lat="50.781367" lon="4.409428">
<ele>128.0</ele>
</trkpt>
<trkpt lat="50.781286" lon="4.409607">
<ele>129.0</ele>
</trkpt>
<trkpt lat="50.78116" lon="4.409789">
<ele>130.0</ele>
</trkpt>
<trkpt lat="50.780804" lon="4.409993">
<ele>130.8</ele>
</trkpt>
<trkpt lat="50.780389" lon="4.410334">
<ele>131.8</ele>
</trkpt>
<trkpt lat="50.780232" lon="4.410563">
<ele>132.3</ele>
</trkpt>
<trkpt lat="50.780094" lon="4.410827">
<ele>132.8</ele>
</trkpt>
<trkpt lat="50.779723" lon="4.411582">
<ele>135.8</ele>
</trkpt>
<trkpt lat="50.779591" lon="4.411791">
<ele>135.5</ele>
</trkpt>
<trkpt lat="50.779125" lon="4.412435">
<ele>132.5</ele>
</trkpt>
<trkpt lat="50.778676" lon="4.412979">
<ele>134.0</ele>
</trkpt>
<trkpt lat="50.778194" lon="4.413466">
<ele>136.8</ele>
</trkpt>
<trkpt lat="50.777427" lon="4.414302">
<ele>137.5</ele>
</trkpt>
<trkpt lat="50.777165" lon="4.414736">
<ele>137.3</ele>
</trkpt>
<trkpt lat="50.776927" lon="4.415201">
<ele>137.5</ele>
</trkpt>
<trkpt lat="50.776778" lon="4.415613">
<ele>137.3</ele>
</trkpt>
<trkpt lat="50.776553" lon="4.416425">
<ele>134.8</ele>
</trkpt>
<trkpt lat="50.776326" lon="4.417304">
<ele>132.3</ele>
</trkpt>
<trkpt lat="50.776129" lon="4.418383">
<ele>129.5</ele>
</trkpt>
</trkseg>
</trk>
</gpx>
+346
View File
@@ -0,0 +1,346 @@
<?xml version="1.0" encoding="UTF-8"?>
<gpx xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.topografix.com/GPX/1/1" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd http://www.topografix.com/GPX/gpx_style/0/2 http://www.topografix.com/GPX/gpx_style/0/2/gpx_style.xsd"
xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1"
xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3"
xmlns:gpx_style="http://www.topografix.com/GPX/gpx_style/0/2" version="1.1" creator="https://gpx.studio">
<metadata>
<name>with_tracks_and_segments</name>
<author>
<name>gpx.studio</name>
<link href="https://gpx.studio"></link>
</author>
</metadata>
<trk>
<name>track 1</name>
<type>Running</type>
<trkseg>
<trkpt lat="50.790867" lon="4.404968">
<ele>109.0</ele>
<time>2023-12-31T23:00:00.000Z</time>
</trkpt>
<trkpt lat="50.790714" lon="4.405036">
<ele>110.8</ele>
<time>2023-12-31T23:00:03.180Z</time>
</trkpt>
<trkpt lat="50.790336" lon="4.405259">
<ele>110.3</ele>
<time>2023-12-31T23:00:11.254Z</time>
</trkpt>
<trkpt lat="50.790165" lon="4.405331">
<ele>110.0</ele>
<time>2023-12-31T23:00:14.795Z</time>
</trkpt>
<trkpt lat="50.790008" lon="4.405359">
<ele>110.3</ele>
<time>2023-12-31T23:00:17.957Z</time>
</trkpt>
<trkpt lat="50.789818" lon="4.405359">
<ele>109.3</ele>
<time>2023-12-31T23:00:21.759Z</time>
</trkpt>
<trkpt lat="50.789409" lon="4.40534">
<ele>107.0</ele>
<time>2023-12-31T23:00:29.948Z</time>
</trkpt>
<trkpt lat="50.789105" lon="4.405411">
<ele>106.0</ele>
<time>2023-12-31T23:00:36.098Z</time>
</trkpt>
<trkpt lat="50.788799" lon="4.405527">
<ele>108.5</ele>
<time>2023-12-31T23:00:42.396Z</time>
</trkpt>
<trkpt lat="50.788645" lon="4.405606">
<ele>109.8</ele>
<time>2023-12-31T23:00:45.636Z</time>
</trkpt>
<trkpt lat="50.7885" lon="4.405711">
<ele>110.8</ele>
<time>2023-12-31T23:00:48.827Z</time>
</trkpt>
<trkpt lat="50.78822" lon="4.405959">
<ele>112.0</ele>
<time>2023-12-31T23:00:55.249Z</time>
</trkpt>
<trkpt lat="50.787956" lon="4.406092">
<ele>112.8</ele>
<time>2023-12-31T23:01:00.794Z</time>
</trkpt>
<trkpt lat="50.787814" lon="4.406143">
<ele>113.5</ele>
<time>2023-12-31T23:01:03.708Z</time>
</trkpt>
<trkpt lat="50.787674" lon="4.406177">
<ele>114.3</ele>
<time>2023-12-31T23:01:06.542Z</time>
</trkpt>
<trkpt lat="50.787451" lon="4.406199">
<ele>115.3</ele>
<time>2023-12-31T23:01:11.014Z</time>
</trkpt>
</trkseg>
<trkseg>
<trkpt lat="50.78727108169855" lon="4.406133681127736">
<ele>115.0</ele>
<time>2023-12-31T23:01:14.708Z</time>
</trkpt>
<trkpt lat="50.787297" lon="4.406177">
<ele>114.8</ele>
<time>2023-12-31T23:01:15.462Z</time>
</trkpt>
<trkpt lat="50.78716" lon="4.406098">
<ele>114.3</ele>
<time>2023-12-31T23:01:18.380Z</time>
</trkpt>
<trkpt lat="50.787045" lon="4.405984">
<ele>114.3</ele>
<time>2023-12-31T23:01:21.096Z</time>
</trkpt>
<trkpt lat="50.786683" lon="4.405653">
<ele>114.5</ele>
<time>2023-12-31T23:01:29.464Z</time>
</trkpt>
<trkpt lat="50.786538" lon="4.405543">
<ele>115.0</ele>
<time>2023-12-31T23:01:32.682Z</time>
</trkpt>
<trkpt lat="50.78635" lon="4.405441">
<ele>115.8</ele>
<time>2023-12-31T23:01:36.660Z</time>
</trkpt>
<trkpt lat="50.786275" lon="4.40542">
<ele>115.8</ele>
<time>2023-12-31T23:01:38.184Z</time>
</trkpt>
<trkpt lat="50.786182" lon="4.405435">
<ele>116.0</ele>
<time>2023-12-31T23:01:40.055Z</time>
</trkpt>
<trkpt lat="50.786121" lon="4.405475">
<ele>115.8</ele>
<time>2023-12-31T23:01:41.376Z</time>
</trkpt>
<trkpt lat="50.786042" lon="4.405558">
<ele>115.5</ele>
<time>2023-12-31T23:01:43.274Z</time>
</trkpt>
<trkpt lat="50.785821" lon="4.405925">
<ele>114.5</ele>
<time>2023-12-31T23:01:49.687Z</time>
</trkpt>
<trkpt lat="50.785672" lon="4.406119">
<ele>112.5</ele>
<time>2023-12-31T23:01:53.549Z</time>
</trkpt>
<trkpt lat="50.785516" lon="4.406256">
<ele>110.8</ele>
<time>2023-12-31T23:01:57.120Z</time>
</trkpt>
<trkpt lat="50.785384" lon="4.406364">
<ele>109.0</ele>
<time>2023-12-31T23:02:00.094Z</time>
</trkpt>
<trkpt lat="50.785126" lon="4.406475">
<ele>106.3</ele>
<time>2023-12-31T23:02:05.445Z</time>
</trkpt>
<trkpt lat="50.784697" lon="4.406537">
<ele>104.3</ele>
<time>2023-12-31T23:02:14.067Z</time>
</trkpt>
<trkpt lat="50.784591" lon="4.40657">
<ele>104.0</ele>
<time>2023-12-31T23:02:16.229Z</time>
</trkpt>
<trkpt lat="50.784507" lon="4.406612">
<ele>103.8</ele>
<time>2023-12-31T23:02:17.992Z</time>
</trkpt>
<trkpt lat="50.784435" lon="4.40669">
<ele>103.3</ele>
<time>2023-12-31T23:02:19.738Z</time>
</trkpt>
<trkpt lat="50.784209" lon="4.407148">
<ele>103.5</ele>
<time>2023-12-31T23:02:27.089Z</time>
</trkpt>
<trkpt lat="50.784162" lon="4.407257">
<ele>103.8</ele>
<time>2023-12-31T23:02:28.758Z</time>
</trkpt>
<trkpt lat="50.784077" lon="4.407372">
<ele>104.8</ele>
<time>2023-12-31T23:02:30.996Z</time>
</trkpt>
<trkpt lat="50.784006" lon="4.407435">
<ele>105.8</ele>
<time>2023-12-31T23:02:32.625Z</time>
</trkpt>
<trkpt lat="50.783924" lon="4.407471">
<ele>106.8</ele>
<time>2023-12-31T23:02:34.328Z</time>
</trkpt>
<trkpt lat="50.783837" lon="4.407486">
<ele>107.8</ele>
<time>2023-12-31T23:02:36.079Z</time>
</trkpt>
<trkpt lat="50.783771" lon="4.407472">
<ele>108.5</ele>
<time>2023-12-31T23:02:37.411Z</time>
</trkpt>
<trkpt lat="50.783697" lon="4.407428">
<ele>109.3</ele>
<time>2023-12-31T23:02:38.993Z</time>
</trkpt>
<trkpt lat="50.783626" lon="4.407363">
<ele>110.0</ele>
<time>2023-12-31T23:02:40.634Z</time>
</trkpt>
<trkpt lat="50.783548" lon="4.407274">
<ele>110.5</ele>
<time>2023-12-31T23:02:42.559Z</time>
</trkpt>
<trkpt lat="50.783458" lon="4.407134">
<ele>110.8</ele>
<time>2023-12-31T23:02:45.085Z</time>
</trkpt>
<trkpt lat="50.783123" lon="4.406435">
<ele>111.8</ele>
<time>2023-12-31T23:02:56.184Z</time>
</trkpt>
<trkpt lat="50.782982" lon="4.406168">
<ele>112.8</ele>
<time>2023-12-31T23:03:00.586Z</time>
</trkpt>
<trkpt lat="50.782871" lon="4.406044">
<ele>113.3</ele>
<time>2023-12-31T23:03:03.305Z</time>
</trkpt>
</trkseg>
</trk>
<trk>
<name>track 2</name>
<type>Running</type>
<trkseg>
<trkpt lat="50.782212" lon="4.406377">
<ele>115.5</ele>
<time>2023-12-31T23:03:17.151Z</time>
</trkpt>
<trkpt lat="50.782175" lon="4.406413">
<ele>115.8</ele>
<time>2023-12-31T23:03:18.020Z</time>
</trkpt>
<trkpt lat="50.781749" lon="4.407018">
<ele>118.5</ele>
<time>2023-12-31T23:03:29.479Z</time>
</trkpt>
<trkpt lat="50.781654" lon="4.407316">
<ele>119.5</ele>
<time>2023-12-31T23:03:33.702Z</time>
</trkpt>
<trkpt lat="50.781563" lon="4.407764">
<ele>121.3</ele>
<time>2023-12-31T23:03:39.656Z</time>
</trkpt>
<trkpt lat="50.781487" lon="4.407984">
<ele>122.0</ele>
<time>2023-12-31T23:03:42.828Z</time>
</trkpt>
<trkpt lat="50.781422" lon="4.408216">
<ele>122.8</ele>
<time>2023-12-31T23:03:46.039Z</time>
</trkpt>
<trkpt lat="50.781395" lon="4.408508">
<ele>123.5</ele>
<time>2023-12-31T23:03:49.773Z</time>
</trkpt>
<trkpt lat="50.781399" lon="4.409114">
<ele>126.3</ele>
<time>2023-12-31T23:03:57.442Z</time>
</trkpt>
<trkpt lat="50.781367" lon="4.409428">
<ele>128.0</ele>
<time>2023-12-31T23:04:01.467Z</time>
</trkpt>
<trkpt lat="50.781286" lon="4.409607">
<ele>129.0</ele>
<time>2023-12-31T23:04:04.252Z</time>
</trkpt>
<trkpt lat="50.78116" lon="4.409789">
<ele>130.0</ele>
<time>2023-12-31T23:04:07.667Z</time>
</trkpt>
<trkpt lat="50.780804" lon="4.409993">
<ele>130.8</ele>
<time>2023-12-31T23:04:15.245Z</time>
</trkpt>
<trkpt lat="50.780389" lon="4.410334">
<ele>131.8</ele>
<time>2023-12-31T23:04:24.605Z</time>
</trkpt>
<trkpt lat="50.780232" lon="4.410563">
<ele>132.3</ele>
<time>2023-12-31T23:04:28.879Z</time>
</trkpt>
<trkpt lat="50.780094" lon="4.410827">
<ele>132.8</ele>
<time>2023-12-31T23:04:33.213Z</time>
</trkpt>
<trkpt lat="50.779723" lon="4.411582">
<ele>135.8</ele>
<time>2023-12-31T23:04:45.314Z</time>
</trkpt>
<trkpt lat="50.779591" lon="4.411791">
<ele>135.5</ele>
<time>2023-12-31T23:04:49.052Z</time>
</trkpt>
<trkpt lat="50.779125" lon="4.412435">
<ele>132.5</ele>
<time>2023-12-31T23:05:01.438Z</time>
</trkpt>
</trkseg>
<trkseg>
<trkpt lat="50.77906316558724" lon="4.412547477922485">
<ele>133.3</ele>
<time>2023-12-31T23:05:03.324Z</time>
</trkpt>
<trkpt lat="50.778676" lon="4.412979">
<ele>134.0</ele>
<time>2023-12-31T23:05:12.804Z</time>
</trkpt>
<trkpt lat="50.778194" lon="4.413466">
<ele>136.8</ele>
<time>2023-12-31T23:05:24.252Z</time>
</trkpt>
<trkpt lat="50.777427" lon="4.414302">
<ele>137.5</ele>
<time>2023-12-31T23:05:42.896Z</time>
</trkpt>
<trkpt lat="50.777165" lon="4.414736">
<ele>137.3</ele>
<time>2023-12-31T23:05:50.490Z</time>
</trkpt>
<trkpt lat="50.776927" lon="4.415201">
<ele>137.5</ele>
<time>2023-12-31T23:05:58.061Z</time>
</trkpt>
<trkpt lat="50.776778" lon="4.415613">
<ele>137.3</ele>
<time>2023-12-31T23:06:04.068Z</time>
</trkpt>
<trkpt lat="50.776553" lon="4.416425">
<ele>134.8</ele>
<time>2023-12-31T23:06:15.288Z</time>
</trkpt>
<trkpt lat="50.776326" lon="4.417304">
<ele>132.3</ele>
<time>2023-12-31T23:06:27.305Z</time>
</trkpt>
<trkpt lat="50.776129" lon="4.418383">
<ele>129.5</ele>
<time>2023-12-31T23:06:41.519Z</time>
</trkpt>
</trkseg>
</trk>
</gpx>
+276
View File
@@ -0,0 +1,276 @@
<?xml version="1.0" encoding="UTF-8"?>
<gpx xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.topografix.com/GPX/1/1"
xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd
http://www.garmin.com/xmlschemas/GpxExtensions/v3 http://www.garmin.com/xmlschemas/GpxExtensionsv3.xsd
http://www.garmin.com/xmlschemas/TrackPointExtension/v1 http://www.garmin.com/xmlschemas/TrackPointExtensionv1.xsd
http://www.topografix.com/GPX/gpx_style/0/2 http://www.topografix.com/GPX/gpx_style/0/2/gpx_style.xsd"
xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1"
xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3"
xmlns:gpx_style="http://www.topografix.com/GPX/gpx_style/0/2" version="1.1"
creator="https://gpx.studio">
<metadata>
<name>with_waypoint</name>
<author>
<name>gpx.studio</name>
<link href="https://gpx.studio"></link>
</author>
</metadata>
<wpt lat="50.7836710064975" lon="4.410764082658738">
<ele>122.0</ele>
<name>waypoint name</name>
<cmt>waypoint comment</cmt>
<desc>waypoint description</desc>
<link href="https://gpx.studio">
<text>waypoint link text</text>
</link>
<sym>Bike Trail</sym>
<type>Bike Trail</type>
</wpt>
<trk>
<name>with_waypoint</name>
<type>Cycling</type>
<trkseg>
<trkpt lat="50.790867" lon="4.404968">
<ele>109.0</ele>
</trkpt>
<trkpt lat="50.790714" lon="4.405036">
<ele>110.8</ele>
</trkpt>
<trkpt lat="50.790336" lon="4.405259">
<ele>110.3</ele>
</trkpt>
<trkpt lat="50.790165" lon="4.405331">
<ele>110.0</ele>
</trkpt>
<trkpt lat="50.790008" lon="4.405359">
<ele>110.3</ele>
</trkpt>
<trkpt lat="50.789818" lon="4.405359">
<ele>109.3</ele>
</trkpt>
<trkpt lat="50.789409" lon="4.40534">
<ele>107.0</ele>
</trkpt>
<trkpt lat="50.789105" lon="4.405411">
<ele>106.0</ele>
</trkpt>
<trkpt lat="50.788799" lon="4.405527">
<ele>108.5</ele>
</trkpt>
<trkpt lat="50.788645" lon="4.405606">
<ele>109.8</ele>
</trkpt>
<trkpt lat="50.7885" lon="4.405711">
<ele>110.8</ele>
</trkpt>
<trkpt lat="50.78822" lon="4.405959">
<ele>112.0</ele>
</trkpt>
<trkpt lat="50.787956" lon="4.406092">
<ele>112.8</ele>
</trkpt>
<trkpt lat="50.787814" lon="4.406143">
<ele>113.5</ele>
</trkpt>
<trkpt lat="50.787674" lon="4.406177">
<ele>114.3</ele>
</trkpt>
<trkpt lat="50.787451" lon="4.406199">
<ele>115.3</ele>
</trkpt>
<trkpt lat="50.787297" lon="4.406177">
<ele>114.8</ele>
</trkpt>
<trkpt lat="50.78716" lon="4.406098">
<ele>114.3</ele>
</trkpt>
<trkpt lat="50.787045" lon="4.405984">
<ele>114.3</ele>
</trkpt>
<trkpt lat="50.786683" lon="4.405653">
<ele>114.5</ele>
</trkpt>
<trkpt lat="50.786538" lon="4.405543">
<ele>115.0</ele>
</trkpt>
<trkpt lat="50.78635" lon="4.405441">
<ele>115.8</ele>
</trkpt>
<trkpt lat="50.786275" lon="4.40542">
<ele>115.8</ele>
</trkpt>
<trkpt lat="50.786182" lon="4.405435">
<ele>116.0</ele>
</trkpt>
<trkpt lat="50.786121" lon="4.405475">
<ele>115.8</ele>
</trkpt>
<trkpt lat="50.786042" lon="4.405558">
<ele>115.5</ele>
</trkpt>
<trkpt lat="50.785821" lon="4.405925">
<ele>114.5</ele>
</trkpt>
<trkpt lat="50.785672" lon="4.406119">
<ele>112.5</ele>
</trkpt>
<trkpt lat="50.785516" lon="4.406256">
<ele>110.8</ele>
</trkpt>
<trkpt lat="50.785384" lon="4.406364">
<ele>109.0</ele>
</trkpt>
<trkpt lat="50.785126" lon="4.406475">
<ele>106.3</ele>
</trkpt>
<trkpt lat="50.784697" lon="4.406537">
<ele>104.3</ele>
</trkpt>
<trkpt lat="50.784591" lon="4.40657">
<ele>104.0</ele>
</trkpt>
<trkpt lat="50.784507" lon="4.406612">
<ele>103.8</ele>
</trkpt>
<trkpt lat="50.784435" lon="4.40669">
<ele>103.3</ele>
</trkpt>
<trkpt lat="50.784209" lon="4.407148">
<ele>103.5</ele>
</trkpt>
<trkpt lat="50.784162" lon="4.407257">
<ele>103.8</ele>
</trkpt>
<trkpt lat="50.784077" lon="4.407372">
<ele>104.8</ele>
</trkpt>
<trkpt lat="50.784006" lon="4.407435">
<ele>105.8</ele>
</trkpt>
<trkpt lat="50.783924" lon="4.407471">
<ele>106.8</ele>
</trkpt>
<trkpt lat="50.783837" lon="4.407486">
<ele>107.8</ele>
</trkpt>
<trkpt lat="50.783771" lon="4.407472">
<ele>108.5</ele>
</trkpt>
<trkpt lat="50.783697" lon="4.407428">
<ele>109.3</ele>
</trkpt>
<trkpt lat="50.783626" lon="4.407363">
<ele>110.0</ele>
</trkpt>
<trkpt lat="50.783548" lon="4.407274">
<ele>110.5</ele>
</trkpt>
<trkpt lat="50.783458" lon="4.407134">
<ele>110.8</ele>
</trkpt>
<trkpt lat="50.783123" lon="4.406435">
<ele>111.8</ele>
</trkpt>
<trkpt lat="50.782982" lon="4.406168">
<ele>112.8</ele>
</trkpt>
<trkpt lat="50.782871" lon="4.406044">
<ele>113.3</ele>
</trkpt>
<trkpt lat="50.78279" lon="4.406021">
<ele>113.5</ele>
</trkpt>
<trkpt lat="50.782714" lon="4.406018">
<ele>113.5</ele>
</trkpt>
<trkpt lat="50.782607" lon="4.406047">
<ele>113.8</ele>
</trkpt>
<trkpt lat="50.782405" lon="4.406194">
<ele>114.8</ele>
</trkpt>
<trkpt lat="50.782175" lon="4.406413">
<ele>115.8</ele>
</trkpt>
<trkpt lat="50.781749" lon="4.407018">
<ele>118.5</ele>
</trkpt>
<trkpt lat="50.781654" lon="4.407316">
<ele>119.5</ele>
</trkpt>
<trkpt lat="50.781563" lon="4.407764">
<ele>121.3</ele>
</trkpt>
<trkpt lat="50.781487" lon="4.407984">
<ele>122.0</ele>
</trkpt>
<trkpt lat="50.781422" lon="4.408216">
<ele>122.8</ele>
</trkpt>
<trkpt lat="50.781395" lon="4.408508">
<ele>123.5</ele>
</trkpt>
<trkpt lat="50.781399" lon="4.409114">
<ele>126.3</ele>
</trkpt>
<trkpt lat="50.781367" lon="4.409428">
<ele>128.0</ele>
</trkpt>
<trkpt lat="50.781286" lon="4.409607">
<ele>129.0</ele>
</trkpt>
<trkpt lat="50.78116" lon="4.409789">
<ele>130.0</ele>
</trkpt>
<trkpt lat="50.780804" lon="4.409993">
<ele>130.8</ele>
</trkpt>
<trkpt lat="50.780389" lon="4.410334">
<ele>131.8</ele>
</trkpt>
<trkpt lat="50.780232" lon="4.410563">
<ele>132.3</ele>
</trkpt>
<trkpt lat="50.780094" lon="4.410827">
<ele>132.8</ele>
</trkpt>
<trkpt lat="50.779723" lon="4.411582">
<ele>135.8</ele>
</trkpt>
<trkpt lat="50.779591" lon="4.411791">
<ele>135.5</ele>
</trkpt>
<trkpt lat="50.779125" lon="4.412435">
<ele>132.5</ele>
</trkpt>
<trkpt lat="50.778676" lon="4.412979">
<ele>134.0</ele>
</trkpt>
<trkpt lat="50.778194" lon="4.413466">
<ele>136.8</ele>
</trkpt>
<trkpt lat="50.777427" lon="4.414302">
<ele>137.5</ele>
</trkpt>
<trkpt lat="50.777165" lon="4.414736">
<ele>137.3</ele>
</trkpt>
<trkpt lat="50.776927" lon="4.415201">
<ele>137.5</ele>
</trkpt>
<trkpt lat="50.776778" lon="4.415613">
<ele>137.3</ele>
</trkpt>
<trkpt lat="50.776553" lon="4.416425">
<ele>134.8</ele>
</trkpt>
<trkpt lat="50.776326" lon="4.417304">
<ele>132.3</ele>
</trkpt>
<trkpt lat="50.776129" lon="4.418383">
<ele>129.5</ele>
</trkpt>
</trkseg>
</trk>
</gpx>
+3
View File
@@ -0,0 +1,3 @@
mod parse;
pub use parse::*;
+637
View File
@@ -0,0 +1,637 @@
use std::rc::Rc;
use crate::gpx::{
Author, GPXFile, Link, LngLat, Track, TrackSegment, Trackpoint, TrackpointChunk, Waypoint,
WaypointChunk,
};
use chrono::DateTime;
use quick_xml::Error;
use quick_xml::events::Event;
use quick_xml::events::attributes::Attributes;
use quick_xml::reader::Reader;
enum GPXElement {
METADATA,
NAME,
COMMENT,
DESCRIPTION,
SOURCE,
AUTHOR(Author),
LINK(Link),
TEXT,
TRACK(Track),
SEGMENT(TrackSegment),
TRACKPOINT(Trackpoint),
WAYPOINT(Waypoint),
ELEVATION,
TIME,
TEMPERATURE,
HEARTRATE,
CADENCE,
POWER,
SYMBOL,
TYPE,
COLOR,
OPACITY,
WIDTH,
}
fn parse_coordinates(attributes: Attributes<'_>) -> LngLat {
let mut coordinates = LngLat::default();
for attr in attributes {
if let Ok(attr) = attr {
match attr.key.as_ref() {
"lat" => coordinates.lat = attr.value.parse().unwrap_or_default(),
"lon" => coordinates.lng = attr.value.parse().unwrap_or_default(),
_ => (),
}
}
}
coordinates
}
pub fn parse(data: &[u8]) -> Result<GPXFile, Error> {
let mut reader = Reader::from_reader(data);
let mut buf = vec![];
let mut gpx = GPXFile::default();
let mut stack: Vec<GPXElement> = vec![];
let mut trkpt_chunk = TrackpointChunk::default();
let mut wpt_chunk = WaypointChunk::default();
loop {
match reader.read_event_into(&mut buf) {
Ok(Event::Start(e)) => match e.name().as_ref() {
"metadata" => stack.push(GPXElement::METADATA),
"name" => stack.push(GPXElement::NAME),
"cmt" => stack.push(GPXElement::COMMENT),
"desc" => stack.push(GPXElement::DESCRIPTION),
"src" => stack.push(GPXElement::SOURCE),
"author" => stack.push(GPXElement::AUTHOR(Author::default())),
"link" => {
let mut link = Link::default();
for attr in e.attributes() {
if let Ok(attr) = attr {
if attr.key.as_ref() == "href" {
link.href = attr.value.to_string();
}
}
}
stack.push(GPXElement::LINK(link));
}
"text" => stack.push(GPXElement::TEXT),
"trk" => stack.push(GPXElement::TRACK(Track::default())),
"trkseg" => {
stack.push(GPXElement::SEGMENT(TrackSegment::default()));
}
"trkpt" => {
let mut trkpt = Trackpoint::default();
trkpt.coordinates = parse_coordinates(e.attributes());
stack.push(GPXElement::TRACKPOINT(trkpt));
}
"wpt" => {
let mut wpt = Waypoint::default();
wpt.coordinates = parse_coordinates(e.attributes());
stack.push(GPXElement::WAYPOINT(wpt));
}
"ele" => stack.push(GPXElement::ELEVATION),
"time" => stack.push(GPXElement::TIME),
e if e.ends_with("atemp") => stack.push(GPXElement::TEMPERATURE),
e if e.ends_with("hr") => stack.push(GPXElement::HEARTRATE),
e if e.ends_with("cad") => stack.push(GPXElement::CADENCE),
"power" => stack.push(GPXElement::POWER),
e if e.ends_with("PowerInWatts") => stack.push(GPXElement::POWER),
"sym" => stack.push(GPXElement::SYMBOL),
"type" => stack.push(GPXElement::TYPE),
e if e.ends_with("color") => stack.push(GPXElement::COLOR),
e if e.ends_with("opacity") => stack.push(GPXElement::OPACITY),
e if e.ends_with("width") => stack.push(GPXElement::WIDTH),
_ => (),
},
Ok(Event::End(e)) => match e.name().as_ref() {
"gpx" => {
if !wpt_chunk.wpt.is_empty() {
gpx.wpt.push(Rc::new(wpt_chunk));
wpt_chunk = WaypointChunk::default();
}
}
"metadata" => {
stack.pop();
}
"author" => {
if let Some(GPXElement::AUTHOR(author)) = stack.pop() {
gpx.info.author = Some(author);
}
}
"link" => {
if let Some(GPXElement::LINK(link)) = stack.pop() {
match stack.last_mut() {
Some(GPXElement::AUTHOR(author)) => {
author.link = Some(link);
}
Some(GPXElement::TRACK(trk)) => {
trk.info.link = Some(link);
}
Some(GPXElement::WAYPOINT(wpt)) => {
wpt.link = Some(link);
}
_ => (),
}
}
}
"trk" => {
if let Some(GPXElement::TRACK(trk)) = stack.pop() {
gpx.trk.push(trk);
}
}
"trkseg" => {
if let Some(GPXElement::SEGMENT(mut trkseg)) = stack.pop() {
if let Some(GPXElement::TRACK(trk)) = stack.last_mut() {
if !trkpt_chunk.trkpt.is_empty() {
trkseg.push(trkpt_chunk);
trkpt_chunk = TrackpointChunk::default();
}
trk.trkseg.push(trkseg);
}
}
}
"trkpt" => {
if let Some(GPXElement::TRACKPOINT(trkpt)) = stack.pop() {
if let Some(GPXElement::SEGMENT(trkseg)) = stack.last_mut() {
trkpt_chunk.trkpt.push(trkpt);
if trkpt_chunk.is_full() {
trkseg.push(trkpt_chunk);
trkpt_chunk = TrackpointChunk::default();
}
}
}
}
"wpt" => {
if let Some(GPXElement::WAYPOINT(wpt)) = stack.pop() {
wpt_chunk.wpt.push(wpt);
if wpt_chunk.is_full() {
gpx.wpt.push(Rc::new(wpt_chunk));
wpt_chunk = WaypointChunk::default();
}
}
}
_ => (),
},
Ok(Event::Text(e)) => match stack.last_mut() {
Some(GPXElement::NAME) => {
stack.pop();
match stack.last_mut() {
Some(GPXElement::METADATA) => {
gpx.info.name = e.to_string();
}
Some(GPXElement::AUTHOR(author)) => {
author.name = Some(e.to_string());
}
Some(GPXElement::TRACK(trk)) => {
trk.info.name = Some(e.to_string());
}
Some(GPXElement::WAYPOINT(wpt)) => {
wpt.name = Some(e.to_string());
}
_ => (),
}
}
Some(GPXElement::COMMENT) => {
stack.pop();
match stack.last_mut() {
Some(GPXElement::TRACK(trk)) => {
trk.info.cmt = Some(e.to_string());
}
Some(GPXElement::WAYPOINT(wpt)) => {
wpt.cmt = Some(e.to_string());
}
_ => (),
}
}
Some(GPXElement::DESCRIPTION) => {
stack.pop();
match stack.last_mut() {
Some(GPXElement::METADATA) => {
gpx.info.desc = Some(e.to_string());
}
Some(GPXElement::TRACK(trk)) => {
trk.info.desc = Some(e.to_string());
}
Some(GPXElement::WAYPOINT(wpt)) => {
wpt.desc = Some(e.to_string());
}
_ => (),
}
}
Some(GPXElement::SOURCE) => {
stack.pop();
match stack.last_mut() {
Some(GPXElement::TRACK(trk)) => {
trk.info.src = Some(e.to_string());
}
_ => (),
}
}
Some(GPXElement::TEXT) => {
stack.pop();
if let Some(GPXElement::LINK(link)) = stack.last_mut() {
link.text = Some(e.to_string());
}
}
Some(GPXElement::ELEVATION) => {
stack.pop();
match stack.last_mut() {
Some(GPXElement::TRACKPOINT(trkpt)) => {
trkpt.ele = e.parse().unwrap_or_default();
}
Some(GPXElement::WAYPOINT(wpt)) => {
wpt.ele = e.parse().unwrap_or_default();
}
_ => (),
}
}
Some(GPXElement::TIME) => {
stack.pop();
if let Some(GPXElement::TRACKPOINT(trkpt)) = stack.last_mut() {
if let Ok(time) = DateTime::parse_from_rfc3339(e.as_ref()) {
trkpt.time = Some(time.timestamp_millis());
}
}
}
Some(GPXElement::TEMPERATURE) => {
stack.pop();
if let Some(GPXElement::TRACKPOINT(trkpt)) = stack.last_mut() {
trkpt.atemp = e.parse().ok();
}
}
Some(GPXElement::HEARTRATE) => {
stack.pop();
if let Some(GPXElement::TRACKPOINT(trkpt)) = stack.last_mut() {
trkpt.hr = e.parse().ok();
}
}
Some(GPXElement::CADENCE) => {
stack.pop();
if let Some(GPXElement::TRACKPOINT(trkpt)) = stack.last_mut() {
trkpt.cad = e.parse().ok();
}
}
Some(GPXElement::POWER) => {
stack.pop();
if let Some(GPXElement::TRACKPOINT(trkpt)) = stack.last_mut() {
trkpt.power = e.parse().ok();
}
}
Some(GPXElement::SYMBOL) => {
stack.pop();
match stack.last_mut() {
Some(GPXElement::WAYPOINT(wpt)) => {
wpt.sym = Some(e.to_string());
}
_ => (),
}
}
Some(GPXElement::TYPE) => {
stack.pop();
match stack.last_mut() {
Some(GPXElement::TRACK(trk)) => {
trk.info.type_ = Some(e.to_string());
}
Some(GPXElement::WAYPOINT(wpt)) => {
wpt.type_ = Some(e.to_string());
}
_ => (),
}
}
Some(GPXElement::COLOR) => {
stack.pop();
match stack.last_mut() {
Some(GPXElement::TRACK(trk)) => {
let mut color = "#".to_string();
color.push_str(&e);
trk.info.color = Some(color);
}
_ => (),
}
}
Some(GPXElement::OPACITY) => {
stack.pop();
match stack.last_mut() {
Some(GPXElement::TRACK(trk)) => {
trk.info.opacity = e.parse().ok();
}
_ => (),
}
}
Some(GPXElement::WIDTH) => {
stack.pop();
match stack.last_mut() {
Some(GPXElement::TRACK(trk)) => {
trk.info.width = e.parse().ok();
}
_ => (),
}
}
_ => (),
},
Ok(Event::Eof) => break,
Err(e) => return Err(e),
_ => (),
}
buf.clear();
}
Ok(gpx)
}
#[cfg(test)]
mod tests {
use std::{fs::File, io::Read};
use super::*;
#[test]
fn test_parse_simple() {
let mut f = File::open("data/simple.gpx").unwrap();
let mut data = String::new();
let _ = f.read_to_string(&mut data);
let gpx = parse(data.as_bytes()).unwrap();
assert_eq!(gpx.info.name, "simple");
assert!(gpx.info.desc.is_some_and(|d| d == "description"));
assert!(gpx.info.author.is_some());
let author = gpx.info.author.as_ref().unwrap();
assert!(author.name.as_ref().is_some_and(|n| n == "gpx.studio"));
assert!(author.link.is_some());
let link = author.link.as_ref().unwrap();
assert_eq!(link.href, "https://gpx.studio");
assert!(link.text.as_ref().is_some_and(|t| t == "link text"));
assert_eq!(gpx.trk.len(), 1);
let trk = &gpx.trk[0];
assert!(trk.info.name.as_ref().is_some_and(|n| n == "track name"));
assert!(trk.info.cmt.as_ref().is_some_and(|c| c == "track comment"));
assert!(
trk.info
.desc
.as_ref()
.is_some_and(|d| d == "track description")
);
assert!(trk.info.src.as_ref().is_some_and(|s| s == "track source"));
assert!(trk.info.link.is_some());
let link = trk.info.link.as_ref().unwrap();
assert_eq!(link.href, "https://gpx.studio");
assert!(link.text.as_ref().is_some_and(|t| t == "track link text"));
assert!(trk.info.type_.as_ref().is_some_and(|c| c == "Cycling"));
assert_eq!(trk.trkseg.len(), 1);
let trkseg = &trk.trkseg[0];
assert_eq!(trkseg.len(), 80);
let trkpt = &trkseg[0];
assert_eq!(trkpt.coordinates.lat, 50.790867);
assert_eq!(trkpt.coordinates.lng, 4.404968);
assert_eq!(trkpt.ele, 109.0);
}
#[test]
fn test_parse_tracks() {
let mut f = File::open("data/with_tracks.gpx").unwrap();
let mut data = String::new();
let _ = f.read_to_string(&mut data);
let gpx = parse(data.as_bytes()).unwrap();
assert_eq!(gpx.trk.len(), 2);
let trk = &gpx.trk[0];
assert!(trk.info.name.as_ref().is_some_and(|n| n == "track 1"));
assert!(trk.info.type_.as_ref().is_some_and(|c| c == "Cycling"));
assert_eq!(trk.trkseg.len(), 1);
let trkseg = &trk.trkseg[0];
assert_eq!(trkseg.len(), 49);
let trkpt = &trkseg[0];
assert_eq!(trkpt.coordinates.lat, 50.790867);
assert_eq!(trkpt.coordinates.lng, 4.404968);
assert_eq!(trkpt.ele, 109.0);
let trk = &gpx.trk[1];
assert!(trk.info.name.as_ref().is_some_and(|n| n == "track 2"));
assert!(trk.info.type_.as_ref().is_some_and(|c| c == "Cycling"));
assert_eq!(trk.trkseg.len(), 1);
let trkseg = &trk.trkseg[0];
assert_eq!(trkseg.len(), 28);
let trkpt = &trkseg[0];
assert_eq!(trkpt.coordinates.lat, 50.782212);
assert_eq!(trkpt.coordinates.lng, 4.406377);
assert_eq!(trkpt.ele, 115.5);
}
#[test]
fn test_parse_segments() {
let mut f = File::open("data/with_segments.gpx").unwrap();
let mut data = String::new();
let _ = f.read_to_string(&mut data);
let gpx = parse(data.as_bytes()).unwrap();
assert_eq!(gpx.trk.len(), 1);
let trk = &gpx.trk[0];
assert_eq!(trk.trkseg.len(), 2);
let trkseg = &trk.trkseg[0];
assert_eq!(trkseg.len(), 49);
let trkpt = &trkseg[0];
assert_eq!(trkpt.coordinates.lat, 50.790867);
assert_eq!(trkpt.coordinates.lng, 4.404968);
assert_eq!(trkpt.ele, 109.0);
let trkseg = &trk.trkseg[1];
assert_eq!(trkseg.len(), 28);
let trkpt = &trkseg[0];
assert_eq!(trkpt.coordinates.lat, 50.782212);
assert_eq!(trkpt.coordinates.lng, 4.406377);
assert_eq!(trkpt.ele, 115.5);
}
#[test]
fn test_parse_tracks_and_segments() {
let mut f = File::open("data/with_tracks_and_segments.gpx").unwrap();
let mut data = String::new();
let _ = f.read_to_string(&mut data);
let gpx = parse(data.as_bytes()).unwrap();
assert_eq!(gpx.trk.len(), 2);
let trk = &gpx.trk[0];
assert_eq!(trk.trkseg.len(), 2);
let trkseg = &trk.trkseg[0];
assert_eq!(trkseg.len(), 16);
let trkpt = &trkseg[0];
assert_eq!(trkpt.coordinates.lat, 50.790867);
assert_eq!(trkpt.coordinates.lng, 4.404968);
assert_eq!(trkpt.ele, 109.0);
let trkseg = &trk.trkseg[1];
assert_eq!(trkseg.len(), 34);
let trkpt = &trkseg[0];
assert_eq!(trkpt.coordinates.lat, 50.78727108169855);
assert_eq!(trkpt.coordinates.lng, 4.406133681127736);
assert_eq!(trkpt.ele, 115.0);
let trk = &gpx.trk[1];
assert_eq!(trk.trkseg.len(), 2);
let trkseg = &trk.trkseg[0];
assert_eq!(trkseg.len(), 19);
let trkpt = &trkseg[0];
assert_eq!(trkpt.coordinates.lat, 50.782212);
assert_eq!(trkpt.coordinates.lng, 4.406377);
assert_eq!(trkpt.ele, 115.5);
let trkseg = &trk.trkseg[1];
assert_eq!(trkseg.len(), 10);
let trkpt = &trkseg[0];
assert_eq!(trkpt.coordinates.lat, 50.77906316558724);
assert_eq!(trkpt.coordinates.lng, 4.412547477922485);
assert_eq!(trkpt.ele, 133.3);
}
#[test]
fn test_parse_waypoint() {
let mut f = File::open("data/with_waypoint.gpx").unwrap();
let mut data = String::new();
let _ = f.read_to_string(&mut data);
let gpx = parse(data.as_bytes()).unwrap();
assert_eq!(gpx.wpt.len(), 1);
let chunk = &gpx.wpt[0];
assert_eq!(chunk.wpt.len(), 1);
let wpt = &chunk.wpt[0];
assert_eq!(wpt.coordinates.lat, 50.7836710064975);
assert_eq!(wpt.coordinates.lng, 4.410764082658738);
assert!(wpt.name.as_ref().is_some_and(|n| n == "waypoint name"));
assert!(wpt.cmt.as_ref().is_some_and(|c| c == "waypoint comment"));
assert!(
wpt.desc
.as_ref()
.is_some_and(|d| d == "waypoint description")
);
assert!(wpt.link.is_some());
let link = wpt.link.as_ref().unwrap();
assert_eq!(link.href, "https://gpx.studio");
assert!(
link.text
.as_ref()
.is_some_and(|t| t == "waypoint link text")
);
assert!(wpt.sym.as_ref().is_some_and(|s| s == "Bike Trail"));
assert!(wpt.type_.as_ref().is_some_and(|t| t == "Bike Trail"));
}
#[test]
fn test_parse_trackpoint_time() {
let mut f = File::open("data/with_time.gpx").unwrap();
let mut data = String::new();
let _ = f.read_to_string(&mut data);
let gpx = parse(data.as_bytes()).unwrap();
assert_eq!(gpx.trk.len(), 1);
let trk = &gpx.trk[0];
assert_eq!(trk.trkseg.len(), 1);
let trkseg = &trk.trkseg[0];
assert!(trkseg.len() > 0);
let trkpt = &trkseg[0];
assert!(trkpt.time.is_some_and(|t| t == 1704063600000));
}
#[test]
fn test_parse_trackpoint_hr() {
let mut f = File::open("data/with_hr.gpx").unwrap();
let mut data = String::new();
let _ = f.read_to_string(&mut data);
let gpx = parse(data.as_bytes()).unwrap();
assert_eq!(gpx.trk.len(), 1);
let trk = &gpx.trk[0];
assert_eq!(trk.trkseg.len(), 1);
let trkseg = &trk.trkseg[0];
assert!(trkseg.len() > 0);
let trkpt = &trkseg[0];
assert!(trkpt.hr.is_some_and(|h| h == 150));
}
#[test]
fn test_parse_trackpoint_cad() {
let mut f = File::open("data/with_cad.gpx").unwrap();
let mut data = String::new();
let _ = f.read_to_string(&mut data);
let gpx = parse(data.as_bytes()).unwrap();
assert_eq!(gpx.trk.len(), 1);
let trk = &gpx.trk[0];
assert_eq!(trk.trkseg.len(), 1);
let trkseg = &trk.trkseg[0];
assert!(trkseg.len() > 0);
let trkpt = &trkseg[0];
assert!(trkpt.cad.is_some_and(|c| c == 80));
}
#[test]
fn test_parse_trackpoint_power_1() {
let mut f = File::open("data/with_power_1.gpx").unwrap();
let mut data = String::new();
let _ = f.read_to_string(&mut data);
let gpx = parse(data.as_bytes()).unwrap();
assert_eq!(gpx.trk.len(), 1);
let trk = &gpx.trk[0];
assert_eq!(trk.trkseg.len(), 1);
let trkseg = &trk.trkseg[0];
assert!(trkseg.len() > 0);
let trkpt = &trkseg[0];
assert!(trkpt.power.is_some_and(|p| p == 200));
}
#[test]
fn test_parse_trackpoint_power_2() {
let mut f = File::open("data/with_power_2.gpx").unwrap();
let mut data = String::new();
let _ = f.read_to_string(&mut data);
let gpx = parse(data.as_bytes()).unwrap();
assert_eq!(gpx.trk.len(), 1);
let trk = &gpx.trk[0];
assert_eq!(trk.trkseg.len(), 1);
let trkseg = &trk.trkseg[0];
assert!(trkseg.len() > 0);
let trkpt = &trkseg[0];
assert!(trkpt.power.is_some_and(|p| p == 200));
}
#[test]
fn test_parse_trackpoint_atemp() {
let mut f = File::open("data/with_temp.gpx").unwrap();
let mut data = String::new();
let _ = f.read_to_string(&mut data);
let gpx = parse(data.as_bytes()).unwrap();
assert_eq!(gpx.trk.len(), 1);
let trk = &gpx.trk[0];
assert_eq!(trk.trkseg.len(), 1);
let trkseg = &trk.trkseg[0];
assert!(trkseg.len() > 0);
let trkpt = &trkseg[0];
assert!(trkpt.atemp.is_some_and(|t| t == 21));
}
#[test]
fn test_parse_track_style() {
let mut f = File::open("data/with_style.gpx").unwrap();
let mut data = String::new();
let _ = f.read_to_string(&mut data);
let gpx = parse(data.as_bytes()).unwrap();
assert_eq!(gpx.trk.len(), 1);
let trk = &gpx.trk[0];
assert_eq!(trk.trkseg.len(), 1);
assert!(trk.info.color.as_ref().is_some_and(|c| c == "#2d3ee9"));
assert!(trk.info.opacity.is_some_and(|o| o == 0.5));
assert!(trk.info.width.is_some_and(|w| w == 6.0));
}
}
+5
View File
@@ -0,0 +1,5 @@
mod simplify;
mod smooth;
pub use simplify::*;
pub use smooth::*;
+65
View File
@@ -0,0 +1,65 @@
use crate::gpx::{TrackSegment, TrackSegmentIndex};
pub fn ramer_douglas_peucker<F, T>(
trkseg: &TrackSegment,
distance: &F,
epsilon: T,
) -> Vec<TrackSegmentIndex>
where
F: Fn(TrackSegmentIndex, TrackSegmentIndex, TrackSegmentIndex) -> T,
T: Default + PartialOrd + Copy,
{
match trkseg.len() {
0 => vec![],
1 => vec![trkseg.first_index().unwrap()],
2 => vec![trkseg.last_index().unwrap()],
_ => {
let first = trkseg.first_index().unwrap();
let last = trkseg.last_index().unwrap();
let mut indices = vec![first];
ramer_douglas_peucker_helper(trkseg, first, last, distance, epsilon, &mut indices);
indices.push(last);
indices
}
}
}
fn ramer_douglas_peucker_helper<F, T>(
trkseg: &TrackSegment,
start: TrackSegmentIndex,
end: TrackSegmentIndex,
distance: &F,
epsilon: T,
indices: &mut Vec<TrackSegmentIndex>,
) where
F: Fn(TrackSegmentIndex, TrackSegmentIndex, TrackSegmentIndex) -> T,
T: Default + PartialOrd + Copy,
{
let mut max_idx = None;
let mut max_dist = T::default();
let mut cur = trkseg.next_index(Some(start));
while let Some(idx) = cur {
if idx == end {
break;
}
let dist = distance(start, end, idx);
if dist > max_dist {
max_idx = Some(idx);
max_dist = dist;
}
cur = trkseg.next_index(cur);
}
if let Some(idx) = max_idx {
if max_dist > epsilon {
ramer_douglas_peucker_helper(trkseg, start, idx, distance, epsilon, indices);
indices.push(idx);
ramer_douglas_peucker_helper(trkseg, idx, end, distance, epsilon, indices);
}
}
}
+50
View File
@@ -0,0 +1,50 @@
#[macro_export]
macro_rules! for_each_window {
(
$trkseg:expr,
$left:expr,
$right:expr,
$window:expr,
|$a:ident, $b:ident| $distance:expr,
|$i:ident, $l:ident, $r:ident| $body:block,
) => {{
let mut start = $left.unwrap_or_default();
let mut cur = $left;
while let Some(idx) = cur {
// advance left if needed
while start != idx && {
let $a = start;
let $b = idx;
$distance
} > $window {
if let Some(next) = $trkseg.next_index(Some(start)) {
if next == idx {
break;
}
start = next;
} else {
break;
}
}
// advance right if needed
let mut end = idx;
while let Some(next) = $trkseg.next_index(Some(end)) {
if {
let $a = idx;
let $b = end;
$distance
} > $window {
break;
}
end = next;
}
// apply body
let $i = idx;
let $l = start;
let $r = end;
$body
// go next
cur = $trkseg.next_index(cur);
}
}};
}
+61
View File
@@ -0,0 +1,61 @@
use std::rc::Rc;
use uuid::Uuid;
use wasm_bindgen::prelude::*;
use crate::{
actions::parse,
gpx::GPXFile,
stack::{Stack, StackEntry},
};
// Control flow
// - get lock to cancel any other updates
// - update files
// - update separate statistics
// - update selection
// - update selection statistics
// - notify UI (changed files, updated selection, new stats)
#[wasm_bindgen]
pub struct Controller {
stack: Stack,
}
#[wasm_bindgen]
impl Controller {
#[wasm_bindgen(constructor)]
pub fn new() -> Self {
Self {
stack: Stack::default(),
}
}
#[wasm_bindgen]
pub fn create_file(&mut self, name: &str) {
let mut file = GPXFile::default();
file.info.name = name.to_string();
self.stack.create_and_push_next(|entry: &mut StackEntry| {
entry.insert(file.id, Rc::new(file));
true
});
}
#[wasm_bindgen]
pub fn load_file(&mut self, data: &[u8]) {
if let Ok(file) = parse(data) {
self.stack.create_and_push_next(|entry: &mut StackEntry| {
entry.insert(file.id, Rc::new(file));
true
});
}
}
#[wasm_bindgen]
pub fn delete_file(&mut self, id: &[u8]) {
if let Ok(id) = Uuid::from_slice(id) {
self.stack
.create_and_push_next(|entry: &mut StackEntry| entry.remove(&id).is_some());
}
}
}
+27
View File
@@ -0,0 +1,27 @@
use crate::gpx::{Trackpoint, Waypoint};
static MAX_TRKPT_CHUNK_SIZE: usize = 4096;
#[derive(Debug, Default)]
pub struct TrackpointChunk {
pub trkpt: Vec<Trackpoint>,
}
impl TrackpointChunk {
pub fn is_full(&self) -> bool {
self.trkpt.len() == MAX_TRKPT_CHUNK_SIZE
}
}
static MAX_WPT_CHUNK_SIZE: usize = 128;
#[derive(Debug, Default)]
pub struct WaypointChunk {
pub wpt: Vec<Waypoint>,
}
impl WaypointChunk {
pub fn is_full(&self) -> bool {
self.wpt.len() == MAX_WPT_CHUNK_SIZE
}
}
+17
View File
@@ -0,0 +1,17 @@
#[derive(Debug, Default)]
pub struct Link {
pub href: String,
pub text: Option<String>,
}
#[derive(Debug, Default, Clone, Copy)]
pub struct LngLat {
pub lng: f64,
pub lat: f64,
}
#[derive(Debug, Default)]
pub struct LngLatBounds {
pub sw: LngLat,
pub ne: LngLat,
}
+41
View File
@@ -0,0 +1,41 @@
use std::rc::Rc;
use uuid::Uuid;
use crate::gpx::{Link, Track, WaypointChunk};
#[derive(Debug)]
pub struct GPXFile {
pub id: Uuid,
pub info: GPXFileInfo,
pub trk: Vec<Track>,
pub wpt: Vec<Rc<WaypointChunk>>,
// TODO routes
}
impl Default for GPXFile {
fn default() -> Self {
Self {
id: Uuid::new_v4(),
info: Default::default(),
trk: Default::default(),
wpt: Default::default(),
}
}
}
#[derive(Debug, Default)]
pub struct GPXFileInfo {
pub name: String,
pub desc: Option<String>,
pub author: Option<Author>,
pub link: Option<Link>,
pub time: Option<i64>,
}
#[derive(Debug, Default)]
pub struct Author {
pub name: Option<String>,
pub email: Option<String>,
pub link: Option<Link>,
}
+15
View File
@@ -0,0 +1,15 @@
mod chunk;
mod common;
mod file;
mod segment;
mod track;
mod trackpoint;
mod waypoint;
pub use chunk::*;
pub use common::*;
pub use file::*;
pub use segment::*;
pub use track::*;
pub use trackpoint::*;
pub use waypoint::*;
+253
View File
@@ -0,0 +1,253 @@
use std::{ops::Index, rc::Rc};
use crate::gpx::{Trackpoint, TrackpointChunk};
#[derive(Debug, Default)]
pub struct TrackSegment {
chunks: Vec<Rc<TrackpointChunk>>,
cumul_length: Vec<usize>,
}
impl TrackSegment {
pub fn push(&mut self, chunk: TrackpointChunk) {
if chunk.trkpt.is_empty() {
return;
}
self.cumul_length
.push(self.cumul_length.last().copied().unwrap_or_default() + chunk.trkpt.len());
self.chunks.push(Rc::new(chunk));
}
pub fn len(&self) -> usize {
self.cumul_length.last().copied().unwrap_or_default()
}
pub fn iter(&self) -> TrackSegmentIterator<'_> {
TrackSegmentIterator::new(self)
}
pub fn first_index(&self) -> Option<TrackSegmentIndex> {
self.next_index(None)
}
pub fn last_index(&self) -> Option<TrackSegmentIndex> {
self.prev_index(None)
}
pub fn next_index(&self, cur: Option<TrackSegmentIndex>) -> Option<TrackSegmentIndex> {
let mut next = cur.map_or_default(|idx| TrackSegmentIndex {
chunk: idx.chunk,
pos: idx.pos + 1,
});
loop {
if next.chunk >= self.chunks.len() {
return None;
}
if next.pos == self.chunks[next.chunk].trkpt.len() {
next.chunk += 1;
next.pos = 0;
} else {
return Some(next);
}
}
}
pub fn prev_index(&self, cur: Option<TrackSegmentIndex>) -> Option<TrackSegmentIndex> {
let mut prev = cur.unwrap_or(TrackSegmentIndex {
chunk: self.chunks.len(),
pos: 0,
});
loop {
if prev.pos == 0 {
if prev.chunk == 0 {
return None;
}
prev.chunk -= 1;
prev.pos = self.chunks[prev.chunk].trkpt.len();
} else {
prev.pos -= 1;
return Some(prev);
}
}
}
pub fn to_flat_index(&self, idx: TrackSegmentIndex) -> usize {
idx.pos
+ if idx.chunk > 0 {
self.cumul_length[idx.chunk - 1]
} else {
0
}
}
fn locate(&self, idx: usize) -> Option<TrackSegmentIndex> {
let chunk = self.cumul_length.partition_point(|l| idx >= *l);
if chunk >= self.chunks.len() {
return None;
}
let pos = if chunk > 0 {
idx - self.cumul_length[chunk - 1]
} else {
idx
};
if pos >= self.chunks[chunk].trkpt.len() {
None
} else {
Some(TrackSegmentIndex { chunk, pos })
}
}
}
impl Index<TrackSegmentIndex> for TrackSegment {
type Output = Trackpoint;
fn index(&self, idx: TrackSegmentIndex) -> &Self::Output {
&self.chunks[idx.chunk].trkpt[idx.pos]
}
}
impl Index<usize> for TrackSegment {
type Output = Trackpoint;
fn index(&self, idx: usize) -> &Self::Output {
&self[self.locate(idx).unwrap()]
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, PartialOrd, Ord)]
pub struct TrackSegmentIndex {
pub chunk: usize,
pub pos: usize,
}
#[derive(Debug, Clone)]
pub struct TrackSegmentIterator<'a> {
trkseg: &'a TrackSegment,
idx: Option<TrackSegmentIndex>,
}
impl<'a> TrackSegmentIterator<'a> {
pub fn new(trkseg: &'a TrackSegment) -> Self {
Self {
trkseg,
idx: Default::default(),
}
}
}
impl<'a> Iterator for TrackSegmentIterator<'a> {
type Item = &'a Trackpoint;
fn next(&mut self) -> Option<Self::Item> {
self.idx = self.trkseg.next_index(self.idx);
self.idx.map(|idx| &self.trkseg[idx])
}
fn nth(&mut self, n: usize) -> Option<Self::Item> {
let idx = self
.idx
.map_or_default(|idx| self.trkseg.to_flat_index(idx))
+ n;
self.idx = self.trkseg.locate(idx);
self.idx.map(|idx| &self.trkseg[idx])
}
}
#[cfg(test)]
mod tests {
use crate::gpx::TrackpointChunk;
use super::*;
fn create_track_segment(nb_chunks: usize) -> TrackSegment {
let mut trkseg = TrackSegment::default();
let mut count = 0;
for n in 1..=nb_chunks {
let mut chunk = TrackpointChunk::default();
for _ in 0..n {
let mut trkpt = Trackpoint::default();
trkpt.ele = count as f64;
chunk.trkpt.push(trkpt);
count += 1;
}
trkseg.push(chunk);
}
trkseg
}
#[test]
fn test_len() {
let nb_chunks = 10;
let trkseg = create_track_segment(nb_chunks);
assert_eq!(trkseg.len(), nb_chunks * (nb_chunks + 1) / 2);
}
#[test]
fn test_locate() {
let nb_chunks = 10;
let trkseg = create_track_segment(nb_chunks);
for i in 0..trkseg.len() {
let idx = trkseg.locate(i);
assert!(idx.is_some());
assert_eq!(trkseg[idx.unwrap()].ele, i as f64);
}
assert!(trkseg.locate(trkseg.len()).is_none());
}
#[test]
fn test_index() {
let nb_chunks = 10;
let trkseg = create_track_segment(nb_chunks);
for i in 0..trkseg.len() {
assert_eq!(trkseg[i].ele, i as f64);
}
}
#[test]
#[should_panic]
fn test_index_out_of_bounds_1() {
let nb_chunks = 10;
let trkseg = create_track_segment(nb_chunks);
trkseg[trkseg.len()].ele;
}
#[test]
#[should_panic]
fn test_index_out_of_bounds_2() {
let nb_chunks = 10;
let trkseg = create_track_segment(nb_chunks);
trkseg[TrackSegmentIndex {
chunk: trkseg.chunks.len(),
pos: 0,
}]
.ele;
}
#[test]
fn test_iter() {
let nb_chunks = 10;
let trkseg = create_track_segment(nb_chunks);
for (i, trkpt) in trkseg.iter().enumerate() {
assert!(std::ptr::eq(&trkseg[i], trkpt));
}
}
#[test]
fn test_iter_nth() {
let nb_chunks = 10;
let trkseg = create_track_segment(nb_chunks);
let tenth = trkseg.iter().nth(10);
assert!(tenth.is_some());
assert!(std::ptr::eq(&trkseg[10], tenth.unwrap()));
}
#[test]
fn test_iter_skip() {
let nb_chunks = 10;
let trkseg = create_track_segment(nb_chunks);
for (i, trkpt) in trkseg.iter().enumerate().skip(5) {
assert!(std::ptr::eq(&trkseg[i], trkpt));
}
}
}
+20
View File
@@ -0,0 +1,20 @@
use crate::gpx::{Link, TrackSegment};
#[derive(Debug, Default)]
pub struct Track {
pub info: TrackInfo,
pub trkseg: Vec<TrackSegment>,
}
#[derive(Debug, Default)]
pub struct TrackInfo {
pub name: Option<String>,
pub cmt: Option<String>,
pub desc: Option<String>,
pub src: Option<String>,
pub link: Option<Link>,
pub type_: Option<String>,
pub color: Option<String>,
pub opacity: Option<f64>,
pub width: Option<f64>,
}
+19
View File
@@ -0,0 +1,19 @@
use crate::gpx::LngLat;
#[derive(Debug, Default)]
pub struct Trackpoint {
pub coordinates: LngLat,
pub ele: f64,
pub time: Option<i64>,
pub atemp: Option<i16>,
pub hr: Option<u16>,
pub cad: Option<u16>,
pub power: Option<u16>,
// TODO OSM data? or store intervals at a higher level?
}
impl Trackpoint {
pub fn time_diff(&self, other: &Trackpoint) -> Option<i64> {
self.time.zip(other.time).map(|(t1, t2)| t1 - t2)
}
}
+14
View File
@@ -0,0 +1,14 @@
use crate::gpx::{Link, LngLat};
#[derive(Debug, Default)]
pub struct Waypoint {
pub coordinates: LngLat,
pub ele: f64,
pub time: Option<i64>,
pub name: Option<String>,
pub cmt: Option<String>,
pub desc: Option<String>,
pub link: Option<Link>,
pub sym: Option<String>,
pub type_: Option<String>,
}
+7
View File
@@ -0,0 +1,7 @@
mod actions;
mod controller;
mod gpx;
mod stack;
mod statistics;
mod utils;
mod algorithms;
+100
View File
@@ -0,0 +1,100 @@
use std::{collections::HashMap, rc::Rc};
use uuid::Uuid;
use crate::gpx::GPXFile;
#[derive(Default)]
pub struct Stack {
entries: Vec<StackEntry>,
index: Option<usize>,
}
impl Stack {
pub fn current(&self) -> Option<&StackEntry> {
match self.index {
Some(i) => Some(&self.entries[i]),
None => None,
}
}
pub fn create_and_push_next<F>(&mut self, f: F)
where
F: FnOnce(&mut StackEntry) -> bool,
{
let mut next = self.current().map_or_default(|c| c.clone());
if f(&mut next) {
self.push(next);
}
}
// pub fn update(&mut self, files: &[Rc<GPXFile>], ids: &[GPXFileId]) {
// let mut next = match self.current() {
// Some(current) => current.clone(),
// None => StackEntry::default(),
// };
// for (file, id) in files.iter().zip(ids) {
// next.insert(*id, file.clone());
// }
// self.push(next);
// }
// pub fn delete(&mut self, files: &[GPXFileId]) {
// if let Some(current) = self.current() {
// let mut next = current.clone();
// for file in files {
// next.remove(file);
// }
// self.push(next);
// }
// }
pub fn can_undo(&self) -> bool {
self.index.is_some()
}
pub fn can_redo(&self) -> bool {
match self.index {
Some(i) => i + 1 < self.entries.len(),
None => !self.entries.is_empty(),
}
}
pub fn undo(&mut self) {
if let Some(i) = self.index {
if i == 0 {
self.index = None;
} else {
self.index = Some(i - 1);
}
}
}
pub fn redo(&mut self) {
match self.index {
Some(i) => {
if i + 1 < self.entries.len() {
self.index = Some(i + 1);
}
}
None => {
if !self.entries.is_empty() {
self.index = Some(0);
}
}
}
}
fn push(&mut self, entry: StackEntry) {
if let Some(i) = self.index {
if i + 1 < self.entries.len() {
self.entries.truncate(i + 1);
}
}
self.entries.push(entry);
self.index = Some(self.entries.len() - 1);
}
}
pub type StackEntry = HashMap<Uuid, Rc<GPXFile>>;
+292
View File
@@ -0,0 +1,292 @@
use crate::{
algorithms::ramer_douglas_peucker,
for_each_window,
gpx::{LngLat, LngLatBounds, TrackSegment, Trackpoint},
utils::{distance, slope, speed},
};
#[derive(Default, Debug)]
pub struct GPXStatistics {
pub total_distance: f64,
pub moving_distance: Option<f64>,
pub moving_time: Option<i64>,
pub elevation_gain: f64,
pub elevation_loss: f64,
pub start_time: Option<i64>,
pub end_time: Option<i64>,
pub bounds: LngLatBounds,
pub local: Vec<TrackpointStatistics>,
}
impl GPXStatistics {
pub fn total_time(&self) -> Option<i64> {
self.start_time.zip(self.end_time).map(|(t1, t2)| t2 - t1)
}
pub fn total_speed(&self) -> Option<f64> {
self.total_time().map(|t| speed(self.total_distance, t))
}
pub fn moving_speed(&self) -> Option<f64> {
self.moving_distance
.zip(self.moving_time)
.map(|(d, t)| speed(d, t))
}
pub fn compute(trkseg: &TrackSegment) -> Self {
let mut stats = Self::default();
if trkseg.len() == 0 {
return stats;
}
let mut prev = &trkseg[0];
for cur in trkseg.iter() {
stats.accumulate(prev, cur);
prev = cur;
}
stats.compute_smoothed_speed(trkseg);
stats.compute_smoothed_elevation_gain(trkseg);
stats
}
fn accumulate(&mut self, prev: &Trackpoint, cur: &Trackpoint) {
self.accumulate_distance_and_time(prev, cur);
self.update_time_bounds(cur.time);
self.update_bounds(&cur.coordinates);
self.local
.push(TrackpointStatistics::from_partial_stats(&self));
}
fn accumulate_distance_and_time(&mut self, prev: &Trackpoint, cur: &Trackpoint) {
let dist = distance(prev.coordinates, cur.coordinates);
let time = cur.time_diff(prev);
self.total_distance += dist;
if let Some(time) = time {
let speed = speed(dist, time);
if speed >= 0.5 && speed <= 1500.0 {
self.moving_distance = self.moving_distance.map_or(Some(dist), |d| Some(d + dist));
self.moving_time = self.moving_time.map_or(Some(time), |t| Some(t + time));
}
}
}
fn update_time_bounds(&mut self, time: Option<i64>) {
if let Some(time) = time {
if self.start_time.is_none() {
self.start_time = Some(time);
}
self.end_time = Some(time);
}
}
fn update_bounds(&mut self, coordinates: &LngLat) {
self.bounds.sw.lng = self.bounds.sw.lng.min(coordinates.lng);
self.bounds.sw.lat = self.bounds.sw.lat.min(coordinates.lat);
self.bounds.ne.lng = self.bounds.ne.lng.min(coordinates.lng);
self.bounds.ne.lat = self.bounds.ne.lat.min(coordinates.lat);
}
fn compute_smoothed_speed(&mut self, trkseg: &TrackSegment) {
for_each_window!(
trkseg,
trkseg.first_index(),
trkseg.last_index(),
Some(10000),
|i, j| trkseg[i].time_diff(&trkseg[j]),
|i, left, right| {
let i = trkseg.to_flat_index(i);
let left = trkseg.to_flat_index(left);
let right = trkseg.to_flat_index(right);
self.local[i].speed = trkseg[right].time_diff(&trkseg[left]).map(|t| {
speed(
self.local[right].total_distance - self.local[left].total_distance,
t,
)
});
},
);
}
fn compute_smoothed_elevation_gain(&mut self, trkseg: &TrackSegment) {
let simplified = ramer_douglas_peucker(
trkseg,
&|i, j, k| {
let x1 = self.local[trkseg.to_flat_index(i)].total_distance * 1000.0;
let x2 = self.local[trkseg.to_flat_index(j)].total_distance * 1000.0;
let x3 = self.local[trkseg.to_flat_index(k)].total_distance * 1000.0;
let y1 = trkseg[i].ele;
let y2 = trkseg[j].ele;
let y3 = trkseg[k].ele;
let dist = ((y2 - y1).powi(2) + (x2 - x1).powi(2)).sqrt();
if dist == 0.0 {
((x3 - x1).powi(2) + (y3 - y1).powi(2)).sqrt()
} else {
((y2 - y1) * x3 - (x2 - x1) * y3 + x2 * y1 - y2 * x1).abs() / dist
}
},
20.0,
);
for i in 0..(simplified.len() - 1) {
let start = simplified[i];
let end = simplified[i + 1];
let last = i + 1 == simplified.len() - 1;
let mut cumul_ele = 0.0;
let mut current_left = start;
let mut current_right = Some(start);
let mut prev_smoothed_ele = trkseg[start].ele;
for_each_window!(
trkseg,
Some(start),
Some(end),
0.1,
|i, j| {
let i = trkseg.to_flat_index(i);
let j = trkseg.to_flat_index(j);
self.local[j].total_distance - self.local[i].total_distance
},
|i, left, right| {
while current_left != left {
cumul_ele -= trkseg[current_left].ele;
current_left = trkseg.next_index(Some(current_left)).unwrap();
}
while let Some(current) = current_right {
if current > right {
break;
}
cumul_ele += trkseg[current].ele;
current_right = trkseg.next_index(current_right);
}
let flat_i = trkseg.to_flat_index(i);
let flat_left = trkseg.to_flat_index(left);
let flat_right = trkseg.to_flat_index(right);
let smoothed_ele: f64 = if i == start || i == end {
trkseg[i].ele
} else {
cumul_ele / (flat_right - flat_left + 1) as f64
};
let delta = smoothed_ele - prev_smoothed_ele;
if delta > 0.0 {
self.elevation_gain += delta;
} else if delta < 0.0 {
self.elevation_loss -= delta;
}
if i < end || last {
self.local[flat_i].elevation_gain = self.elevation_gain;
self.local[flat_i].elevation_loss = self.elevation_loss;
}
prev_smoothed_ele = smoothed_ele;
},
);
let flat_start = trkseg.to_flat_index(start);
let flat_end = trkseg.to_flat_index(end);
let segment_dist =
self.local[flat_end].total_distance - self.local[flat_start].total_distance;
let segment_ele = trkseg[end].ele - trkseg[start].ele;
let segment_slope = slope(segment_ele, segment_dist);
for k in flat_start..(flat_end + last as usize) {
self.local[k].slope_segment = SlopeSegment {
slope: segment_slope,
distance: segment_dist,
};
}
}
for_each_window!(
trkseg,
trkseg.first_index(),
trkseg.last_index(),
0.05,
|i, j| {
let i = trkseg.to_flat_index(i);
let j = trkseg.to_flat_index(j);
self.local[j].total_distance - self.local[i].total_distance
},
|i, left, right| {
let flat_i = trkseg.to_flat_index(i);
let flat_left = trkseg.to_flat_index(left);
let flat_right = trkseg.to_flat_index(right);
let dist =
self.local[flat_right].total_distance - self.local[flat_left].total_distance;
let ele = trkseg[right].ele - trkseg[left].ele;
self.local[flat_i].slope = slope(ele, dist);
},
);
}
}
#[derive(Default, Debug)]
pub struct SlopeSegment {
pub slope: f64,
pub distance: f64,
}
#[derive(Default, Debug)]
pub struct TrackpointStatistics {
pub total_distance: f64,
pub moving_distance: Option<f64>,
pub total_time: Option<i64>,
pub moving_time: Option<i64>,
pub speed: Option<f64>,
pub elevation_gain: f64,
pub elevation_loss: f64,
pub slope: f64,
pub slope_segment: SlopeSegment,
}
impl TrackpointStatistics {
fn from_partial_stats(stats: &GPXStatistics) -> Self {
Self {
total_distance: stats.total_distance,
moving_distance: stats.moving_distance,
total_time: stats.start_time.zip(stats.end_time).map(|(t1, t2)| t2 - t1),
moving_time: stats.moving_time,
speed: None,
elevation_gain: stats.elevation_gain,
elevation_loss: stats.elevation_loss,
slope: Default::default(),
slope_segment: Default::default(),
}
}
}
#[cfg(test)]
mod tests {
use std::{fs::File, io::Read};
use crate::actions::parse;
use super::*;
#[test]
fn test_compute_smoothed_speed() {
let mut f = File::open("data/with_time.gpx").unwrap();
let mut data = String::new();
let _ = f.read_to_string(&mut data);
let gpx = parse(data.as_bytes()).unwrap();
let trkseg = &gpx.trk[0].trkseg[0];
let stats = GPXStatistics::compute(trkseg);
assert_eq!(stats.local.len(), trkseg.len());
for trkpt_stats in stats.local.iter() {
assert!(trkpt_stats.speed.is_some());
let speed = trkpt_stats.speed.unwrap();
assert_ne!(speed, f64::NAN);
assert!((speed - 20.0).abs() < 0.1);
}
}
}
+111
View File
@@ -0,0 +1,111 @@
use std::f64::consts::PI;
use crate::gpx::LngLat;
static TO_RADIANS: f64 = PI / 180.0;
static EARTH_RADIUS: f64 = 6371.0088;
/// Computes the distance in kilometers between two coordinates using the Haversine formula
pub fn distance(p1: LngLat, p2: LngLat) -> f64 {
let lat1 = p1.lat * TO_RADIANS;
let lat2 = p2.lat * TO_RADIANS;
let delta_lat = lat2 - lat1;
let delta_lng = (p2.lng - p1.lng) * TO_RADIANS;
let a =
(delta_lat / 2.0).sin().powi(2) + lat1.cos() * lat2.cos() * (delta_lng / 2.0).sin().powi(2);
let c = 2.0 * a.min(1.0).sqrt().asin();
EARTH_RADIUS * c
}
/// Computes the speed for a given distance in kilometers and a time in milliseconds
pub fn speed(distance: f64, time: i64) -> f64 {
distance / (time as f64 / 3600_000.0)
}
pub fn slope(ele: f64, distance: f64) -> f64 {
if distance == 0.0 {
100.0
} else {
0.1 * ele / distance
}
}
static METERS_PER_LATITUDE_DEGREE: f64 = 111320.0;
fn get_meters_per_longitude_degree(latitude: f64) -> f64 {
((latitude * PI) / 180.0).cos() * METERS_PER_LATITUDE_DEGREE
}
// Calculates the point on the line segment defined by p1 and p2
// that is closest to the third point, p3.
// Uses simple planar geometry (ignores earth curvature).
fn projected(p1: LngLat, p2: LngLat, coord3: LngLat) -> LngLat {
// Convert to meters using approximate scaling
let meters_per_longitude_degree = get_meters_per_longitude_degree(p1.lat);
let x1 = p1.lng * meters_per_longitude_degree;
let y1 = p1.lat * METERS_PER_LATITUDE_DEGREE;
let x2 = p2.lng * meters_per_longitude_degree;
let y2 = p2.lat * METERS_PER_LATITUDE_DEGREE;
let x3 = coord3.lng * meters_per_longitude_degree;
let y3 = coord3.lat * METERS_PER_LATITUDE_DEGREE;
let dx = x2 - x1;
let dy = y2 - y1;
let segment_length_squared = dx * dx + dy * dy;
if segment_length_squared == 0.0 {
// p1 and p2 are the same point
p1
} else {
// Project p3 onto the line defined by p1-p2
let t =
0.0_f64.max(1.0_f64.min(((x3 - x1) * dx + (y3 - y1) * dy) / segment_length_squared));
// Find the closest point on the segment
let proj_x = x1 + t * dx;
let proj_y = y1 + t * dy;
// Convert back to degrees
LngLat {
lng: proj_x / meters_per_longitude_degree,
lat: proj_y / METERS_PER_LATITUDE_DEGREE,
}
}
}
/// Calculates the perpendicular distance in meters
/// between a line segment (defined by p1 and p2) and a third point, p3.
/// Uses simple planar geometry (ignores earth curvature).
fn crossarc(p1: LngLat, p2: LngLat, p3: LngLat) -> f64 {
// Convert to meters using approximate scaling
let meters_per_longitude_degree = get_meters_per_longitude_degree(p1.lat);
let x1 = p1.lng * meters_per_longitude_degree;
let y1 = p1.lat * METERS_PER_LATITUDE_DEGREE;
let x2 = p2.lng * meters_per_longitude_degree;
let y2 = p2.lat * METERS_PER_LATITUDE_DEGREE;
let x3 = p3.lng * meters_per_longitude_degree;
let y3 = p3.lat * METERS_PER_LATITUDE_DEGREE;
let dx = x2 - x1;
let dy = y2 - y1;
let segment_length_squared = dx * dx + dy * dy;
if segment_length_squared == 0.0 {
// p1 and p2 are the same point
((x3 - x1) * (x3 - x1) + (y3 - y1) * (y3 - y1)).sqrt()
} else {
// Project p3 onto the line defined by p1 - p2
let t =
0.0_f64.max(1.0_f64.min(((x3 - x1) * dx + (y3 - y1) * dy) / segment_length_squared));
// Find the closest point on the segment
let proj_x = x1 + t * dx;
let proj_y = y1 + t * dy;
// Return distance from p3 to the projected point
((x3 - proj_x) * (x3 - proj_x) + (y3 - proj_y) * (y3 - proj_y)).sqrt()
}
}
+6 -5
View File
@@ -1398,10 +1398,7 @@ export class TrackPoint {
: undefined; : undefined;
} }
setExtensions(extensions: Record<string, string>) { setExtension(key: string, value: string) {
if (Object.keys(extensions).length === 0) {
return;
}
if (!this.extensions) { if (!this.extensions) {
this.extensions = {}; this.extensions = {};
} }
@@ -1411,8 +1408,12 @@ export class TrackPoint {
if (!this.extensions['gpxtpx:TrackPointExtension']['gpxtpx:Extensions']) { if (!this.extensions['gpxtpx:TrackPointExtension']['gpxtpx:Extensions']) {
this.extensions['gpxtpx:TrackPointExtension']['gpxtpx:Extensions'] = {}; this.extensions['gpxtpx:TrackPointExtension']['gpxtpx:Extensions'] = {};
} }
Object.entries(extensions).forEach(([key, value]) => {
this.extensions['gpxtpx:TrackPointExtension']['gpxtpx:Extensions'][key] = value; this.extensions['gpxtpx:TrackPointExtension']['gpxtpx:Extensions'][key] = value;
}
setExtensions(extensions: Record<string, string>) {
Object.entries(extensions).forEach(([key, value]) => {
this.setExtension(key, value);
}); });
} }
+6 -3
View File
@@ -1,9 +1,8 @@
{ {
"$schema": "https://shadcn-svelte.com/schema.json", "$schema": "https://shadcn-svelte.com/schema.json",
"style": "default",
"tailwind": { "tailwind": {
"css": "src/app.css", "css": "src/app.css",
"baseColor": "slate" "baseColor": "neutral"
}, },
"aliases": { "aliases": {
"components": "$lib/components", "components": "$lib/components",
@@ -13,5 +12,9 @@
"lib": "$lib" "lib": "$lib"
}, },
"typescript": true, "typescript": true,
"registry": "https://shadcn-svelte.com/registry" "registry": "https://shadcn-svelte.com/registry",
"style": "nova",
"iconLibrary": "lucide",
"menuColor": "default",
"menuAccent": "subtle"
} }
+3688 -3847
View File
File diff suppressed because it is too large Load Diff
+14 -11
View File
@@ -14,7 +14,9 @@
"format": "prettier --write . --config ../.prettierrc --ignore-path ../.prettierignore --ignore-path ./.gitignore" "format": "prettier --write . --config ../.prettierrc --ignore-path ../.prettierignore --ignore-path ./.gitignore"
}, },
"devDependencies": { "devDependencies": {
"@lucide/svelte": "^0.544.0", "@fontsource-variable/inter": "^5.2.8",
"@internationalized/date": "^3.12.0",
"@lucide/svelte": "^1.7.0",
"@sveltejs/adapter-static": "^3.0.8", "@sveltejs/adapter-static": "^3.0.8",
"@sveltejs/enhanced-img": "^0.6.0", "@sveltejs/enhanced-img": "^0.6.0",
"@sveltejs/kit": "^2.21.2", "@sveltejs/kit": "^2.21.2",
@@ -30,7 +32,8 @@
"@types/sortablejs": "^1.15.8", "@types/sortablejs": "^1.15.8",
"@typescript-eslint/eslint-plugin": "^8.33.1", "@typescript-eslint/eslint-plugin": "^8.33.1",
"@typescript-eslint/parser": "^8.33.1", "@typescript-eslint/parser": "^8.33.1",
"bits-ui": "^2.14.4", "bits-ui": "^2.17.2",
"clsx": "^2.1.1",
"eslint": "^9.28.0", "eslint": "^9.28.0",
"eslint-config-prettier": "^10.1.5", "eslint-config-prettier": "^10.1.5",
"eslint-plugin-svelte": "^3.9.1", "eslint-plugin-svelte": "^3.9.1",
@@ -43,39 +46,39 @@
"postcss": "^8.4.47", "postcss": "^8.4.47",
"prettier": "^3.5.3", "prettier": "^3.5.3",
"prettier-plugin-svelte": "^3.4.0", "prettier-plugin-svelte": "^3.4.0",
"shadcn-svelte": "^1.2.7",
"svelte": "^5.33.18", "svelte": "^5.33.18",
"svelte-check": "^4.0.0", "svelte-check": "^4.0.0",
"svelte-dnd-action": "^0.9.65", "svelte-dnd-action": "^0.9.65",
"svelte-sonner": "^1.0.5", "svelte-sonner": "^1.1.0",
"tailwind-variants": "^3.1.1", "tailwind-merge": "^3.5.0",
"tailwind-variants": "^3.2.2",
"tailwindcss": "^4.1.8", "tailwindcss": "^4.1.8",
"tslib": "^2.8.1", "tslib": "^2.8.1",
"tsx": "^4.19.1", "tsx": "^4.19.1",
"tw-animate-css": "^1.3.4", "tw-animate-css": "^1.4.0",
"typescript": "^5.8.3", "typescript": "^5.8.3",
"vaul-svelte": "^1.0.0-next.7", "vaul-svelte": "^1.0.0-next.7",
"vite": "^6.3.5", "vite": "^6.3.5",
"vite-plugin-node-polyfills": "^0.23.0" "vite-plugin-wasm": "^3.6.0"
}, },
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"@docsearch/js": "^3.9.0", "@docsearch/js": "^3.9.0",
"@internationalized/date": "^3.8.2",
"@mapbox/sphericalmercator": "^2.0.1", "@mapbox/sphericalmercator": "^2.0.1",
"@mapbox/tilebelt": "^2.0.2", "@mapbox/tilebelt": "^2.0.2",
"@maplibre/maplibre-gl-geocoder": "^1.9.4", "@maplibre/maplibre-gl-geocoder": "^1.9.4",
"chart.js": "^4.5.1", "chart.js": "^4.5.1",
"chartjs-plugin-zoom": "^2.2.0", "chartjs-plugin-zoom": "^2.2.0",
"clsx": "^2.1.1",
"dexie": "^4.0.11", "dexie": "^4.0.11",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
"gpx": "file:../gpx", "gpx": "file:../gpx",
"gpx-rs": "file:../gpx-rs/pkg",
"immer": "^10.1.1", "immer": "^10.1.1",
"jszip": "^3.10.1", "jszip": "^3.10.1",
"mapillary-js": "^4.1.2", "mapillary-js": "^4.1.2",
"maplibre-gl": "^5.16.0", "maplibre-gl": "^5.21.1",
"sanitize-html": "^2.17.0", "sanitize-html": "^2.17.0",
"sortablejs": "^1.15.6", "sortablejs": "^1.15.6"
"tailwind-merge": "^3.3.0"
} }
} }
+91 -53
View File
@@ -1,76 +1,93 @@
@import 'tailwindcss'; @import 'tailwindcss';
@import 'tw-animate-css'; @import 'tw-animate-css';
@import "shadcn-svelte/tailwind.css";
@import "@fontsource-variable/inter";
@custom-variant dark (&:is(.dark *)); @custom-variant dark (&:is(.dark *));
:root { :root {
--background: hsl(0 0% 100%) /* <- Wrap in HSL */; --background: oklch(1 0 0);
--foreground: hsl(240 10% 3.9%); --foreground: oklch(0.145 0 0);
--muted: hsl(240 4.8% 95.9%); --muted: oklch(0.97 0 0);
--muted-foreground: hsl(240 3.8% 46.1%); --muted-foreground: oklch(0.556 0 0);
--popover: hsl(0 0% 100%); --popover: oklch(1 0 0);
--popover-foreground: hsl(240 10% 3.9%); --popover-foreground: oklch(0.145 0 0);
--card: hsl(0 0% 100%); --card: oklch(1 0 0);
--card-foreground: hsl(240 10% 3.9%); --card-foreground: oklch(0.145 0 0);
--border: hsl(240 5.9% 90%); --border: oklch(0.922 0 0);
--input: hsl(240 5.9% 90%); --input: oklch(0.922 0 0);
--primary: hsl(240 5.9% 10%); --primary: oklch(0.205 0 0);
--primary-foreground: hsl(0 0% 98%); --primary-foreground: oklch(0.985 0 0);
--secondary: hsl(240 4.8% 95.9%); --secondary: oklch(0.97 0 0);
--secondary-foreground: hsl(240 5.9% 10%); --secondary-foreground: oklch(0.205 0 0);
--accent: hsl(240 4.8% 95.9%); --accent: oklch(0.97 0 0);
--accent-foreground: hsl(240 5.9% 10%); --accent-foreground: oklch(0.205 0 0);
--destructive: hsl(0 72.2% 50.6%); --destructive: oklch(0.577 0.245 27.325);
--destructive-foreground: hsl(0 0% 98%); --destructive-foreground: hsl(0 0% 98%);
--ring: hsl(240 10% 3.9%); --ring: oklch(0.708 0 0);
--sidebar: hsl(0 0% 98%); --sidebar: oklch(0.985 0 0);
--sidebar-foreground: hsl(240 5.3% 26.1%); --sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: hsl(240 5.9% 10%); --sidebar-primary: oklch(0.205 0 0);
--sidebar-primary-foreground: hsl(0 0% 98%); --sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: hsl(240 4.8% 95.9%); --sidebar-accent: oklch(0.97 0 0);
--sidebar-accent-foreground: hsl(240 5.9% 10%); --sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: hsl(220 13% 91%); --sidebar-border: oklch(0.922 0 0);
--sidebar-ring: hsl(217.2 91.2% 59.8%); --sidebar-ring: oklch(0.708 0 0);
--support: rgb(220 15 130); --support: rgb(220 15 130);
--link: rgb(0 110 180); --link: rgb(0 110 180);
--selection: hsl(240 4.8% 93%); --selection: hsl(240 4.8% 93%);
--radius: 0.5rem; --radius: 0.5rem;
--chart-1: oklch(0.87 0 0);
--chart-2: oklch(0.556 0 0);
--chart-3: oklch(0.439 0 0);
--chart-4: oklch(0.371 0 0);
--chart-5: oklch(0.269 0 0);
} }
.dark { .dark {
--background: hsl(240 10% 3.9%); --background: oklch(0.145 0 0);
--foreground: hsl(0 0% 98%); --foreground: oklch(0.985 0 0);
--muted: hsl(240 3.7% 15.9%); --muted: oklch(0.269 0 0);
--muted-foreground: hsl(240 5% 64.9%); --muted-foreground: oklch(0.708 0 0);
--popover: hsl(240 10% 3.9%); --popover: oklch(0.205 0 0);
--popover-foreground: hsl(0 0% 98%); --popover-foreground: oklch(0.985 0 0);
--card: hsl(240 10% 3.9%); --card: oklch(0.205 0 0);
--card-foreground: hsl(0 0% 98%); --card-foreground: oklch(0.985 0 0);
--border: hsl(240 3.7% 15.9%); --border: oklch(1 0 0 / 10%);
--input: hsl(240 3.7% 15.9%); --input: oklch(1 0 0 / 15%);
--primary: hsl(0 0% 98%); --primary: oklch(0.922 0 0);
--primary-foreground: hsl(240 5.9% 10%); --primary-foreground: oklch(0.205 0 0);
--secondary: hsl(240 3.7% 15.9%); --secondary: oklch(0.269 0 0);
--secondary-foreground: hsl(0 0% 98%); --secondary-foreground: oklch(0.985 0 0);
--accent: hsl(240 3.7% 15.9%); --accent: oklch(0.269 0 0);
--accent-foreground: hsl(0 0% 98%); --accent-foreground: oklch(0.985 0 0);
--destructive: hsl(0 62.8% 30.6%); --destructive: oklch(0.704 0.191 22.216);
--destructive-foreground: hsl(0 0% 98%); --destructive-foreground: hsl(0 0% 98%);
--ring: hsl(240 4.9% 83.9%); --ring: oklch(0.556 0 0);
--sidebar: hsl(240 5.9% 10%); --sidebar: oklch(0.205 0 0);
--sidebar-foreground: hsl(240 4.8% 95.9%); --sidebar-foreground: oklch(0.985 0 0);
--sidebar-primary: hsl(224.3 76.3% 48%); --sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary-foreground: hsl(0 0% 100%); --sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: hsl(240 3.7% 15.9%); --sidebar-accent: oklch(0.269 0 0);
--sidebar-accent-foreground: hsl(240 4.8% 95.9%); --sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: hsl(240 3.7% 15.9%); --sidebar-border: oklch(1 0 0 / 10%);
--sidebar-ring: hsl(217.2 91.2% 59.8%); --sidebar-ring: oklch(0.556 0 0);
--support: rgb(255 110 190); --support: rgb(255 110 190);
--link: rgb(80 190 255); --link: rgb(80 190 255);
--selection: hsl(240 3.7% 22%); --selection: hsl(240 3.7% 22%);
--chart-1: oklch(0.87 0 0);
--chart-2: oklch(0.556 0 0);
--chart-3: oklch(0.439 0 0);
--chart-4: oklch(0.371 0 0);
--chart-5: oklch(0.269 0 0);
} }
@theme inline { @theme inline {
@@ -113,14 +130,35 @@
--color-link: var(--link); --color-link: var(--link);
--breakpoint-xs: 540px; --breakpoint-xs: 540px;
--font-sans: 'Inter Variable', sans-serif;
--color-chart-5: var(--chart-5);
--color-chart-4: var(--chart-4);
--color-chart-3: var(--chart-3);
--color-chart-2: var(--chart-2);
--color-chart-1: var(--chart-1);
--radius-2xl: calc(var(--radius) * 1.8);
--radius-3xl: calc(var(--radius) * 2.2);
--radius-4xl: calc(var(--radius) * 2.6);
} }
@layer base { @layer base {
* { * {
@apply border-border; @apply border-border outline-ring/50;
} }
body { body {
@apply bg-background text-foreground; @apply bg-background text-foreground;
} }
html {
@apply font-sans;
}
} }
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 313 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 729 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 768 KiB

After

Width:  |  Height:  |  Size: 525 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 596 KiB

After

Width:  |  Height:  |  Size: 710 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 448 KiB

+41 -17
View File
@@ -31,9 +31,32 @@ import bikerouterGravel from './custom/bikerouter-gravel.json';
export const maptilerKeyPlaceHolder = 'MAPTILER_KEY'; export const maptilerKeyPlaceHolder = 'MAPTILER_KEY';
export const basemaps: { [key: string]: string | StyleSpecification } = { export const basemaps: { [key: string]: string | StyleSpecification } = {
maptilerTopo: `https://api.maptiler.com/maps/topo-v4/style.json?key=${maptilerKeyPlaceHolder}`, libertyTopo: 'https://styles.gpx.studio/liberty-topo.json',
maptilerOutdoors: `https://api.maptiler.com/maps/outdoor-v4/style.json?key=${maptilerKeyPlaceHolder}`, libertySatellite: 'https://styles.gpx.studio/liberty-satellite.json',
maptilerSatellite: `https://api.maptiler.com/maps/hybrid-v4/style.json?key=${maptilerKeyPlaceHolder}`, osm: 'https://styles.gpx.studio/osm.json',
osmTopo: 'https://styles.gpx.studio/osm-topo.json',
esriSatellite: {
version: 8,
sources: {
esriSatellite: {
type: 'raster',
tiles: [
'https://services.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/WMTS/tile/1.0.0/World_Imagery/default/default028mm/{z}/{y}/{x}.jpg',
],
tileSize: 256,
maxzoom: 19,
attribution:
'© <a href="https://www.esri.com/" target="_blank">Esri</a>, Vantor, Earthstar Geographics, and the GIS User Community',
},
},
layers: [
{
id: 'esriSatellite',
type: 'raster',
source: 'esriSatellite',
},
],
},
openStreetMap: { openStreetMap: {
version: 8, version: 8,
sources: { sources: {
@@ -776,9 +799,11 @@ export type LayerTreeType = { [key: string]: LayerTreeType | boolean };
export const basemapTree: LayerTreeType = { export const basemapTree: LayerTreeType = {
basemaps: { basemaps: {
world: { world: {
maptilerTopo: true, libertyTopo: true,
maptilerOutdoors: true, libertySatellite: true,
maptilerSatellite: true, osm: true,
osmTopo: true,
esriSatellite: true,
openStreetMap: true, openStreetMap: true,
openTopoMap: true, openTopoMap: true,
openHikingMap: true, openHikingMap: true,
@@ -911,7 +936,7 @@ export const overpassTree: LayerTreeType = {
}; };
// Default basemap used // Default basemap used
export const defaultBasemap = 'maptilerTopo'; export const defaultBasemap = 'libertyTopo';
// Default overlays used (none) // Default overlays used (none)
export const defaultOverlays: LayerTreeType = { export const defaultOverlays: LayerTreeType = {
@@ -1000,9 +1025,11 @@ export const defaultOverpassQueries: LayerTreeType = {
export const defaultBasemapTree: LayerTreeType = { export const defaultBasemapTree: LayerTreeType = {
basemaps: { basemaps: {
world: { world: {
maptilerTopo: true, libertyTopo: true,
maptilerOutdoors: true, libertySatellite: true,
maptilerSatellite: true, osm: true,
osmTopo: true,
esriSatellite: false,
openStreetMap: true, openStreetMap: true,
openTopoMap: true, openTopoMap: true,
openHikingMap: true, openHikingMap: true,
@@ -1422,7 +1449,7 @@ export const overpassQueryData: Record<string, OverpassQueryData> = {
color: 'DarkBlue', color: 'DarkBlue',
}, },
tags: { tags: {
railway: 'station', railway: ['station', 'halt'],
}, },
symbol: 'Ground Transportation', symbol: 'Ground Transportation',
}, },
@@ -1460,14 +1487,11 @@ export const overpassQueryData: Record<string, OverpassQueryData> = {
}; };
export const terrainSources: { [key: string]: RasterDEMSourceSpecification } = { export const terrainSources: { [key: string]: RasterDEMSourceSpecification } = {
'maptiler-dem': {
type: 'raster-dem',
url: `https://api.maptiler.com/tiles/terrain-rgb-v2/tiles.json?key=${maptilerKeyPlaceHolder}`,
},
mapterhorn: { mapterhorn: {
type: 'raster-dem', type: 'raster-dem',
url: 'https://tiles.mapterhorn.com/tilejson.json', url: 'https://tiles.gpx.studio/mapterhorn.json',
encoding: 'terrarium',
}, },
}; };
export const defaultTerrainSource = 'maptiler-dem'; export const defaultTerrainSource = 'mapterhorn';
@@ -64,3 +64,9 @@
</svelte:head> </svelte:head>
<div id="docsearch" class={props.class ?? ''}></div> <div id="docsearch" class={props.class ?? ''}></div>
<style>
#docsearch :global(button) {
margin-left: 0px;
}
</style>
@@ -26,7 +26,7 @@
<Tooltip.Root> <Tooltip.Root>
<Tooltip.Trigger> <Tooltip.Trigger>
{#snippet child({ props })} {#snippet child({ props })}
<Button {...props} {variant} class={className} {onclick}> <Button {...props} {variant} class="bg-inherit {className}" {onclick}>
{@render children()} {@render children()}
</Button> </Button>
{/snippet} {/snippet}
+8 -6
View File
@@ -1,15 +1,15 @@
<script lang="ts"> <script lang="ts">
import { Button } from '$lib/components/ui/button'; import { Button } from '$lib/components/ui/button';
import LanguageSelect from '$lib/components/LanguageSelect.svelte'; import LanguageSelect from '$lib/components/LanguageSelect.svelte';
import ModeSwitch from '$lib/components/ModeSwitch.svelte';
import Logo from '$lib/components/Logo.svelte'; import Logo from '$lib/components/Logo.svelte';
import { AtSign, BookOpenText, Heart, House, Map } from '@lucide/svelte'; import { AtSign, BookOpenText, Heart, House, Map } from '@lucide/svelte';
import { i18n } from '$lib/i18n.svelte'; import { i18n } from '$lib/i18n.svelte';
import { getURLForLanguage } from '$lib/utils'; import { getURLForLanguage } from '$lib/utils';
</script> </script>
<footer class="w-full"> <footer class="w-full px-12 py-10 border-t flex flex-col items-center">
<div class="mx-6 border-t"> <div class="w-full max-w-5xl flex flex-row flex-wrap justify-between gap-x-10 gap-y-6">
<div class="mx-12 py-10 flex flex-row flex-wrap justify-between gap-x-10 gap-y-6">
<div class="grow flex flex-col items-start"> <div class="grow flex flex-col items-start">
<Logo class="h-8" width="153" /> <Logo class="h-8" width="153" />
<Button <Button
@@ -20,7 +20,10 @@
> >
MIT © 2026 gpx.studio MIT © 2026 gpx.studio
</Button> </Button>
<LanguageSelect class="w-40 mt-3" /> <div class="mt-3 flex flex-row gap-1.5">
<LanguageSelect />
<ModeSwitch />
</div>
</div> </div>
<div class="grow max-w-2xl flex flex-row flex-wrap justify-between gap-x-10 gap-y-6"> <div class="grow max-w-2xl flex flex-row flex-wrap justify-between gap-x-10 gap-y-6">
<div class="flex flex-col items-start gap-1"> <div class="flex flex-col items-start gap-1">
@@ -86,7 +89,7 @@
<Button <Button
variant="link" variant="link"
class="h-6 px-0 has-[>svg]:px-0 text-muted-foreground" class="h-6 px-0 has-[>svg]:px-0 text-muted-foreground"
href="https://ko-fi.com/gpxstudio" href="https://opencollective.com/gpxstudio"
target="_blank" target="_blank"
> >
<Heart size="16" /> <Heart size="16" />
@@ -113,5 +116,4 @@
</div> </div>
</div> </div>
</div> </div>
</div>
</footer> </footer>
+14 -11
View File
@@ -12,16 +12,17 @@
const { velocityUnits } = settings; const { velocityUnits } = settings;
let panelHeight: number = $state(0);
let panelWidth: number = $state(0);
let { let {
gpxStatistics, gpxStatistics,
slicedGPXStatistics, slicedGPXStatistics,
orientation, orientation,
panelSize,
}: { }: {
gpxStatistics: Readable<GPXStatisticsGroup>; gpxStatistics: Readable<GPXStatisticsGroup>;
slicedGPXStatistics: Readable<[GPXGlobalStatistics, number, number] | undefined>; slicedGPXStatistics: Readable<[GPXGlobalStatistics, number, number] | undefined>;
orientation: 'horizontal' | 'vertical'; orientation: 'horizontal' | 'vertical';
panelSize: number;
} = $props(); } = $props();
let statistics = $derived( let statistics = $derived(
@@ -32,12 +33,15 @@
<Card.Root <Card.Root
class="h-full {orientation === 'vertical' class="h-full {orientation === 'vertical'
? 'min-w-40 sm:min-w-44' ? 'min-w-40 sm:min-w-44'
: 'w-full h-10'} border-none shadow-none p-0 text-sm sm:text-base" : 'w-full h-fit my-1'} ring-0 p-0 text-sm sm:text-base bg-transparent"
> >
<Card.Content <Card.Content class="h-full p-0">
class="h-full flex {orientation === 'vertical' <div
? 'flex-col justify-center' bind:clientHeight={panelHeight}
: 'flex-row w-full justify-evenly'} gap-4 p-0" bind:clientWidth={panelWidth}
class="flex {orientation === 'vertical'
? 'flex-col h-full justify-center'
: 'flex-row w-full justify-evenly'} gap-4"
> >
<Tooltip label={i18n._('quantities.distance')}> <Tooltip label={i18n._('quantities.distance')}>
<span class="flex flex-row items-center"> <span class="flex flex-row items-center">
@@ -53,9 +57,8 @@
<WithUnits value={statistics.elevation.loss} type="elevation" /> <WithUnits value={statistics.elevation.loss} type="elevation" />
</span> </span>
</Tooltip> </Tooltip>
{#if panelSize > 120 || orientation === 'horizontal'} {#if panelHeight > 120 || (orientation === 'horizontal' && panelWidth > 450)}
<Tooltip <Tooltip
class={orientation === 'horizontal' ? 'hidden xs:block' : ''}
label="{$velocityUnits === 'speed' label="{$velocityUnits === 'speed'
? i18n._('quantities.speed') ? i18n._('quantities.speed')
: i18n._('quantities.pace')} ({i18n._('quantities.moving')} / {i18n._( : i18n._('quantities.pace')} ({i18n._('quantities.moving')} / {i18n._(
@@ -70,9 +73,8 @@
</span> </span>
</Tooltip> </Tooltip>
{/if} {/if}
{#if panelSize > 160 || orientation === 'horizontal'} {#if panelHeight > 150 || (orientation === 'horizontal' && panelWidth > 620)}
<Tooltip <Tooltip
class={orientation === 'horizontal' ? 'hidden md:block' : ''}
label="{i18n._('quantities.time')} ({i18n._('quantities.moving')} / {i18n._( label="{i18n._('quantities.time')} ({i18n._('quantities.moving')} / {i18n._(
'quantities.total' 'quantities.total'
)})" )})"
@@ -85,5 +87,6 @@
</span> </span>
</Tooltip> </Tooltip>
{/if} {/if}
</div>
</Card.Content> </Card.Content>
</Card.Root> </Card.Root>
+2 -2
View File
@@ -14,12 +14,12 @@
} = $props(); } = $props();
</script> </script>
<div class="text-sm bg-secondary rounded border flex flex-row items-center p-2 {className}"> <div class="text-[13px] bg-secondary rounded border flex flex-row items-center p-2 {className}">
<CircleQuestionMark size="16" class="w-4 mr-2 shrink-0 grow-0" /> <CircleQuestionMark size="16" class="w-4 mr-2 shrink-0 grow-0" />
<div> <div>
{@render children()} {@render children()}
{#if link} {#if link}
<a href={link} target="_blank" class="text-sm text-link hover:underline"> <a href={link} target="_blank" class="text-[13px] text-link hover:underline">
{i18n._('menu.more')} {i18n._('menu.more')}
</a> </a>
{/if} {/if}
@@ -5,16 +5,10 @@
import { getURLForLanguage } from '$lib/utils'; import { getURLForLanguage } from '$lib/utils';
import { Languages } from '@lucide/svelte'; import { Languages } from '@lucide/svelte';
import { i18n } from '$lib/i18n.svelte'; import { i18n } from '$lib/i18n.svelte';
let {
class: className = '',
}: {
class?: string;
} = $props();
</script> </script>
<Select.Root type="single" value={i18n.lang}> <Select.Root type="single" value={i18n.lang}>
<Select.Trigger class="min-w-[180px] {className}" aria-label={i18n._('menu.language')}> <Select.Trigger class="w-[180px] px-2" aria-label={i18n._('menu.language')}>
<Languages size="16" /> <Languages size="16" />
<span class="mr-auto"> <span class="mr-auto">
{languages[i18n.lang]} {languages[i18n.lang]}
+1 -7
View File
@@ -8,7 +8,7 @@
...others ...others
}: { }: {
iconOnly?: boolean; iconOnly?: boolean;
company?: 'gpx.studio' | 'maptiler' | 'github' | 'crowdin' | 'facebook' | 'reddit'; company?: 'gpx.studio' | 'github' | 'crowdin' | 'facebook' | 'reddit';
[key: string]: any; [key: string]: any;
} = $props(); } = $props();
</script> </script>
@@ -19,12 +19,6 @@
alt="Logo of gpx.studio." alt="Logo of gpx.studio."
{...others} {...others}
/> />
{:else if company === 'maptiler'}
<img
src="{base}/maptiler-logo{mode.current === 'dark' ? '-dark' : ''}.svg"
alt="Logo of Maptiler."
{...others}
/>
{:else if company === 'github'} {:else if company === 'github'}
<svg <svg
role="img" role="img"
+42 -13
View File
@@ -43,6 +43,8 @@
BookOpenText, BookOpenText,
ChartArea, ChartArea,
Maximize, Maximize,
Maximize2,
Minimize2,
} from '@lucide/svelte'; } from '@lucide/svelte';
import { map } from '$lib/components/map/map'; import { map } from '$lib/components/map/map';
import { editMetadata } from '$lib/components/file-list/metadata/utils.svelte'; import { editMetadata } from '$lib/components/file-list/metadata/utils.svelte';
@@ -70,7 +72,7 @@
import { copied, selection } from '$lib/logic/selection'; import { copied, selection } from '$lib/logic/selection';
import { allHidden } from '$lib/logic/hidden'; import { allHidden } from '$lib/logic/hidden';
import { boundsManager } from '$lib/logic/bounds'; import { boundsManager } from '$lib/logic/bounds';
import { tick } from 'svelte'; import { tick, onMount } from 'svelte';
import { allowedPastes } from '$lib/components/file-list/sortable-file-list'; import { allowedPastes } from '$lib/components/file-list/sortable-file-list';
const { const {
@@ -105,6 +107,23 @@
} }
let layerSettingsOpen = $state(false); let layerSettingsOpen = $state(false);
let fullscreen = $state(false);
function toggleFullscreen() {
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen?.();
} else {
document.exitFullscreen?.();
}
}
onMount(() => {
const handler = () => {
fullscreen = document.fullscreenElement !== null;
};
document.addEventListener('fullscreenchange', handler);
return () => document.removeEventListener('fullscreenchange', handler);
});
</script> </script>
<div class="absolute md:top-2 left-0 right-0 z-20 flex flex-row justify-center pointer-events-none"> <div class="absolute md:top-2 left-0 right-0 z-20 flex flex-row justify-center pointer-events-none">
@@ -375,8 +394,18 @@
<Menubar.Item inset onclick={() => map.toggle3D()}> <Menubar.Item inset onclick={() => map.toggle3D()}>
<Box size="16" /> <Box size="16" />
{i18n._('menu.toggle_3d')} {i18n._('menu.toggle_3d')}
<Shortcut key="{i18n._('menu.ctrl')} {i18n._('menu.drag')}" /> <Shortcut key={i18n._('menu.right_click_drag')} />
</Menubar.Item> </Menubar.Item>
<Menubar.Separator />
<Menubar.CheckboxItem checked={fullscreen} onCheckedChange={toggleFullscreen}>
{#if fullscreen}
<Minimize2 size="16" />
{:else}
<Maximize2 size="16" />
{/if}
{i18n._('menu.fullscreen')}
<Shortcut key="F11" />
</Menubar.CheckboxItem>
</Menubar.Content> </Menubar.Content>
</Menubar.Menu> </Menubar.Menu>
<Menubar.Menu> <Menubar.Menu>
@@ -389,7 +418,7 @@
<Menubar.Content class="border-none"> <Menubar.Content class="border-none">
<Menubar.Sub> <Menubar.Sub>
<Menubar.SubTrigger> <Menubar.SubTrigger>
<Ruler size="16" class="mr-2" />{i18n._('menu.distance_units')} <Ruler size="16" />{i18n._('menu.distance_units')}
</Menubar.SubTrigger> </Menubar.SubTrigger>
<Menubar.SubContent> <Menubar.SubContent>
<Menubar.RadioGroup bind:value={$distanceUnits}> <Menubar.RadioGroup bind:value={$distanceUnits}>
@@ -407,7 +436,7 @@
</Menubar.Sub> </Menubar.Sub>
<Menubar.Sub> <Menubar.Sub>
<Menubar.SubTrigger> <Menubar.SubTrigger>
<Zap size="16" class="mr-2" />{i18n._('menu.velocity_units')} <Zap size="16" />{i18n._('menu.velocity_units')}
</Menubar.SubTrigger> </Menubar.SubTrigger>
<Menubar.SubContent> <Menubar.SubContent>
<Menubar.RadioGroup bind:value={$velocityUnits}> <Menubar.RadioGroup bind:value={$velocityUnits}>
@@ -422,7 +451,7 @@
</Menubar.Sub> </Menubar.Sub>
<Menubar.Sub> <Menubar.Sub>
<Menubar.SubTrigger> <Menubar.SubTrigger>
<Thermometer size="16" class="mr-2" />{i18n._('menu.temperature_units')} <Thermometer size="16" />{i18n._('menu.temperature_units')}
</Menubar.SubTrigger> </Menubar.SubTrigger>
<Menubar.SubContent> <Menubar.SubContent>
<Menubar.RadioGroup bind:value={$temperatureUnits}> <Menubar.RadioGroup bind:value={$temperatureUnits}>
@@ -438,7 +467,7 @@
<Menubar.Separator /> <Menubar.Separator />
<Menubar.Sub> <Menubar.Sub>
<Menubar.SubTrigger> <Menubar.SubTrigger>
<Languages size="16" class="mr-2" /> <Languages size="16" />
{i18n._('menu.language')} {i18n._('menu.language')}
</Menubar.SubTrigger> </Menubar.SubTrigger>
<Menubar.SubContent> <Menubar.SubContent>
@@ -454,9 +483,9 @@
<Menubar.Sub> <Menubar.Sub>
<Menubar.SubTrigger> <Menubar.SubTrigger>
{#if mode.current === 'light' || !mode.current} {#if mode.current === 'light' || !mode.current}
<Sun size="16" class="mr-2" /> <Sun size="16" />
{:else} {:else}
<Moon size="16" class="mr-2" /> <Moon size="16" />
{/if} {/if}
{i18n._('menu.mode')} {i18n._('menu.mode')}
</Menubar.SubTrigger> </Menubar.SubTrigger>
@@ -479,7 +508,7 @@
<Menubar.Separator /> <Menubar.Separator />
<Menubar.Sub> <Menubar.Sub>
<Menubar.SubTrigger> <Menubar.SubTrigger>
<PersonStanding size="16" class="mr-2" /> <PersonStanding size="16" />
{i18n._('menu.street_view_source')} {i18n._('menu.street_view_source')}
</Menubar.SubTrigger> </Menubar.SubTrigger>
<Menubar.SubContent> <Menubar.SubContent>
@@ -500,12 +529,12 @@
</Menubar.Content> </Menubar.Content>
</Menubar.Menu> </Menubar.Menu>
</Menubar.Root> </Menubar.Root>
<div class="h-fit flex flex-row items-center ml-1 gap-1"> <div class="h-fit flex flex-row items-center">
<Button <Button
variant="ghost" variant="ghost"
href="./help" href="./help"
target="_blank" target="_blank"
class="cursor-default h-fit rounded-sm px-3 py-0.5" class="cursor-default h-fit rounded-md px-3 py-0.5"
aria-label={i18n._('menu.help')} aria-label={i18n._('menu.help')}
> >
<BookOpenText size="18" class="md:hidden" /> <BookOpenText size="18" class="md:hidden" />
@@ -515,9 +544,9 @@
</Button> </Button>
<Button <Button
variant="ghost" variant="ghost"
href="https://ko-fi.com/gpxstudio" href="https://opencollective.com/gpxstudio"
target="_blank" target="_blank"
class="cursor-default h-fit rounded-sm font-bold text-support hover:text-support px-3 py-0.5" class="cursor-default h-fit rounded-md font-bold text-support hover:text-support px-3 py-0.5"
aria-label={i18n._('menu.donate')} aria-label={i18n._('menu.donate')}
> >
<HeartHandshake size="18" class="md:hidden" /> <HeartHandshake size="18" class="md:hidden" />
+1 -1
View File
@@ -12,7 +12,7 @@
</script> </script>
<Button <Button
variant="ghost" variant="outline"
size="icon" size="icon"
class={className} class={className}
onclick={() => { onclick={() => {
+9 -10
View File
@@ -1,22 +1,23 @@
<script lang="ts"> <script lang="ts">
import Logo from '$lib/components/Logo.svelte'; import Logo from '$lib/components/Logo.svelte';
import { Button } from '$lib/components/ui/button'; import { Button } from '$lib/components/ui/button';
import AlgoliaDocSearch from '$lib/components/AlgoliaDocSearch.svelte';
import ModeSwitch from '$lib/components/ModeSwitch.svelte';
import { BookOpenText, House, Map } from '@lucide/svelte'; import { BookOpenText, House, Map } from '@lucide/svelte';
import { i18n } from '$lib/i18n.svelte'; import { i18n } from '$lib/i18n.svelte';
import { getURLForLanguage } from '$lib/utils'; import { getURLForLanguage } from '$lib/utils';
</script> </script>
<nav class="w-full sticky top-0 bg-background z-50"> <nav class="sticky top-0 w-full px-12 py-2 bg-background z-50 flex flex-col items-center border-b">
<div class="mx-6 py-2 flex flex-row items-center border-b gap-4 sm:gap-8"> <div class="w-full max-w-5xl flex flex-row items-center gap-4 sm:gap-8">
<a href={getURLForLanguage(i18n.lang, '/')} class="shrink-0 translate-y-0.5"> <a
<Logo class="h-8 sm:hidden" iconOnly={true} width="26" /> href={getURLForLanguage(i18n.lang, '/')}
<Logo class="h-8 hidden sm:block" width="153" /> class="shrink-0 translate-y-0.25 justify-self-start"
>
<Logo class="h-8 xs:hidden" iconOnly={true} width="26" />
<Logo class="h-8 hidden xs:block" width="153" />
</a> </a>
<Button <Button
variant="link" variant="link"
class="text-base px-0 has-[>svg]:px-0" class="text-base px-0 has-[>svg]:px-0 ml-auto"
href={getURLForLanguage(i18n.lang, '/')} href={getURLForLanguage(i18n.lang, '/')}
> >
<House size="18" /> <House size="18" />
@@ -39,7 +40,5 @@
<BookOpenText size="18" /> <BookOpenText size="18" />
{i18n._('menu.help')} {i18n._('menu.help')}
</Button> </Button>
<AlgoliaDocSearch class="ml-auto" />
<ModeSwitch class="hidden xs:inline-flex" />
</div> </div>
</nav> </nav>
@@ -35,7 +35,7 @@
<Button <Button
variant="ghost" variant="ghost"
size="icon" size="icon"
class="w-full flex flex-row gap-1 {side === 'right' class="w-full flex flex-row gap-1 border-none {side === 'right'
? 'justify-between' ? 'justify-between'
: 'justify-start pl-1'} h-fit {nohover : 'justify-start pl-1'} h-fit {nohover
? 'hover:bg-background' ? 'hover:bg-background'
@@ -62,7 +62,7 @@
<Button <Button
variant="ghost" variant="ghost"
size="icon" size="icon"
class="w-full flex flex-row gap-1 {side === 'right' class="w-full flex flex-row gap-1 border-none {side === 'right'
? 'justify-between' ? 'justify-between'
: 'justify-start pl-1'} h-fit {nohover ? 'hover:bg-background' : ''}" : 'justify-start pl-1'} h-fit {nohover ? 'hover:bg-background' : ''}"
> >
@@ -19,7 +19,7 @@
@apply text-foreground; @apply text-foreground;
@apply text-3xl; @apply text-3xl;
@apply font-semibold; @apply font-semibold;
@apply mb-3 pt-6; @apply mb-3;
} }
:global(.markdown h2) { :global(.markdown h2) {
@@ -12,7 +12,7 @@
<div class="rounded-md overflow-hidden overflow-clip shadow-xl mx-auto"> <div class="rounded-md overflow-hidden overflow-clip shadow-xl mx-auto">
{#if src === 'getting-started/interface'} {#if src === 'getting-started/interface'}
<enhanced:img <enhanced:img
src="/src/lib/assets/img/docs/getting-started/interface.png" src="/src/lib/assets/img/docs/getting-started/interface.webp"
{alt} {alt}
class="w-full max-w-3xl" class="w-full max-w-3xl"
/> />
@@ -20,13 +20,13 @@
<enhanced:img <enhanced:img
src="/src/lib/assets/img/docs/tools/routing.png" src="/src/lib/assets/img/docs/tools/routing.png"
{alt} {alt}
class="w-full max-w-3xl" class="w-full max-w-lg"
/> />
{:else if src === 'tools/split'} {:else if src === 'tools/split'}
<enhanced:img <enhanced:img
src="/src/lib/assets/img/docs/tools/split.png" src="/src/lib/assets/img/docs/tools/split.png"
{alt} {alt}
class="w-full max-w-3xl" class="w-full max-w-lg"
/> />
{/if} {/if}
</div> </div>
@@ -1,10 +1,10 @@
<script lang="ts"> <script lang="ts">
import maptilerTopoMap from '$lib/assets/img/home/maptiler-topo.png?enhanced'; import topoMap from '$lib/assets/img/docs/maps/topo.png?enhanced';
import waymarkedMap from '$lib/assets/img/home/waymarked.png?enhanced'; import waymarkedMap from '$lib/assets/img/docs/maps/waymarked.png?enhanced';
</script> </script>
<div class="relative h-80 aspect-square rounded-2xl shadow-xl overflow-clip"> <div class="relative h-80 aspect-square rounded-2xl shadow-xl overflow-clip">
<enhanced:img src={maptilerTopoMap} alt="MapTiler Topo map screenshot." class="absolute" /> <enhanced:img src={topoMap} alt="Topo map screenshot." class="absolute" />
<enhanced:img <enhanced:img
src={waymarkedMap} src={waymarkedMap}
alt="Waymarked Trails map screenshot." alt="Waymarked Trails map screenshot."
@@ -68,33 +68,31 @@
<canvas bind:this={overlay} class="w-full h-full absolute pointer-events-none"></canvas> <canvas bind:this={overlay} class="w-full h-full absolute pointer-events-none"></canvas>
<canvas bind:this={canvas} class="w-full h-full absolute"></canvas> <canvas bind:this={canvas} class="w-full h-full absolute"></canvas>
{#if showControls} {#if showControls}
<div class="absolute bottom-10 right-1.5"> <div class="absolute bottom-9 right-2.5">
<Popover.Root> <Popover.Root>
<Popover.Trigger> <Popover.Trigger>
<ButtonWithTooltip <ButtonWithTooltip
label={i18n._('chart.settings')} label={i18n._('chart.settings')}
variant="outline" variant="outline"
side="left" side="left"
class="w-7 h-7 p-0 flex justify-center opacity-70 hover:opacity-100 transition-opacity duration-300 hover:bg-background" class="w-7 h-7 p-0 flex justify-center opacity-70 hover:opacity-100 transition-opacity duration-300 bg-background"
> >
<ChartNoAxesColumn size="18" /> <ChartNoAxesColumn size="18" />
</ButtonWithTooltip> </ButtonWithTooltip>
</Popover.Trigger> </Popover.Trigger>
<Popover.Content <Popover.Content
class="w-fit p-0 flex flex-col" class="w-fit p-0 flex flex-col gap-0 overflow-hidden"
side="top" side="top"
align="end" align="end"
sideOffset={-32} sideOffset={-32}
> >
<ToggleGroup.Root <ToggleGroup.Root
class="flex flex-col items-start gap-0 p-1 w-full border-none" class="flex flex-col w-full border-none"
type="single" type="single"
size="sm"
bind:value={$elevationFill} bind:value={$elevationFill}
> >
<ToggleGroup.Item <ToggleGroup.Item value="slope" class="w-full flex flex-row justify-start">
class="p-0 pr-1.5 h-6 w-full gap-1.5 rounded flex justify-start data-[state=on]:bg-background data-[state=on]:hover:bg-accent hover:bg-accent hover:text-foreground"
value="slope"
>
<div class="w-6 flex justify-center items-center"> <div class="w-6 flex justify-center items-center">
{#if $elevationFill === 'slope'} {#if $elevationFill === 'slope'}
<Circle class="size-1.5 fill-current text-current" /> <Circle class="size-1.5 fill-current text-current" />
@@ -104,9 +102,8 @@
{i18n._('quantities.slope')} {i18n._('quantities.slope')}
</ToggleGroup.Item> </ToggleGroup.Item>
<ToggleGroup.Item <ToggleGroup.Item
class="p-0 pr-1.5 h-6 w-full gap-1.5 rounded flex justify-start data-[state=on]:bg-background data-[state=on]:hover:bg-accent hover:bg-accent hover:text-foreground"
value="surface" value="surface"
variant="outline" class="w-full flex flex-row justify-start"
> >
<div class="w-6 flex justify-center items-center"> <div class="w-6 flex justify-center items-center">
{#if $elevationFill === 'surface'} {#if $elevationFill === 'surface'}
@@ -117,9 +114,8 @@
{i18n._('quantities.surface')} {i18n._('quantities.surface')}
</ToggleGroup.Item> </ToggleGroup.Item>
<ToggleGroup.Item <ToggleGroup.Item
class="p-0 pr-1.5 h-6 w-full gap-1.5 rounded flex justify-start data-[state=on]:bg-background data-[state=on]:hover:bg-accent hover:bg-accent hover:text-foreground"
value="highway" value="highway"
variant="outline" class="w-full flex flex-row justify-start"
> >
<div class="w-6 flex justify-center items-center"> <div class="w-6 flex justify-center items-center">
{#if $elevationFill === 'highway'} {#if $elevationFill === 'highway'}
@@ -132,14 +128,12 @@
</ToggleGroup.Root> </ToggleGroup.Root>
<Separator /> <Separator />
<ToggleGroup.Root <ToggleGroup.Root
class="flex flex-col items-start gap-0 p-1" class="flex flex-col gap-0"
type="multiple" type="multiple"
size="sm"
bind:value={$additionalDatasets} bind:value={$additionalDatasets}
> >
<ToggleGroup.Item <ToggleGroup.Item value="speed" class="w-full flex flex-row justify-start">
class="p-0 pr-1.5 h-6 w-full gap-1.5 rounded flex justify-start data-[state=on]:bg-background data-[state=on]:hover:bg-accent hover:bg-accent hover:text-foreground"
value="speed"
>
<div class="w-6 flex justify-center items-center"> <div class="w-6 flex justify-center items-center">
{#if $additionalDatasets.includes('speed')} {#if $additionalDatasets.includes('speed')}
<Check size="14" /> <Check size="14" />
@@ -150,10 +144,7 @@
? i18n._('quantities.speed') ? i18n._('quantities.speed')
: i18n._('quantities.pace')} : i18n._('quantities.pace')}
</ToggleGroup.Item> </ToggleGroup.Item>
<ToggleGroup.Item <ToggleGroup.Item value="hr" class="w-full flex flex-row justify-start">
class="p-0 pr-1.5 h-6 w-full gap-1.5 rounded flex justify-start data-[state=on]:bg-background data-[state=on]:hover:bg-accent hover:bg-accent hover:text-foreground"
value="hr"
>
<div class="w-6 flex justify-center items-center"> <div class="w-6 flex justify-center items-center">
{#if $additionalDatasets.includes('hr')} {#if $additionalDatasets.includes('hr')}
<Check size="14" /> <Check size="14" />
@@ -162,10 +153,7 @@
<HeartPulse size="15" /> <HeartPulse size="15" />
{i18n._('quantities.heartrate')} {i18n._('quantities.heartrate')}
</ToggleGroup.Item> </ToggleGroup.Item>
<ToggleGroup.Item <ToggleGroup.Item value="cad" class="w-full flex flex-row justify-start">
class="p-0 pr-1.5 h-6 w-full gap-1.5 rounded flex justify-start data-[state=on]:bg-background data-[state=on]:hover:bg-accent hover:bg-accent hover:text-foreground"
value="cad"
>
<div class="w-6 flex justify-center items-center"> <div class="w-6 flex justify-center items-center">
{#if $additionalDatasets.includes('cad')} {#if $additionalDatasets.includes('cad')}
<Check size="14" /> <Check size="14" />
@@ -174,10 +162,7 @@
<Orbit size="15" /> <Orbit size="15" />
{i18n._('quantities.cadence')} {i18n._('quantities.cadence')}
</ToggleGroup.Item> </ToggleGroup.Item>
<ToggleGroup.Item <ToggleGroup.Item value="atemp" class="w-full flex flex-row justify-start">
class="p-0 pr-1.5 h-6 w-full gap-1.5 rounded flex justify-start data-[state=on]:bg-background data-[state=on]:hover:bg-accent hover:bg-accent hover:text-foreground"
value="atemp"
>
<div class="w-6 flex justify-center items-center"> <div class="w-6 flex justify-center items-center">
{#if $additionalDatasets.includes('atemp')} {#if $additionalDatasets.includes('atemp')}
<Check size="14" /> <Check size="14" />
@@ -186,10 +171,7 @@
<Thermometer size="15" /> <Thermometer size="15" />
{i18n._('quantities.temperature')} {i18n._('quantities.temperature')}
</ToggleGroup.Item> </ToggleGroup.Item>
<ToggleGroup.Item <ToggleGroup.Item value="power" class="w-full flex flex-row justify-start">
class="p-0 pr-1.5 h-6 w-full gap-1.5 rounded flex justify-start data-[state=on]:bg-background data-[state=on]:hover:bg-accent hover:bg-accent hover:text-foreground"
value="power"
>
<div class="w-6 flex justify-center items-center"> <div class="w-6 flex justify-center items-center">
{#if $additionalDatasets.includes('power')} {#if $additionalDatasets.includes('power')}
<Check size="14" /> <Check size="14" />
@@ -45,26 +45,6 @@
settings.initialize(); settings.initialize();
function applyOptions() { function applyOptions() {
let downloads: Promise<GPXFile | null>[] = getFilesFromEmbeddingOptions(options).map(
(url) => {
return fetch(url)
.then((response) => response.blob())
.then((blob) => new File([blob], url.split('/').pop() ?? url))
.then(loadFile);
}
);
Promise.all(downloads).then((answers) => {
const files = answers.filter((file) => file !== null) as GPXFile[];
let ids: string[] = [];
files.forEach((file, index) => {
let id = `gpx-${index}-embed`;
file._data.id = id;
ids.push(id);
});
fileStateCollection.setEmbeddedFiles(files);
$fileOrder = ids;
selection.selectAll();
});
if (allowedEmbeddingBasemaps.includes(options.basemap)) { if (allowedEmbeddingBasemaps.includes(options.basemap)) {
$currentBasemap = options.basemap; $currentBasemap = options.basemap;
} }
@@ -90,6 +70,27 @@
].filter((dataset) => dataset !== null) ].filter((dataset) => dataset !== null)
); );
elevationFill.set(options.elevation.fill == 'none' ? undefined : options.elevation.fill); elevationFill.set(options.elevation.fill == 'none' ? undefined : options.elevation.fill);
let downloads: Promise<GPXFile | null>[] = getFilesFromEmbeddingOptions(options).map(
(url) => {
return fetch(url)
.then((response) => response.blob())
.then((blob) => new File([blob], url.split('/').pop() ?? url))
.then(loadFile);
}
);
Promise.all(downloads).then((answers) => {
const files = answers.filter((file) => file !== null) as GPXFile[];
let ids: string[] = [];
files.forEach((file, index) => {
let id = `gpx-${index}-embed`;
file._data.id = id;
ids.push(id);
});
fileStateCollection.setEmbeddedFiles(files);
$fileOrder = ids;
selection.selectAll();
});
} }
$effect(() => { $effect(() => {
@@ -117,13 +118,12 @@
{/if} {/if}
</div> </div>
<div <div
class="{options.elevation.show ? '' : 'h-10'} flex flex-row gap-2 px-2 sm:px-4" class="{options.elevation.show ? '' : 'h-10'} flex flex-row gap-2 p-2 sm:px-4"
style={options.elevation.show ? `height: ${options.elevation.height}px` : ''} style={options.elevation.show ? `height: ${options.elevation.height}px` : ''}
> >
<GPXStatistics <GPXStatistics
{gpxStatistics} {gpxStatistics}
{slicedGPXStatistics} {slicedGPXStatistics}
panelSize={options.elevation.height}
orientation={options.elevation.show ? 'vertical' : 'horizontal'} orientation={options.elevation.show ? 'vertical' : 'horizontal'}
/> />
{#if options.elevation.show} {#if options.elevation.show}
@@ -29,7 +29,7 @@ export const defaultEmbeddingOptions = {
key: '', key: '',
files: [], files: [],
ids: [], ids: [],
basemap: 'maptilerTopo', basemap: 'libertyTopo',
elevation: { elevation: {
show: true, show: true,
height: 170, height: 170,
@@ -90,6 +90,9 @@ export function getCleanedEmbeddingOptions(
delete cleanedOptions[key]; delete cleanedOptions[key];
} }
} }
if (cleanedOptions['key'] && cleanedOptions['key'] === PUBLIC_MAPTILER_KEY) {
delete cleanedOptions['key'];
}
return cleanedOptions; return cleanedOptions;
} }
@@ -123,7 +126,7 @@ export function convertOldEmbeddingOptions(options: URLSearchParams): any {
if (options.has('source')) { if (options.has('source')) {
let basemap = options.get('source')!; let basemap = options.get('source')!;
if (basemap === 'satellite') { if (basemap === 'satellite') {
newOptions.basemap = 'maptilerSatellite'; newOptions.basemap = 'libertySatellite';
} else if (basemap === 'otm') { } else if (basemap === 'otm') {
newOptions.basemap = 'openTopoMap'; newOptions.basemap = 'openTopoMap';
} else if (basemap === 'ohm') { } else if (basemap === 'ohm') {
@@ -100,7 +100,11 @@
</span> </span>
</div> </div>
<div class="w-full flex flex-row flex-wrap gap-2"> <div class="w-full flex flex-row flex-wrap gap-2">
<Button class="bg-support grow" href="https://ko-fi.com/gpxstudio" target="_blank"> <Button
class="bg-support grow"
href="https://opencollective.com/gpxstudio"
target="_blank"
>
{i18n._('menu.support_button')} {i18n._('menu.support_button')}
<span>🙏</span> <span>🙏</span>
</Button> </Button>
@@ -114,10 +114,10 @@
<ContextMenu.Trigger class="grow truncate"> <ContextMenu.Trigger class="grow truncate">
<Button <Button
variant="ghost" variant="ghost"
class="relative w-full p-0 overflow-hidden focus-visible:ring-0 focus-visible:ring-offset-0 {orientation === class="relative w-full p-0 overflow-hidden border-none focus-visible:ring-0 focus-visible:ring-offset-0 flex flex-row {orientation ===
'vertical' 'vertical'
? 'h-fit' ? 'h-7'
: 'h-9 px-1.5 shadow-md'} pointer-events-auto" : 'h-9 px-1.5'} pointer-events-auto"
> >
{#if item instanceof ListFileItem || item instanceof ListTrackItem} {#if item instanceof ListFileItem || item instanceof ListTrackItem}
<MetadataDialog bind:open={openEditMetadata} {node} {item} /> <MetadataDialog bind:open={openEditMetadata} {node} {item} />
@@ -126,7 +126,7 @@
{#if item.level === ListLevel.FILE || item.level === ListLevel.TRACK} {#if item.level === ListLevel.FILE || item.level === ListLevel.TRACK}
<div <div
class="absolute {orientation === 'vertical' class="absolute {orientation === 'vertical'
? 'top-0 bottom-0 right-1 w-1' ? 'top-0 bottom-0 right-0 w-1'
: 'top-0 h-1 left-0 right-0'}" : 'top-0 h-1 left-0 right-0'}"
style="background:linear-gradient(to {orientation === 'vertical' style="background:linear-gradient(to {orientation === 'vertical'
? 'bottom' ? 'bottom'
@@ -139,7 +139,7 @@
></div> ></div>
{/if} {/if}
<span <span
class="w-full text-left truncate py-1 flex flex-row items-center {hidden class="grow text-left truncate ml-1 flex flex-row items-center {hidden
? 'text-muted-foreground' ? 'text-muted-foreground'
: ''} {$cut && $copied?.some((i) => i.getFullId() === item.getFullId()) : ''} {$cut && $copied?.some((i) => i.getFullId() === item.getFullId())
? 'text-muted-foreground' ? 'text-muted-foreground'
@@ -16,7 +16,6 @@
</script> </script>
<Button <Button
size="sm"
class="justify-start {className}" class="justify-start {className}"
variant="outline" variant="outline"
onclick={() => { onclick={() => {
@@ -39,7 +39,6 @@
/> />
{#if trackpoint.fileId === undefined} {#if trackpoint.fileId === undefined}
<Button <Button
size="sm"
variant="outline" variant="outline"
class="justify-start" class="justify-start"
href={`https://www.openstreetmap.org/edit?#map=${(($map?.getZoom() ?? 17) + 1).toFixed(0)}/${trackpoint.item.getLatitude().toFixed(5)}/${trackpoint.item.getLongitude().toFixed(5)}`} href={`https://www.openstreetmap.org/edit?#map=${(($map?.getZoom() ?? 17) + 1).toFixed(0)}/${trackpoint.item.getLatitude().toFixed(5)}/${trackpoint.item.getLongitude().toFixed(5)}`}
@@ -88,7 +88,6 @@
<CopyCoordinates coordinates={waypoint.item.attributes} /> <CopyCoordinates coordinates={waypoint.item.attributes} />
{#if $currentTool === Tool.WAYPOINT && selected} {#if $currentTool === Tool.WAYPOINT && selected}
<Button <Button
class="p-1 has-[>svg]:px-2 h-8"
variant="outline" variant="outline"
onclick={() => { onclick={() => {
if (waypoint.fileId) { if (waypoint.fileId) {
@@ -5,7 +5,7 @@ import { get } from 'svelte/store';
import { map } from '$lib/components/map/map'; import { map } from '$lib/components/map/map';
import { allHidden } from '$lib/logic/hidden'; import { allHidden } from '$lib/logic/hidden';
import type { GeoJSONSource } from 'maplibre-gl'; import type { GeoJSONSource } from 'maplibre-gl';
import { ANCHOR_LAYER_KEY } from '../style'; import { ANCHOR_LAYER_KEY } from '$lib/components/map/style';
const { distanceMarkers, distanceUnits } = settings; const { distanceMarkers, distanceUnits } = settings;
@@ -74,7 +74,7 @@ export class DistanceMarkers {
layout: { layout: {
'text-field': ['get', 'distance'], 'text-field': ['get', 'distance'],
'text-size': 14, 'text-size': 14,
'text-font': ['Open Sans Bold'], 'text-font': ['Noto Sans Bold'],
}, },
paint: { paint: {
'text-color': 'black', 'text-color': 'black',
@@ -28,7 +28,7 @@ import { fileActions } from '$lib/logic/file-actions';
import { splitAs } from '$lib/components/toolbar/tools/scissors/scissors'; import { splitAs } from '$lib/components/toolbar/tools/scissors/scissors';
import { mapCursor, MapCursorState } from '$lib/logic/map-cursor'; import { mapCursor, MapCursorState } from '$lib/logic/map-cursor';
import { ANCHOR_LAYER_KEY } from '$lib/components/map/style'; import { ANCHOR_LAYER_KEY } from '$lib/components/map/style';
import { gpxColors } from './gpx-layers'; import { gpxColors } from '$lib/components/map/gpx-layer/gpx-layers';
const colors = [ const colors = [
'#ff0000', '#ff0000',
@@ -255,13 +255,12 @@ export class GPXLayer {
'text-keep-upright': false, 'text-keep-upright': false,
'text-max-angle': 361, 'text-max-angle': 361,
'text-allow-overlap': true, 'text-allow-overlap': true,
'text-font': ['Open Sans Bold'], 'text-font': ['Noto Sans Bold'],
'symbol-placement': 'line', 'symbol-placement': 'line',
'symbol-spacing': 20, 'symbol-spacing': 20,
}, },
paint: { paint: {
'text-color': 'white', 'text-color': 'white',
'text-opacity': 0.7,
'text-halo-width': 0.2, 'text-halo-width': 0.2,
'text-halo-color': 'white', 'text-halo-color': 'white',
}, },
@@ -31,7 +31,7 @@ export class StartEndMarkers {
unsubscribes: (() => void)[] = []; unsubscribes: (() => void)[] = [];
constructor() { constructor() {
map.onLoad(() => this.update()); map.onLoad((map_) => map_.on('style.load', this.updateBinded));
this.unsubscribes.push(gpxStatistics.subscribe(this.updateBinded)); this.unsubscribes.push(gpxStatistics.subscribe(this.updateBinded));
this.unsubscribes.push(slicedGPXStatistics.subscribe(this.updateBinded)); this.unsubscribes.push(slicedGPXStatistics.subscribe(this.updateBinded));
this.unsubscribes.push(hoveredPoint.subscribe(this.updateBinded)); this.unsubscribes.push(hoveredPoint.subscribe(this.updateBinded));
@@ -232,16 +232,13 @@
<div class="flex flex-col"> <div class="flex flex-col">
{#if $customBasemapOrder.length > 0} {#if $customBasemapOrder.length > 0}
<div class="px-3 py-2">
<div class="flex flex-row items-center gap-1 font-semibold mb-2"> <div class="flex flex-row items-center gap-1 font-semibold mb-2">
<Map size="16" /> <Map size="16" />
{i18n._('layers.label.basemaps')} {i18n._('layers.label.basemaps')}
<div class="grow">
<Separator />
</div> </div>
</div>
{/if}
<div <div
class="ml-1.5 flex flex-col gap-1 {$customBasemapOrder.length > 0 ? 'mb-2' : ''}" class="ml-1.5 flex flex-col gap-1"
use:dndzone={{ use:dndzone={{
items: customBasemapItems, items: customBasemapItems,
type: 'basemap', type: 'basemap',
@@ -258,14 +255,17 @@
onfinalize={(e) => { onfinalize={(e) => {
customBasemapItems = e.detail.items; customBasemapItems = e.detail.items;
$customBasemapOrder = customBasemapItems.map((item) => item.id); $customBasemapOrder = customBasemapItems.map((item) => item.id);
$selectedBasemapTree.basemaps['custom'] = customBasemapItems.reduce((acc, item) => { $selectedBasemapTree.basemaps['custom'] = customBasemapItems.reduce(
(acc, item) => {
acc[item.id] = true; acc[item.id] = true;
return acc; return acc;
}, {}); },
{}
);
}} }}
> >
{#each customBasemapItems as item (item.id)} {#each customBasemapItems as item (item.id)}
<div class="flex flex-row items-center gap-2"> <div class="flex flex-row items-center gap-1">
<Move size="12" /> <Move size="12" />
<span class="grow">{item.name}</span> <span class="grow">{item.name}</span>
<Button <Button
@@ -287,17 +287,18 @@
</div> </div>
{/each} {/each}
</div> </div>
</div>
<Separator />
{/if}
{#if $customOverlayOrder.length > 0} {#if $customOverlayOrder.length > 0}
<div class="px-3 py-2">
<div class="flex flex-row items-center gap-1 font-semibold mb-2"> <div class="flex flex-row items-center gap-1 font-semibold mb-2">
<Layers2 size="16" /> <Layers2 size="16" />
{i18n._('layers.label.overlays')} {i18n._('layers.label.overlays')}
<div class="grow"> <div class="grow"></div>
<Separator />
</div> </div>
</div>
{/if}
<div <div
class="ml-1.5 flex flex-col gap-1 {$customOverlayOrder.length > 0 ? 'mb-2' : ''}" class="ml-1.5 flex flex-col gap-1"
use:dndzone={{ use:dndzone={{
items: customOverlayItems, items: customOverlayItems,
type: 'overlay', type: 'overlay',
@@ -314,14 +315,17 @@
onfinalize={(e) => { onfinalize={(e) => {
customOverlayItems = e.detail.items; customOverlayItems = e.detail.items;
$customOverlayOrder = customOverlayItems.map((item) => item.id); $customOverlayOrder = customOverlayItems.map((item) => item.id);
$selectedOverlayTree.overlays['custom'] = customOverlayItems.reduce((acc, item) => { $selectedOverlayTree.overlays['custom'] = customOverlayItems.reduce(
(acc, item) => {
acc[item.id] = true; acc[item.id] = true;
return acc; return acc;
}, {}); },
{}
);
}} }}
> >
{#each customOverlayItems as item (item.id)} {#each customOverlayItems as item (item.id)}
<div class="flex flex-row items-center gap-2"> <div class="flex flex-row items-center gap-1">
<Move size="12" /> <Move size="12" />
<span class="grow">{item.name}</span> <span class="grow">{item.name}</span>
<Button <Button
@@ -343,9 +347,12 @@
</div> </div>
{/each} {/each}
</div> </div>
<Card.Root class="py-0 gap-0 shadow-none"> </div>
<Separator />
{/if}
<Card.Root class="py-0 gap-0 shadow-none ring-0">
<Card.Header class="p-3"> <Card.Header class="p-3">
<Card.Title class="text-base"> <Card.Title class="text-sm font-semibold">
{#if selectedLayerId} {#if selectedLayerId}
{i18n._('layers.custom_layers.edit')} {i18n._('layers.custom_layers.edit')}
{:else} {:else}
@@ -353,7 +360,7 @@
{/if} {/if}
</Card.Title> </Card.Title>
</Card.Header> </Card.Header>
<Card.Content class="p-3 pt-0"> <Card.Content class="px-3 py-2">
<fieldset class="flex flex-col gap-2"> <fieldset class="flex flex-col gap-2">
<Label for="name">{i18n._('menu.metadata.name')}</Label> <Label for="name">{i18n._('menu.metadata.name')}</Label>
<Input bind:value={name} id="name" class="h-8" /> <Input bind:value={name} id="name" class="h-8" />
@@ -410,7 +417,7 @@
</div> </div>
</RadioGroup.Root> </RadioGroup.Root>
{#if selectedLayerId} {#if selectedLayerId}
<div class="mt-2 flex flex-row gap-2"> <div class="mt-2 flex flex-row gap-1">
<Button variant="outline" onclick={createLayer} class="grow"> <Button variant="outline" onclick={createLayer} class="grow">
<Save size="16" /> <Save size="16" />
{i18n._('layers.custom_layers.update')} {i18n._('layers.custom_layers.update')}
@@ -121,7 +121,7 @@
<Accordion.Root class="flex flex-col" bind:value={accordionValue} type="single"> <Accordion.Root class="flex flex-col" bind:value={accordionValue} type="single">
<Accordion.Item value="layer-selection" class="flex flex-col"> <Accordion.Item value="layer-selection" class="flex flex-col">
<Accordion.Trigger>{i18n._('layers.selection')}</Accordion.Trigger> <Accordion.Trigger>{i18n._('layers.selection')}</Accordion.Trigger>
<Accordion.Content class="grow flex flex-col border rounded"> <Accordion.Content class="grow flex flex-col border rounded-md mb-1.5">
<div class="py-2 pl-3 pr-2"> <div class="py-2 pl-3 pr-2">
<LayerTree <LayerTree
layerTree={basemapTree} layerTree={basemapTree}
@@ -152,7 +152,9 @@
</Accordion.Item> </Accordion.Item>
<Accordion.Item value="overlay-opacity"> <Accordion.Item value="overlay-opacity">
<Accordion.Trigger>{i18n._('layers.opacity')}</Accordion.Trigger> <Accordion.Trigger>{i18n._('layers.opacity')}</Accordion.Trigger>
<Accordion.Content class="flex flex-col gap-3 overflow-visible"> <Accordion.Content
class="flex flex-col gap-3 overflow-visible border rounded-md px-3 py-2 mb-1.5"
>
<div class="flex flex-row gap-6 items-center"> <div class="flex flex-row gap-6 items-center">
<Label> <Label>
{i18n._('layers.custom_layers.overlay')} {i18n._('layers.custom_layers.overlay')}
@@ -231,10 +233,10 @@
<Accordion.Item value="custom-layers"> <Accordion.Item value="custom-layers">
<Accordion.Trigger>{i18n._('layers.custom_layers.title')}</Accordion.Trigger <Accordion.Trigger>{i18n._('layers.custom_layers.title')}</Accordion.Trigger
> >
<Accordion.Content> <Accordion.Content
<ScrollArea> class="flex flex-col overflow-visible border rounded-md p-0 mb-1.5"
>
<CustomLayers /> <CustomLayers />
</ScrollArea>
</Accordion.Content> </Accordion.Content>
</Accordion.Item> </Accordion.Item>
<Accordion.Item value="terrain-source"> <Accordion.Item value="terrain-source">
@@ -88,6 +88,14 @@
<span class="font-mono">{key}</span> <span class="font-mono">{key}</span>
{#if key === 'website' || key.startsWith('website:') || key.endsWith(':website') || key === 'contact:facebook' || key === 'contact:instagram' || key === 'contact:twitter'} {#if key === 'website' || key.startsWith('website:') || key.endsWith(':website') || key === 'contact:facebook' || key === 'contact:instagram' || key === 'contact:twitter'}
<a href={value} target="_blank" class="text-link underline">{value}</a> <a href={value} target="_blank" class="text-link underline">{value}</a>
{:else if key === 'wikipedia' || key.startsWith('wikipedia:') || key.endsWith(':wikipedia')}
<a
href="https://wikipedia.org/wiki/{value}"
target="_blank"
class="text-link underline"
>
{value}
</a>
{:else if key === 'phone' || key === 'contact:phone'} {:else if key === 'phone' || key === 'contact:phone'}
<a href={'tel:' + value} class="text-link underline">{value}</a> <a href={'tel:' + value} class="text-link underline">{value}</a>
{:else if key === 'email' || key === 'contact:email'} {:else if key === 'email' || key === 'contact:email'}
@@ -7,7 +7,7 @@ import { MapPopup } from '$lib/components/map/map-popup';
import { settings } from '$lib/logic/settings'; import { settings } from '$lib/logic/settings';
import { db } from '$lib/db'; import { db } from '$lib/db';
import type { GeoJSONSource } from 'maplibre-gl'; import type { GeoJSONSource } from 'maplibre-gl';
import { ANCHOR_LAYER_KEY } from '../style'; import { ANCHOR_LAYER_KEY } from '$lib/components/map/style';
import type { MapLayerEventManager } from '$lib/components/map/map-layer-event-manager'; import type { MapLayerEventManager } from '$lib/components/map/map-layer-event-manager';
import { loadSVGIcon } from '$lib/utils'; import { loadSVGIcon } from '$lib/utils';
@@ -24,7 +24,7 @@ liveQuery(() => db.overpassdata.toArray()).subscribe((pois) => {
}); });
export class OverpassLayer { export class OverpassLayer {
overpassUrl = 'https://maps.mail.ru/osm/tools/overpass/api/interpreter'; overpassUrl = 'https://overpass.gpx.studio/api/interpreter';
minZoom = 12; minZoom = 12;
queryZoom = 12; queryZoom = 12;
expirationTime = 7 * 24 * 3600 * 1000; expirationTime = 7 * 24 * 3600 * 1000;
@@ -1,5 +1,4 @@
import type { LayerTreeType } from '$lib/assets/layers'; import type { LayerTreeType } from '$lib/assets/layers';
import { writable } from 'svelte/store';
export function anySelectedLayer(node: LayerTreeType) { export function anySelectedLayer(node: LayerTreeType) {
return ( return (
@@ -142,6 +142,7 @@ export class MapLayerEventManager {
} }
private _handleMouseMove(e: maplibregl.MapMouseEvent) { private _handleMouseMove(e: maplibregl.MapMouseEvent) {
if (e.originalEvent.buttons > 0) return;
const featuresByLayer = this._getRenderedFeaturesByLayer(e); const featuresByLayer = this._getRenderedFeaturesByLayer(e);
Object.keys(this._listeners).forEach((layerId) => { Object.keys(this._listeners).forEach((layerId) => {
const features = featuresByLayer[layerId] || []; const features = featuresByLayer[layerId] || [];
+1 -1
View File
@@ -54,7 +54,7 @@ export class MapLibreGLMap {
zoom: 0, zoom: 0,
hash: hash, hash: hash,
boxZoom: false, boxZoom: false,
maxPitch: 85, maxPitch: 90,
}); });
this.layerEventManager = new MapLayerEventManager(map); this.layerEventManager = new MapLayerEventManager(map);
map.addControl( map.addControl(
@@ -53,7 +53,7 @@
<CustomControl class="w-[29px] h-[29px] shrink-0"> <CustomControl class="w-[29px] h-[29px] shrink-0">
<ButtonWithTooltip <ButtonWithTooltip
variant="ghost" variant="ghost"
class="w-full h-full" class="w-full h-full border-none rounded-sm"
side="left" side="left"
label={i18n._('menu.toggle_street_view')} label={i18n._('menu.toggle_street_view')}
onclick={() => { onclick={() => {
@@ -2,7 +2,7 @@ import maplibregl, { type LayerSpecification, type VectorSourceSpecification } f
import { Viewer, type ViewerBearingEvent } from 'mapillary-js/dist/mapillary.module'; import { Viewer, type ViewerBearingEvent } from 'mapillary-js/dist/mapillary.module';
import 'mapillary-js/dist/mapillary.css'; import 'mapillary-js/dist/mapillary.css';
import { mapCursor, MapCursorState } from '$lib/logic/map-cursor'; import { mapCursor, MapCursorState } from '$lib/logic/map-cursor';
import { ANCHOR_LAYER_KEY } from '../style'; import { ANCHOR_LAYER_KEY } from '$lib/components/map/style';
import type { MapLayerEventManager } from '$lib/components/map/map-layer-event-manager'; import type { MapLayerEventManager } from '$lib/components/map/map-layer-event-manager';
const mapillarySource: VectorSourceSpecification = { const mapillarySource: VectorSourceSpecification = {
+67 -15
View File
@@ -10,7 +10,8 @@ import {
import { getLayers } from '$lib/components/map/layer-control/utils'; import { getLayers } from '$lib/components/map/layer-control/utils';
import { i18n } from '$lib/i18n.svelte'; import { i18n } from '$lib/i18n.svelte';
const { currentBasemap, currentOverlays, customLayers, opacities, terrainSource } = settings; const { currentBasemap, currentOverlays, customLayers, opacities, terrainSource, distanceUnits } =
settings;
const emptySource: maplibregl.GeoJSONSourceSpecification = { const emptySource: maplibregl.GeoJSONSourceSpecification = {
type: 'geojson', type: 'geojson',
@@ -57,15 +58,24 @@ export class StyleManager {
opacities.subscribe(() => this.updateOverlays()); opacities.subscribe(() => this.updateOverlays());
terrainSource.subscribe(() => this.updateTerrain()); terrainSource.subscribe(() => this.updateTerrain());
customLayers.subscribe(() => this.updateBasemap()); customLayers.subscribe(() => this.updateBasemap());
distanceUnits.subscribe(() => {
const map = get(this._map);
if (map && (map.getLayer('contours_m') || map.getLayer('contours_ft'))) {
this.updateBasemap();
}
});
} }
updateBasemap() { updateBasemap() {
const map_ = get(this._map); const map_ = get(this._map);
if (!map_) return; if (!map_) return;
this.buildStyle().then((style) => map_.setStyle(style)); let basemap = get(currentBasemap);
this.buildStyle(basemap).then((style) => {
if (get(currentBasemap) === basemap) map_.setStyle(style);
});
} }
async buildStyle(): Promise<maplibregl.StyleSpecification> { async buildStyle(basemap: string): Promise<maplibregl.StyleSpecification> {
const custom = get(customLayers); const custom = get(customLayers);
const style: maplibregl.StyleSpecification = { const style: maplibregl.StyleSpecification = {
@@ -79,10 +89,26 @@ export class StyleManager {
layers: [], layers: [],
}; };
let basemap = get(currentBasemap);
const basemapInfo = basemaps[basemap] ?? custom[basemap]?.value ?? basemaps[defaultBasemap]; const basemapInfo = basemaps[basemap] ?? custom[basemap]?.value ?? basemaps[defaultBasemap];
const basemapStyle = await this.get(basemapInfo);
let basemapStyle = basemaps.openStreetMap as maplibregl.StyleSpecification;
try {
basemapStyle = await this.get(basemapInfo);
for (const source in basemapStyle.sources) {
const src = basemapStyle.sources[source];
if (
src &&
typeof src === 'object' &&
'url' in src &&
typeof src.url === 'string' &&
src.url.includes(maptilerKeyPlaceHolder)
) {
src.url = src.url.replace(maptilerKeyPlaceHolder, this._maptilerKey);
}
}
} catch (e) {
console.error(e.message);
}
this.merge(style, basemapStyle); this.merge(style, basemapStyle);
const terrain = this.getCurrentTerrain(); const terrain = this.getCurrentTerrain();
@@ -107,16 +133,21 @@ export class StyleManager {
if (!layers[overlay]) { if (!layers[overlay]) {
if (this._pastOverlays.has(overlay)) { if (this._pastOverlays.has(overlay)) {
const overlayInfo = custom[overlay]?.value ?? overlays[overlay]; const overlayInfo = custom[overlay]?.value ?? overlays[overlay];
try {
const overlayStyle = await this.get(overlayInfo); const overlayStyle = await this.get(overlayInfo);
for (let layer of overlayStyle.layers ?? []) { for (let layer of overlayStyle.layers ?? []) {
if (map_.getLayer(layer.id)) { if (map_.getLayer(layer.id)) {
map_.removeLayer(layer.id); map_.removeLayer(layer.id);
} }
} }
} catch (e) {
// Should not happen
}
this._pastOverlays.delete(overlay); this._pastOverlays.delete(overlay);
} }
} else { } else {
const overlayInfo = custom[overlay]?.value ?? overlays[overlay]; const overlayInfo = custom[overlay]?.value ?? overlays[overlay];
try {
const overlayStyle = await this.get(overlayInfo); const overlayStyle = await this.get(overlayInfo);
const opacity = overlayOpacities[overlay]; const opacity = overlayOpacities[overlay];
@@ -144,8 +175,10 @@ export class StyleManager {
map_.addLayer(layer, ANCHOR_LAYER_KEY.overlays); map_.addLayer(layer, ANCHOR_LAYER_KEY.overlays);
} }
} }
this._pastOverlays.add(overlay); this._pastOverlays.add(overlay);
} catch (e) {
console.error(e.message);
}
} }
} }
} catch (e) {} } catch (e) {}
@@ -174,10 +207,10 @@ export class StyleManager {
): Promise<maplibregl.StyleSpecification> { ): Promise<maplibregl.StyleSpecification> {
if (typeof styleInfo === 'string') { if (typeof styleInfo === 'string') {
let styleUrl = styleInfo as string; let styleUrl = styleInfo as string;
if (styleUrl.includes(maptilerKeyPlaceHolder)) {
styleUrl = styleUrl.replace(maptilerKeyPlaceHolder, this._maptilerKey);
}
const response = await fetch(styleUrl, { cache: 'force-cache' }); const response = await fetch(styleUrl, { cache: 'force-cache' });
if (!response.ok) {
throw new Error(`HTTP error fetching style "${styleInfo}": ${response.status}`);
}
const style = await response.json(); const style = await response.json();
return style; return style;
} else { } else {
@@ -187,18 +220,40 @@ export class StyleManager {
merge(style: maplibregl.StyleSpecification, other: maplibregl.StyleSpecification) { merge(style: maplibregl.StyleSpecification, other: maplibregl.StyleSpecification) {
style.sources = { ...style.sources, ...other.sources }; style.sources = { ...style.sources, ...other.sources };
const units = get(distanceUnits);
for (let layer of other.layers ?? []) { for (let layer of other.layers ?? []) {
if ('source' in layer) {
if (layer.source == 'contours_m' && units === 'imperial') continue;
if (layer.source == 'contours_ft' && units !== 'imperial') continue;
}
if (layer.type === 'symbol' && layer.layout && layer.layout['text-field']) { if (layer.type === 'symbol' && layer.layout && layer.layout['text-field']) {
const textField = layer.layout['text-field']; const textField = layer.layout['text-field'];
if (Array.isArray(textField)) {
if ( if (
Array.isArray(textField) && textField.length == 4 &&
textField.length >= 2 && Array.isArray(textField[3]) &&
textField[3][0] === 'coalesce' &&
Array.isArray(textField[3][1]) &&
textField[3][1][0] === 'get' &&
typeof textField[3][1][1] === 'string' &&
textField[3][1][1].startsWith('name')
) {
// OpenFreeMap styles
layer.layout['text-field'] = [
'coalesce',
['get', `name:${i18n.lang}`],
['get', 'name'],
];
}
if (
textField.length == 3 &&
textField[0] === 'coalesce' && textField[0] === 'coalesce' &&
Array.isArray(textField[1]) && Array.isArray(textField[1]) &&
textField[1][0] === 'get' && textField[1][0] === 'get' &&
typeof textField[1][1] === 'string' && typeof textField[1][1] === 'string' &&
textField[1][1].startsWith('name') textField[1][1].startsWith('name')
) { ) {
// OpenMapTiles styles
layer.layout['text-field'] = [ layer.layout['text-field'] = [
'coalesce', 'coalesce',
['get', `name:${i18n.lang}`], ['get', `name:${i18n.lang}`],
@@ -206,6 +261,7 @@ export class StyleManager {
]; ];
} }
} }
}
style.layers.push(layer); style.layers.push(layer);
} }
if (other.sprite && !style.sprite) { if (other.sprite && !style.sprite) {
@@ -218,10 +274,6 @@ export class StyleManager {
getCurrentTerrain() { getCurrentTerrain() {
const terrain = get(terrainSource); const terrain = get(terrainSource);
const source = terrainSources[terrain];
if (source.url && source.url.includes(maptilerKeyPlaceHolder)) {
source.url = source.url.replace(maptilerKeyPlaceHolder, this._maptilerKey);
}
const map_ = get(this._map); const map_ = get(this._map);
return { return {
source: terrain, source: terrain,
@@ -17,7 +17,7 @@
<div class="flex flex-col gap-3 w-full max-w-80 {props.class ?? ''}"> <div class="flex flex-col gap-3 w-full max-w-80 {props.class ?? ''}">
<Button <Button
variant="outline" variant="outline"
class="whitespace-normal h-fit" class="whitespace-normal h-fit min-h-8 py-1"
disabled={!validSelection} disabled={!validSelection}
onclick={() => fileActions.addElevationToSelection()} onclick={() => fileActions.addElevationToSelection()}
> >
@@ -76,7 +76,7 @@
{/if} {/if}
<Button <Button
variant="outline" variant="outline"
class="whitespace-normal h-fit" class="whitespace-normal h-fit min-h-8 py-1"
disabled={(mergeType === MergeType.TRACES && !canMergeTraces) || disabled={(mergeType === MergeType.TRACES && !canMergeTraces) ||
(mergeType === MergeType.CONTENTS && !canMergeContents)} (mergeType === MergeType.CONTENTS && !canMergeContents)}
onclick={() => { onclick={() => {
@@ -185,8 +185,8 @@
<div class="flex flex-col gap-3 w-full max-w-80 {props.class ?? ''}"> <div class="flex flex-col gap-3 w-full max-w-80 {props.class ?? ''}">
<fieldset class="flex flex-col gap-2"> <fieldset class="flex flex-col gap-2">
<div class="flex flex-row gap-2 justify-center"> <div class="flex flex-row gap-1.5 justify-center">
<div class="flex flex-col gap-2 grow"> <div class="flex flex-col gap-1 grow">
<Label for="speed" class="flex flex-row"> <Label for="speed" class="flex flex-row">
<Zap size="16" /> <Zap size="16" />
{#if $velocityUnits === 'speed'} {#if $velocityUnits === 'speed'}
@@ -239,7 +239,7 @@
{/if} {/if}
</div> </div>
</div> </div>
<div class="flex flex-col gap-2 grow"> <div class="flex flex-col gap-1 grow">
<Label for="duration" class="flex flex-row"> <Label for="duration" class="flex flex-row">
<Timer size="16" /> <Timer size="16" />
{i18n._('toolbar.time.total_time')} {i18n._('toolbar.time.total_time')}
@@ -253,11 +253,12 @@
/> />
</div> </div>
</div> </div>
<div class="flex flex-col gap-1">
<Label class="flex flex-row"> <Label class="flex flex-row">
<CirclePlay size="16" /> <CirclePlay size="16" />
{i18n._('toolbar.time.start')} {i18n._('toolbar.time.start')}
</Label> </Label>
<div class="flex flex-row gap-2"> <div class="flex flex-row gap-1.5">
<DatePicker <DatePicker
bind:value={startDate} bind:value={startDate}
disabled={!canUpdate} disabled={!canUpdate}
@@ -279,11 +280,13 @@
}} }}
/> />
</div> </div>
</div>
<div class="flex flex-col gap-1">
<Label class="flex flex-row"> <Label class="flex flex-row">
<CircleStop size="16" /> <CircleStop size="16" />
{i18n._('toolbar.time.end')} {i18n._('toolbar.time.end')}
</Label> </Label>
<div class="flex flex-row gap-2"> <div class="flex flex-row gap-1.5">
<DatePicker <DatePicker
bind:value={endDate} bind:value={endDate}
disabled={!canUpdate} disabled={!canUpdate}
@@ -305,6 +308,7 @@
}} }}
/> />
</div> </div>
</div>
{#if $gpxStatistics.global.time.moving === 0 || $gpxStatistics.global.time.moving === undefined} {#if $gpxStatistics.global.time.moving === 0 || $gpxStatistics.global.time.moving === undefined}
<div class="mt-0.5 flex flex-row gap-1 items-center"> <div class="mt-0.5 flex flex-row gap-1 items-center">
<Checkbox id="artificial-time" bind:checked={artificial} disabled={!canUpdate} /> <Checkbox id="artificial-time" bind:checked={artificial} disabled={!canUpdate} />
@@ -314,11 +318,11 @@
</div> </div>
{/if} {/if}
</fieldset> </fieldset>
<div class="flex flex-row gap-2 items-center"> <div class="flex flex-row gap-1.5 items-center">
<Button <Button
variant="outline" variant="outline"
disabled={!canUpdate} disabled={!canUpdate}
class="grow whitespace-normal h-fit" class="grow shrink whitespace-normal h-fit min-h-8 py-1"
onclick={() => { onclick={() => {
let effectiveSpeed = getSpeed(); let effectiveSpeed = getSpeed();
if ( if (
@@ -14,7 +14,7 @@
let props: { class?: string } = $props(); let props: { class?: string } = $props();
let sliderValue = $state([50]); let sliderValue = $state(50);
const maxTolerance = 10000; const maxTolerance = 10000;
let validSelection = $derived( let validSelection = $derived(
@@ -25,7 +25,7 @@
$effect(() => { $effect(() => {
tolerance.set( tolerance.set(
minTolerance * 2 ** (sliderValue[0] / (100 / Math.log2(maxTolerance / minTolerance))) minTolerance * 2 ** (sliderValue / (100 / Math.log2(maxTolerance / minTolerance)))
); );
}); });
@@ -36,7 +36,7 @@
<div class="flex flex-col gap-3 w-full max-w-80 {props.class ?? ''}"> <div class="flex flex-col gap-3 w-full max-w-80 {props.class ?? ''}">
<div class="p-2"> <div class="p-2">
<Slider bind:value={sliderValue} min={0} max={100} step={1} type="multiple" /> <Slider bind:value={sliderValue} min={0} max={100} step={1} type="single" />
</div> </div>
<Label class="flex flex-row justify-between"> <Label class="flex flex-row justify-between">
<span>{i18n._('toolbar.reduce.tolerance')}</span> <span>{i18n._('toolbar.reduce.tolerance')}</span>
@@ -163,6 +163,9 @@ export class ReducedGPXLayerCollection {
reduce() { reduce() {
let itemsAndPoints = new Map<ListItem, TrackPoint[]>(); let itemsAndPoints = new Map<ListItem, TrackPoint[]>();
this._simplified.forEach(([item, maxPts, points], itemFullId) => { this._simplified.forEach(([item, maxPts, points], itemFullId) => {
if (!get(selection).hasAnyParent(item)) {
return;
}
itemsAndPoints.set( itemsAndPoints.set(
item, item,
points points
@@ -191,7 +191,7 @@
<ButtonWithTooltip <ButtonWithTooltip
label={i18n._('toolbar.routing.reverse.tooltip')} label={i18n._('toolbar.routing.reverse.tooltip')}
variant="outline" variant="outline"
class="gap-1 text-xs" class="gap-1 text-xs px-1.5 py-1.5 h-fit"
disabled={!validSelection} disabled={!validSelection}
onclick={fileActions.reverseSelection} onclick={fileActions.reverseSelection}
> >
@@ -200,7 +200,7 @@
<ButtonWithTooltip <ButtonWithTooltip
label={i18n._('toolbar.routing.route_back_to_start.tooltip')} label={i18n._('toolbar.routing.route_back_to_start.tooltip')}
variant="outline" variant="outline"
class="gap-1 text-xs" class="gap-1 text-xs px-1.5 py-1.5 h-fit"
disabled={!validSelection} disabled={!validSelection}
onclick={() => { onclick={() => {
const selected = selection.getOrderedSelection(); const selected = selection.getOrderedSelection();
@@ -236,14 +236,14 @@
<ButtonWithTooltip <ButtonWithTooltip
label={i18n._('toolbar.routing.round_trip.tooltip')} label={i18n._('toolbar.routing.round_trip.tooltip')}
variant="outline" variant="outline"
class="gap-1 text-xs" class="gap-1 text-xs px-1.5 py-1.5 h-fit"
disabled={!validSelection} disabled={!validSelection}
onclick={fileActions.createRoundTripForSelection} onclick={fileActions.createRoundTripForSelection}
> >
<Repeat class="size-3" />{i18n._('toolbar.routing.round_trip.button')} <Repeat class="size-3" />{i18n._('toolbar.routing.round_trip.button')}
</ButtonWithTooltip> </ButtonWithTooltip>
</div> </div>
<div class="w-full flex flex-row gap-2 items-end justify-between"> <div class="w-full flex flex-row gap-1 items-end justify-between">
<Help link={getURLForLanguage(i18n.lang, '/help/toolbar/routing')}> <Help link={getURLForLanguage(i18n.lang, '/help/toolbar/routing')}>
{#if !validSelection} {#if !validSelection}
{i18n._('toolbar.routing.help_no_file')} {i18n._('toolbar.routing.help_no_file')}
@@ -55,9 +55,12 @@ export class RoutingControls {
fileUnsubscribe: () => void = () => {}; fileUnsubscribe: () => void = () => {};
unsubscribes: Function[] = []; unsubscribes: Function[] = [];
updateControlsBinded: () => void = this.updateControls.bind(this);
appendAnchorBinded: (e: MapMouseEvent) => void = this.appendAnchor.bind(this); appendAnchorBinded: (e: MapMouseEvent) => void = this.appendAnchor.bind(this);
addIntermediateAnchorBinded: (e: MapMouseEvent) => void = this.addIntermediateAnchor.bind(this);
draggedAnchorIndex: number | null = null; draggedAnchorIndex: number | null = null;
lastDraggedAnchorEventTime: number = 0;
draggingStartingPosition: maplibregl.Point = new maplibregl.Point(0, 0); draggingStartingPosition: maplibregl.Point = new maplibregl.Point(0, 0);
onMouseEnterBinded: () => void = this.onMouseEnter.bind(this); onMouseEnterBinded: () => void = this.onMouseEnter.bind(this);
onMouseLeaveBinded: () => void = this.onMouseLeave.bind(this); onMouseLeaveBinded: () => void = this.onMouseLeave.bind(this);
@@ -84,7 +87,7 @@ export class RoutingControls {
this.file = file; this.file = file;
for (let zoom = MIN_ANCHOR_ZOOM; zoom <= MAX_ANCHOR_ZOOM; zoom++) { for (let zoom = MIN_ANCHOR_ZOOM; zoom <= MAX_ANCHOR_ZOOM; zoom++) {
this.layers.set(zoom, { this.layers.set(zoom, {
id: `routing-controls-${zoom}`, id: `routing-controls-${this.fileId}-${zoom}`,
anchors: [], anchors: [],
}); });
} }
@@ -129,10 +132,12 @@ export class RoutingControls {
this.loadIcons(); this.loadIcons();
map_.on('style.load', this.updateControlsBinded);
map_.on('click', this.appendAnchorBinded); map_.on('click', this.appendAnchorBinded);
layerEventManager.on('mousemove', this.fileId, this.showTemporaryAnchorBinded); layerEventManager.on('mousemove', this.fileId, this.showTemporaryAnchorBinded);
layerEventManager.on('click', this.fileId, this.addIntermediateAnchorBinded);
this.fileUnsubscribe = this.file.subscribe(this.updateControls.bind(this)); this.fileUnsubscribe = this.file.subscribe(this.updateControlsBinded);
} }
updateControls() { updateControls() {
@@ -232,8 +237,10 @@ export class RoutingControls {
this.active = false; this.active = false;
map_?.off('style.load', this.updateControlsBinded);
map_?.off('click', this.appendAnchorBinded); map_?.off('click', this.appendAnchorBinded);
layerEventManager?.off('mousemove', this.fileId, this.showTemporaryAnchorBinded); layerEventManager?.off('mousemove', this.fileId, this.showTemporaryAnchorBinded);
layerEventManager?.off('click', this.fileId, this.addIntermediateAnchorBinded);
map_?.off('mousemove', this.updateTemporaryAnchorBinded); map_?.off('mousemove', this.updateTemporaryAnchorBinded);
this.layers.forEach((layer) => { this.layers.forEach((layer) => {
@@ -518,15 +525,19 @@ export class RoutingControls {
if (get(streetViewEnabled) && get(streetViewSource) === 'google') { if (get(streetViewEnabled) && get(streetViewSource) === 'google') {
return; return;
} }
if (
this.draggedAnchorIndex !== null ||
Date.now() - this.lastDraggedAnchorEventTime < 100
) {
// Exit if anchor is being dragged
return;
}
if ( if (
e.target.queryRenderedFeatures(e.point, { e.target.queryRenderedFeatures(e.point, {
layers: this.layers layers: [this.fileId, ...[...this.layers.values()].map((layer) => layer.id)],
.values()
.map((layer) => layer.id)
.toArray(),
}).length }).length
) { ) {
// Clicked on routing control, ignoring // Clicked on routing control or layer, ignoring
return; return;
} }
this.appendAnchorWithCoordinates({ this.appendAnchorWithCoordinates({
@@ -598,6 +609,15 @@ export class RoutingControls {
await this.routeBetweenAnchors([lastAnchor, newAnchor], [lastAnchorPoint, newAnchorPoint]); await this.routeBetweenAnchors([lastAnchor, newAnchor], [lastAnchorPoint, newAnchorPoint]);
} }
addIntermediateAnchor(e: maplibregl.MapMouseEvent) {
e.preventDefault();
if (this.temporaryAnchor !== null) {
this.turnIntoPermanentAnchor();
return;
}
}
getNeighbouringAnchors(anchor: Anchor): [Anchor | null, Anchor | null] { getNeighbouringAnchors(anchor: Anchor): [Anchor | null, Anchor | null] {
let previousAnchor: Anchor | null = null; let previousAnchor: Anchor | null = null;
let nextAnchor: Anchor | null = null; let nextAnchor: Anchor | null = null;
@@ -683,17 +703,7 @@ export class RoutingControls {
try { try {
response = await route(targetTrackPoints.map((trkpt) => trkpt.getCoordinates())); response = await route(targetTrackPoints.map((trkpt) => trkpt.getCoordinates()));
} catch (e: any) { } catch (e: any) {
if (e.message.includes('from-position not mapped in existing datafile')) { toast.error(i18n._(e.message, e.message));
toast.error(i18n._('toolbar.routing.error.from'));
} else if (e.message.includes('via1-position not mapped in existing datafile')) {
toast.error(i18n._('toolbar.routing.error.via'));
} else if (e.message.includes('to-position not mapped in existing datafile')) {
toast.error(i18n._('toolbar.routing.error.to'));
} else if (e.message.includes('Time-out')) {
toast.error(i18n._('toolbar.routing.error.timeout'));
} else {
toast.error(e.message);
}
return false; return false;
} }
@@ -810,7 +820,7 @@ export class RoutingControls {
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"> `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
<circle cx="10" cy="10" r="8" fill="white" stroke="black" stroke-width="2" /> <circle cx="10" cy="10" r="8" fill="white" stroke="black" stroke-width="2" />
</svg>`, </svg>`,
_map.getCanvasContainer().offsetWidth > 1000 ? 50 : 80 _map.getCanvasContainer().offsetWidth > 1000 ? 56 : 80
); );
} }
@@ -828,8 +838,11 @@ export class RoutingControls {
onClick(e: MapLayerMouseEvent) { onClick(e: MapLayerMouseEvent) {
e.preventDefault(); e.preventDefault();
if (this.temporaryAnchor !== null) { if (
this.turnIntoPermanentAnchor(); this.draggedAnchorIndex !== null ||
Date.now() - this.lastDraggedAnchorEventTime < 100
) {
// Exit if anchor is being dragged
return; return;
} }
@@ -918,6 +931,8 @@ export class RoutingControls {
lat: e.lngLat.lat, lat: e.lngLat.lat,
lon: e.lngLat.lng, lon: e.lngLat.lng,
}); });
this.lastDraggedAnchorEventTime = Date.now();
} }
onMouseUp(e: MapLayerMouseEvent | MapLayerTouchEvent) { onMouseUp(e: MapLayerMouseEvent | MapLayerTouchEvent) {
@@ -956,6 +971,7 @@ export class RoutingControls {
} }
this.draggedAnchorIndex = null; this.draggedAnchorIndex = null;
this.lastDraggedAnchorEventTime = Date.now();
} }
showTemporaryAnchor(e: MapLayerMouseEvent) { showTemporaryAnchor(e: MapLayerMouseEvent) {
@@ -1083,7 +1099,9 @@ export class RoutingControls {
if (!this.temporaryAnchor) { if (!this.temporaryAnchor) {
return; return;
} }
let source = get(map)?.getSource('routing-controls-0') as GeoJSONSource | undefined; let source = get(map)?.getSource(`routing-controls-${this.fileId}-0`) as
| GeoJSONSource
| undefined;
if (source) { if (source) {
if (this.temporaryAnchor) { if (this.temporaryAnchor) {
source.updateData({ source.updateData({
@@ -1098,7 +1116,9 @@ export class RoutingControls {
return; return;
} }
const map_ = get(map); const map_ = get(map);
let source = map_?.getSource('routing-controls-0') as GeoJSONSource | undefined; let source = map_?.getSource(`routing-controls-${this.fileId}-0`) as
| GeoJSONSource
| undefined;
if (source) { if (source) {
if (this.temporaryAnchor) { if (this.temporaryAnchor) {
source.updateData({ source.updateData({
@@ -6,37 +6,213 @@ import { get } from 'svelte/store';
const { routing, routingProfile, privateRoads } = settings; const { routing, routingProfile, privateRoads } = settings;
export const routingProfiles: { [key: string]: string } = { export type RoutingProfile = {
bike: 'Trekking-dry', engine: 'graphhopper' | 'brouter';
racing_bike: 'fastbike', profile: string;
gravel_bike: 'gravel', };
mountain_bike: 'MTB',
foot: 'Hiking-Alpine-SAC6', export const routingProfiles: { [key: string]: RoutingProfile } = {
motorcycle: 'Car-FastEco', bike: { engine: 'graphhopper', profile: 'bike' },
water: 'river', racing_bike: { engine: 'graphhopper', profile: 'racingbike' },
railway: 'rail', gravel_bike: { engine: 'graphhopper', profile: 'gravelbike' },
mountain_bike: { engine: 'graphhopper', profile: 'mtb' },
foot: { engine: 'graphhopper', profile: 'foot' },
motorcycle: { engine: 'graphhopper', profile: 'motorbike' },
water: { engine: 'brouter', profile: 'river' },
railway: { engine: 'brouter', profile: 'rail' },
}; };
export function route(points: Coordinates[]): Promise<TrackPoint[]> { export function route(points: Coordinates[]): Promise<TrackPoint[]> {
if (get(routing)) { if (get(routing)) {
return getRoute(points, routingProfiles[get(routingProfile)], get(privateRoads)); const profile = routingProfiles[get(routingProfile)];
if (profile.engine === 'graphhopper') {
return getGraphHopperRoute(points, profile.profile, get(privateRoads));
} else {
return getBRouterRoute(points, profile.profile);
}
} else { } else {
return getIntermediatePoints(points); return getIntermediatePoints(points);
} }
} }
async function getRoute( const graphhopperDetails = ['road_class', 'surface', 'hike_rating', 'mtb_rating'];
const hikeRatingToSACScale: { [key: string]: string } = {
'1': 'hiking',
'2': 'mountain_hiking',
'3': 'demanding_mountain_hiking',
'4': 'alpine_hiking',
'5': 'demanding_alpine_hiking',
'6': 'difficult_alpine_hiking',
};
const mtbRatingToScale: { [key: string]: string } = {
'1': '0',
'2': '1',
'3': '2',
'4': '3',
'5': '4',
'6': '5',
'7': '6',
};
const graphhopperBlockPrivateCustomModels: { [key: string]: any } = {
bike: {
priority: [
{
if: 'bike_road_access == PRIVATE',
multiply_by: '0.0',
},
],
},
racingbike: {
priority: [
{
if: 'bike_road_access == PRIVATE',
multiply_by: '0.0',
},
],
},
gravelbike: {
priority: [
{
if: 'bike_road_access == PRIVATE',
multiply_by: '0.0',
},
],
},
mtb: {
priority: [
{
if: 'bike_road_access == PRIVATE',
multiply_by: '0.0',
},
],
},
foot: {
priority: [
{
if: 'foot_road_access == PRIVATE',
multiply_by: '0.0',
},
],
},
motorcycle: {
priority: [
{
if: 'road_access == PRIVATE',
multiply_by: '0.0',
},
],
},
};
async function getGraphHopperRoute(
points: Coordinates[], points: Coordinates[],
brouterProfile: string, graphHopperProfile: string,
privateRoads: boolean privateRoads: boolean
): Promise<TrackPoint[]> { ): Promise<TrackPoint[]> {
let url = `https://brouter.gpx.studio?lonlats=${points.map((point) => `${point.lon.toFixed(8)},${point.lat.toFixed(8)}`).join('|')}&profile=${brouterProfile + (privateRoads ? '-private' : '')}&format=geojson&alternativeidx=0`; let response = await fetch('https://graphhopper.gpx.studio/route', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
points: points.map((point) => [point.lon, point.lat]),
profile: graphHopperProfile,
elevation: true,
points_encoded: false,
details: graphhopperDetails,
custom_model: privateRoads
? {}
: graphhopperBlockPrivateCustomModels[graphHopperProfile] || {},
}),
});
if (!response.ok) {
const error = await response.json();
if (error.message.includes('Cannot find point 0')) {
throw new Error('toolbar.routing.error.from');
} else if (error.message.includes('Cannot find point 1')) {
if (points.length == 3) {
throw new Error('toolbar.routing.error.via');
} else {
throw new Error('toolbar.routing.error.to');
}
} else if (error.hints[0].details.includes('PointDistanceExceededException')) {
throw new Error('toolbar.routing.error.distance');
} else if (error.hints[0].details.includes('ConnectionNotFoundException')) {
throw new Error('toolbar.routing.error.connection');
} else {
throw new Error(error.message);
}
}
let json = await response.json();
let route: TrackPoint[] = [];
let coordinates = json.paths[0].points.coordinates;
let details = json.paths[0].details;
for (let i = 0; i < coordinates.length; i++) {
route.push(
new TrackPoint({
attributes: {
lat: coordinates[i][1],
lon: coordinates[i][0],
},
ele: coordinates[i][2] ?? (i > 0 ? route[i - 1].ele : 0),
extensions: {},
})
);
}
for (let key of graphhopperDetails) {
let detail = details[key];
for (let i = 0; i < detail.length; i++) {
for (let j = detail[i][0]; j < detail[i][1] + (i == detail.length - 1); j++) {
if (detail[i][2] !== undefined && detail[i][2] !== 'missing') {
if (key === 'road_class') {
route[j].setExtension('highway', detail[i][2]);
} else if (key === 'hike_rating') {
const sacScale = hikeRatingToSACScale[detail[i][2]];
if (sacScale) {
route[j].setExtension('sac_scale', sacScale);
}
} else if (key === 'mtb_rating') {
const mtbScale = mtbRatingToScale[detail[i][2]];
if (mtbScale) {
route[j].setExtension('mtb_scale', mtbScale);
}
} else if (key === 'surface' && detail[i][2] !== 'other') {
route[j].setExtension('surface', detail[i][2]);
}
}
}
}
}
return route;
}
async function getBRouterRoute(
points: Coordinates[],
brouterProfile: string
): Promise<TrackPoint[]> {
let url = `https://brouter.de/brouter?lonlats=${points.map((point) => `${point.lon.toFixed(8)},${point.lat.toFixed(8)}`).join('|')}&profile=${brouterProfile}&format=geojson&alternativeidx=0`;
let response = await fetch(url); let response = await fetch(url);
// Check if the response is ok
if (!response.ok) { if (!response.ok) {
throw new Error(`${await response.text()}`); const error = await response.text();
if (error.includes('from-position not mapped in existing datafile')) {
throw new Error('toolbar.routing.error.from');
} else if (error.includes('via1-position not mapped in existing datafile')) {
throw new Error('toolbar.routing.error.via');
} else if (error.includes('to-position not mapped in existing datafile')) {
throw new Error('toolbar.routing.error.to');
} else if (error.includes('Time-out')) {
throw new Error('toolbar.routing.error.timeout');
} else {
throw new Error(error);
}
} }
let geojson = await response.json(); let geojson = await response.json();
@@ -52,14 +228,13 @@ async function getRoute(
let tags = messageIdx < messages.length ? getTags(messages[messageIdx][tagIdx]) : {}; let tags = messageIdx < messages.length ? getTags(messages[messageIdx][tagIdx]) : {};
for (let i = 0; i < coordinates.length; i++) { for (let i = 0; i < coordinates.length; i++) {
let coord = coordinates[i];
route.push( route.push(
new TrackPoint({ new TrackPoint({
attributes: { attributes: {
lat: coord[1], lat: coordinates[i][1],
lon: coord[0], lon: coordinates[i][0],
}, },
ele: coord[2] ?? (i > 0 ? route[i - 1].ele : 0), ele: coordinates[i][2] ?? (i > 0 ? route[i - 1].ele : 0),
}) })
); );

Some files were not shown because too many files have changed in this diff Show More