diff --git a/.github/workflows/lint-go.yml b/.github/workflows/lint-go.yml index be0f84d..e8bdb45 100644 --- a/.github/workflows/lint-go.yml +++ b/.github/workflows/lint-go.yml @@ -12,40 +12,20 @@ jobs: name: lint runs-on: ubuntu-latest steps: - - name: setup cgo dependencies - run: sudo apt-get update && sudo apt-get install libcurl4-openssl-dev libssl-dev + - uses: actions/checkout@v4 - - uses: actions/setup-go@v4 + - name: Set up Go + uses: actions/setup-go@v6 with: - go-version: '1.21' - cache: false + check-latest: true + go-version-file: 'go.mod' - - uses: actions/checkout@v3 + - name: Tidy + # Pin 3rd party action to a specific version as security best-practice. + # Version used: https://github.com/katexochen/go-tidy-check/releases/tag/v2.0.1 + uses: katexochen/go-tidy-check@427c8c07d3d83ab8d7290cad04ce71c12eab3674 - - name: tidy - uses: katexochen/go-tidy-check@v2 - - - name: golangci-lint - uses: golangci/golangci-lint-action@v3 + - name: Golangci-lint + uses: golangci/golangci-lint-action@v9 with: - # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version - version: latest - - # Optional: working directory, useful for monorepos - # working-directory: somedir - - # Optional: golangci-lint command line arguments. - # args: --issues-exit-code=0 - - # Optional: show only new issues if it's a pull request. The default value is `false`. - # only-new-issues: true - - # Optional: if set to true then the all caching functionality will be complete disabled, - # takes precedence over all other caching options. - # skip-cache: true - - # Optional: if set to true then the action don't cache or restore ~/go/pkg. - # skip-pkg-cache: true - - # Optional: if set to true then the action don't cache or restore ~/.cache/go-build. - # skip-build-cache: true + version: v2.9.0 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b52b72e..d634aec 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,15 +9,13 @@ jobs: test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - - name: setup cgo dependencies - run: sudo apt-get update && sudo apt-get install libcurl4-openssl-dev libssl-dev + - uses: actions/checkout@v4 - name: Set up Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v6 with: - go-version: 'stable' + check-latest: true + go-version-file: 'go.mod' - name: Download run: go mod download all diff --git a/.golangci.yaml b/.golangci.yaml index 7c65b89..9f7392d 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -1,98 +1,209 @@ --- +version: "2" + run: # Timeout for analysis. timeout: 5m - # Modules download mode (do not modify go.mod) - module-download-mode: readonly - - # Include test files (see below to exclude certain linters) - tests: true - -issues: - exclude-rules: - # Exclude certain linters for test code - - path: "_test\\.go" - linters: - - bodyclose - - dupl - - funlen + modules-download-mode: readonly + # Allow multiple parallel golangci-lint instances running. + allow-parallel-runners: true output: - format: colored-line-number - print-issued-lines: true - print-linter-name: true + formats: + text: + print-linter-name: true + print-issued-lines: true + colors: true -linters-settings: - depguard: - rules: - main: - # Packages that are not allowed where the value is a suggestion. - deny: - - pkg: "github.com/pkg/errors" - desc: Should be replaced by standard lib errors package - cyclop: - # The maximal code complexity to report. - max-complexity: 15 - skip-tests: true - funlen: - lines: 100 - nestif: - min-complexity: 6 +formatters: + enable: + - gofmt # checks if the code is formatted according to 'gofmt' command + - goimports # in addition to fixing imports, goimports also formats your code in the same style as gofmt + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ + settings: + golines: + max-len: 150 linters: - disable-all: true + default: none enable: # enabled by default by golangci-lint - - errcheck # checking for unchecked errors, these unchecked errors can be critical bugs in some cases - - gosimple # specializes in simplifying a code - - govet # reports suspicious constructs, such as Printf calls whose arguments do not align with the format string - - ineffassign # detects when assignments to existing variables are not used - - staticcheck # is a go vet on steroids, applying a ton of static analysis checks - - typecheck # like the front-end of a Go compiler, parses and type-checks Go code - - unused # checks for unused constants, variables, functions and types + - errcheck # Errcheck is a program for checking for unchecked errors in Go code. These unchecked errors can be critical bugs in some cases. + - govet # Vet examines Go source code and reports suspicious constructs. It is roughly the same as 'go vet' and uses its passes. + - ineffassign # Detects when assignments to existing variables are not used. + - staticcheck # It's the set of rules from staticcheck. + - unused # Checks Go code for unused constants, variables, functions and types. # extra enabled by us - - asasalint # checks for pass []any as any in variadic func(...any) - - asciicheck # checks that your code does not contain non-ASCII identifiers - - bidichk # checks for dangerous unicode character sequences - - bodyclose # checks whether HTTP response body is closed successfully - - cyclop # checks function and package cyclomatic complexity - - dupl # tool for code clone detection - - durationcheck # checks for two durations multiplied together - - errname # checks that sentinel errors are prefixed with the Err and error types are suffixed with the Error - - errorlint # finds code that will cause problems with the error wrapping scheme introduced in Go 1.13 - - exhaustive # checks exhaustiveness of enum switch statements - - copyloopvar # checks for pointers to enclosing loop variables - - forbidigo # forbids identifiers - - funlen # tool for detection of long functions - - gocheckcompilerdirectives # validates go compiler directive comments (//go:) - - goconst # finds repeated strings that could be replaced by a constant - - gocritic # provides diagnostics that check for bugs, performance and style issues - - goimports # in addition to fixing imports, goimports also formats your code in the same style as gofmt - - gomoddirectives # manages the use of 'replace', 'retract', and 'excludes' directives in go.mod - - gomodguard # allow and block lists linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations - - goprintffuncname # checks that printf-like functions are named with f at the end - - gosec # inspects source code for security problems - - loggercheck # checks key value pairs for common logger libraries (kitlog,klog,logr,zap) - - makezero # finds slice declarations with non-zero initial length - - nakedret # finds naked returns in functions greater than a specified function length - - nestif # reports deeply nested if statements - - nilerr # finds the code that returns nil even if it checks that the error is not nil - - nolintlint # reports ill-formed or insufficient nolint directives - - nosprintfhostport # checks for misuse of Sprintf to construct a host with port in a URL - - perfsprint # Golang linter for performance, aiming at usages of fmt.Sprintf which have faster alternatives - - predeclared # finds code that shadows one of Go's predeclared identifiers - - promlinter # checks Prometheus metrics naming via promlint - - reassign # checks that package variables are not reassigned - - revive # fast, configurable, extensible, flexible, and beautiful linter for Go, drop-in replacement of golint - - rowserrcheck # checks whether Err of rows is checked successfully - - sqlclosecheck # checks that sql.Rows and sql.Stmt are closed - - sloglint # A Go linter that ensures consistent code style when using log/slog - - tenv # detects using os.Setenv instead of t.Setenv since Go1.17 - - testableexamples # checks if examples are testable (have an expected output) - - tparallel # detects inappropriate usage of t.Parallel() method in your Go test codes - - unconvert # removes unnecessary type conversions - - unparam # reports unused function parameters - - usestdlibvars # detects the possibility to use variables/constants from the Go standard library - - wastedassign # finds wasted assignment statements - fast: false + - asasalint # Check for pass []any as any in variadic func(...any). + - asciicheck # Checks that all code identifiers does not have non-ASCII symbols in the name. + - bidichk # Checks for dangerous unicode character sequences. + - bodyclose # Checks whether HTTP response body is closed successfully. + - containedctx # Containedctx is a linter that detects struct contained context.Context field. + - copyloopvar # A linter detects places where loop variables are copied. + - cyclop # Checks function and package cyclomatic complexity. + - decorder # Check declaration order and count of types, constants, variables and functions. + - depguard # Go linter that checks if package imports are in a list of acceptable packages. + - dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, #= f()). + - dupl # Detects duplicate fragments of code. + - dupword # Checks for duplicate words in the source code. + - durationcheck # Check for two durations multiplied together. + - errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and reports occurrences where the check for the returned error can be omitted. + - errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`. + - errorlint # Find code that can cause problems with the error wrapping scheme introduced in Go 1.13. + - exhaustive # Check exhaustiveness of enum switch statements. + - exptostd # Detects functions from golang.org/x/exp/ that can be replaced by std functions. + - fatcontext # Detects nested contexts in loops and function literals. + - forbidigo # Forbids identifiers. + - funcorder # Checks the order of functions, methods, and constructors. + - funlen # Checks for long functions. + - ginkgolinter # Enforces standards of using ginkgo and gomega. + - gocheckcompilerdirectives # Checks that go compiler directive comments (//go #) are valid. + - gochecksumtype # Run exhaustiveness checks on Go "sum types". + - goconst # Finds repeated strings that could be replaced by a constant. + - gocritic # Provides diagnostics that check for bugs, performance and style issues. + - goheader # Check if file header matches to pattern. + - gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod. + - goprintffuncname # Checks that printf-like functions are named with `f` at the end. + - gosec # Inspects source code for security problems. + - gosmopolitan # Report certain i18n/l10n anti-patterns in your Go codebase. + - grouper # Analyze expression groups. + - importas # Enforces consistent import aliases. + - inamedparam # Reports interfaces with unnamed method parameters. + - interfacebloat # A linter that checks the number of methods inside an interface. + - intrange # Intrange is a linter to find places where for loops could make use of an integer range. + - loggercheck # Checks key value pairs for common logger libraries (kitlog,klog,logr,slog,zap). + - maintidx # Maintidx measures the maintainability index of each function. + - makezero # Find slice declarations with non-zero initial length. + - mirror # Reports wrong mirror patterns of bytes/strings usage. + - misspell # Finds commonly misspelled English words. + - nakedret # Checks that functions with naked returns are not longer than a maximum size (can be zero). + - nestif # Reports deeply nested if statements. + - nilerr # Find the code that returns nil even if it checks that the error is not nil. + - nilnesserr # Reports constructs that checks for err != nil, but returns a different nil value error. + - nolintlint # Reports ill-formed or insufficient nolint directives. + - nosprintfhostport # Checks for misuse of Sprintf to construct a host with port in a URL. + - perfsprint # Checks that fmt.Sprintf can be replaced with a faster alternative. + - prealloc # Find slice declarations that could potentially be pre-allocated. + - predeclared # Find code that shadows one of Go's predeclared identifiers. + - promlinter # Check Prometheus metrics naming via promlint. + - protogetter # Reports direct reads from proto message fields when getters should be used. + - reassign # Checks that package variables are not reassigned. + - recvcheck # Checks for receiver type consistency. + - revive # Fast, configurable, extensible, flexible, and beautiful linter for Go. Drop-in replacement of golint. + - rowserrcheck # Checks whether Rows.Err of rows is checked successfully. + - sloglint # Ensure consistent code style when using log/slog. + - spancheck # Checks for mistakes with OpenTelemetry/Census spans. + - sqlclosecheck # Checks that sql.Rows, sql.Stmt, sqlx.NamedStmt, pgx.Query are closed. + - tagliatelle # Checks the struct tags. + - testableexamples # Linter checks if examples are testable (have an expected output). + - testifylint # Checks usage of github.com/stretchr/testify. + - thelper # Thelper detects tests helpers which is not start with t.Helper() method. + - tparallel # Tparallel detects inappropriate usage of t.Parallel() method in your Go test codes. + - unconvert # Remove unnecessary type conversions. + - unparam # Reports unused function parameters. + - usestdlibvars # A linter that detect the possibility to use variables/constants from the Go standard library. + - usetesting # Reports uses of functions with replacement inside the testing package. + - wastedassign # Finds wasted assignment statements. + - zerologlint # Detects the wrong usage of `zerolog` that a user forgets to dispatch with `Send` or `Msg`. + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + rules: + - linters: + - bodyclose + - dogsled + - dupl + - funlen + - cyclop + - containedctx + - maintidx + - goconst + path: _test\.go + - linters: + - cyclop + path: (.+)_test\.go + paths: + - third_party$ + - builtin$ + - examples$ + - internal/ogc/features/cql/parser$ + settings: + cyclop: + # The maximal code complexity to report. + # Default: 10 + max-complexity: 15 + depguard: + rules: + main: + deny: + - pkg: "math/rand$" + desc: use math/rand/v2 + - pkg: "github.com/sirupsen/logrus" + desc: no longer maintained + - pkg: "github.com/pkg/errors" + desc: Should be replaced by standard lib errors package + forbidigo: + # Forbid the following identifiers (list of regexp). + # Default: ["^(fmt\\.Print(|f|ln)|print|println)$"] + forbid: + - pattern: ^(fmt\\.Print(|f|ln)|print|println)$ + - pattern: http\.NotFound.* + msg: return RFC 7807 problem details instead + - pattern: http\.Error.* + msg: return RFC 7807 problem details instead + funlen: + # Checks the number of lines in a function. + # If lower than 0, disable the check. + # Default: 60 + lines: 100 + nestif: + # Minimal complexity of if statements to report. + # Default: 5 + min-complexity: 6 + gomoddirectives: + replace-allow-list: + - github.com/wk8/go-ordered-map/v2 + - github.com/PDOK/gokoala + - github.com/docker/compose/v2 + revive: + rules: + # default rules + - name: blank-imports + - name: context-as-argument + - name: context-keys-type + - name: dot-imports + - name: empty-block + - name: error-naming + - name: error-return + - name: error-strings + - name: errorf + - name: exported + - name: increment-decrement + - name: indent-error-flow + - name: package-comments + - name: range + - name: receiver-naming + - name: redefines-builtin-id + - name: superfluous-else + - name: time-naming + - name: unexported-return + - name: unreachable-code + - name: unused-parameter + - name: var-declaration + # enabled or tweaked by us + - name: use-any + - name: var-naming + arguments: + - [ "ID" ] # AllowList + - [ "VM" ] # DenyList + - - skip-package-name-checks: true diff --git a/Dockerfile b/Dockerfile index 786ea78..a2f6fa4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ ARG GDAL_VERSION=3.6.3 FROM ghcr.io/osgeo/gdal:ubuntu-small-${GDAL_VERSION} AS base -FROM golang:1.21-bullseye AS build-env +FROM golang:1.26-bookworm AS build-env ENV GO111MODULE=on ENV GOPROXY=https://proxy.golang.org diff --git a/geomhelp/geomhelp.go b/geomhelp/geomhelp.go index a34bfb7..758a059 100644 --- a/geomhelp/geomhelp.go +++ b/geomhelp/geomhelp.go @@ -2,6 +2,7 @@ package geomhelp import ( "math" + "strings" "github.com/go-spatial/geom" "github.com/go-spatial/geom/encoding/wkt" @@ -26,13 +27,14 @@ func Shoelace(pts [][2]float64) float64 { // from paulmach/orb // Original implementation: http://rosettacode.org/wiki/Ray-casting_algorithm#Go // -//nolint:cyclop,nestif +//nolint:cyclop func RayIntersect(pt, start, end [2]float64) (intersects, on bool) { if start[0] > end[0] { start, end = end, start } - if pt[0] == start[0] { + switch pt[0] { + case start[0]: if pt[1] == start[1] { // pt == start return false, true @@ -50,7 +52,7 @@ func RayIntersect(pt, start, end [2]float64) (intersects, on bool) { // Move the y coordinate to deal with degenerate case pt[0] = math.Nextafter(pt[0], math.Inf(1)) - } else if pt[0] == end[0] { + case end[0]: if pt[1] == end[1] { // matching the end point return false, true @@ -133,20 +135,24 @@ func WktMustEncode(g geom.Geometry, maxLen uint) (s string) { if len(pp) > 0 { s = wktMustEncodeTruncated(pp, maxLen) } + var builder strings.Builder for i := range lines { - s += wktMustEncodeTruncated(lines[i], maxLen) + builder.WriteString(wktMustEncodeTruncated(lines[i], maxLen)) } for i := range points { - s += wktMustEncodeTruncated(points[i], maxLen) + builder.WriteString(wktMustEncodeTruncated(points[i], maxLen)) } + s += builder.String() return s } func WktMustEncodeSlice(geoms []geom.Polygon, maxLen uint) string { s := "" + var builder strings.Builder for i := range geoms { - s += WktMustEncode(geoms[i], maxLen) + "\n" + builder.WriteString(WktMustEncode(geoms[i], maxLen) + "\n") } + s += builder.String() return s } diff --git a/go.mod b/go.mod index 3cc3429..89a2d74 100644 --- a/go.mod +++ b/go.mod @@ -1,43 +1,42 @@ module github.com/pdok/texel -go 1.21.6 +go 1.26.2 require ( github.com/carlmjohnson/versioninfo v0.22.5 - github.com/creasty/defaults v1.7.0 - github.com/go-playground/validator/v10 v10.16.0 + github.com/creasty/defaults v1.8.0 + github.com/go-playground/validator/v10 v10.30.3 github.com/go-spatial/geom v0.0.0-20220918193402-3cd2f5a9a082 github.com/iancoleman/strcase v0.3.0 github.com/muesli/reflow v0.3.0 github.com/perimeterx/marshmallow v1.1.5 - github.com/stretchr/testify v1.8.4 + github.com/stretchr/testify v1.11.1 github.com/tobshub/go-sortedmap v1.0.3 - github.com/urfave/cli/v2 v2.25.7 + github.com/urfave/cli/v2 v2.27.7 github.com/wk8/go-ordered-map/v2 v2.1.8 - golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa + golang.org/x/exp v0.0.0-20260611194520-c48552f49976 ) require ( github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/buger/jsonparser v1.1.1 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/gabriel-vasile/mimetype v1.4.2 // indirect + github.com/gabriel-vasile/mimetype v1.4.13 // indirect github.com/gdey/errors v0.0.0-20190426172550-8ebd5bc891fb // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/josharian/intern v1.0.0 // indirect - github.com/leodido/go-urn v1.2.4 // indirect + github.com/leodido/go-urn v1.4.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-runewidth v0.0.12 // indirect github.com/mattn/go-sqlite3 v1.14.17 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect - golang.org/x/crypto v0.7.0 // indirect - golang.org/x/net v0.8.0 // indirect - golang.org/x/sys v0.14.0 // indirect - golang.org/x/text v0.8.0 // indirect + github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect + golang.org/x/crypto v0.52.0 // indirect + golang.org/x/sys v0.45.0 // indirect + golang.org/x/text v0.37.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 9db1eb2..4d8f73b 100644 --- a/go.sum +++ b/go.sum @@ -5,15 +5,15 @@ github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMU github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/carlmjohnson/versioninfo v0.22.5 h1:O00sjOLUAFxYQjlN/bzYTuZiS0y6fWDQjMRvwtKgwwc= github.com/carlmjohnson/versioninfo v0.22.5/go.mod h1:QT9mph3wcVfISUKd0i9sZfVrPviHuSF+cUtLjm2WSf8= -github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= -github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creasty/defaults v1.7.0 h1:eNdqZvc5B509z18lD8yc212CAqJNvfT1Jq6L8WowdBA= -github.com/creasty/defaults v1.7.0/go.mod h1:iGzKe6pbEHnpMPtfDXZEr0NVxWnPTjb1bbDy08fPzYM= +github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= +github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/creasty/defaults v1.8.0 h1:z27FJxCAa0JKt3utc0sCImAEb+spPucmKoOdLHvHYKk= +github.com/creasty/defaults v1.8.0/go.mod h1:iGzKe6pbEHnpMPtfDXZEr0NVxWnPTjb1bbDy08fPzYM= 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/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= -github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= +github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM= +github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= github.com/gdey/errors v0.0.0-20190426172550-8ebd5bc891fb h1:FYO+lZtAUnakgSW9xYs7QvgawjCDM5wgHaXoDhYHNH4= github.com/gdey/errors v0.0.0-20190426172550-8ebd5bc891fb/go.mod h1:PFaV7MgSRe92Wo9O2H2i1CIm7urUk10AgdSHKyBfjmQ= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= @@ -22,8 +22,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.16.0 h1:x+plE831WK4vaKHO/jpgUGsvLKIqRRkz6M78GuJAfGE= -github.com/go-playground/validator/v10 v10.16.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-playground/validator/v10 v10.30.3 h1:4MU6YkEwx7GbcPJOZxrtbu+QfF3pJLJuaYTeAH0DYy8= +github.com/go-playground/validator/v10 v10.30.3/go.mod h1:4Axh7oCNGcoGkqLoE4YWt6n20mcEIsPRlB7vPk3lpyc= github.com/go-spatial/geom v0.0.0-20220918193402-3cd2f5a9a082 h1:3+Swhq7I1stScm1DE4PUWKjGJRbi+VPvisj6tFz0prs= github.com/go-spatial/geom v0.0.0-20220918193402-3cd2f5a9a082/go.mod h1:YU06tBGGstQCUX7vMuyF44RRneTdjGxOrp8OJHu4t9Q= github.com/go-spatial/proj v0.2.0 h1:sii5Now3GFEyR9hV2SBJFWFz95s4xSaZmP0aYDk1K6c= @@ -36,8 +36,8 @@ github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSAS github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= -github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-runewidth v0.0.12 h1:Y41i/hVW3Pgwr8gV+J23B9YEY0zxjptBuCWEaxmAOow= @@ -59,44 +59,36 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ 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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/tobshub/go-sortedmap v1.0.3 h1:oUhj/5tqzjTX4bhWqB1ZFTDtMULJ1ZYUnS8WAugSfjY= github.com/tobshub/go-sortedmap v1.0.3/go.mod h1:JLxyU94+lfKuCgelxXpwRr29ei6SqLbaeVuNVMvENbE= github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= -github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= -github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= +github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU= +github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4= github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= -github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= -github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= -golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= +golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= +golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= +golang.org/x/exp v0.0.0-20260611194520-c48552f49976 h1:X8Hz2ImujgbmetVuW+w2YkyZChE3cBpZi2P158rTG9M= +golang.org/x/exp v0.0.0-20260611194520-c48552f49976/go.mod h1:vnf4pv9iKZXY58sQE1L86zmNWJ4159e1RkcWiLCkeEY= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= -golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= +golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= +golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= golang.org/x/tools v0.0.0-20191114222411-4191b8cbba09/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/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.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index 54acba2..6b5c732 100644 --- a/main.go +++ b/main.go @@ -204,9 +204,8 @@ func initGPKGTarget(targetPathFmt string, tmID int, overwrite bool, pagesize int targetPath := fmt.Sprintf(targetPathFmt, tmID) if overwrite { err := os.Remove(targetPath) - var pathError *os.PathError if err != nil { - if !(errors.As(err, &pathError) && errors.Is(pathError.Err, syscall.ENOENT)) { + if pathError, ok := errors.AsType[*os.PathError](err); !ok || !errors.Is(pathError.Err, syscall.ENOENT) { log.Fatalf("could not remove target file: %e", err) } } diff --git a/mapslicehelp/mapslicehelp.go b/mapslicehelp/mapslicehelp.go index 0e79e0a..8ce89c1 100644 --- a/mapslicehelp/mapslicehelp.go +++ b/mapslicehelp/mapslicehelp.go @@ -1,12 +1,11 @@ package mapslicehelp import ( + "cmp" "slices" "github.com/tobshub/go-sortedmap" - orderedmap "github.com/wk8/go-ordered-map/v2" - "golang.org/x/exp/constraints" ) func LastElement[T any](elements []T) *T { @@ -17,7 +16,7 @@ func LastElement[T any](elements []T) *T { return nil } -func AsKeys[T constraints.Ordered](elements []T) map[T]any { +func AsKeys[T cmp.Ordered](elements []T) map[T]any { mapped := make(map[T]any, len(elements)) for _, element := range elements { mapped[element] = struct{}{} @@ -25,7 +24,7 @@ func AsKeys[T constraints.Ordered](elements []T) map[T]any { return mapped } -func FindLastKeyWithMaxValue[K comparable, V constraints.Ordered](m *orderedmap.OrderedMap[K, V]) (maxK K, maxV V, numWinners uint) { +func FindLastKeyWithMaxValue[K comparable, V cmp.Ordered](m *orderedmap.OrderedMap[K, V]) (maxK K, maxV V, numWinners uint) { first := true for p := m.Newest(); p != nil; p = p.Prev() { if first || p.Value > maxV { @@ -81,7 +80,7 @@ func ReverseClone[S ~[]E, E any](s S) S { } l := len(s) c := make(S, l) - for i := 0; i < l; i++ { + for i := range l { c[l-1-i] = s[i] } return c diff --git a/morton/morton.go b/morton/morton.go index 9ae08a7..ac5d614 100644 --- a/morton/morton.go +++ b/morton/morton.go @@ -40,7 +40,7 @@ func MustToZ(x, y uint) Z { func FromZ(z Z) (x, y uint) { x = z y = z >> 1 - for i := 0; i <= 5; i++ { + for i := range 6 { x = (x | (x >> powersOfTwo[i])) & masks[i] y = (y | (y >> powersOfTwo[i])) & masks[i] } diff --git a/pointindex/pointindex.go b/pointindex/pointindex.go index 1e145e3..52f7f39 100644 --- a/pointindex/pointindex.go +++ b/pointindex/pointindex.go @@ -8,14 +8,14 @@ import ( "slices" "strconv" - "golang.org/x/exp/maps" + "github.com/go-spatial/geom/encoding/wkt" + "golang.org/x/exp/maps" //nolint:exptostd "github.com/pdok/texel/mathhelp" "github.com/pdok/texel/morton" "github.com/pdok/texel/tms20" "github.com/go-spatial/geom" - "github.com/go-spatial/geom/encoding/wkt" "github.com/pdok/texel/intgeom" ) @@ -116,7 +116,7 @@ func (ix *PointIndex) InsertPolygon(polygon geom.Polygon) error { pointsCount += len(ring) } var level uint - for level = 0; level <= ix.deepestLevel; level++ { + for level = 0; level <= ix.deepestLevel; level++ { //nolint:intrange if ix.quadrants[level] == nil { ix.quadrants[level] = make(map[morton.Z]Quadrant, pointsCount) // TODO smaller for the shallower levels } @@ -164,10 +164,58 @@ func (ix *PointIndex) InsertCoord(deepestX int, deepestY int) error { return nil } +// SnapClosestPoints returns the points (centroids) in the index that are intersected by a line +// on multiple levels +func (ix *PointIndex) SnapClosestPoints(line geom.Line, levelMap map[Level]any, ringID int) map[Level][][2]float64 { + intLine := intgeom.FromGeomLine(line) + quadrantsPerLevel := ix.snapClosestPoints(intLine, levelMap) + + pointsPerLevel := make(map[Level][][2]float64, len(levelMap)) + for level, quadrants := range quadrantsPerLevel { + if len(quadrants) == 0 { + continue + } + if ix.hitOnce[level] == nil { + ix.hitOnce[level] = make(map[intgeom.Point][]int) + } + if ix.hitMultiple[level] == nil { + ix.hitMultiple[level] = make(map[intgeom.Point][]int) + } + points := make([][2]float64, len(quadrants)) + for i, quadrant := range quadrants { + points[i] = quadrant.intCentroid.ToGeomPoint() + // ignore first point to avoid superfluous duplicates + if i > 0 { + checkPointHits(ix, quadrant.intCentroid, ringID, level) + } + } + pointsPerLevel[level] = points + } + return pointsPerLevel +} + +// ToWkt creates a WKT representation of the pointcloud. For debugging/visualising. +func (ix *PointIndex) ToWkt(writer io.Writer) { + for level, quadrants := range ix.quadrants { + for _, quadrant := range quadrants { + _ = wkt.Encode(writer, quadrant.intExtent.ToGeomExtent()) + _, _ = fmt.Fprintf(writer, "\n") + if level == ix.deepestLevel { + _ = wkt.Encode(writer, quadrant.intCentroid.ToGeomPoint()) + _, _ = fmt.Fprintf(writer, "\n") + } + } + } +} + +func (ix *PointIndex) GetHitMultiple(l Level) map[intgeom.Point][]int { + return ix.hitMultiple[l] +} + // insertCoord adds a point into this pc, assuming the point is inside its extent func (ix *PointIndex) insertCoord(deepestX int, deepestY int) { var l Level - for l = 0; l <= ix.deepestLevel; l++ { + for l = 0; l <= ix.deepestLevel; l++ { //nolint:intrange //nolint:gosec // G115 x := uint(deepestX) / mathhelp.Pow2(ix.deepestLevel-l) //nolint:gosec // G115 @@ -209,36 +257,6 @@ func (ix *PointIndex) getQuadrantExtentAndCentroid(level Level, x, y uint, intRo return intExtent, intCentroid } -// SnapClosestPoints returns the points (centroids) in the index that are intersected by a line -// on multiple levels -func (ix *PointIndex) SnapClosestPoints(line geom.Line, levelMap map[Level]any, ringID int) map[Level][][2]float64 { - intLine := intgeom.FromGeomLine(line) - quadrantsPerLevel := ix.snapClosestPoints(intLine, levelMap) - - pointsPerLevel := make(map[Level][][2]float64, len(levelMap)) - for level, quadrants := range quadrantsPerLevel { - if len(quadrants) == 0 { - continue - } - if ix.hitOnce[level] == nil { - ix.hitOnce[level] = make(map[intgeom.Point][]int) - } - if ix.hitMultiple[level] == nil { - ix.hitMultiple[level] = make(map[intgeom.Point][]int) - } - points := make([][2]float64, len(quadrants)) - for i, quadrant := range quadrants { - points[i] = quadrant.intCentroid.ToGeomPoint() - // ignore first point to avoid superfluous duplicates - if i > 0 { - checkPointHits(ix, quadrant.intCentroid, ringID, level) - } - } - pointsPerLevel[level] = points - } - return pointsPerLevel -} - func (ix *PointIndex) snapClosestPoints(intLine intgeom.Line, levelMap map[Level]any) map[Level][]Quadrant { if len(levelMap) == 0 || !lineIntersects(intLine, ix.intExtent) { return nil @@ -359,12 +377,13 @@ func findIntersectingQuadrants(intLine intgeom.Line, quadrants map[Q]Quadrant, p func getQuadrantZs(parentZ morton.Z) [4]morton.Z { parentX, parentY := morton.FromZ(parentZ) quadrantZs := [4]morton.Z{} - for i := 0; i < 4; i++ { + for i := range 4 { //nolint:gosec // G115 x := parentX*2 + uint(oneIfRight(i)) //nolint:gosec // G115 y := parentY*2 + uint(oneIfTop(i)) z := morton.MustToZ(x, y) + //nolint:gosec // G602 quadrantZs[i] = z } return quadrantZs @@ -440,10 +459,6 @@ func lineIntersects(intLine intgeom.Line, intExtent intgeom.Extent) bool { return false } -func (ix *PointIndex) GetHitMultiple(l Level) map[intgeom.Point][]int { - return ix.hitMultiple[l] -} - func checkPointHits(ix *PointIndex, vertex intgeom.Point, ringID int, level uint) { levelHitOnce := ix.hitOnce[level] levelHitMultiple := ix.hitMultiple[level] @@ -469,9 +484,10 @@ func isExclusiveEdge(edgeI int) bool { // getExclusiveTip returns the tip point of an inclusive edge that is not-inclusive func getExclusiveTip(edgeI int, edge intgeom.Line) intgeom.Point { i := edgeI % 4 - if i == 0 { + switch i { + case 0: return edge[1] - } else if i == 3 { + case 3: return edge[0] } panic(fmt.Sprintf("not an inclusive edge: %v", edgeI)) @@ -511,25 +527,11 @@ func oneIfTop(quadrantI int) int { return (quadrantI & top) >> 1 } -// ToWkt creates a WKT representation of the pointcloud. For debugging/visualising. -func (ix *PointIndex) ToWkt(writer io.Writer) { - for level, quadrants := range ix.quadrants { - for _, quadrant := range quadrants { - _ = wkt.Encode(writer, quadrant.intExtent.ToGeomExtent()) - _, _ = fmt.Fprintf(writer, "\n") - if level == ix.deepestLevel { - _ = wkt.Encode(writer, quadrant.intCentroid.ToGeomPoint()) - _, _ = fmt.Fprintf(writer, "\n") - } - } - } -} - //nolint:nestif func IsQuadTree(tms tms20.TileMatrixSet) error { var previousTMID int var previousTM *tms20.TileMatrix - tmIDs := maps.Keys(tms.TileMatrices) + tmIDs := maps.Keys(tms.TileMatrices) //nolint:exptostd slices.Sort(tmIDs) for _, tmID := range tmIDs { tm := tms.TileMatrices[tmID] diff --git a/pointindex/pointindex_test.go b/pointindex/pointindex_test.go index 0aa650b..40927ea 100644 --- a/pointindex/pointindex_test.go +++ b/pointindex/pointindex_test.go @@ -21,7 +21,7 @@ import ( ) func assertNoErr(t assert.TestingT, err error, _ ...any) bool { - return assert.Nil(t, err) + return assert.NoError(t, err) } func TestPointIndex_containsPoint(t *testing.T) { @@ -122,10 +122,10 @@ func TestPointIndex_getQuadrantExtentAndCentroid(t *testing.T) { deepestRes: tt.intRootExtent.XSpan() / 1, } extent, centroid := ix.getQuadrantExtentAndCentroid(0, 0, 0, tt.intRootExtent) - if !assert.EqualValues(t, tt.want.extent, extent) { + if !assert.Equal(t, tt.want.extent, extent) { t.Errorf("getQuadrantExtentAndCentroid() = %v, want %v", extent, tt.want.extent) } - if !assert.EqualValues(t, tt.want.centroid, centroid) { + if !assert.Equal(t, tt.want.centroid, centroid) { t.Errorf("getQuadrantExtentAndCentroid() = %v, want %v", centroid, tt.want.centroid) } }) @@ -280,7 +280,7 @@ func TestPointIndex_InsertPoint(t *testing.T) { if tt.want.hitMultiple == nil { tt.want.hitMultiple = make(map[morton.Z]map[intgeom.Point][]int) } - assert.EqualValues(t, tt.want, *ix) + assert.Equal(t, tt.want, *ix) }) } } @@ -335,10 +335,10 @@ func TestPointIndex_InsertPoint_Deepest(t *testing.T) { err = ix.InsertPoint(tt.point) require.NoError(t, err) - assert.Equal(t, 1, len(ix.quadrants[ix.deepestLevel])) + assert.Len(t, ix.quadrants[ix.deepestLevel], 1) for z, quadrant := range ix.quadrants[ix.deepestLevel] { - assert.EqualValues(t, tt.want, quadrant) - assert.EqualValues(t, tt.want.z, z) + assert.Equal(t, tt.want, quadrant) + assert.Equal(t, tt.want.z, z) } }) } @@ -480,7 +480,7 @@ func TestPointIndex_SnapClosestPoints(t *testing.T) { levels = []Level{ix.deepestLevel} } got := ix.SnapClosestPoints(tt.line, mapslicehelp.AsKeys(levels), tt.ringID) - if !assert.EqualValues(t, tt.want, got) { + if !assert.Equal(t, tt.want, got) { ix.ToWkt(os.Stdout) t.Errorf("SnapClosestPoints() = %v, want %v", got, tt.want) } @@ -542,14 +542,18 @@ func newSimplePointIndex(deepestLevel Level, cellSize float64) *PointIndex { } func loadEmbeddedTileMatrixSet(t *testing.T, tmsID string) tms20.TileMatrixSet { + t.Helper() + tms, err := tms20.LoadEmbeddedTileMatrixSet(tmsID) require.NoError(t, err) return tms } func newPointIndexFromEmbeddedTileMatrixSet(t *testing.T, tmsID string, deepestTMID tms20.TMID) *PointIndex { + t.Helper() + tms, err := FromTileMatrixSet(loadEmbeddedTileMatrixSet(t, tmsID), deepestTMID) - require.Nil(t, err) + require.NoError(t, err) return tms } diff --git a/processing/gpkg/gpkg.go b/processing/gpkg/gpkg.go index 1c704a2..8c4328f 100644 --- a/processing/gpkg/gpkg.go +++ b/processing/gpkg/gpkg.go @@ -12,11 +12,11 @@ import ( ) type featureGPKG struct { - columns []interface{} + columns []any geometry geom.Geometry } -func (f featureGPKG) Columns() []interface{} { +func (f featureGPKG) Columns() []any { return f.columns } @@ -65,6 +65,7 @@ func geometryTypeFromString(geometrytype string) gpkg.GeometryType { } } +//nolint:recvcheck // TODO double check this type SourceGeopackage struct { Table Table handle *gpkg.Handle @@ -92,9 +93,9 @@ func (source SourceGeopackage) ReadFeatures(features chan<- processing.Feature) } for rows.Next() { - vals := make([]interface{}, len(cols)) - valPtrs := make([]interface{}, len(cols)) - for i := 0; i < len(cols); i++ { + vals := make([]any, len(cols)) + valPtrs := make([]any, len(cols)) + for i := range cols { valPtrs[i] = &vals[i] } @@ -102,7 +103,7 @@ func (source SourceGeopackage) ReadFeatures(features chan<- processing.Feature) log.Fatalf("err reading row values: %v", err) } var f featureGPKG - var c []interface{} + var c []any for i, colName := range cols { switch colName { @@ -245,7 +246,7 @@ func (target *TargetGeopackage) writeFeatures(features []processing.Feature) { _, err = stmt.Exec(data...) if err != nil { - var fid interface{} = "unknown" + var fid any = "unknown" if len(data) > 0 { fid = data[0] } @@ -284,7 +285,7 @@ func openGeopackage(file string) *gpkg.Handle { // used for creating feature tables in the target Geopackage func (t Table) createSQL() string { create := fmt.Sprintf(`CREATE TABLE IF NOT EXISTS "%v"`, t.Name) - var columnparts []string + var columnparts []string //nolint:prealloc for _, column := range t.columns { columnpart := column.name + ` ` + column.ctype if column.notnull == 1 { @@ -304,7 +305,7 @@ func (t Table) createSQL() string { // selectSQL build a SELECT statement based on the table and columns // used for reading the source features func (t Table) selectSQL() string { - var csql []string + var csql []string //nolint:prealloc for _, c := range t.columns { csql = append(csql, c.name) } diff --git a/processing/interface.go b/processing/interface.go index 300a3c2..1aa20ab 100644 --- a/processing/interface.go +++ b/processing/interface.go @@ -5,7 +5,7 @@ import ( ) type Feature interface { - Columns() []interface{} + Columns() []any Geometry() geom.Geometry } @@ -15,9 +15,9 @@ type FeatureForTileMatrix interface { } type Source interface { - ReadFeatures(chan<- Feature) + ReadFeatures(ch chan<- Feature) } type Target interface { - WriteFeatures(<-chan Feature) + WriteFeatures(ch <-chan Feature) } diff --git a/processing/processing.go b/processing/processing.go index ffa7c4e..9a6c166 100644 --- a/processing/processing.go +++ b/processing/processing.go @@ -159,7 +159,7 @@ type featureForTileMatrixWrapper struct { tileMatrixID int } -func (f *featureForTileMatrixWrapper) Columns() []interface{} { +func (f *featureForTileMatrixWrapper) Columns() []any { return f.wrapped.Columns() } @@ -185,7 +185,7 @@ func wrapFeatureForTileMatrix(feature Feature, tileMatrixID int, newGeometry geo func polygonsToMulti(polygons []geom.Polygon) geom.MultiPolygon { l := len(polygons) multiPolygon := make(geom.MultiPolygon, l) - for i := 0; i < l; i++ { + for i := range l { multiPolygon[i] = polygons[i] } return multiPolygon diff --git a/snap/snap.go b/snap/snap.go index ea1a79c..f3bf968 100644 --- a/snap/snap.go +++ b/snap/snap.go @@ -9,10 +9,10 @@ import ( "sort" "github.com/pdok/texel/geomhelp" - "github.com/pdok/texel/pointindex" - "github.com/pdok/texel/mapslicehelp" + "github.com/pdok/texel/pointindex" "github.com/tobshub/go-sortedmap" + "golang.org/x/exp/maps" //nolint:exptostd "github.com/go-spatial/geom/winding" @@ -20,7 +20,6 @@ import ( "github.com/pdok/texel/intgeom" "github.com/pdok/texel/tms20" orderedmap "github.com/wk8/go-ordered-map/v2" - "golang.org/x/exp/maps" ) const ( @@ -168,7 +167,7 @@ func reverseWindingOrderIfConfigured(polygons [][][][2]float64, config Config) { func outersToPolygons(outers [][][2]float64) [][][][2]float64 { polygons := make([][][][2]float64, len(outers)) - for i := 0; i < len(outers); i++ { + for i := range outers { polygons[i] = [][][2]float64{outers[i]} } return polygons @@ -182,7 +181,7 @@ func dedupeInnersOuters(outers [][][2]float64, inners [][][2]float64) ([][][2]fl lenAll := lenOuters + lenInners processedIndexes := make(map[int]IsOuter) indexesToDelete := make(map[int]IsOuter) - for i := 0; i < lenAll; i++ { + for i := range lenAll { if _, processed := processedIndexes[i]; processed { continue } @@ -264,7 +263,7 @@ func ringsAreEqual(ringI, ringJ [][2]float64, iIsOuter, jIsOuter bool) bool { } differentWindingOrder := iIsOuter && !jIsOuter // Check if rings are equal - for k := 0; k < ringLen; k++ { + for k := range ringLen { if !differentWindingOrder && ringI[k] != ringJ[(idx+k)%ringLen] { return false } @@ -349,7 +348,7 @@ func ringContains(ring [][2]float64, point [2]float64) (contains, onBoundary boo return true, true } - for i := 0; i < len(ring)-1; i++ { + for i := range len(ring) - 1 { intersects, on := geomhelp.RayIntersect(point, ring[i], ring[i+1]) if on { return true, true @@ -485,7 +484,7 @@ func splitRing(ring [][2]float64, isOuter bool, hitMultiple map[intgeom.Point][] panicPartialRingsRemainingOnStack(stack) } } - completeRingKeys := maps.Keys(completeRings) + completeRingKeys := maps.Keys(completeRings) //nolint:exptostd sort.Ints(completeRingKeys) for _, completeRingKey := range completeRingKeys { completeRing := completeRings[completeRingKey] @@ -593,7 +592,7 @@ func kmpDeduplicate(ring [][2]float64) [][2]float64 { switch { case len(matches) > 1 && (len(matches)-len(reverseMatches)) == 1: // zigzag found (segment occurs one time more often than its reverse) - // mark all but one occurrance of segment for removal + // mark all but one occurrence of segment for removal sequenceStart := start + len(segment) sequenceEnd := start + matches[len(matches)-1] + len(segment) sequencesToRemove.Insert(fmt.Sprint(segment), [2]int{sequenceStart, sequenceEnd}) @@ -602,7 +601,7 @@ func kmpDeduplicate(ring [][2]float64) [][2]float64 { visitedPoints = [][2]float64{} case len(matches) > 1 && len(matches) == len(reverseMatches): // multiple backtrace found (segment occurs more than once, and equally as many times as its reverse) - // mark all but one occurrance of segment and one occurrance of its reverse for removal + // mark all but one occurrence of segment and one occurrence of its reverse for removal sequenceStart := start + (2 * len(segment)) - 1 sequenceEnd := start + matches[len(matches)-1] + len(segment) sequencesToRemove.Insert(fmt.Sprint(segment), [2]int{sequenceStart, sequenceEnd}) diff --git a/snap/snap_test.go b/snap/snap_test.go index 99c2f57..6f2fb8a 100644 --- a/snap/snap_test.go +++ b/snap/snap_test.go @@ -790,7 +790,7 @@ func TestSnap_snapPolygon(t *testing.T) { } got := SnapPolygon(tt.polygon, tt.tms, tt.tmIDs, tt.config) for tmID, wantPoly := range tt.want { - if !assert.EqualValues(t, wantPoly, got[tmID]) { + if !assert.Equal(t, wantPoly, got[tmID]) { t.Errorf("snapPolygon(%v, _, %v)\n= %v\nwant: %v", wkt.MustEncode(tt.polygon), tmID, geomhelp.WktMustEncodeSlice(got[tmID], 0), geomhelp.WktMustEncodeSlice(wantPoly, 0)) } @@ -1032,6 +1032,8 @@ func newSimpleTileMatrixSet(deepestTMID pointindex.Level, cellSize float64) tms2 } func loadEmbeddedTileMatrixSet(t *testing.T, tmsID string) tms20.TileMatrixSet { + t.Helper() + tms, err := tms20.LoadEmbeddedTileMatrixSet(tmsID) require.NoError(t, err) return tms @@ -1058,7 +1060,7 @@ func (f fakeCRS) Code() string { func squareRingArray(number int, isOuter bool) [][][2]float64 { outerSquare := [][2]float64{{0, 0}, {1, 0}, {1, 1}, {0, 1}} // square, counter clockwise innerSquare := [][2]float64{{0, 0}, {0, 1}, {1, 1}, {1, 0}} // square, clockwise - var squares = [][][2]float64{} + var squares = [][][2]float64{} //nolint:prealloc var square [][2]float64 // outer or inner if isOuter { @@ -1067,7 +1069,7 @@ func squareRingArray(number int, isOuter bool) [][][2]float64 { square = innerSquare } // add squares - for i := 0; i < number; i++ { + for range number { squares = append(squares, square) } return squares diff --git a/tms20/tms20.go b/tms20/tms20.go index 174156b..a951f45 100644 --- a/tms20/tms20.go +++ b/tms20/tms20.go @@ -100,7 +100,7 @@ type TileMatrixSet struct { type TMID = int func (tms *TileMatrixSet) MarshalJSON() ([]byte, error) { - var tileMatrices []*TileMatrix + var tileMatrices []*TileMatrix //nolint:prealloc for tmID := range tms.TileMatrices { tm := tms.TileMatrices[tmID] tileMatrices = append(tileMatrices, &tm) @@ -156,14 +156,14 @@ func (tms *TileMatrixSet) UnmarshalJSON(data []byte) error { return validate.Struct(tms) } -func unmarshalTileMatrices(rawTileMatrices interface{}) (map[TMID]TileMatrix, error) { - rawTileMatricesList, ok := rawTileMatrices.([]interface{}) +func unmarshalTileMatrices(rawTileMatrices any) (map[TMID]TileMatrix, error) { + rawTileMatricesList, ok := rawTileMatrices.([]any) if !ok { return nil, errors.New(`"tileMatrices" should be an array`) } tileMatrices := make(map[TMID]TileMatrix, len(rawTileMatricesList)) for _, rawTileMatrix := range rawTileMatricesList { - rawTileMatrixMap, ok := rawTileMatrix.(map[string]interface{}) + rawTileMatrixMap, ok := rawTileMatrix.(map[string]any) if !ok { return nil, errors.New(`"tileMatrices" should be objects`) } @@ -183,14 +183,14 @@ func unmarshalTileMatrices(rawTileMatrices interface{}) (map[TMID]TileMatrix, er // unmarshalCRS tries 4 different CRS types (oneOf) // TODO maybe there is already a library that can parse this, something like gdal/ogr -func unmarshalCRS(rawCrs interface{}) (CRS, error) { - var rawCrsMap map[string]interface{} +func unmarshalCRS(rawCrs any) (CRS, error) { + var rawCrsMap map[string]any rawCrsString, asString := rawCrs.(string) if asString { - rawCrsMap = map[string]interface{}{"uri": rawCrsString} + rawCrsMap = map[string]any{"uri": rawCrsString} } else { var ok bool - rawCrsMap, ok = rawCrs.(map[string]interface{}) + rawCrsMap, ok = rawCrs.(map[string]any) if !ok { return nil, fmt.Errorf(`wrong type key "crs": %T`, rawCrs) } @@ -257,8 +257,8 @@ func (crs *URICRS) UnmarshalJSON(data []byte) error { return unmarshalJSONMapUsingUnmarshalJSONFromMap(crs, data) } -func (crs *URICRS) UnmarshalJSONFromMap(data interface{}) error { - dataMap, ok := data.(map[string]interface{}) +func (crs *URICRS) UnmarshalJSONFromMap(data any) error { + dataMap, ok := data.(map[string]any) if !ok { return fmt.Errorf(`data is not a map but a %T`, data) } @@ -315,7 +315,7 @@ type WKTCRS struct { description string // An object defining the CRS using the JSON encoding for Well-known text representation of coordinate reference systems 2.0 wkt ProjJSON - originalWKT map[string]interface{} + originalWKT map[string]any } // TODO expand the ProjJSON type @@ -330,8 +330,8 @@ type ProjJSONID struct { func (crs *WKTCRS) MarshalJSON() ([]byte, error) { return json.Marshal(struct { - Description string `json:"description,omitempty"` - WKT map[string]interface{} `json:"wkt"` + Description string `json:"description,omitempty"` + WKT map[string]any `json:"wkt"` }{ Description: crs.description, WKT: crs.originalWKT, @@ -342,8 +342,8 @@ func (crs *WKTCRS) UnmarshalJSON(data []byte) error { return unmarshalJSONMapUsingUnmarshalJSONFromMap(crs, data) } -func (crs *WKTCRS) UnmarshalJSONFromMap(data interface{}) error { - dataMap, ok := data.(map[string]interface{}) +func (crs *WKTCRS) UnmarshalJSONFromMap(data any) error { + dataMap, ok := data.(map[string]any) if !ok { return fmt.Errorf(`data is not a map but a %T`, data) } @@ -360,7 +360,7 @@ func (crs *WKTCRS) UnmarshalJSONFromMap(data interface{}) error { if !ok { return errors.New(`wkt property not found`) } - crs.originalWKT, ok = rawWKT.(map[string]interface{}) + crs.originalWKT, ok = rawWKT.(map[string]any) if !ok { return fmt.Errorf(`wkt property is not an object but a %T`, rawWKT) } @@ -395,13 +395,13 @@ func (crs *WKTCRS) Code() string { type ReferenceSystemCRS struct { description string // A reference system data structure as defined in the MD_ReferenceSystem of the ISO 19115 - referenceSystem map[string]interface{} `validate:"required"` + referenceSystem map[string]any `validate:"required"` } func (crs *ReferenceSystemCRS) MarshalJSON() ([]byte, error) { return json.Marshal(struct { - Description string `json:"description,omitempty"` - ReferenceSystem map[string]interface{} `json:"wkt"` + Description string `json:"description,omitempty"` + ReferenceSystem map[string]any `json:"wkt"` }{ Description: crs.description, ReferenceSystem: crs.referenceSystem, @@ -412,8 +412,8 @@ func (crs *ReferenceSystemCRS) UnmarshalJSON(data []byte) error { return unmarshalJSONMapUsingUnmarshalJSONFromMap(crs, data) } -func (crs *ReferenceSystemCRS) UnmarshalJSONFromMap(data interface{}) error { - dataMap, ok := data.(map[string]interface{}) +func (crs *ReferenceSystemCRS) UnmarshalJSONFromMap(data any) error { + dataMap, ok := data.(map[string]any) if !ok { return fmt.Errorf(`data is not a map but a %T`, data) } @@ -430,7 +430,7 @@ func (crs *ReferenceSystemCRS) UnmarshalJSONFromMap(data interface{}) error { if !ok { return errors.New(`referenceSystem property not found`) } - crs.referenceSystem, ok = rawReferenceSystem.(map[string]interface{}) + crs.referenceSystem, ok = rawReferenceSystem.(map[string]any) if !ok { return fmt.Errorf(`referenceSystem property is not an object but a %T`, rawReferenceSystem) } @@ -589,13 +589,13 @@ func (tm *TileMatrix) UnmarshalJSON(data []byte) error { return unmarshalJSONMapUsingUnmarshalJSONFromMap(tm, data) } -func (tm *TileMatrix) UnmarshalJSONFromMap(data interface{}) error { +func (tm *TileMatrix) UnmarshalJSONFromMap(data any) error { err := defaults.Set(tm) if err != nil { return err } - dataMap, ok := data.(map[string]interface{}) + dataMap, ok := data.(map[string]any) if !ok { return fmt.Errorf(`data is not a map but a %T`, data) } @@ -617,7 +617,7 @@ const ( extJSON = ".json" ) -func (c *CornerOfOrigin) UnmarshalJSONFromMap(data interface{}) error { +func (c *CornerOfOrigin) UnmarshalJSONFromMap(data any) error { dataString, ok := data.(string) if !ok { return fmt.Errorf(`CornerOfOrigin data is not a string but a %T`, data) @@ -794,7 +794,7 @@ func (tms *TileMatrixSet) MatrixBoundingBox(tmID TMID) (bottomLeft geom.Point, t } func unmarshalJSONMapUsingUnmarshalJSONFromMap(target marshmallow.UnmarshalerFromJSONMap, data []byte) error { - var dataMap map[string]interface{} + var dataMap map[string]any err := json.Unmarshal(data, &dataMap) if err != nil { return err diff --git a/tms20/tms20_test.go b/tms20/tms20_test.go index 6e2a681..279e2ab 100644 --- a/tms20/tms20_test.go +++ b/tms20/tms20_test.go @@ -117,7 +117,7 @@ func TestTileMatrixSet_Size(t *testing.T) { t.Run(fmt.Sprintf("%v.Size(%v)", tt.id, tt.zoom), func(t *testing.T) { tms, err := loadTestOrEmbeddedTileMatrix(tt.id) require.NoError(t, err) - tile, ok := tms.Size(tt.args.zoom) + tile, ok := tms.Size(tt.zoom) if ok != tt.ok { t.Errorf("Size(...) ok = %v, want %v", ok, tt.ok) } @@ -168,7 +168,7 @@ func TestTileMatrixSet_FromNative(t *testing.T) { t.Run(fmt.Sprintf("%v.FromNative(%v, %v)", tt.id, tt.zoom, tt.pt.XY()), func(t *testing.T) { tms, err := loadTestOrEmbeddedTileMatrix(tt.id) require.NoError(t, err) - tile, ok := tms.FromNative(tt.args.zoom, tt.args.pt) + tile, ok := tms.FromNative(tt.zoom, tt.pt) if ok != tt.ok { t.Errorf("FromNative(...) ok = %v, want %v", ok, tt.ok) }