From b906b55f723584081e741a6e3c0f585c052d1423 Mon Sep 17 00:00:00 2001 From: Jan-Lukas Else Date: Tue, 16 Nov 2021 18:01:11 +0100 Subject: [PATCH] Support GPX routes and waypoints as well --- editor.go | 1 + geoMap.go | 10 +++-- geoTrack.go | 70 +++++++++++++++++++++++++-------- geoTrack_test.go | 59 +++++++++++++++++++++++++-- templateAssets.go | 1 - templates/assets/js/geomap.js | 13 ++++-- templates/assets/js/geotrack.js | 13 ++++-- templates/strings/de.yaml | 4 +- templates/strings/default.yaml | 4 +- templates/trackdetails.gohtml | 4 +- testdata/test2.gpx | 17 ++++++++ testdata/test3.gpx | 2 + 12 files changed, 159 insertions(+), 39 deletions(-) create mode 100644 testdata/test2.gpx create mode 100644 testdata/test3.gpx diff --git a/editor.go b/editor.go index 4eb0049..5ba4512 100644 --- a/editor.go +++ b/editor.go @@ -235,6 +235,7 @@ func (a *goBlog) editorPostDesc(blog string) string { a.cfg.Micropub.PhotoDescriptionParam, a.cfg.Micropub.ReplyParam, a.cfg.Micropub.ReplyTitleParam, + gpxParameter, } { if param == "" { continue diff --git a/geoMap.go b/geoMap.go index 855039c..fcaa878 100644 --- a/geoMap.go +++ b/geoMap.go @@ -39,8 +39,9 @@ func (a *goBlog) serveGeoMap(w http.ResponseWriter, r *http.Request) { } type templateTrack struct { - Paths [][]*trackPoint - Post string + Paths [][]*trackPoint + Points []*trackPoint + Post string } var locations []*templateLocation @@ -55,8 +56,9 @@ func (a *goBlog) serveGeoMap(w http.ResponseWriter, r *http.Request) { } if t, err := a.getTrack(p); err == nil && t != nil { tracks = append(tracks, &templateTrack{ - Paths: t.Paths, - Post: p.Path, + Paths: t.Paths, + Points: t.Points, + Post: p.Path, }) } } diff --git a/geoTrack.go b/geoTrack.go index 804e6e4..30aa615 100644 --- a/geoTrack.go +++ b/geoTrack.go @@ -3,6 +3,7 @@ package main import ( "encoding/json" "errors" + "log" "math" "github.com/tkrajina/gpxgo/gpx" @@ -20,6 +21,8 @@ type trackResult struct { HasPoints bool Paths [][]*trackPoint PathsJSON string + Points []*trackPoint + PointsJSON string Kilometers string Hours string Name string @@ -34,7 +37,9 @@ func (a *goBlog) getTrack(p *post) (result *trackResult, err error) { // Parse GPX parseResult, err := trackParseGPX(gpxString) 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) @@ -45,18 +50,28 @@ func (a *goBlog) getTrack(p *post) (result *trackResult, err error) { return nil, err } + pointsJSON, err := json.Marshal(parseResult.points) + if err != nil { + return nil, err + } + result = &trackResult{ HasPoints: len(parseResult.paths) > 0 && len(parseResult.paths[0]) > 0, Paths: parseResult.paths, PathsJSON: string(pathsJSON), + Points: parseResult.points, + PointsJSON: string(pointsJSON), 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(math.Mod(parseResult.md.MovingTime, 3600)/60), // Minutes math.Floor(math.Mod(parseResult.md.MovingTime, 60)), // Seconds - ), + ) } return result, nil @@ -68,6 +83,7 @@ type trackPoint struct { type trackParseResult struct { paths [][]*trackPoint + points []*trackPoint gpxData *gpx.GPX md *gpx.MovingData } @@ -80,12 +96,12 @@ func trackParseGPX(gpxString string) (result *trackParseResult, err error) { points []*trackPoint } - var paths []*trackPath - result.gpxData, err = gpx.ParseString(gpxString) if err != nil { return nil, err } + + var paths []*trackPath for _, track := range result.gpxData.Tracks { for _, segment := range track.Segments { md := segment.MovingData() @@ -100,17 +116,37 @@ func trackParseGPX(gpxString string) (result *trackParseResult, err error) { paths = append(paths, path) } } - - result.md = &gpx.MovingData{} - for _, path := range paths { - // Combine moving data - result.md.MaxSpeed = math.Max(result.md.MaxSpeed, path.gpxMovingData.MaxSpeed) - result.md.MovingDistance = result.md.MovingDistance + path.gpxMovingData.MovingDistance - result.md.MovingTime = result.md.MovingTime + path.gpxMovingData.MovingTime - result.md.StoppedDistance = result.md.StoppedDistance + path.gpxMovingData.StoppedDistance - result.md.StoppedTime = result.md.StoppedTime + path.gpxMovingData.StoppedTime + for _, route := range result.gpxData.Routes { + path := &trackPath{} + 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 = append(result.paths, path.points) + result.paths[i] = path.points + // 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.MovingDistance = result.md.MovingDistance + path.gpxMovingData.MovingDistance + result.md.MovingTime = result.md.MovingTime + path.gpxMovingData.MovingTime + result.md.StoppedDistance = result.md.StoppedDistance + path.gpxMovingData.StoppedDistance + result.md.StoppedTime = result.md.StoppedTime + path.gpxMovingData.StoppedTime + } + } + + result.points = []*trackPoint{} + for _, point := range result.gpxData.Waypoints { + result.points = append(result.points, &trackPoint{ + Lat: point.GetLatitude(), Lon: point.GetLongitude(), + }) } return result, nil diff --git a/geoTrack_test.go b/geoTrack_test.go index 475ad64..69c4b22 100644 --- a/geoTrack_test.go +++ b/geoTrack_test.go @@ -30,9 +30,11 @@ func Test_geoTrack(t *testing.T) { _ = app.initDatabase(false) app.initComponents(false) + // First test (just with track) + gpxBytes, _ := os.ReadFile("testdata/test.gpx") - post := &post{ + p := &post{ Blog: "en", Parameters: map[string][]string{ "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) 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, "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) 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, "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) + } diff --git a/templateAssets.go b/templateAssets.go index 8919a4f..20cb297 100644 --- a/templateAssets.go +++ b/templateAssets.go @@ -134,7 +134,6 @@ func (a *goBlog) initChromaCSS() error { // Write the CSS to the file chromahtml.New( chromahtml.ClassPrefix("c-"), - chromahtml.WithAllClasses(true), ).WriteCSS(chromaTempFile, chromaStyle) // Close the file _ = chromaTempFile.Close() diff --git a/templates/assets/js/geomap.js b/templates/assets/js/geomap.js index e8eca8c..71122d9 100644 --- a/templates/assets/js/geomap.js +++ b/templates/assets/js/geomap.js @@ -9,21 +9,26 @@ attribution: '© OpenStreetMap contributors' }).addTo(map) - let mapFeatures = [] + let features = [] 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() })) }) tracks.forEach(track => { 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() })) }) }) - map.fitBounds(L.featureGroup(mapFeatures).getBounds(), { padding: [5, 5] }) + map.fitBounds(L.featureGroup(features).getBounds(), { padding: [5, 5] }) })() \ No newline at end of file diff --git a/templates/assets/js/geotrack.js b/templates/assets/js/geotrack.js index 33036ad..25325d2 100644 --- a/templates/assets/js/geotrack.js +++ b/templates/assets/js/geotrack.js @@ -1,6 +1,7 @@ (function () { 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') @@ -8,11 +9,15 @@ attribution: '© OpenStreetMap contributors' }).addTo(map) - let polylines = [] + let features = [] 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] }) })() \ No newline at end of file diff --git a/templates/strings/de.yaml b/templates/strings/de.yaml index 8c0e872..7ca8791 100644 --- a/templates/strings/de.yaml +++ b/templates/strings/de.yaml @@ -14,12 +14,12 @@ docomment: "Kommentieren" download: "Herunterladen" drafts: "Entwürfe" 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)" fileuses: "Datei-Verwendungen" gentts: "Text-To-Speech-Audio erzeugen" 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" interactionslabel: "Hast du eine Antwort hierzu veröffentlicht? Füge hier die URL ein." kilometers: "Kilometer" diff --git a/templates/strings/default.yaml b/templates/strings/default.yaml index 7a23295..01a3b96 100644 --- a/templates/strings/default.yaml +++ b/templates/strings/default.yaml @@ -17,13 +17,13 @@ docomment: "Comment" download: "Download" drafts: "Drafts" 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)" feed: "Feed" fileuses: "file uses" gentts: "Generate Text-To-Speech audio" gpxhelper: "GPX helper" -gpxhelperdesc: "Minify GPX and generate YAML for the frontmatter." +gpxhelperdesc: "💡 Minify GPX and generate YAML for the frontmatter." indieauth: "IndieAuth" interactions: "Interactions & Comments" interactionslabel: "Have you published a response to this? Paste the URL here." diff --git a/templates/trackdetails.gohtml b/templates/trackdetails.gohtml index 712aa5e..05cf90a 100644 --- a/templates/trackdetails.gohtml +++ b/templates/trackdetails.gohtml @@ -1,11 +1,13 @@ {{ define "trackdetails" }} {{ if .Data.HasTrack }} {{ $track := (gettrack .Data) }} + {{ if $track }} {{ if $track.HasPoints }} {{ $lang := .Blog.Lang }}

{{ with $track.Name }}{{ . }} {{ end }}{{ with $track.Kilometers }}🏁 {{ . }} {{ string $lang "kilometers" }} {{ end }}{{ with $track.Hours }}⌛ {{ . }}{{ end }}

-
+
{{ end }} {{ end }} + {{ end }} {{ end }} \ No newline at end of file diff --git a/testdata/test2.gpx b/testdata/test2.gpx new file mode 100644 index 0000000..45fcaa0 --- /dev/null +++ b/testdata/test2.gpx @@ -0,0 +1,17 @@ + + + Example gpx + + 2372 + LAGORETICO + + Example gpx1 + 2376 + 2375 + 2372 + 2373 + 2374 + 2375 + 2376 + + \ No newline at end of file diff --git a/testdata/test3.gpx b/testdata/test3.gpx new file mode 100644 index 0000000..e0e12e0 --- /dev/null +++ b/testdata/test3.gpx @@ -0,0 +1,2 @@ + +50k Nottinghamshire Run50k Nottinghamshire Run19.119.219.815.416.916.917.417.415.315.415.420.817.317.318.319.117.617.917.917.817.717.417.917.717.819.619.319.918.718.618.417.317.31816.616.516.416.416.616.817.818.817.717.416.615.516.215.41617.317.717.518.618.418.920.922.131.332.435.938.342.341.453.16260.757.965.27274.276.281.78786.487.391.999100.3100.9103.1104.8110.9101.497.376.266.262.556.555.353.453.766.688.199.9114.195.997.295.377.668.362.860.462.164.462.665.368.478.985.196.9106.7109.9114.6121.7121.4119.4125.7114.2103.995.784.484.767.754.757.463.766.870.672.675.16157.554.153.953.262.270.47678.385.497.798.698.9100.8101.9102103.2103.7104.1105.8106.8107.3106.6105.2104.9103.89994.291.581.473.576.490.991.6106103117.1141.9151.1154.1151.7153.9141.2129.6122.5122.6126.8130128.1124.6113.7111.3106.29179.175.581.472.571.173.277.378.8141141.3136.2132.9128.7126125121.3125.7127111.7112.1108116.5112.9125.3132.9124.2116.7112102.197.3101.696.795.797.197.199.594.18986.195110.2117.6115.1112.4111.9114.5111.4107.9105.3106.6106.6107.4110.1108.4110.3109.5109.6114.6114.3117.2119.9115.2112.9113.5113.5104.2100.594.795.998.995.987.366.265.663.272.564.37273.284.884.880.470.154.356.354.35863.463.479.879.898.4108.5111.9113.8114.4113.2111.3111.1112.6111.398.7100.3102.498.497.696.193.394.593.295.994.892.189.288.88782.381.779.578.971.871.871.270.569.669.161.955.345.829.523.718.81919.1