Skip to content

Commit

Permalink
Adding a --tilejson arg to the show command (#110)
Browse files Browse the repository at this point in the history
* adding funcs to read tilejson

* adding tilejson option to show command

* refactoring tilejson creation into function
  • Loading branch information
mikeocool authored Jan 9, 2024
1 parent 80f0517 commit 8953bc3
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 24 deletions.
12 changes: 7 additions & 5 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@ var cli struct {
} `cmd:"" help:"Convert an MBTiles or older spec version to PMTiles."`

Show struct {
Path string `arg:""`
Bucket string `help:"Remote bucket"`
Metadata bool `help:"Print only the JSON metadata."`
Path string `arg:""`
Bucket string `help:"Remote bucket"`
Metadata bool `help:"Print only the JSON metadata."`
Tilejson bool `help:"Print the TileJSON."`
PublicUrl string `help:"Public URL of tile endpoint to use in the Tilejson output e.g. https://example.com/tiles/pmtiles/{z}/{x}/{y}"`
} `cmd:"" help:"Inspect a local or remote archive."`

Tile struct {
Expand Down Expand Up @@ -113,12 +115,12 @@ func main() {

switch ctx.Command() {
case "show <path>":
err := pmtiles.Show(logger, cli.Show.Bucket, cli.Show.Path, cli.Show.Metadata, false, 0, 0, 0)
err := pmtiles.Show(logger, cli.Show.Bucket, cli.Show.Path, cli.Show.Metadata, cli.Show.Tilejson, cli.Show.PublicUrl, false, 0, 0, 0)
if err != nil {
logger.Fatalf("Failed to show archive, %v", err)
}
case "tile <path> <z> <x> <y>":
err := pmtiles.Show(logger, cli.Tile.Bucket, cli.Tile.Path, false, true, cli.Tile.Z, cli.Tile.X, cli.Tile.Y)
err := pmtiles.Show(logger, cli.Tile.Bucket, cli.Tile.Path, false, false, "", true, cli.Tile.Z, cli.Tile.X, cli.Tile.Y)
if err != nil {
logger.Fatalf("Failed to show tile, %v", err)
}
Expand Down
23 changes: 5 additions & 18 deletions pmtiles/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,29 +234,16 @@ func (server *Server) get_tilejson(ctx context.Context, http_headers map[string]
var metadata_map map[string]interface{}
json.Unmarshal(metadata_bytes, &metadata_map)

tilejson := make(map[string]interface{})

if server.publicHostname == "" {
return 501, http_headers, []byte("PUBLIC_HOSTNAME must be set for TileJSON")
}

tilejson_bytes, err := CreateTilejson(header, metadata_bytes, server.publicHostname+"/"+name+"/{z}/{x}/{y}"+headerExt(header))
if err != nil {
return 500, http_headers, []byte("Error generating tilejson")
}

http_headers["Content-Type"] = "application/json"
tilejson["tilejson"] = "3.0.0"
tilejson["scheme"] = "xyz"
tilejson["tiles"] = []string{server.publicHostname + "/" + name + "/{z}/{x}/{y}" + headerExt(header)}
tilejson["vector_layers"] = metadata_map["vector_layers"]
tilejson["attribution"] = metadata_map["attribution"]
tilejson["description"] = metadata_map["description"]
tilejson["name"] = metadata_map["name"]
tilejson["version"] = metadata_map["version"]

E7 := 10000000.0
tilejson["bounds"] = []float64{float64(header.MinLonE7) / E7, float64(header.MinLatE7) / E7, float64(header.MaxLonE7) / E7, float64(header.MaxLatE7) / E7}
tilejson["center"] = []interface{}{float64(header.CenterLonE7) / E7, float64(header.CenterLatE7) / E7, header.CenterZoom}
tilejson["minzoom"] = header.MinZoom
tilejson["maxzoom"] = header.MaxZoom

tilejson_bytes, err := json.Marshal(tilejson)

return 200, http_headers, tilejson_bytes
}
Expand Down
20 changes: 19 additions & 1 deletion pmtiles/show.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ import (
"context"
"encoding/json"
"fmt"

// "github.com/dustin/go-humanize"
"io"
"log"
"os"
)

func Show(logger *log.Logger, bucketURL string, key string, show_metadata_only bool, show_tile bool, z int, x int, y int) error {
func Show(logger *log.Logger, bucketURL string, key string, show_metadata_only bool, show_tilejson bool, public_url string, show_tile bool, z int, x int, y int) error {
ctx := context.Background()

bucketURL, key, err := NormalizeBucketKey(bucketURL, "", key)
Expand Down Expand Up @@ -87,8 +88,25 @@ func Show(logger *log.Logger, bucketURL string, key string, show_metadata_only b
}
metadata_reader.Close()

if show_metadata_only && show_tilejson {
return fmt.Errorf("Cannot use --metadata and --tilejson together.")
}

if show_metadata_only {
fmt.Print(string(metadata_bytes))
} else if show_tilejson {
if public_url == "" {
// Using Fprintf instead of logger here, as this message should be written to Stderr in case
// Stdout is being redirected.
fmt.Fprintln(os.Stderr, "Warning: No --public-url specified; using placeholder tiles URL.")
public_url = "https://example.com/{z}/{x}/{y}.mvt"

}
tilejson_bytes, err := CreateTilejson(header, metadata_bytes, public_url)
if err != nil {
return fmt.Errorf("Failed to create tilejson for %s, %w", key, err)
}
fmt.Print(string(tilejson_bytes))
} else {
fmt.Printf("pmtiles spec version: %d\n", header.SpecVersion)
// fmt.Printf("total size: %s\n", humanize.Bytes(uint64(r.Size())))
Expand Down
29 changes: 29 additions & 0 deletions pmtiles/tilejson.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package pmtiles

import (
"encoding/json"
)

func CreateTilejson(header HeaderV3, metadata_bytes []byte, tileUrl string) ([]byte, error) {
var metadata_map map[string]interface{}
json.Unmarshal(metadata_bytes, &metadata_map)

tilejson := make(map[string]interface{})

tilejson["tilejson"] = "3.0.0"
tilejson["scheme"] = "xyz"
tilejson["tiles"] = []string{tileUrl}
tilejson["vector_layers"] = metadata_map["vector_layers"]
tilejson["attribution"] = metadata_map["attribution"]
tilejson["description"] = metadata_map["description"]
tilejson["name"] = metadata_map["name"]
tilejson["version"] = metadata_map["version"]

E7 := 10000000.0
tilejson["bounds"] = []float64{float64(header.MinLonE7) / E7, float64(header.MinLatE7) / E7, float64(header.MaxLonE7) / E7, float64(header.MaxLatE7) / E7}
tilejson["center"] = []interface{}{float64(header.CenterLonE7) / E7, float64(header.CenterLatE7) / E7, header.CenterZoom}
tilejson["minzoom"] = header.MinZoom
tilejson["maxzoom"] = header.MaxZoom

return json.MarshalIndent(tilejson, "", "\t")
}
60 changes: 60 additions & 0 deletions pmtiles/tilejson_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package pmtiles

import (
"encoding/json"
"testing"

"github.com/stretchr/testify/assert"
)

func TestCreateTilejson(t *testing.T) {
// Define test inputs
header := HeaderV3{
MinZoom: 0.0,
MaxZoom: 14.0,
MinLonE7: -1144000000,
MinLatE7: 479000000,
MaxLonE7: -1139000000,
MaxLatE7: 483000000,
CenterLonE7: -1141500000,
CenterLatE7: 481000000,
}
metadataBytes := []byte(`
{
"vector_layers": [{"id": "layer1"}],
"attribution": "Attribution",
"description": "Description",
"name": "Name",
"version": "1.0"
}`)
tileURL := "https://example.com/tiles.pmtiles/{z}/{x}/{y}"

// Call the function
tilejsonBytes, err := CreateTilejson(header, metadataBytes, tileURL)

// Check for errors
if err != nil {
t.Errorf("CreateTilejson returned an error: %v", err)
}

// Parse the tilejsonBytes to check the output
var tilejson map[string]interface{}
err = json.Unmarshal(tilejsonBytes, &tilejson)
if err != nil {
t.Errorf("Failed to parse the generated TileJSON: %v", err)
}

assert.Equal(t, "3.0.0", tilejson["tilejson"])
assert.Equal(t, "xyz", tilejson["scheme"])
assert.Equal(t, []interface{}{"https://example.com/tiles.pmtiles/{z}/{x}/{y}"}, tilejson["tiles"])
assert.Equal(t, []interface{}{map[string]interface{}{"id": "layer1"}}, tilejson["vector_layers"])
assert.Equal(t, "Attribution", tilejson["attribution"])
assert.Equal(t, "Description", tilejson["description"])
assert.Equal(t, "Name", tilejson["name"])
assert.Equal(t, "1.0", tilejson["version"])

assert.Equal(t, []interface{}{-114.400000, 47.900000, -113.900000, 48.300000}, tilejson["bounds"])
assert.Equal(t, []interface{}{-114.150000, 48.100000, 0.0}, tilejson["center"])
assert.Equal(t, 0.0, tilejson["minzoom"])
assert.Equal(t, 14.0, tilejson["maxzoom"])
}

0 comments on commit 8953bc3

Please sign in to comment.