diff --git a/README.md b/README.md index 984ee85..7ade0c0 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,9 @@ vector tiles can be omitted, and less data needs to be processed. - A MULTIPOLYGON will be split into separate POLYGONs that will be sieved. So a MULTIPOLYGON containing elements smaller then the given resolution will have those parts removed. +- :warning: Spatialite lib is mandatory for running this application. This lib is needed for + creating the RTree triggers on the spatial tables for updating/maintaining the + RTree. ## Usage @@ -53,10 +56,6 @@ the following. ![with interiors](./images/with-interiors.jpg) ![without interiors](./images/without-interiors.jpg) -## TODO - -- [ ] usage of a CLI package - ## Inspiration Code is inspired by the PostGis [Sieve diff --git a/go.mod b/go.mod index 5d92498..f1d8c79 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,12 @@ go 1.17 require ( github.com/go-spatial/geom v0.0.0-20220426070044-6e8855d2cfe6 github.com/mattn/go-sqlite3 v1.14.13 // indirect + github.com/urfave/cli/v2 v2.8.1 ) -require github.com/gdey/errors v0.0.0-20190426172550-8ebd5bc891fb // indirect +require ( + github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect + github.com/gdey/errors v0.0.0-20190426172550-8ebd5bc891fb // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect +) diff --git a/go.sum b/go.sum index 2b27655..6f31228 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,7 @@ +github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/arolek/p v0.0.0-20191103215535-df3c295ed582/go.mod h1:JPNItmi3yb44Q5QWM+Kh5n9oeRhfcJzPNS90mbLo25U= +github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU= +github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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= @@ -13,14 +16,23 @@ github.com/mattn/go-sqlite3 v1.14.13/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4 github.com/mattn/goveralls v0.0.3-0.20180319021929-1c14a4061c1c/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +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/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/urfave/cli/v2 v2.8.1 h1:CGuYNZF9IKZY/rfBe3lJpccSoIY1ytfvmgQT90cNOl4= +github.com/urfave/cli/v2 v2.8.1/go.mod h1:Z41J9TPoffeoqP0Iza0YbAhGvymRdZAd2uPmZ5JxRdY= +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= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/main.go b/main.go new file mode 100644 index 0000000..1d63ee0 --- /dev/null +++ b/main.go @@ -0,0 +1,107 @@ +package main + +import ( + "fmt" + "log" + "os" + + "github.com/go-spatial/geom/encoding/gpkg" + "github.com/urfave/cli/v2" +) + +const SOURCE string = `source` +const TARGET string = `target` +const RESOLUTION string = `resolution` +const PAGESIZE string = `pagesize` + +func main() { + app := cli.NewApp() + app.Name = "GOSieve" + app.Usage = "A Golang Polygon Sieve application" + + app.Flags = []cli.Flag{ + &cli.StringFlag{ + Name: SOURCE, + Aliases: []string{"s"}, + Usage: "Source GPKG", + Required: true, + EnvVars: []string{"SOURCE_GPKG"}, + }, + &cli.StringFlag{ + Name: TARGET, + Aliases: []string{"t"}, + Usage: "Target GPKG", + Required: true, + EnvVars: []string{"TARGET_GPKG"}, + }, + &cli.Float64Flag{ + Name: RESOLUTION, + Aliases: []string{"r"}, + Usage: "Resolution, the threshold area to determine if a feature is sieved or not", + Value: 0.0, + Required: false, + EnvVars: []string{"SIEVE_RESOLUTION"}, + }, + &cli.IntFlag{ + Name: PAGESIZE, + Aliases: []string{"p"}, + Usage: "Page Size, how many features are written per transaction to the target GPKG", + Value: 1000, + Required: false, + EnvVars: []string{"SIEVE_PAGESIZE"}, + }, + } + + app.Action = func(c *cli.Context) error { + + srcHandle, err := gpkg.Open(c.String(SOURCE)) + if err != nil { + log.Fatalf("error opening source GeoPackage: %s", err) + } + defer srcHandle.Close() + + trgHandle, err := gpkg.Open(c.String(TARGET)) + if err != nil { + log.Fatalf("error opening target GeoPackage: %s", err) + } + defer trgHandle.Close() + + tables := getSourceTableInfo(srcHandle) + + err = initTargetGeopackage(trgHandle, tables) + if err != nil { + log.Fatalf("error initialization the target GeoPackage: %s", err) + } + + log.Println("=== start sieving ===") + + // Process the tables sequential + for _, table := range tables { + log.Printf(" sieving %s", table.name) + preSieve := make(chan feature) + postSieve := make(chan feature) + kill := make(chan bool) + + go writeFeatures(postSieve, kill, trgHandle, table, c.Int(PAGESIZE)) + go sieveFeatures(preSieve, postSieve, c.Float64(RESOLUTION)) + go readFeatures(srcHandle, preSieve, table) + + for { + if <-kill { + break + } + } + close(kill) + log.Println(fmt.Sprintf(` finished %s`, table.name)) + log.Println("") + } + + log.Println("=== done sieving ===") + return nil + } + + err := app.Run(os.Args) + if err != nil { + log.Fatal(err) + } +} diff --git a/sieve.go b/sieve.go index 81ea5d1..a99872d 100644 --- a/sieve.go +++ b/sieve.go @@ -1,7 +1,6 @@ package main import ( - "flag" "fmt" "log" "math" @@ -401,57 +400,6 @@ func writeFeaturesArray(features [][]interface{}, h *gpkg.Handle, t table) { tx.Commit() } -func main() { - log.Println("=== start sieving ===") - sourceGeopackage := flag.String("s", "empty", "source geopackage") - targetGeopackage := flag.String("t", "empty", "target geopackage") - pageSize := flag.Int("p", 1000, "target geopackage") - resolution := flag.Float64("r", 0.0, "resolution for sieving") - flag.Parse() - - srcHandle, err := gpkg.Open(*sourceGeopackage) - if err != nil { - log.Fatalf("error opening source GeoPackage: %s", err) - } - defer srcHandle.Close() - - trgHandle, err := gpkg.Open(*targetGeopackage) - if err != nil { - log.Fatalf("error opening target GeoPackage: %s", err) - } - defer trgHandle.Close() - - tables := getSourceTableInfo(srcHandle) - - err = initTargetGeopackage(trgHandle, tables) - if err != nil { - log.Fatalf("error initialization the target GeoPackage: %s", err) - } - - // Process the tables sequential - for _, table := range tables { - log.Printf(" sieving %s", table.name) - preSieve := make(chan feature) - postSieve := make(chan feature) - kill := make(chan bool) - - go writeFeatures(postSieve, kill, trgHandle, table, *pageSize) - go sieveFeatures(preSieve, postSieve, *resolution) - go readFeatures(srcHandle, preSieve, table) - - for { - if <-kill { - break - } - } - close(kill) - log.Println(fmt.Sprintf(` finished %s`, table.name)) - log.Println("") - } - - log.Println("=== done sieving ===") -} - // multiPolygonSieve will split it self into the separated polygons that will be sieved before building a new MULTIPOLYGON func multiPolygonSieve(mp geom.MultiPolygon, resolution float64) geom.MultiPolygon { var sievedMultiPolygon geom.MultiPolygon