Skip to content

Commit

Permalink
Render SVG shape of the GPX route when "showroute: false" and in edit…
Browse files Browse the repository at this point in the history
…or preview
  • Loading branch information
jlelse committed Jul 24, 2024
1 parent a147aa0 commit 45a38e3
Show file tree
Hide file tree
Showing 9 changed files with 98 additions and 84 deletions.
2 changes: 1 addition & 1 deletion geoMap.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func (a *goBlog) serveGeoMapTracks(w http.ResponseWriter, r *http.Request) {

var tracks []*templateTrack
for _, p := range allPostsWithTracks {
if t, err := a.getTrack(p, true); err == nil && t != nil {
if t, err := a.getTrack(p); err == nil && t != nil {
tracks = append(tracks, &templateTrack{
Paths: t.Paths,
Points: t.Points,
Expand Down
2 changes: 1 addition & 1 deletion geoTiles.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func (a *goBlog) getMaxZoom() int {
if c := a.cfg.MapTiles; c != nil && c.MaxZoom > 0 {
return c.MaxZoom
}
return 20
return 19
}

func (a *goBlog) getMapAttribution() string {
Expand Down
2 changes: 1 addition & 1 deletion geoTiles_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func Test_getMaxZoom(t *testing.T) {
cfg: &config{},
}

assert.Equal(t, 20, app.getMaxZoom())
assert.Equal(t, 19, app.getMaxZoom())

app.cfg.MapTiles = &configMapTiles{
MaxZoom: 10,
Expand Down
60 changes: 21 additions & 39 deletions geoTrack.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package main

import (
"encoding/json"
"errors"
"math"

Expand All @@ -25,24 +24,24 @@ func (p *post) showTrackRoute() bool {
}

type trackResult struct {
Paths [][]*trackPoint
PathsJSON string
Points []*trackPoint
PointsJSON string
Kilometers string
Hours string
Uphill string
Downhill string
Name string
MapAttribution string
MinZoom, MaxZoom int
Paths [][]*trackPoint
Points []*trackPoint
Kilometers string
Hours string
Uphill string
Downhill string
Name string
}

func (t *trackResult) hasPath() bool {
return t.Paths != nil
}

func (t *trackResult) hasMapFeatures() bool {
return t.Points != nil || t.Paths != nil
return t.Points != nil || t.hasPath()
}

func (a *goBlog) getTrack(p *post, withMapFeatures bool) (result *trackResult, err error) {
func (a *goBlog) getTrack(p *post) (result *trackResult, err error) {
gpxString := p.firstParameter(gpxParameter)
if gpxString == "" {
return nil, errors.New("no gpx parameter in post")
Expand All @@ -63,27 +62,11 @@ func (a *goBlog) getTrack(p *post, withMapFeatures bool) (result *trackResult, e
Name: parseResult.gpxData.Name,
}

if withMapFeatures {
// Add Paths
pathsJSON, err := json.Marshal(parseResult.paths)
if err != nil {
return nil, err
}
result.Paths = parseResult.paths
result.PathsJSON = string(pathsJSON)
// Add Points
pointsJSON, err := json.Marshal(parseResult.points)
if err != nil {
return nil, err
}
result.Points = parseResult.points
result.PointsJSON = string(pointsJSON)
// Map settings
result.MapAttribution = a.getMapAttribution()
result.MinZoom = a.getMinZoom()
result.MaxZoom = a.getMaxZoom()
}

// Add Paths
result.Paths = parseResult.paths
// Add Points
result.Points = parseResult.points
// Calculate moving statistics
if parseResult.md != nil {
result.Kilometers = lp.Sprintf("%.2f", parseResult.md.MovingDistance/1000)
result.Hours = lp.Sprintf(
Expand All @@ -93,7 +76,6 @@ func (a *goBlog) getTrack(p *post, withMapFeatures bool) (result *trackResult, e
math.Floor(math.Mod(parseResult.md.MovingTime, 60)), // Seconds
)
}

if parseResult.ud != nil {
result.Uphill = lp.Sprintf("%.0f", parseResult.ud.Uphill)
result.Downhill = lp.Sprintf("%.0f", parseResult.ud.Downhill)
Expand Down Expand Up @@ -139,7 +121,7 @@ func trackParseGPX(gpxString string) (result *trackParseResult, err error) {
}
for _, point := range segment.Points {
path.points = append(path.points, &trackPoint{
Lat: point.GetLatitude(), Lon: point.GetLongitude(),
Lat: point.Latitude, Lon: point.Longitude,
})
}
paths = append(paths, path)
Expand All @@ -149,7 +131,7 @@ func trackParseGPX(gpxString string) (result *trackParseResult, err error) {
path := &trackPath{}
for _, point := range route.Points {
path.points = append(path.points, &trackPoint{
Lat: point.GetLatitude(), Lon: point.GetLongitude(),
Lat: point.Latitude, Lon: point.Longitude,
})
}
paths = append(paths, path)
Expand Down Expand Up @@ -182,7 +164,7 @@ func trackParseGPX(gpxString string) (result *trackParseResult, err error) {
result.points = []*trackPoint{}
for _, point := range result.gpxData.Waypoints {
result.points = append(result.points, &trackPoint{
Lat: point.GetLatitude(), Lon: point.GetLongitude(),
Lat: point.Latitude, Lon: point.Longitude,
})
}

Expand Down
27 changes: 4 additions & 23 deletions geoTrack_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func Test_geoTrack(t *testing.T) {
},
}

resEn, err := app.getTrack(p, true)
resEn, err := app.getTrack(p)
require.NoError(t, err)

assert.NotEmpty(t, resEn.Paths)
Expand All @@ -46,7 +46,7 @@ func Test_geoTrack(t *testing.T) {

p.Blog = "de"

resDe, err := app.getTrack(p, true)
resDe, err := app.getTrack(p)
require.NoError(t, err)

assert.NotEmpty(t, resDe.Paths)
Expand All @@ -67,33 +67,14 @@ func Test_geoTrack(t *testing.T) {
},
}

resEn, err = app.getTrack(p, true)
resEn, err = app.getTrack(p)
require.NoError(t, err)

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)

// Test "privacy" feature to hide track

p = &post{
Blog: "en",
Parameters: map[string][]string{
"gpx": {string(gpxBytes)},
"showroute": {"false"},
},
}

resEn, err = app.getTrack(p, p.showTrackRoute())
require.NoError(t, err)

assert.False(t, p.showTrackRoute())
assert.Empty(t, resEn.Paths)
assert.Empty(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")
Expand All @@ -107,7 +88,7 @@ func Test_geoTrack(t *testing.T) {
},
}

resEn, err = app.getTrack(p, true)
resEn, err = app.getTrack(p)
require.NoError(t, err)

assert.NotEmpty(t, resEn.Paths)
Expand Down
2 changes: 1 addition & 1 deletion original-assets/styles/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ $colors: (
}

html {
scrollbar-color: var(--primary) transparent;
@include lightmode;
@media (prefers-color-scheme: dark) {
@include darkmode;
}
scrollbar-color: var(--primary) transparent;
}

body {
Expand Down
2 changes: 1 addition & 1 deletion templates/assets/css/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
}

html {
scrollbar-color: var(--primary) transparent;
--background: #fff;
--primary: #000;
color: #000;
scrollbar-color: var(--primary) transparent;
}
@media (prefers-color-scheme: dark) {
html {
Expand Down
4 changes: 2 additions & 2 deletions ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func (a *goBlog) renderEditorPreview(hb *htmlbuilder.HtmlBuilder, bc *configBlog
a.renderPostTitle(hb, p)
a.renderPostMeta(hb, p, bc, "preview")
a.postHtmlToWriter(hb, &postHtmlOptions{p: p, absolute: true})
// a.renderPostGPX(hb, p, bc)
a.renderPostTrack(hb, p, bc, true)
a.renderPostTax(hb, p, bc)
}

Expand Down Expand Up @@ -919,7 +919,7 @@ func (a *goBlog) renderPost(hb *htmlbuilder.HtmlBuilder, rd *renderData) {
// External Videp
a.renderPostVideo(hb, p)
// GPS Track
a.renderPostGPX(hb, p, rd.Blog)
a.renderPostTrack(hb, p, rd.Blog, false)
// Taxonomies
a.renderPostTax(hb, p, rd.Blog)
hb.WriteElementClose("article")
Expand Down
81 changes: 66 additions & 15 deletions uiComponents.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package main

import (
"cmp"
"encoding/json"
"fmt"
"math"
"strings"
"time"

Expand Down Expand Up @@ -476,15 +478,23 @@ func (*goBlog) renderPostTitle(hb *htmlbuilder.HtmlBuilder, p *post) {
hb.WriteElementClose("h1")
}

func (a *goBlog) renderPostGPX(hb *htmlbuilder.HtmlBuilder, p *post, b *configBlog) {
func (a *goBlog) renderPostTrack(hb *htmlbuilder.HtmlBuilder, p *post, b *configBlog, disableInteractiveMap bool) {
if p == nil || !p.hasTrack() {
return
}
track, err := a.getTrack(p, p.showTrackRoute())
track, err := a.getTrack(p)
if err != nil || track == nil {
return
}
// Track stats
a.renderPostTrackStatistics(hb, track, b)
if !disableInteractiveMap && p.showTrackRoute() && track.hasMapFeatures() {
a.renderPostTrackMap(hb, track)
} else if track.hasPath() {
a.renderPostTrackSVG(hb, track)
}
}

func (a *goBlog) renderPostTrackStatistics(hb *htmlbuilder.HtmlBuilder, track *trackResult, b *configBlog) {
hb.WriteElementOpen("p")
if track.Name != "" {
hb.WriteElementOpen("strong")
Expand Down Expand Up @@ -518,19 +528,60 @@ func (a *goBlog) renderPostGPX(hb *htmlbuilder.HtmlBuilder, p *post, b *configBl
hb.WriteEscaped(a.ts.GetTemplateStringVariant(b.Lang, "meters"))
}
hb.WriteElementClose("p")
// Map (only show if it has features)
if track.hasMapFeatures() {
hb.WriteElementOpen(
"div", "id", "map", "class", "p",
"data-paths", track.PathsJSON,
"data-points", track.PointsJSON,
"data-minzoom", track.MinZoom, "data-maxzoom", track.MaxZoom,
"data-attribution", track.MapAttribution,
)
hb.WriteElementClose("div")
hb.WriteElementOpen("script", "defer", "", "src", a.assetFileName("js/geomap.js"))
hb.WriteElementClose("script")
}

func (a *goBlog) renderPostTrackMap(hb *htmlbuilder.HtmlBuilder, track *trackResult) {
pathsJSON, _ := json.Marshal(track.Paths)
pointsJSON, _ := json.Marshal(track.Points)
hb.WriteElementOpen(
"div", "id", "map", "class", "p",
"data-paths", string(pathsJSON),
"data-points", string(pointsJSON),
"data-minzoom", a.getMinZoom(),
"data-maxzoom", a.getMaxZoom(),
"data-attribution", a.getMapAttribution(),
)
hb.WriteElementClose("div")
hb.WriteElementOpen("script", "defer", "", "src", a.assetFileName("js/geomap.js"))
hb.WriteElementClose("script")
}

func (a *goBlog) renderPostTrackSVG(hb *htmlbuilder.HtmlBuilder, track *trackResult) {
const width, height = 700.0, 400.0
// Calculate min/max values
minLat, minLon := math.Inf(1), math.Inf(1) // Positive infinity
maxLat, maxLon := math.Inf(-1), math.Inf(-1) // Negative infinity
for _, path := range track.Paths {
for _, point := range path {
minLat = math.Min(minLat, point.Lat)
maxLat = math.Max(maxLat, point.Lat)
minLon = math.Min(minLon, point.Lon)
maxLon = math.Max(maxLon, point.Lon)
}
}
// Calculate scaling and offsets
dataAspectRatio := (maxLon - minLon) / (maxLat - minLat)
svgAspectRatio := width / height
var scale, xOffset, yOffset float64
if dataAspectRatio > svgAspectRatio {
scale = width / (maxLon - minLon)
yOffset = (height - (maxLat-minLat)*scale) / 2
} else {
scale = height / (maxLat - minLat)
xOffset = (width - (maxLon-minLon)*scale) / 2
}
// Generate SVG
hb.WriteElementOpen("svg", "width", "100%", "viewbox", fmt.Sprintf("0 0 %.0f %.0f", width, height))
for _, path := range track.Paths {
hb.WriteString(`<polyline points="`)
for _, pt := range path {
x := xOffset + (pt.Lon-minLon)*scale
y := height - (yOffset + (pt.Lat-minLat)*scale)
hb.WriteString(fmt.Sprintf("%f,%f ", x, y))
}
hb.WriteString(`" fill="none" stroke="currentColor" stroke-width="3" />`)
}
hb.WriteElementClose("svg")
}

func (a *goBlog) renderPostReactions(hb *htmlbuilder.HtmlBuilder, p *post) {
Expand Down

0 comments on commit 45a38e3

Please sign in to comment.