mirror of https://github.com/jlelse/GoBlog
Support GPX routes and waypoints as well
This commit is contained in:
parent
aabdde7d39
commit
b906b55f72
|
@ -235,6 +235,7 @@ func (a *goBlog) editorPostDesc(blog string) string {
|
||||||
a.cfg.Micropub.PhotoDescriptionParam,
|
a.cfg.Micropub.PhotoDescriptionParam,
|
||||||
a.cfg.Micropub.ReplyParam,
|
a.cfg.Micropub.ReplyParam,
|
||||||
a.cfg.Micropub.ReplyTitleParam,
|
a.cfg.Micropub.ReplyTitleParam,
|
||||||
|
gpxParameter,
|
||||||
} {
|
} {
|
||||||
if param == "" {
|
if param == "" {
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -40,6 +40,7 @@ func (a *goBlog) serveGeoMap(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
type templateTrack struct {
|
type templateTrack struct {
|
||||||
Paths [][]*trackPoint
|
Paths [][]*trackPoint
|
||||||
|
Points []*trackPoint
|
||||||
Post string
|
Post string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,6 +57,7 @@ func (a *goBlog) serveGeoMap(w http.ResponseWriter, r *http.Request) {
|
||||||
if t, err := a.getTrack(p); err == nil && t != nil {
|
if t, err := a.getTrack(p); err == nil && t != nil {
|
||||||
tracks = append(tracks, &templateTrack{
|
tracks = append(tracks, &templateTrack{
|
||||||
Paths: t.Paths,
|
Paths: t.Paths,
|
||||||
|
Points: t.Points,
|
||||||
Post: p.Path,
|
Post: p.Path,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
60
geoTrack.go
60
geoTrack.go
|
@ -3,6 +3,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"log"
|
||||||
"math"
|
"math"
|
||||||
|
|
||||||
"github.com/tkrajina/gpxgo/gpx"
|
"github.com/tkrajina/gpxgo/gpx"
|
||||||
|
@ -20,6 +21,8 @@ type trackResult struct {
|
||||||
HasPoints bool
|
HasPoints bool
|
||||||
Paths [][]*trackPoint
|
Paths [][]*trackPoint
|
||||||
PathsJSON string
|
PathsJSON string
|
||||||
|
Points []*trackPoint
|
||||||
|
PointsJSON string
|
||||||
Kilometers string
|
Kilometers string
|
||||||
Hours string
|
Hours string
|
||||||
Name string
|
Name string
|
||||||
|
@ -34,7 +37,9 @@ func (a *goBlog) getTrack(p *post) (result *trackResult, err error) {
|
||||||
// Parse GPX
|
// Parse GPX
|
||||||
parseResult, err := trackParseGPX(gpxString)
|
parseResult, err := trackParseGPX(gpxString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
// Failed to parse, but just log error
|
||||||
|
log.Printf("failed to parse GPX: %v", err)
|
||||||
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
l, _ := language.Parse(a.cfg.Blogs[p.Blog].Lang)
|
l, _ := language.Parse(a.cfg.Blogs[p.Blog].Lang)
|
||||||
|
@ -45,18 +50,28 @@ func (a *goBlog) getTrack(p *post) (result *trackResult, err error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pointsJSON, err := json.Marshal(parseResult.points)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
result = &trackResult{
|
result = &trackResult{
|
||||||
HasPoints: len(parseResult.paths) > 0 && len(parseResult.paths[0]) > 0,
|
HasPoints: len(parseResult.paths) > 0 && len(parseResult.paths[0]) > 0,
|
||||||
Paths: parseResult.paths,
|
Paths: parseResult.paths,
|
||||||
PathsJSON: string(pathsJSON),
|
PathsJSON: string(pathsJSON),
|
||||||
|
Points: parseResult.points,
|
||||||
|
PointsJSON: string(pointsJSON),
|
||||||
Name: parseResult.gpxData.Name,
|
Name: parseResult.gpxData.Name,
|
||||||
Kilometers: lp.Sprintf("%.2f", parseResult.md.MovingDistance/1000),
|
}
|
||||||
Hours: lp.Sprintf(
|
|
||||||
"%.0f:%2.0f:%2.0f",
|
if parseResult.md != nil {
|
||||||
|
result.Kilometers = lp.Sprintf("%.2f", parseResult.md.MovingDistance/1000)
|
||||||
|
result.Hours = lp.Sprintf(
|
||||||
|
"%.0f:%02.0f:%02.0f",
|
||||||
math.Floor(parseResult.md.MovingTime/3600), // Hours
|
math.Floor(parseResult.md.MovingTime/3600), // Hours
|
||||||
math.Floor(math.Mod(parseResult.md.MovingTime, 3600)/60), // Minutes
|
math.Floor(math.Mod(parseResult.md.MovingTime, 3600)/60), // Minutes
|
||||||
math.Floor(math.Mod(parseResult.md.MovingTime, 60)), // Seconds
|
math.Floor(math.Mod(parseResult.md.MovingTime, 60)), // Seconds
|
||||||
),
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
|
@ -68,6 +83,7 @@ type trackPoint struct {
|
||||||
|
|
||||||
type trackParseResult struct {
|
type trackParseResult struct {
|
||||||
paths [][]*trackPoint
|
paths [][]*trackPoint
|
||||||
|
points []*trackPoint
|
||||||
gpxData *gpx.GPX
|
gpxData *gpx.GPX
|
||||||
md *gpx.MovingData
|
md *gpx.MovingData
|
||||||
}
|
}
|
||||||
|
@ -80,12 +96,12 @@ func trackParseGPX(gpxString string) (result *trackParseResult, err error) {
|
||||||
points []*trackPoint
|
points []*trackPoint
|
||||||
}
|
}
|
||||||
|
|
||||||
var paths []*trackPath
|
|
||||||
|
|
||||||
result.gpxData, err = gpx.ParseString(gpxString)
|
result.gpxData, err = gpx.ParseString(gpxString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var paths []*trackPath
|
||||||
for _, track := range result.gpxData.Tracks {
|
for _, track := range result.gpxData.Tracks {
|
||||||
for _, segment := range track.Segments {
|
for _, segment := range track.Segments {
|
||||||
md := segment.MovingData()
|
md := segment.MovingData()
|
||||||
|
@ -100,17 +116,37 @@ func trackParseGPX(gpxString string) (result *trackParseResult, err error) {
|
||||||
paths = append(paths, path)
|
paths = append(paths, path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for _, route := range result.gpxData.Routes {
|
||||||
result.md = &gpx.MovingData{}
|
path := &trackPath{}
|
||||||
for _, path := range paths {
|
for _, point := range route.Points {
|
||||||
|
path.points = append(path.points, &trackPoint{
|
||||||
|
Lat: point.GetLatitude(), Lon: point.GetLongitude(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
paths = append(paths, path)
|
||||||
|
}
|
||||||
|
result.paths = make([][]*trackPoint, len(paths))
|
||||||
|
for i, path := range paths {
|
||||||
|
// Add points
|
||||||
|
result.paths[i] = path.points
|
||||||
// Combine moving data
|
// Combine moving data
|
||||||
|
if path.gpxMovingData != nil {
|
||||||
|
if result.md == nil {
|
||||||
|
result.md = &gpx.MovingData{}
|
||||||
|
}
|
||||||
result.md.MaxSpeed = math.Max(result.md.MaxSpeed, path.gpxMovingData.MaxSpeed)
|
result.md.MaxSpeed = math.Max(result.md.MaxSpeed, path.gpxMovingData.MaxSpeed)
|
||||||
result.md.MovingDistance = result.md.MovingDistance + path.gpxMovingData.MovingDistance
|
result.md.MovingDistance = result.md.MovingDistance + path.gpxMovingData.MovingDistance
|
||||||
result.md.MovingTime = result.md.MovingTime + path.gpxMovingData.MovingTime
|
result.md.MovingTime = result.md.MovingTime + path.gpxMovingData.MovingTime
|
||||||
result.md.StoppedDistance = result.md.StoppedDistance + path.gpxMovingData.StoppedDistance
|
result.md.StoppedDistance = result.md.StoppedDistance + path.gpxMovingData.StoppedDistance
|
||||||
result.md.StoppedTime = result.md.StoppedTime + path.gpxMovingData.StoppedTime
|
result.md.StoppedTime = result.md.StoppedTime + path.gpxMovingData.StoppedTime
|
||||||
// Add points
|
}
|
||||||
result.paths = append(result.paths, path.points)
|
}
|
||||||
|
|
||||||
|
result.points = []*trackPoint{}
|
||||||
|
for _, point := range result.gpxData.Waypoints {
|
||||||
|
result.points = append(result.points, &trackPoint{
|
||||||
|
Lat: point.GetLatitude(), Lon: point.GetLongitude(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
|
|
|
@ -30,9 +30,11 @@ func Test_geoTrack(t *testing.T) {
|
||||||
_ = app.initDatabase(false)
|
_ = app.initDatabase(false)
|
||||||
app.initComponents(false)
|
app.initComponents(false)
|
||||||
|
|
||||||
|
// First test (just with track)
|
||||||
|
|
||||||
gpxBytes, _ := os.ReadFile("testdata/test.gpx")
|
gpxBytes, _ := os.ReadFile("testdata/test.gpx")
|
||||||
|
|
||||||
post := &post{
|
p := &post{
|
||||||
Blog: "en",
|
Blog: "en",
|
||||||
Parameters: map[string][]string{
|
Parameters: map[string][]string{
|
||||||
"gpx": {
|
"gpx": {
|
||||||
|
@ -41,19 +43,68 @@ func Test_geoTrack(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
resEn, err := app.getTrack(post)
|
resEn, err := app.getTrack(p)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.True(t, resEn.HasPoints)
|
assert.True(t, resEn.HasPoints)
|
||||||
|
assert.NotEmpty(t, resEn.Paths)
|
||||||
|
assert.Empty(t, resEn.Points)
|
||||||
assert.Equal(t, "2.70", resEn.Kilometers)
|
assert.Equal(t, "2.70", resEn.Kilometers)
|
||||||
assert.Equal(t, "0:42:53", resEn.Hours)
|
assert.Equal(t, "0:42:53", resEn.Hours)
|
||||||
|
|
||||||
post.Blog = "de"
|
p.Blog = "de"
|
||||||
|
|
||||||
resDe, err := app.getTrack(post)
|
resDe, err := app.getTrack(p)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.True(t, resDe.HasPoints)
|
assert.True(t, resDe.HasPoints)
|
||||||
|
assert.NotEmpty(t, resDe.Paths)
|
||||||
|
assert.Empty(t, resDe.Points)
|
||||||
assert.Equal(t, "2,70", resDe.Kilometers)
|
assert.Equal(t, "2,70", resDe.Kilometers)
|
||||||
assert.Equal(t, "0:42:53", resDe.Hours)
|
assert.Equal(t, "0:42:53", resDe.Hours)
|
||||||
|
|
||||||
|
// Second file (with track and waypoint)
|
||||||
|
|
||||||
|
gpxBytes, _ = os.ReadFile("testdata/test2.gpx")
|
||||||
|
|
||||||
|
p = &post{
|
||||||
|
Blog: "en",
|
||||||
|
Parameters: map[string][]string{
|
||||||
|
"gpx": {
|
||||||
|
string(gpxBytes),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
resEn, err = app.getTrack(p)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.True(t, resEn.HasPoints)
|
||||||
|
assert.NotEmpty(t, resEn.Paths)
|
||||||
|
assert.NotEmpty(t, resEn.Points)
|
||||||
|
assert.Equal(t, "0.08", resEn.Kilometers)
|
||||||
|
assert.Equal(t, "0:01:29", resEn.Hours)
|
||||||
|
|
||||||
|
// Third file (just with route)
|
||||||
|
|
||||||
|
gpxBytes, _ = os.ReadFile("testdata/test3.gpx")
|
||||||
|
|
||||||
|
p = &post{
|
||||||
|
Blog: "en",
|
||||||
|
Parameters: map[string][]string{
|
||||||
|
"gpx": {
|
||||||
|
string(gpxBytes),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
resEn, err = app.getTrack(p)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.True(t, resEn.HasPoints)
|
||||||
|
assert.NotEmpty(t, resEn.Paths)
|
||||||
|
assert.Empty(t, resEn.Points)
|
||||||
|
assert.Equal(t, "", resEn.Kilometers)
|
||||||
|
assert.Equal(t, "", resEn.Hours)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,7 +134,6 @@ func (a *goBlog) initChromaCSS() error {
|
||||||
// Write the CSS to the file
|
// Write the CSS to the file
|
||||||
chromahtml.New(
|
chromahtml.New(
|
||||||
chromahtml.ClassPrefix("c-"),
|
chromahtml.ClassPrefix("c-"),
|
||||||
chromahtml.WithAllClasses(true),
|
|
||||||
).WriteCSS(chromaTempFile, chromaStyle)
|
).WriteCSS(chromaTempFile, chromaStyle)
|
||||||
// Close the file
|
// Close the file
|
||||||
_ = chromaTempFile.Close()
|
_ = chromaTempFile.Close()
|
||||||
|
|
|
@ -9,21 +9,26 @@
|
||||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||||
}).addTo(map)
|
}).addTo(map)
|
||||||
|
|
||||||
let mapFeatures = []
|
let features = []
|
||||||
|
|
||||||
locations.forEach(loc => {
|
locations.forEach(loc => {
|
||||||
mapFeatures.push(L.marker([loc.Lat, loc.Lon]).addTo(map).on('click', function () {
|
features.push(L.marker([loc.Lat, loc.Lon]).addTo(map).on('click', function () {
|
||||||
window.open(loc.Post, '_blank').focus()
|
window.open(loc.Post, '_blank').focus()
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
|
|
||||||
tracks.forEach(track => {
|
tracks.forEach(track => {
|
||||||
track.Paths.forEach(path => {
|
track.Paths.forEach(path => {
|
||||||
mapFeatures.push(L.polyline(path.map(point => [point.Lat, point.Lon]), { color: 'blue' }).addTo(map).on('click', function () {
|
features.push(L.polyline(path.map(point => [point.Lat, point.Lon]), { color: 'blue' }).addTo(map).on('click', function () {
|
||||||
|
window.open(track.Post, '_blank').focus()
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
track.Points.forEach(point => {
|
||||||
|
features.push(L.marker([point.Lat, point.Lon]).addTo(map).on('click', function () {
|
||||||
window.open(track.Post, '_blank').focus()
|
window.open(track.Post, '_blank').focus()
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
map.fitBounds(L.featureGroup(mapFeatures).getBounds(), { padding: [5, 5] })
|
map.fitBounds(L.featureGroup(features).getBounds(), { padding: [5, 5] })
|
||||||
})()
|
})()
|
|
@ -1,6 +1,7 @@
|
||||||
(function () {
|
(function () {
|
||||||
let mapEl = document.getElementById('map')
|
let mapEl = document.getElementById('map')
|
||||||
let paths = JSON.parse(mapEl.dataset.paths)
|
let paths = (mapEl.dataset.paths == "") ? [] : JSON.parse(mapEl.dataset.paths)
|
||||||
|
let points = (mapEl.dataset.points == "") ? [] : JSON.parse(mapEl.dataset.points)
|
||||||
|
|
||||||
let map = L.map('map')
|
let map = L.map('map')
|
||||||
|
|
||||||
|
@ -8,11 +9,15 @@
|
||||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||||
}).addTo(map)
|
}).addTo(map)
|
||||||
|
|
||||||
let polylines = []
|
let features = []
|
||||||
|
|
||||||
paths.forEach(path => {
|
paths.forEach(path => {
|
||||||
polylines.push(L.polyline(path.map(point => [point.Lat, point.Lon]), { color: 'blue' }).addTo(map))
|
features.push(L.polyline(path.map(point => [point.Lat, point.Lon]), { color: 'blue' }).addTo(map))
|
||||||
})
|
})
|
||||||
|
|
||||||
map.fitBounds(L.featureGroup(polylines).getBounds(), { padding: [5, 5] })
|
points.forEach(point => {
|
||||||
|
features.push(L.marker([point.Lat, point.Lon]).addTo(map))
|
||||||
|
})
|
||||||
|
|
||||||
|
map.fitBounds(L.featureGroup(features).getBounds(), { padding: [5, 5] })
|
||||||
})()
|
})()
|
|
@ -14,12 +14,12 @@ docomment: "Kommentieren"
|
||||||
download: "Herunterladen"
|
download: "Herunterladen"
|
||||||
drafts: "Entwürfe"
|
drafts: "Entwürfe"
|
||||||
editor: "Editor"
|
editor: "Editor"
|
||||||
editorpostdesc: "Leere Parameter werden automatisch entfernt. Mehr mögliche Parameter: %s. Mögliche Zustände für `%s`: %s."
|
editorpostdesc: "💡 Leere Parameter werden automatisch entfernt. Mehr mögliche Parameter: %s. Mögliche Zustände für `%s`: %s."
|
||||||
emailopt: "E-Mail (optional)"
|
emailopt: "E-Mail (optional)"
|
||||||
fileuses: "Datei-Verwendungen"
|
fileuses: "Datei-Verwendungen"
|
||||||
gentts: "Text-To-Speech-Audio erzeugen"
|
gentts: "Text-To-Speech-Audio erzeugen"
|
||||||
gpxhelper: "GPX-Helfer"
|
gpxhelper: "GPX-Helfer"
|
||||||
gpxhelperdesc: "GPX minimieren und YAML für das Frontmatter generieren."
|
gpxhelperdesc: "💡 GPX minimieren und YAML für das Frontmatter generieren."
|
||||||
interactions: "Interaktionen & Kommentare"
|
interactions: "Interaktionen & Kommentare"
|
||||||
interactionslabel: "Hast du eine Antwort hierzu veröffentlicht? Füge hier die URL ein."
|
interactionslabel: "Hast du eine Antwort hierzu veröffentlicht? Füge hier die URL ein."
|
||||||
kilometers: "Kilometer"
|
kilometers: "Kilometer"
|
||||||
|
|
|
@ -17,13 +17,13 @@ docomment: "Comment"
|
||||||
download: "Download"
|
download: "Download"
|
||||||
drafts: "Drafts"
|
drafts: "Drafts"
|
||||||
editor: "Editor"
|
editor: "Editor"
|
||||||
editorpostdesc: "Empty parameters are removed automatically. More possible parameters: %s. Possible states for `%s`: %s."
|
editorpostdesc: "💡 Empty parameters are removed automatically. More possible parameters: %s. Possible states for `%s`: %s."
|
||||||
emailopt: "Email (optional)"
|
emailopt: "Email (optional)"
|
||||||
feed: "Feed"
|
feed: "Feed"
|
||||||
fileuses: "file uses"
|
fileuses: "file uses"
|
||||||
gentts: "Generate Text-To-Speech audio"
|
gentts: "Generate Text-To-Speech audio"
|
||||||
gpxhelper: "GPX helper"
|
gpxhelper: "GPX helper"
|
||||||
gpxhelperdesc: "Minify GPX and generate YAML for the frontmatter."
|
gpxhelperdesc: "💡 Minify GPX and generate YAML for the frontmatter."
|
||||||
indieauth: "IndieAuth"
|
indieauth: "IndieAuth"
|
||||||
interactions: "Interactions & Comments"
|
interactions: "Interactions & Comments"
|
||||||
interactionslabel: "Have you published a response to this? Paste the URL here."
|
interactionslabel: "Have you published a response to this? Paste the URL here."
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
{{ define "trackdetails" }}
|
{{ define "trackdetails" }}
|
||||||
{{ if .Data.HasTrack }}
|
{{ if .Data.HasTrack }}
|
||||||
{{ $track := (gettrack .Data) }}
|
{{ $track := (gettrack .Data) }}
|
||||||
|
{{ if $track }}
|
||||||
{{ if $track.HasPoints }}
|
{{ if $track.HasPoints }}
|
||||||
{{ $lang := .Blog.Lang }}
|
{{ $lang := .Blog.Lang }}
|
||||||
<p>{{ with $track.Name }}<b>{{ . }}</b> {{ end }}{{ with $track.Kilometers }}🏁 {{ . }} {{ string $lang "kilometers" }} {{ end }}{{ with $track.Hours }}⌛ {{ . }}{{ end }}</p>
|
<p>{{ with $track.Name }}<b>{{ . }}</b> {{ end }}{{ with $track.Kilometers }}🏁 {{ . }} {{ string $lang "kilometers" }} {{ end }}{{ with $track.Hours }}⌛ {{ . }}{{ end }}</p>
|
||||||
<div class="p" id="map" data-paths="{{ $track.PathsJSON }}" data-tiles="/tiles/{z}/{x}/{y}.png"></div>
|
<div class="p" id="map" data-paths="{{ $track.PathsJSON }}" data-points="{{ $track.PointsJSON }}"></div>
|
||||||
<script defer src="{{ asset "js/geotrack.js" }}"></script>
|
<script defer src="{{ asset "js/geotrack.js" }}"></script>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
{{ end }}
|
|
@ -0,0 +1,17 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<gpx version="1.0">
|
||||||
|
<name>Example gpx</name>
|
||||||
|
<wpt lat="46.57638889" lon="8.89263889">
|
||||||
|
<ele>2372</ele>
|
||||||
|
<name>LAGORETICO</name>
|
||||||
|
</wpt>
|
||||||
|
<trk><name>Example gpx</name><number>1</number><trkseg>
|
||||||
|
<trkpt lat="46.57608333" lon="8.89241667"><ele>2376</ele><time>2007-10-14T10:09:57Z</time></trkpt>
|
||||||
|
<trkpt lat="46.57619444" lon="8.89252778"><ele>2375</ele><time>2007-10-14T10:10:52Z</time></trkpt>
|
||||||
|
<trkpt lat="46.57641667" lon="8.89266667"><ele>2372</ele><time>2007-10-14T10:12:39Z</time></trkpt>
|
||||||
|
<trkpt lat="46.57650000" lon="8.89280556"><ele>2373</ele><time>2007-10-14T10:13:12Z</time></trkpt>
|
||||||
|
<trkpt lat="46.57638889" lon="8.89302778"><ele>2374</ele><time>2007-10-14T10:13:20Z</time></trkpt>
|
||||||
|
<trkpt lat="46.57652778" lon="8.89322222"><ele>2375</ele><time>2007-10-14T10:13:48Z</time></trkpt>
|
||||||
|
<trkpt lat="46.57661111" lon="8.89344444"><ele>2376</ele><time>2007-10-14T10:14:08Z</time></trkpt>
|
||||||
|
</trkseg></trk>
|
||||||
|
</gpx>
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue