diff --git a/db/go.mod b/db/go.mod index 23aff2eb..1178589b 100644 --- a/db/go.mod +++ b/db/go.mod @@ -5,20 +5,20 @@ go 1.23.0 toolchain go1.24.1 require ( + github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73 github.com/meilisearch/meilisearch-go v0.29.0 github.com/pocketbase/dbx v1.11.0 github.com/pocketbase/pocketbase v0.26.1 + github.com/tkrajina/gpxgo v1.4.0 ) require ( git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/go-ap/errors v0.0.0-20250409143711-5686c11ae650 // indirect - github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73 // indirect github.com/go-sql-driver/mysql v1.8.1 // indirect github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/gorilla/css v1.0.1 // indirect - github.com/twpayne/go-geom v1.6.1 // indirect github.com/valyala/fastjson v1.6.4 // indirect golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect ) @@ -48,13 +48,12 @@ require ( github.com/spf13/cast v1.7.1 github.com/spf13/cobra v1.9.1 // indirect github.com/spf13/pflag v1.0.6 // indirect - github.com/twpayne/go-gpx v1.5.0 github.com/twpayne/go-polyline v1.1.1 golang.org/x/crypto v0.37.0 // indirect golang.org/x/image v0.25.0 // indirect - golang.org/x/net v0.39.0 // indirect + golang.org/x/net v0.39.0 golang.org/x/oauth2 v0.28.0 // indirect - golang.org/x/sync v0.13.0 // indirect + golang.org/x/sync v0.13.0 golang.org/x/sys v0.32.0 // indirect golang.org/x/text v0.24.0 // indirect modernc.org/libc v1.61.13 // indirect diff --git a/db/go.sum b/db/go.sum index af134c56..711d42c1 100644 --- a/db/go.sum +++ b/db/go.sum @@ -2,10 +2,6 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:cliQ4HHsCo6xi2oWZYKWW4bly/Ory9FuTpFPRxj/mAg= git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078/go.mod h1:g/V2Hjas6Z1UHUp4yIx6bATpNzJ7DYtD0FG3+xARWxs= -github.com/alecthomas/assert/v2 v2.10.0 h1:jjRCHsj6hBJhkmhznrCzoNpbA3zqy0fYiUcYZP/GkPY= -github.com/alecthomas/assert/v2 v2.10.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= -github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= -github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= @@ -59,8 +55,6 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= -github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= -github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= @@ -89,8 +83,8 @@ github.com/pocketbase/pocketbase v0.26.1 h1:0WBqIRKKPCqp+xHPVLB4fevkoT9HVlR4BSuN github.com/pocketbase/pocketbase v0.26.1/go.mod h1:t5y5pfnhrEg//RuSzSg0a926OLZ0oQj66jYs3BzDJwA= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= @@ -109,10 +103,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/twpayne/go-geom v1.6.1 h1:iLE+Opv0Ihm/ABIcvQFGIiFBXd76oBIar9drAwHFhR4= -github.com/twpayne/go-geom v1.6.1/go.mod h1:Kr+Nly6BswFsKM5sd31YaoWS5PeDDH2NftJTK7Gd028= -github.com/twpayne/go-gpx v1.5.0 h1:HvFSJ+0r0sbhOQ8mTvd0/n0FhcgjTFsKQGG6o7PV6G4= -github.com/twpayne/go-gpx v1.5.0/go.mod h1:vjvu/125399qj6k+px2v2v8dm08DM4I4dFBJmHHt2TE= +github.com/tkrajina/gpxgo v1.4.0 h1:cSD5uSwy3VZuNFieTEZLyRnuIwhonQEkGPkPGW4XNag= +github.com/tkrajina/gpxgo v1.4.0/go.mod h1:BXSMfUAvKiEhMEXAFM2NvNsbjsSvp394mOvdcNjettg= github.com/twpayne/go-polyline v1.1.1 h1:/tSF1BR7rN4HWj4XKqvRUNrCiYVMCvywxTFVofvDV0w= github.com/twpayne/go-polyline v1.1.1/go.mod h1:ybd9IWWivW/rlXPXuuckeKUyF3yrIim+iqA7kSl4NFY= github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ= @@ -130,6 +122,7 @@ golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc= @@ -138,11 +131,17 @@ golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= +golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/db/integrations/komoot/komoot.go b/db/integrations/komoot/komoot.go index b350f9e6..a2041d5a 100644 --- a/db/integrations/komoot/komoot.go +++ b/db/integrations/komoot/komoot.go @@ -1,7 +1,6 @@ package komoot import ( - "bytes" "encoding/base64" "encoding/json" "errors" @@ -17,7 +16,7 @@ import ( "github.com/pocketbase/pocketbase/core" "github.com/pocketbase/pocketbase/tools/filesystem" "github.com/pocketbase/pocketbase/tools/security" - "github.com/twpayne/go-gpx" + "github.com/tkrajina/gpxgo/gpx" ) func SyncKomoot(app core.App) error { @@ -438,35 +437,36 @@ func fetchPhoto(url string, width string, height string) (*filesystem.File, erro } func generateTourGPX(detailedTour *DetailedKomootTour) (*filesystem.File, error) { - var points []*gpx.WptType + var points []gpx.GPXPoint for _, item := range detailedTour.Embedded.Coordinates.Items { t := detailedTour.Date.Unix() + int64(item.T/1000) - points = append(points, &gpx.WptType{Lat: item.Lat, Lon: item.Lng, Ele: item.Alt, Time: time.Unix(t, 0)}) + points = append(points, gpx.GPXPoint{ + Point: gpx.Point{Latitude: item.Lat, Longitude: item.Lng, Elevation: *gpx.NewNullableFloat64(item.Alt)}, + Timestamp: time.Unix(t, 0)}) } - gpx := &gpx.GPX{ + gpxData := &gpx.GPX{ Version: "1.1", Creator: "komoot GPX Exporter", - Trk: []*gpx.TrkType{ + Tracks: []gpx.GPXTrack{ { Name: detailedTour.Name, - TrkSeg: []*gpx.TrkSegType{ + Segments: []gpx.GPXTrackSegment{ { - TrkPt: points, + Points: points, }, }, }, }, } - var buf bytes.Buffer - err := gpx.Write(&buf) + gpxAsXML, err := gpxData.ToXml(gpx.ToXmlParams{Version: "1.1", Indent: true}) if err != nil { return nil, err } - gpxFile, err := filesystem.NewFileFromBytes(buf.Bytes(), detailedTour.Name+".gpx") + gpxFile, err := filesystem.NewFileFromBytes(gpxAsXML, detailedTour.Name+".gpx") if err != nil { return nil, err } diff --git a/db/integrations/strava/strava.go b/db/integrations/strava/strava.go index 77bdd7d4..e291ea9d 100644 --- a/db/integrations/strava/strava.go +++ b/db/integrations/strava/strava.go @@ -15,7 +15,7 @@ import ( "github.com/pocketbase/pocketbase/core" "github.com/pocketbase/pocketbase/tools/filesystem" "github.com/pocketbase/pocketbase/tools/security" - "github.com/twpayne/go-gpx" + "github.com/tkrajina/gpxgo/gpx" "github.com/twpayne/go-polyline" ) @@ -610,7 +610,7 @@ func generateActivityGPX(activity *DetailedStravaActivity, accessToken string) ( timeStream := streamResponse.Time altitudeStream := streamResponse.Altitude - var points []*gpx.WptType + var points []gpx.GPXPoint for i, latlng := range latLngStream.Data { lat := latlng[0] @@ -618,30 +618,31 @@ func generateActivityGPX(activity *DetailedStravaActivity, accessToken string) ( alt := altitudeStream.Data[i] t := activity.StartDate.Unix() + int64(timeStream.Data[i]) - points = append(points, &gpx.WptType{Lat: lat, Lon: lon, Ele: alt, Time: time.Unix(t, 0)}) + points = append(points, gpx.GPXPoint{ + Point: gpx.Point{Latitude: lat, Longitude: lon, Elevation: *gpx.NewNullableFloat64(alt)}, + Timestamp: time.Unix(t, 0)}) } - gpx := &gpx.GPX{ + gpxData := &gpx.GPX{ Version: "1.1", Creator: "Strava GPX Exporter", - Trk: []*gpx.TrkType{ + Tracks: []gpx.GPXTrack{ { Name: activity.Name, - TrkSeg: []*gpx.TrkSegType{ + Segments: []gpx.GPXTrackSegment{ { - TrkPt: points, + Points: points, }, }, }, }, } - var buf bytes.Buffer - err = gpx.Write(&buf) + gpxAsXML, err := gpxData.ToXml(gpx.ToXmlParams{Version: "1.1", Indent: true}) if err != nil { return nil, err } - gpxFile, err := filesystem.NewFileFromBytes(buf.Bytes(), activity.Name+".gpx") + gpxFile, err := filesystem.NewFileFromBytes(gpxAsXML, activity.Name+".gpx") if err != nil { return nil, err } diff --git a/db/util/meilisearch.go b/db/util/meilisearch.go index 1b70110c..a986909d 100644 --- a/db/util/meilisearch.go +++ b/db/util/meilisearch.go @@ -14,7 +14,7 @@ import ( "github.com/meilisearch/meilisearch-go" "github.com/pocketbase/dbx" "github.com/pocketbase/pocketbase/core" - "github.com/twpayne/go-gpx" + "github.com/tkrajina/gpxgo/gpx" "github.com/twpayne/go-polyline" ) @@ -120,15 +120,17 @@ func getPolyline(app core.App, r *core.Record) (string, error) { if err != nil { return "", err } - gpxData, err := gpx.Read(content) + gpxData, err := gpx.Parse(content) if err != nil { return "", err } + + gpxData.SimplifyTracks(50) coordinates := make([][]float64, 4) - for _, trk := range gpxData.Trk { - for _, seg := range trk.TrkSeg { - for _, pt := range seg.TrkPt { - coordinates = append(coordinates, []float64{pt.Lat, pt.Lon}) + for _, trk := range gpxData.Tracks { + for _, seg := range trk.Segments { + for _, pt := range seg.Points { + coordinates = append(coordinates, []float64{pt.Latitude, pt.Longitude}) } } } diff --git a/web/src/lib/components/trail/map_with_elevation_maplibre.svelte b/web/src/lib/components/trail/map_with_elevation_maplibre.svelte index 7baf8569..9af23802 100644 --- a/web/src/lib/components/trail/map_with_elevation_maplibre.svelte +++ b/web/src/lib/components/trail/map_with_elevation_maplibre.svelte @@ -398,8 +398,21 @@ if (!geojson || !map || !map.style) { return; } - - layerManager.addLayer("preview", new PreviewLayer(geojson)); + layerManager.addLayer( + "preview", + new PreviewLayer(map, geojson, { + preview: { + onEnter: (e) => { + const trail = trails.find(t => t.id === (e as any).features[0].properties.trail) + if(!trail) return; + highlightCluster(trail) + }, + onLeave: (e) => { + unHighlightCluster() + } + }, + }), + ); } function moveCrosshairToCursorPosition(e: M.MapMouseEvent) { diff --git a/web/src/lib/vendor/maplibre-layer-manager/preview-layer.ts b/web/src/lib/vendor/maplibre-layer-manager/preview-layer.ts index 2d9d6e08..0db6eabd 100644 --- a/web/src/lib/vendor/maplibre-layer-manager/preview-layer.ts +++ b/web/src/lib/vendor/maplibre-layer-manager/preview-layer.ts @@ -1,11 +1,31 @@ -import type { StyleSpecification } from "maplibre-gl"; +import type { MapMouseEvent, StyleSpecification } from "maplibre-gl"; import type { BaseLayer } from "./layers"; +import * as M from "maplibre-gl"; export class PreviewLayer implements BaseLayer { - spec: StyleSpecification; + private map: M.Map; + + spec: StyleSpecification; + listeners: Record void; onMouseDown?: (e: MapMouseEvent) => void; onEnter?: (e: MapMouseEvent) => void; onLeave?: (e: MapMouseEvent) => void; onMouseMove?: (e: MapMouseEvent) => void; }> = { + "preview": { + onEnter: () => this.map!.getCanvas().style.cursor = "pointer", + onLeave: () => this.map!.getCanvas().style.cursor = "" + }, + "preview-start-points": { + onEnter: () => this.map!.getCanvas().style.cursor = "pointer", + onLeave: () => this.map!.getCanvas().style.cursor = "" + } + }; + + constructor(map: M.Map, geojson: GeoJSON.FeatureCollection, listeners?: Record void; onMouseDown?: (e: MapMouseEvent) => void; onEnter?: (e: MapMouseEvent) => void; onLeave?: (e: MapMouseEvent) => void; onMouseMove?: (e: MapMouseEvent) => void; }>) { + + this.map = map; + this.listeners = { + "preview": { ...this.listeners["preview"], ...listeners?.["preview"] }, + "preview-start-points": { ...this.listeners["preview-start-points"], ...listeners?.["preview-start-points"] } + } - constructor(geojson: GeoJSON.FeatureCollection) { const startPoints: GeoJSON.FeatureCollection = { type: "FeatureCollection", features: geojson.features.map((f, i) => ({ @@ -28,7 +48,7 @@ export class PreviewLayer implements BaseLayer { type: "geojson", data: geojson, }, - "preview-start-point": { + "preview-start-points": { type: "geojson", data: startPoints, } @@ -45,9 +65,9 @@ export class PreviewLayer implements BaseLayer { }, }, { - id: "preview-start-point", + id: "preview-start-points", type: "circle", - source: "preview-start-point", + source: "preview-start-points", minzoom: 10, paint: { "circle-color": "#242734",