diff --git a/cmd/search.go b/cmd/search.go index 72af54c..44dd836 100644 --- a/cmd/search.go +++ b/cmd/search.go @@ -51,13 +51,28 @@ to quickly create a Cobra application.`, return } outputInJSON, _ := cmd.Flags().GetBool("json") - mada.Search(args[0], outputInJSON) + searchForFokontany, _ := cmd.Flags().GetBool("fokontany") + searchForCommune, _ := cmd.Flags().GetBool("commune") + searchForDistrict, _ := cmd.Flags().GetBool("district") + searchForRegion, _ := cmd.Flags().GetBool("region") + options := mada.SearchOptions{ + OutputInJSON: outputInJSON, + SearchForFokontany: searchForFokontany, + SearchForCommune: searchForCommune, + SearchForDistrict: searchForDistrict, + SearchForRegion: searchForRegion, + } + mada.Search(args[0], options) }, } func init() { rootCmd.AddCommand(searchCmd) searchCmd.Flags().BoolP("json", "j", false, "Output in JSON format") + searchCmd.Flags().BoolP("fokontany", "f", false, "Search for a fokontany") + searchCmd.Flags().BoolP("commune", "c", false, "Search for a commune") + searchCmd.Flags().BoolP("district", "d", false, "Search for a district") + searchCmd.Flags().BoolP("region", "r", false, "Search for a region") // Here you will define your flags and configuration settings. diff --git a/go.mod b/go.mod index 58a7463..84104c3 100644 --- a/go.mod +++ b/go.mod @@ -33,6 +33,7 @@ require ( github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.4 // indirect + github.com/google/open-location-code/go v0.0.0-20220120191843-cafb35c0d74d // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/lib/pq v1.10.6 // indirect github.com/mattn/go-colorable v0.1.12 // indirect diff --git a/go.sum b/go.sum index 5101687..b2c296d 100644 --- a/go.sum +++ b/go.sum @@ -91,6 +91,8 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/open-location-code/go v0.0.0-20220120191843-cafb35c0d74d h1:1/3RagbDc9Eow+XUawT2CUfm5XYLv8Nx2rhyRpakNS8= +github.com/google/open-location-code/go v0.0.0-20220120191843-cafb35c0d74d/go.mod h1:eJfRN6aj+kR/rnua/rw9jAgYhqoMHldQkdTi+sePRKk= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= diff --git a/mada/commune.go b/mada/commune.go index 7eb8a6e..c385a4a 100644 --- a/mada/commune.go +++ b/mada/commune.go @@ -72,26 +72,26 @@ func (c *CommuneService) ShowCommune(id string, outputInJSON bool) { rows, _ := c.db.Query("SELECT uid, name, region, district, country, ST_AsText(geom) FROM commune WHERE uid = $1", id) defer rows.Close() var uid, name, region, district, country, g string - rows.Next() - rows.Scan(&uid, &name, ®ion, &district, &country, &g) - - p, _ := wkt.Unmarshal(g) - - if outputInJSON { - b, _ := json.MarshalIndent(Commune{ - ID: uid, - Name: name, - Region: region, - District: district, - Country: country, - Coordinates: p.(*geom.Polygon).Coords(), - }, "", " ") - - fmt.Println(string(b)) - return - } + for rows.Next() { + rows.Scan(&uid, &name, ®ion, &district, &country, &g) + + p, _ := wkt.Unmarshal(g) + + if outputInJSON { + b, _ := json.MarshalIndent(Commune{ + ID: uid, + Name: name, + Region: region, + District: district, + Country: country, + Coordinates: p.(*geom.Polygon).Coords(), + }, "", " ") + + fmt.Println(string(b)) + return + } - fmt.Printf(` + fmt.Printf(` id %s name @@ -108,4 +108,5 @@ func (c *CommuneService) ShowCommune(id string, outputInJSON bool) { geometry %v `, uid, name, district, region, p.(*geom.Polygon).Coords()) + } } diff --git a/mada/district.go b/mada/district.go index 49db3bf..ab4d9c5 100644 --- a/mada/district.go +++ b/mada/district.go @@ -71,24 +71,24 @@ func (d *DistrictService) ShowDistrict(id string, outputInJSON bool) { rows, _ := d.db.Query("SELECT uid, name, region, ST_AsText(geom) FROM district WHERE uid = $1", id) defer rows.Close() var uid, name, region, g string - rows.Next() - rows.Scan(&uid, &name, ®ion, &g) - - p, _ := wkt.Unmarshal(g) - - if outputInJSON { - b, _ := json.MarshalIndent(District{ - ID: uid, - Name: name, - Region: region, - Country: "Madagascar", - Coordinates: p.(*geom.Polygon).Coords(), - }, "", " ") - fmt.Println(string(b)) - return - } + for rows.Next() { + rows.Scan(&uid, &name, ®ion, &g) + + p, _ := wkt.Unmarshal(g) + + if outputInJSON { + b, _ := json.MarshalIndent(District{ + ID: uid, + Name: name, + Region: region, + Country: "Madagascar", + Coordinates: p.(*geom.Polygon).Coords(), + }, "", " ") + fmt.Println(string(b)) + return + } - fmt.Printf(` + fmt.Printf(` id %s name @@ -103,4 +103,5 @@ func (d *DistrictService) ShowDistrict(id string, outputInJSON bool) { geometry %v `, uid, name, region, p.(*geom.Polygon).Coords()) + } } diff --git a/mada/fokontany.go b/mada/fokontany.go index cc39dbf..4935254 100644 --- a/mada/fokontany.go +++ b/mada/fokontany.go @@ -76,43 +76,44 @@ func (f *FokontanyService) ShowFokontany(id string, outputInJSON bool) { log.Fatal(err) } defer rows.Close() + var uid, name, commune, district, region, country, g string - rows.Next() - rows.Scan(&uid, &name, &commune, ®ion, &district, &country, &g) - - p, _ := wkt.Unmarshal(g) - - if outputInJSON { - b, _ := json.MarshalIndent(Fokontany{ - ID: uid, - Name: name, - Commune: commune, - Region: region, - District: district, - Country: country, - Coordinates: p.(*geom.Polygon).Coords(), - }, "", " ") - fmt.Println(string(b)) - return + for rows.Next() { + rows.Scan(&uid, &name, &commune, ®ion, &district, &country, &g) + + p, _ := wkt.Unmarshal(g) + + if outputInJSON { + b, _ := json.MarshalIndent(Fokontany{ + ID: uid, + Name: name, + Commune: commune, + Region: region, + District: district, + Country: country, + Coordinates: p.(*geom.Polygon).Coords(), + }, "", " ") + fmt.Println(string(b)) + return + } + fmt.Printf(` + id + %s + name + %s + commune + %s + district + %s + region + %s + country + %s + type + fokontany + geometry + %v + `, uid, name, commune, district, region, country, p.(*geom.Polygon).Coords()) + } - fmt.Printf(` - id - %s - name - %s - commune - %s - district - %s - region - %s - country - %s - type - fokontany - country - Madagascar - geometry - %v - `, uid, name, commune, region, district, country, p.(*geom.Polygon).Coords()) } diff --git a/mada/region.go b/mada/region.go index 23d3a55..051d09a 100644 --- a/mada/region.go +++ b/mada/region.go @@ -70,18 +70,18 @@ func (r *RegionService) ShowRegion(id string, outputInJSON bool) { rows, _ := r.db.Query("SELECT uid, name, ST_AsText(geom) FROM region WHERE uid = $1", id) defer rows.Close() var uid, name, g string - rows.Next() - rows.Scan(&uid, &name, &g) + for rows.Next() { + rows.Scan(&uid, &name, &g) - p, _ := wkt.Unmarshal(g) + p, _ := wkt.Unmarshal(g) - if outputInJSON { - b, _ := json.MarshalIndent(Region{ID: uid, Name: name, Country: "Madagascar", Coordinates: p.(*geom.Polygon).Coords()}, "", " ") - fmt.Println(string(b)) - return - } + if outputInJSON { + b, _ := json.MarshalIndent(Region{ID: uid, Name: name, Country: "Madagascar", Coordinates: p.(*geom.Polygon).Coords()}, "", " ") + fmt.Println(string(b)) + return + } - fmt.Printf(` + fmt.Printf(` id %s name @@ -93,4 +93,5 @@ func (r *RegionService) ShowRegion(id string, outputInJSON bool) { geometry %v `, uid, name, p.(*geom.Polygon).Coords()) + } } diff --git a/mada/search.go b/mada/search.go index 202fff4..4ad3913 100644 --- a/mada/search.go +++ b/mada/search.go @@ -1,21 +1,39 @@ package mada import ( + "database/sql" "encoding/json" "fmt" + "log" "os" "github.com/blevesearch/bleve/v2" "github.com/blevesearch/bleve/v2/search/highlight/highlighter/ansi" + olc "github.com/google/open-location-code/go" + "github.com/twpayne/go-geom" + "github.com/twpayne/go-geom/encoding/wkt" ) -func Search(term string, outputInJSON bool) { +type SearchOptions struct { + OutputInJSON bool + SearchForFokontany bool + SearchForCommune bool + SearchForDistrict bool + SearchForRegion bool +} + +func Search(term string, opt SearchOptions) { index, err := InitializeBleve() if err != nil { panic(err) } + err = SearchPoint(term, opt) + if err == nil { + return + } + query := bleve.NewQueryStringQuery(term) search := bleve.NewSearchRequest(query) search.Fields = []string{"*"} @@ -28,7 +46,7 @@ func Search(term string, outputInJSON bool) { return } - if !outputInJSON { + if !opt.OutputInJSON { fmt.Println(searchResults) return } @@ -44,3 +62,227 @@ func InitializeBleve() (bleve.Index, error) { } return bleve.Open(DATABASE_PATH) } + +func SearchPoint(term string, opt SearchOptions) (err error) { + var db *sql.DB + if os.Getenv("MADA_POSTGRES_URL") != "" { + db, err = OpenPostgresConnection() + } else { + db, err = OpenSQLiteConnection() + } + + defer db.Close() + + area, err := olc.Decode(term) + + if err != nil { + return err + } + + if opt.SearchForFokontany { + searchInFokontany(db, area, opt) + return nil + } + if opt.SearchForCommune { + searchInCommune(db, area, opt) + return nil + } + if opt.SearchForDistrict { + searchInDistrict(db, area, opt) + return nil + } + if opt.SearchForRegion { + searchInRegion(db, area, opt) + return nil + } + + searchInFokontany(db, area, opt) + + return nil +} + +func searchInFokontany(db *sql.DB, area olc.CodeArea, opt SearchOptions) bool { + point := fmt.Sprintf("POINT(%f %f)", area.LngLo, area.LatLo) + rows, err := db.Query("SELECT uid, name, commune, region, district, country, ST_AsText(geom) from fokontany f where st_contains(f.geom, st_geometryfromtext($1, 4326))", point) + if err != nil { + log.Fatal(err) + } + defer rows.Close() + var uid, name, commune, district, region, country, g string + var noresults bool = true + for rows.Next() { + noresults = false + rows.Scan(&uid, &name, &commune, ®ion, &district, &country, &g) + + p, _ := wkt.Unmarshal(g) + + if opt.OutputInJSON { + b, _ := json.MarshalIndent(Fokontany{ + ID: uid, + Name: name, + Commune: commune, + Region: region, + District: district, + Country: country, + Coordinates: p.(*geom.Polygon).Coords(), + }, "", " ") + fmt.Println(string(b)) + return noresults + } + fmt.Printf(` + id + %s + name + %s + commune + %s + district + %s + region + %s + country + %s + type + fokontany + geometry + %v + `, uid, name, commune, district, region, country, p.(*geom.Polygon).Coords()) + + } + return noresults +} + +func searchInCommune(db *sql.DB, area olc.CodeArea, opt SearchOptions) bool { + point := fmt.Sprintf("POINT(%f %f)", area.LngLo, area.LatLo) + rows, err := db.Query("SELECT uid, name, region, district, country, ST_AsText(geom) from commune f where st_contains(f.geom, st_geometryfromtext($1, 4326))", point) + if err != nil { + log.Fatal(err) + } + defer rows.Close() + var uid, name, district, region, country, g string + var noresults bool = true + for rows.Next() { + noresults = false + rows.Scan(&uid, &name, ®ion, &district, &country, &g) + + p, _ := wkt.Unmarshal(g) + + if opt.OutputInJSON { + b, _ := json.MarshalIndent(Commune{ + ID: uid, + Name: name, + Region: region, + District: district, + Country: country, + Coordinates: p.(*geom.Polygon).Coords(), + }, "", " ") + fmt.Println(string(b)) + return noresults + } + fmt.Printf(` + id + %s + name + %s + district + %s + region + %s + country + %s + type + commune + geometry + %v + `, uid, name, district, region, country, p.(*geom.Polygon).Coords()) + + } + return noresults +} + +func searchInDistrict(db *sql.DB, area olc.CodeArea, opt SearchOptions) bool { + point := fmt.Sprintf("POINT(%f %f)", area.LngLo, area.LatLo) + rows, err := db.Query("SELECT uid, name, region, country, ST_AsText(geom) from district f where st_contains(f.geom, st_geometryfromtext($1, 4326))", point) + if err != nil { + log.Fatal(err) + } + defer rows.Close() + var uid, name, region, country, g string + var noresults bool = true + for rows.Next() { + noresults = false + rows.Scan(&uid, &name, ®ion, &country, &g) + + p, _ := wkt.Unmarshal(g) + + if opt.OutputInJSON { + b, _ := json.MarshalIndent(District{ + ID: uid, + Name: name, + Region: region, + Country: country, + Coordinates: p.(*geom.Polygon).Coords(), + }, "", " ") + fmt.Println(string(b)) + return noresults + } + fmt.Printf(` + id + %s + name + %s + region + %s + country + %s + type + district + geometry + %v + `, uid, name, region, country, p.(*geom.Polygon).Coords()) + + } + return noresults +} + +func searchInRegion(db *sql.DB, area olc.CodeArea, opt SearchOptions) bool { + point := fmt.Sprintf("POINT(%f %f)", area.LngLo, area.LatLo) + rows, err := db.Query("SELECT uid, name, country, ST_AsText(geom) from region f where st_contains(f.geom, st_geometryfromtext($1, 4326))", point) + if err != nil { + log.Fatal(err) + } + defer rows.Close() + var uid, name, country, g string + var noresults bool = true + for rows.Next() { + noresults = false + rows.Scan(&uid, &name, &country, &g) + + p, _ := wkt.Unmarshal(g) + + if opt.OutputInJSON { + b, _ := json.MarshalIndent(Region{ + ID: uid, + Name: name, + Country: country, + Coordinates: p.(*geom.Polygon).Coords(), + }, "", " ") + fmt.Println(string(b)) + return noresults + } + fmt.Printf(` + id + %s + name + %s + country + %s + type + region + geometry + %v + `, uid, name, country, p.(*geom.Polygon).Coords()) + + } + return noresults +}