diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index 5665847..0000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,25 +0,0 @@ -stages: - - build - - deploy - -build-gpx-anomalies: - stage: build - image: golang:1.15-buster - before_script: - - apt-get -qq update && apt-get --yes install zip - script: - - env GOOS=windows go build -o gpx-anomalies.exe ./cmd/gpx-anomalies - - zip gpx-anomalies.zip gpx-anomalies.exe - artifacts: - paths: - - gpx-anomalies.zip - -publish-gpx-anomalies: - stage: deploy - image: curlimages/curl:latest - variables: - GIT_STRATEGY: none - script: - - 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file gpx-anomalies.zip "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/gpx-anomalies/latest/gpx-anomalies.zip"' - rules: - - if: '$CI_COMMIT_BRANCH == "master"' diff --git a/cmd/analyze-gpx/main.go b/cmd/analyze-gpx/main.go index fa1c0bc..e88cdb8 100644 --- a/cmd/analyze-gpx/main.go +++ b/cmd/analyze-gpx/main.go @@ -19,11 +19,8 @@ import ( func main() { log.SetFlags(0) stopNames := flag.String("stops", "", "Source for refreshment stops") - stopRect := flag.Float64("sr", placenames.DefaultGPXSummarizerConfig.CoffeeStopSearchRectangleSize, "Size (m) of the rectangle we search for coffee stops near the route") - stopDupDist := flag.Float64("sdd", placenames.DefaultGPXSummarizerConfig.CoffeeStopDuplicateDistance, "Suppress recurrences of coffee stops within this distance (km)") - dupDist := flag.Float64("dd", placenames.DefaultGPXSummarizerConfig.PointOfInterestDuplicateDistance, "Suppress recurrences of points of interest within this distance (km)") - minDist := flag.Float64("md", placenames.DefaultGPXSummarizerConfig.PointOfInterestMinimumDistance, "Minimum distance (km) between points of interest") - minSettlement := flag.String("ms", "Other Settlement", "Exclude populated places smaller than this (City, Town, Village, Hamlet, Other Settlement)") + minDist := flag.Float64("min-dist", 0.2, "Minimum distance (km) between points of interest") + minSettlement := flag.String("min-settlement", "Other Settlement", "Exclude populated places smaller than this (City, Town, Village, Hamlet, Other Settlement)") flag.Parse() if flag.NArg() != 1 { log.Fatal("Usage: %s [--stops=ctccambridge|cyclingmaps] [--min-dist X] [--min-settlement S] GPX_FILE_OR_DIRECTORY") @@ -41,16 +38,12 @@ func main() { log.Fatal(err) } } - gs, err := placenames.NewGPXSummarizer( - placenames.WithMinimumSettlement(*minSettlement), - placenames.WithPointOfInterestMinimumDistance(*minDist), - placenames.WithPointOfInterestDuplicateDistance(*dupDist), - placenames.WithCoffeeStopSearchRectangleSize(*stopRect), - placenames.WithCoffeeStopDuplicateDistance(*stopDupDist), - ) + gs, err := placenames.NewGPXSummarizer() if err != nil { log.Fatal(err) } + gs.SetMinDistance(*minDist) + gs.SetMinSettlement(*minSettlement) if info.IsDir() { err = summarizeDirectory(gs, stops, inFile) } else { diff --git a/cmd/gpx-anomalies/main.go b/cmd/gpx-anomalies/main.go deleted file mode 100644 index 28856c4..0000000 --- a/cmd/gpx-anomalies/main.go +++ /dev/null @@ -1,135 +0,0 @@ -package main - -import ( - "fmt" - "log" - "math" - "os" - - "github.com/fofanov/go-osgb" - "github.com/twpayne/go-gpx" - "github.com/urfave/cli/v2" -) - -func main() { - log.SetFlags(0) - app := &cli.App{ - Name: "gpx-anomalies", - Usage: "Find repeated points in a GPX track", - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "gpx-file", - Aliases: []string{"g"}, - Usage: "Name of GPX file to process", - Required: true, - }, - &cli.Float64Flag{ - Name: "fuzz", - Aliases: []string{"f"}, - Usage: "Consider two points coincident if they are within FUZZ kilometres of each other", - Value: 0.005, - }, - &cli.Float64Flag{ - Name: "min-distance", - Aliases: []string{"min"}, - Usage: "Only show repeats that appear at least MIN kilometers apart", - Value: 0.1, - }, - &cli.Float64Flag{ - Name: "max-distance", - Aliases: []string{"max"}, - Usage: "Do not show repeats that appear more than MAX kilometers apart", - Value: 5.0, - }, - }, - Action: func(c *cli.Context) error { - points, err := readGPXTrack(c.String("gpx-file")) - if err != nil { - log.Fatal(err) - } - findDuplicates( - points, - c.Float64("fuzz")*1000.0, - c.Float64("min-distance")*1000.0, - c.Float64("max-distance")*1000.0, - ) - return nil - }, - } - if err := app.Run(os.Args); err != nil { - log.Fatal(err) - } - -} - -func findDuplicates(points []RoutePoint, fuzz, minDist, maxDist float64) { - var lastError *RoutePoint - for i := range points { - p := points[i] - for j := i + 1; j < len(points); j++ { - q := points[j] - if p.Distance == q.Distance { - continue - } - d := euclideanDistance(p.Coordinate, q.Coordinate) - D := q.Distance - p.Distance - if d < fuzz && D > minDist && D < maxDist { - if lastError == nil || p.Distance-lastError.Distance > 500 { - fmt.Printf("Point (%0.f, %0.f) revisited at %0.2f km and %0.2f km\n", - p.Coordinate.Easting, p.Coordinate.Northing, p.Distance/1000.0, q.Distance/1000.0) - } - lastError = &p - } - } - } -} - -func euclideanDistance(p, q *osgb.OSGB36Coordinate) float64 { - x := p.Easting - q.Easting - y := p.Northing - q.Northing - return math.Sqrt(x*x + y*y) -} - -func readGPXTrack(filename string) ([]RoutePoint, error) { - r, err := os.Open(filename) - if err != nil { - return nil, fmt.Errorf("error opening %s for reading: %v", filename, err) - } - defer r.Close() - g, err := gpx.Read(r) - if err != nil { - return nil, fmt.Errorf("error reading GPS track %s: %v", filename, err) - } - trans, err := osgb.NewOSTN15Transformer() - if err != nil { - return nil, fmt.Errorf("error constructing coordinate transformer: %v", err) - } - distance := 0.0 - var prevPoint *osgb.OSGB36Coordinate - var points []RoutePoint - for _, trk := range g.Trk { - for _, seg := range trk.TrkSeg { - for _, trkPt := range seg.TrkPt { - gpsCoord := osgb.NewETRS89Coord(trkPt.Lon, trkPt.Lat, trkPt.Ele) - p, err := trans.ToNationalGrid(gpsCoord) - if err != nil { - return nil, fmt.Errorf("error converting coordinates to National Grid: %v", err) - } - if prevPoint != nil { - distance += euclideanDistance(prevPoint, p) - } - points = append(points, RoutePoint{ - Coordinate: p, - Distance: distance, - }) - prevPoint = p - } - } - } - return points, nil -} - -type RoutePoint struct { - Coordinate *osgb.OSGB36Coordinate - Distance float64 -} diff --git a/go.mod b/go.mod index 2a46e8e..6c63ab4 100644 --- a/go.mod +++ b/go.mod @@ -3,14 +3,12 @@ module github.com/ray1729/gpx-utils go 1.13 require ( - github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect + github.com/d4l3k/messagediff v1.2.1 // indirect github.com/dhconnelly/rtreego v1.0.0 github.com/fofanov/go-osgb v0.0.0-20170711141822-6893d1f95cd9 - github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/twpayne/go-geom v1.3.6 // indirect github.com/twpayne/go-gpx v1.2.0 - github.com/urfave/cli/v2 v2.3.0 - github.com/wlbr/mule v0.0.0-20200517121540-6f9faa2e2d0b // indirect - golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 // indirect - golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1 // indirect + github.com/wlbr/mule v0.0.0-20200329114911-0724e1639b62 // indirect + golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7 // indirect + golang.org/x/text v0.3.4 // indirect ) diff --git a/go.sum b/go.sum index 8926029..d3ee44a 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,4 @@ github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/DATA-DOG/go-sqlmock v1.3.2/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= @@ -8,12 +7,8 @@ github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jB github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/d4l3k/messagediff v1.2.1/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkEQxENCrlLo= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dhconnelly/rtreego v1.0.0 h1:1+V1STGw+zwx7jpvH/fwbeC5w5gZfn+XinARU45oRek= github.com/dhconnelly/rtreego v1.0.0/go.mod h1:SDozu0Fjy17XH1svEXJgdYq8Tah6Zjfa/4Q33Z80+KM= @@ -37,14 +32,7 @@ github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zM github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/ory/dockertest/v3 v3.6.0/go.mod h1:4ZOpj8qBUmh8fcBSVzkH2bws2s91JdGvHUqan4GHEuQ= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -53,32 +41,31 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/twpayne/go-geom v1.0.0 h1:ARrRnN4+rBX3LZFZQy9NFeXXlgQqVL4OOvAcnLdgy2g= github.com/twpayne/go-geom v1.0.0/go.mod h1:RWsl+e3XSahOul/KH2BHCfF0QxSL4RMnMlFw/TNmET0= github.com/twpayne/go-geom v1.3.6 h1:O27mIXZnMYiZi0ZD8ewjs/IT/ZOFVbZHBzPjA9skdmg= github.com/twpayne/go-geom v1.3.6/go.mod h1:XTyWHR6+l9TUYONbbK4ImUTYbWDCu2ySSPrZmmiA0Pg= +github.com/twpayne/go-gpx v1.1.1 h1:vbg0lRc/ZKSu8ev84/hJWZtplKJdBbucNmks4TNzSqQ= +github.com/twpayne/go-gpx v1.1.1/go.mod h1:fQ+EsiFNgDuErUYyI0ZOgZPB+ACxW58L16oormty798= github.com/twpayne/go-gpx v1.2.0 h1:Jjq0NKXgHmEXXhmQue4KWtAVG5gxkAYY+FvsM1AliLQ= github.com/twpayne/go-gpx v1.2.0/go.mod h1:70xTQn0dGph3dgKIPxfl0K3XMVNpulC70/e383iHouA= github.com/twpayne/go-kml v1.0.0/go.mod h1:LlvLIQSfMqYk2O7Nx8vYAbSLv4K9rjMvLlEdUKWdjq0= github.com/twpayne/go-kml v1.5.1/go.mod h1:kz8jAiIz6FIdU2Zjce9qGlVtgFYES9vt7BTPBHf5jl4= github.com/twpayne/go-polyline v1.0.0/go.mod h1:ICh24bcLYBX8CknfvNPKqoTbe+eg+MX1NPyJmSBo7pU= github.com/twpayne/go-waypoint v0.0.0-20200706203930-b263a7f6e4e8/go.mod h1:qj5pHncxKhu9gxtZEYWypA/z097sxhFlbTyOyt9gcnU= -github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M= -github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= -github.com/wlbr/mule v0.0.0-20200517121540-6f9faa2e2d0b h1:wZDyxL+jeSaBLmUFM+k/P97BbS8z3QYKcfbvWPnKq9Q= -github.com/wlbr/mule v0.0.0-20200517121540-6f9faa2e2d0b/go.mod h1:uDXgZTfL0uJWiY/MQKcqI5VPQV8PCooNsWXozHf7CJ8= +github.com/wlbr/mule v0.0.0-20200329114911-0724e1639b62 h1:vHDdpwOGHzfFKbMLEnnM0s1jnGLjsQ9EPWtCFWMJs8o= +github.com/wlbr/mule v0.0.0-20200329114911-0724e1639b62/go.mod h1:uDXgZTfL0uJWiY/MQKcqI5VPQV8PCooNsWXozHf7CJ8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/net v0.0.0-20180824152047-4bcd98cce591 h1:4S2XUgvg3hUNTvxI307qkFPb9zKHG3Nf9TXFzX/DZZI= golang.org/x/net v0.0.0-20180824152047-4bcd98cce591/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20191003171128-d98b1b443823/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7 h1:3uJsdck53FDIpWwLeAXlia9p4C8j0BO2xZrqzKpL0D8= golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1 h1:4qWs8cYYH6PoEFy4dfhDFgoMGkwAcETd+MmPdCPMzUc= -golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -87,23 +74,16 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200121082415-34d275377bf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= diff --git a/pkg/cafes/common.go b/pkg/cafes/common.go index b216e7e..e2f21bc 100644 --- a/pkg/cafes/common.go +++ b/pkg/cafes/common.go @@ -9,9 +9,6 @@ import ( "github.com/dhconnelly/rtreego" ) -// Size (in metres) of the bounding box around a stop -const stopRectangleSize = 50 - type RefreshmentStop struct { Name string Url string @@ -21,7 +18,7 @@ type RefreshmentStop struct { func (s *RefreshmentStop) Bounds() *rtreego.Rect { p := rtreego.Point{s.Easting, s.Northing} - return p.ToRect(stopRectangleSize) + return p.ToRect(100) } func (s *RefreshmentStop) Contains(p rtreego.Point) bool { diff --git a/pkg/cafes/ctccam.go b/pkg/cafes/ctccam.go index 0f396ba..a95c147 100644 --- a/pkg/cafes/ctccam.go +++ b/pkg/cafes/ctccam.go @@ -54,12 +54,7 @@ func BuildCtcCamIndex(r io.Reader) (*rtreego.Rtree, error) { func FetchCtcCamIndex() (*rtreego.Rtree, error) { log.Printf("Fetching %s", ctcCamWaypointsUrl) - req, err := http.NewRequest(http.MethodGet, ctcCamWaypointsUrl, nil) - if err != nil { - return nil, fmt.Errorf("error constructing waypoints request: %v", err) - } - req.Header.Set("User-Agent", "gpx-utils") - res, err := http.DefaultClient.Do(req) + res, err := http.Get(ctcCamWaypointsUrl) if err != nil { return nil, fmt.Errorf("error getting %s: %v", ctcCamWaypointsUrl, err) } diff --git a/pkg/placenames/summarize.go b/pkg/placenames/summarize.go index 9eaa445..b436237 100644 --- a/pkg/placenames/summarize.go +++ b/pkg/placenames/summarize.go @@ -9,8 +9,9 @@ import ( "github.com/dhconnelly/rtreego" "github.com/fofanov/go-osgb" - "github.com/ray1729/gpx-utils/pkg/cafes" "github.com/twpayne/go-gpx" + + "github.com/ray1729/gpx-utils/pkg/cafes" ) var populatedPlaceRank = map[string]int{ @@ -21,80 +22,14 @@ var populatedPlaceRank = map[string]int{ "Other Settlement": 1, } -// GPXSummarizerConfig allows override of defaults used by the search algorithm. -type GPXSummarizerConfig struct { - CoffeeStopSearchRectangleSize float64 - CoffeeStopDuplicateDistance float64 - PointOfInterestDuplicateDistance float64 - PointOfInterestMinimumDistance float64 - MinimumSettlementRank int -} - -var DefaultGPXSummarizerConfig = GPXSummarizerConfig{ - CoffeeStopSearchRectangleSize: 500.0, // m - CoffeeStopDuplicateDistance: 2.0, // km - PointOfInterestDuplicateDistance: 1.0, // km - PointOfInterestMinimumDistance: 0.0, // km - MinimumSettlementRank: 1, // "Other Settlement" -} - -type Option func(*GPXSummarizerConfig) - -// WithCoffeeStopSearchRectangleSize overrides the size (in metres) of the rectangle searched -// for coffee stops near the route. Default 500m. -func WithCoffeeStopSearchRectangleSize(d float64) Option { - return func(c *GPXSummarizerConfig) { - c.CoffeeStopSearchRectangleSize = d - } -} - -// WithCoffeeStopDuplicateDistance overrides the distance (in kilometers) we look back along the -// route when suppressing duplicate coffee stop entries. This should be at least twice the -// CoffeeStopSearchRectangleSize. Default 2km. -func WithCoffeeStopDuplicateDistance(d float64) Option { - return func(c *GPXSummarizerConfig) { - c.CoffeeStopDuplicateDistance = d - } -} - -// WithPointOfInterestDuplicateDistance overrides the distance (in km) we look back along -// the route when suppressing duplicate points of interest. -func WithPointOfInterestDuplicateDistance(d float64) Option { - return func(c *GPXSummarizerConfig) { - c.PointOfInterestDuplicateDistance = d - } -} - -// WithPointOfInterestMinimumDistance overrides the minimum distance (in km) between points -// of interest (if two POI appear within this distance, the second one is suppressed). Default -// 0km (no suppression). -func WithPointOfInterestMinimumDistance(d float64) Option { - return func(c *GPXSummarizerConfig) { - c.PointOfInterestMinimumDistance = d - } -} - -func WithMinimumSettlement(s string) Option { - rank, ok := populatedPlaceRank[s] - if !ok { - panic(fmt.Sprintf("invalid settlement type: %s", s)) - } - return func(c *GPXSummarizerConfig) { - c.MinimumSettlementRank = rank - } -} - type GPXSummarizer struct { - poi *rtreego.Rtree - trans osgb.CoordinateTransformer - conf GPXSummarizerConfig + poi *rtreego.Rtree + trans osgb.CoordinateTransformer + minDist float64 + minSettlementRank int } -func NewGPXSummarizer(opts ...Option) (*GPXSummarizer, error) { - conf := DefaultGPXSummarizerConfig - for _, f := range opts { - f(&conf) - } +func NewGPXSummarizer() (*GPXSummarizer, error) { trans, err := osgb.NewOSTN15Transformer() if err != nil { return nil, err @@ -103,7 +38,15 @@ func NewGPXSummarizer(opts ...Option) (*GPXSummarizer, error) { if err != nil { return nil, err } - return &GPXSummarizer{poi: rt, trans: trans, conf: conf}, nil + return &GPXSummarizer{poi: rt, trans: trans, minDist: 0.2, minSettlementRank: 1}, nil +} + +func (gs *GPXSummarizer) SetMinSettlement(t string) { + gs.minSettlementRank = populatedPlaceRank[t] +} + +func (gs *GPXSummarizer) SetMinDistance(d float64) { + gs.minDist = d } func distance(p1, p2 rtreego.Point) float64 { @@ -165,6 +108,7 @@ func (gs *GPXSummarizer) SummarizeTrack(r io.Reader, stops *rtreego.Rtree) (*Tra var prevPlace string var prevPlacePoint rtreego.Point var prevPoint rtreego.Point + var prevStop *cafes.RefreshmentStop var start rtreego.Point var dN, dE float64 @@ -197,44 +141,25 @@ func (gs *GPXSummarizer) SummarizeTrack(r io.Reader, stops *rtreego.Rtree) (*Tra s.Distance += distance(thisPoint, prevPoint) dE += thisPoint[0] - start[0] dN += thisPoint[1] - start[1] - if nn.Contains(thisPoint) && populatedPlaceRank[nn.Type] >= gs.conf.MinimumSettlementRank { + if nn.Contains(thisPoint) { s.Counties[nn.County]++ - seenRecently := false - for i := len(s.PointsOfInterest) - 1; i >= 0; i-- { - if i < len(s.PointsOfInterest)-1 && s.Distance-s.PointsOfInterest[i].Distance > gs.conf.PointOfInterestDuplicateDistance { - break - } - if nn.Name == s.PointsOfInterest[i].Name { - seenRecently = true - break - } - } - if !seenRecently && distance(thisPoint, prevPlacePoint) > gs.conf.PointOfInterestMinimumDistance { + if nn.Name != prevPlace && + distance(thisPoint, prevPlacePoint) > gs.minDist && + populatedPlaceRank[nn.Type] >= gs.minSettlementRank { s.PointsOfInterest = append(s.PointsOfInterest, POI{Name: nn.Name, Type: nn.Type, Distance: s.Distance}) prevPlace = nn.Name prevPlacePoint = thisPoint } } if stops != nil { - for _, nearbyStop := range stops.SearchIntersect(thisPoint.ToRect(gs.conf.CoffeeStopSearchRectangleSize)) { - stop := nearbyStop.(*cafes.RefreshmentStop) - seenRecently := false - for i := len(s.RefreshmentStops) - 1; i >= 0; i-- { - if i < len(s.RefreshmentStops)-1 && s.Distance-s.RefreshmentStops[i].Distance > gs.conf.CoffeeStopDuplicateDistance { - break - } - if s.RefreshmentStops[i].Name == stop.Name { - seenRecently = true - break - } - } - if !seenRecently { - s.RefreshmentStops = append(s.RefreshmentStops, RefreshmentStop{ - Name: stop.Name, - Url: stop.Url, - Distance: s.Distance, - }) - } + stop, ok := stops.NearestNeighbor(thisPoint).(*cafes.RefreshmentStop) + if ok && stop.Contains(thisPoint) && (prevStop == nil || stop.Name != prevStop.Name) { + s.RefreshmentStops = append(s.RefreshmentStops, RefreshmentStop{ + Name: stop.Name, + Url: stop.Url, + Distance: s.Distance, + }) + prevStop = stop } } prevPoint = thisPoint diff --git a/pkg/rwgps/handler.go b/pkg/rwgps/handler.go index 4ac61d4..f2fe661 100644 --- a/pkg/rwgps/handler.go +++ b/pkg/rwgps/handler.go @@ -41,7 +41,7 @@ func (h *RWGPSHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } routeId, err := strconv.Atoi(rawRouteId) if err != nil { - log.Printf("Error parsing route id '%s': %v", rawRouteId, err) + log.Println("Error parsing route id '%s': %v", rawRouteId, err) http.Error(w, fmt.Sprintf("Invalid routeId: %s", rawRouteId), http.StatusBadRequest) return } diff --git a/scripts/summarizeRoutes.py b/scripts/summarizeRoutes.py deleted file mode 100644 index c18bd4f..0000000 --- a/scripts/summarizeRoutes.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/python3 - -import csv -import sys - -path = sys.argv[1] - -with open(path) as f: - r = csv.reader(f) - skip = True - for x in r: - if skip: - skip = False - continue - coffee = x[7].strip() - lunch = x[8].strip() - tea = x[9].strip() - if coffee and tea: - print(coffee + ", " + lunch + ", " + tea) - -