diff --git a/Makefile b/Makefile index 2049f9d..b5b802b 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,5 @@ cli: + go build -mod vendor -o bin/assign-geometry cmd/assign-geometry/main.go go build -mod vendor -o bin/assign-parent cmd/assign-parent/main.go go build -mod vendor -o bin/exportify cmd/exportify/main.go go build -mod vendor -o bin/create cmd/create/main.go diff --git a/README.md b/README.md index 22ddcbb..092e0a0 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,58 @@ $> ./bin/as-featurecollection \ 1729792699 ``` +### assign-geometry + +Assign the geometry from a given record to one or more other records. + +``` +$> ./bin/assign-geometry -h +Assign the geometry from a given record to one or more other records. + +Usage: + ./bin/assign-geometry [options] target-id-(N) target-id-(N) + +For example: + ./bin/assign-geometry -reader-uri fs:///usr/local/data/whosonfirst-data-admin-ca/data -source-id 1234 5678 + +Valid options are: + -exporter-uri string + A valid whosonfirst/go-whosonfirst-export URI (default "whosonfirst://") + -reader-uri string + A valid whosonfirst/go-reader URI. + -source-id int + A valid Who's On First ID. + -stdin + Read target IDs from STDIN + -writer-uri string + A valid whosonfirst/go-writer URI. (default "stdout://") +``` + +_This tool can (and probably be should) be adapted in the general-purpose assign properties or geometry tool. Today it only handles geometries._ + +For example: + +``` +$> /usr/local/whosonfirst/go-whosonfirst-travel/bin/wof-travel-id \ + -supersedes \ + -ids \ + -source fs:///usr/local/data/sfomuseum-data-architecture/data \ + 1729813675 \ + +| bin/assign-geometry \ + -stdin \ + -reader-uri fs:///usr/local/data/sfomuseum-data-architecture/data \ + -writer-uri fs:///usr/local/data/sfomuseum-data-architecture/data \ + -source-id 1729813675 +``` + +This is a fairly involved example, so here's a description of what's happening: + +* First we are using the `wof-travel-id` tool in the [go-whosonfirst-travel](https://github.com/whosonfirst/go-whosonfirst-travel) package to find all the records that, recursively, ID `1729813675` supersedes. +* We are exporting that list as a line-separated list of IDs (having specified the `-ids` flag). +* We are piping that output to the `assign-geometry` tool and reading the list of IDs from `STDIN` (having specified the `-stdin` flag). +* For each of those IDs we are assigning the geometry from the record with the ID `1729813675` and writing those updates back to disk. + ### assign-parent ``` diff --git a/cmd/assign-geometry/main.go b/cmd/assign-geometry/main.go new file mode 100644 index 0000000..e0bc51b --- /dev/null +++ b/cmd/assign-geometry/main.go @@ -0,0 +1,139 @@ +// Assign the geometry from a given record to one or more other records. +package main + +// This could (should) be extended to be a basic assign tool. + +import ( + "bufio" + "context" + "flag" + "github.com/whosonfirst/go-reader" + "github.com/whosonfirst/go-whosonfirst-export/v2" + wof_reader "github.com/whosonfirst/go-whosonfirst-reader" + wof_writer "github.com/whosonfirst/go-whosonfirst-writer" + "github.com/whosonfirst/go-writer" + "log" + "os" + "strconv" + "github.com/tidwall/gjson" + "github.com/tidwall/sjson" + "fmt" +) + +func main() { + + reader_uri := flag.String("reader-uri", "", "A valid whosonfirst/go-reader URI.") + writer_uri := flag.String("writer-uri", "stdout://", "A valid whosonfirst/go-writer URI.") + + exporter_uri := flag.String("exporter-uri", "whosonfirst://", "A valid whosonfirst/go-whosonfirst-export URI") + + source_id := flag.Int64("source-id", 0, "A valid Who's On First ID.") + + from_stdin := flag.Bool("stdin", false, "Read target IDs from STDIN") + + flag.Usage = func() { + fmt.Fprintf(os.Stderr, "Assign the geometry from a given record to one or more other records.\n\n") + fmt.Fprintf(os.Stderr, "Usage:\n\t %s [options] target-id-(N) target-id-(N)\n\n", os.Args[0]) + fmt.Fprintf(os.Stderr, "For example:\n") + fmt.Fprintf(os.Stderr, "\t%s -reader-uri fs:///usr/local/data/whosonfirst-data-admin-ca/data -source-id 1234 5678\n\n", os.Args[0]) + fmt.Fprintf(os.Stderr, "Valid options are:\n") + flag.PrintDefaults() + } + + flag.Parse() + + // START OF put me in a function... + + str_ids := flag.Args() + + if *from_stdin { + + scanner := bufio.NewScanner(os.Stdin) + + for scanner.Scan() { + str_ids = append(str_ids, scanner.Text()) + } + + err := scanner.Err() + + if err != nil { + log.Fatalf("Failed to read input from STDIN, %v", err) + } + } + + target_ids := make([]int64, len(str_ids)) + + for idx, this_id := range str_ids { + + id, err := strconv.ParseInt(this_id, 10, 64) + + if err != nil { + log.Fatalf("Failed to parse '%s', %v", this_id, err) + } + + target_ids[idx] = id + } + + // END OF put me in a function + + ctx := context.Background() + + r, err := reader.NewReader(ctx, *reader_uri) + + if err != nil { + log.Fatalf("Failed to create reader for '%s', %v", *reader_uri, err) + } + + wr, err := writer.NewWriter(ctx, *writer_uri) + + if err != nil { + log.Fatalf("Failed to create new writer for '%s', %v", *writer_uri, err) + } + + ex, err := export.NewExporter(ctx, *exporter_uri) + + if err != nil { + log.Fatalf("Failed to create new exporter for '%s', %v", *exporter_uri, err) + } + + source_body, err := wof_reader.LoadBytesFromID(ctx, r, *source_id) + + if err != nil { + log.Fatalf("Failed to load source '%d', %v", *source_id) + } + + geom_rsp := gjson.GetBytes(source_body, "geometry") + + if !geom_rsp.Exists() { + log.Fatal("Source record is missing a geometry") + } + + source_geom := geom_rsp.Value() + + for _, id := range target_ids { + + target_body, err := wof_reader.LoadBytesFromID(ctx, r, id) + + if err != nil { + log.Fatalf("Failed to load target '%d', %v", id) + } + + new_body, err := sjson.SetBytes(target_body, "geometry", source_geom) + + if err != nil { + log.Fatalf("Failed to update target geometry for '%d', %v", id, err) + } + + new_body, err = ex.Export(ctx, new_body) + + if err != nil { + log.Fatalf("Failed to export target '%d', %v", id, err) + } + + err = wof_writer.WriteFeatureBytes(ctx, wr, new_body) + + if err != nil { + log.Fatalf("Failed to write target '%d', %v", id, err) + } + } +} diff --git a/cmd/create/main.go b/cmd/create/main.go index b4494cb..7978930 100644 --- a/cmd/create/main.go +++ b/cmd/create/main.go @@ -189,7 +189,7 @@ func main() { results_cb := hierarchy.FirstButForgivingSPRResultsFunc update_cb := hierarchy.DefaultPointInPolygonHierarchyResolverUpdateCallback() - + new_body, err := resolver.PointInPolygonAndUpdate(ctx, inputs, results_cb, update_cb, body) if err != nil { @@ -208,7 +208,7 @@ func main() { } id_rsp := gjson.GetBytes(new_body, "properties.wof:id") - + err = wof_writer.WriteFeatureBytes(ctx, wr, new_body) if err != nil { diff --git a/cmd/deprecate/main.go b/cmd/deprecate/main.go index e60dbcf..1cf8a46 100644 --- a/cmd/deprecate/main.go +++ b/cmd/deprecate/main.go @@ -7,7 +7,7 @@ import ( "github.com/sfomuseum/go-flags/multi" "github.com/whosonfirst/go-reader" "github.com/whosonfirst/go-whosonfirst-export/v2" - "github.com/whosonfirst/go-whosonfirst-exportify" + "github.com/whosonfirst/go-whosonfirst-exportify" wof_reader "github.com/whosonfirst/go-whosonfirst-reader" "github.com/whosonfirst/go-writer" "log" @@ -126,10 +126,10 @@ func deprecateId(ctx context.Context, r reader.Reader, wr writer.Writer, ex expo } now := time.Now() - + to_update := map[string]interface{}{ "properties.edtf:deprecated": now.Format("2006-01-02"), - "properties.mz:is_current": 0, + "properties.mz:is_current": 0, } new_body, err := export.AssignProperties(ctx, body, to_update) @@ -137,6 +137,6 @@ func deprecateId(ctx context.Context, r reader.Reader, wr writer.Writer, ex expo if err != nil { return err } - + return exportify.ExportWithWriter(ctx, ex, wr, new_body) } diff --git a/cmd/rename-property/main.go b/cmd/rename-property/main.go index 8bea00b..9a75063 100644 --- a/cmd/rename-property/main.go +++ b/cmd/rename-property/main.go @@ -4,13 +4,13 @@ import ( "context" "flag" _ "github.com/sfomuseum/go-flags/multi" + "github.com/tidwall/gjson" + "github.com/tidwall/sjson" "github.com/whosonfirst/go-whosonfirst-export/v2" "github.com/whosonfirst/go-whosonfirst-iterate/emitter" "github.com/whosonfirst/go-whosonfirst-iterate/iterator" wof_writer "github.com/whosonfirst/go-whosonfirst-writer" "github.com/whosonfirst/go-writer" - "github.com/tidwall/gjson" - "github.com/tidwall/sjson" "io" "io/ioutil" "log" @@ -23,7 +23,7 @@ func main() { writer_uri := flag.String("writer-uri", "null://", "A valid whosonfirst/go-writer URI.") old_property := flag.String("old-property", "", "The fully qualified path of the property to rename.") - new_property := flag.String("new-property", "", "The fully qualified path of the property to be (re)named.") + new_property := flag.String("new-property", "", "The fully qualified path of the property to be (re)named.") flag.Parse() @@ -57,7 +57,7 @@ func main() { old_rsp := gjson.GetBytes(body, *old_property) - if !old_rsp.Exists(){ + if !old_rsp.Exists() { return nil } @@ -67,12 +67,12 @@ func main() { return err } - body, err = sjson.DeleteBytes(body, *old_property) - + body, err = sjson.DeleteBytes(body, *old_property) + if err != nil { return err } - + new_body, err := ex.Export(ctx, body) if err != nil { @@ -106,4 +106,3 @@ func main() { log.Fatal(err) } } -