Skip to content

Commit

Permalink
Tilejson fixups (#114)
Browse files Browse the repository at this point in the history
* more renames of public_hostname to public_url; update caddy module [#110]

* TileJSON does not emit null values for optional string fields [#110]

* fix formatting
  • Loading branch information
bdon authored Jan 11, 2024
1 parent 8953bc3 commit ef87ca0
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 45 deletions.
2 changes: 1 addition & 1 deletion caddy/Caddyfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ localhost:2019 {
cache_size 256

# used to embed a tiles URL in TileJSON.
public_hostname https://localhost:2019/tiles
public_url https://localhost:2019/tiles
}
}
}
16 changes: 8 additions & 8 deletions caddy/pmtiles_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ func init() {

// Middleware creates a Z/X/Y tileserver backed by a local or remote bucket of PMTiles archives.
type Middleware struct {
Bucket string `json:"bucket"`
CacheSize int `json:"cache_size"`
PublicHostname string `json:"public_hostname"`
logger *zap.Logger
server *pmtiles.Server
Bucket string `json:"bucket"`
CacheSize int `json:"cache_size"`
PublicUrl string `json:"public_url"`
logger *zap.Logger
server *pmtiles.Server
}

// CaddyModule returns the Caddy module information.
Expand All @@ -45,7 +45,7 @@ func (m *Middleware) Provision(ctx caddy.Context) error {
m.logger = ctx.Logger()
logger := log.New(io.Discard, "", log.Ldate)
prefix := "." // serve only the root of the bucket for now, at the root route of Caddyfile
server, err := pmtiles.NewServer(m.Bucket, prefix, logger, m.CacheSize, "", m.PublicHostname)
server, err := pmtiles.NewServer(m.Bucket, prefix, logger, m.CacheSize, "", m.PublicUrl)
if err != nil {
return err
}
Expand Down Expand Up @@ -95,8 +95,8 @@ func (m *Middleware) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
return d.ArgErr()
}
m.CacheSize = num
case "public_hostname":
if !d.Args(&m.PublicHostname) {
case "public_url":
if !d.Args(&m.PublicUrl) {
return d.ArgErr()
}
}
Expand Down
18 changes: 9 additions & 9 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ var cli struct {
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}"`
PublicUrl string `help:"Public base URL of tile endpoint for TileJSON e.g. https://example.com/tiles"`
} `cmd:"" help:"Inspect a local or remote archive."`

Tile struct {
Expand Down Expand Up @@ -76,13 +76,13 @@ var cli struct {
} `cmd:"" hidden:""`

Serve struct {
Path string `arg:"" help:"Local path or bucket prefix"`
Interface string `default:"0.0.0.0"`
Port int `default:8080`
Cors string `help:"Value of HTTP CORS header."`
CacheSize int `default:64 help:"Size of cache in Megabytes."`
Bucket string `help:"Remote bucket"`
PublicHostname string `help:"Public hostname of tile endpoint e.g. https://example.com"`
Path string `arg:"" help:"Local path or bucket prefix"`
Interface string `default:"0.0.0.0"`
Port int `default:8080`
Cors string `help:"Value of HTTP CORS header."`
CacheSize int `default:64 help:"Size of cache in Megabytes."`
Bucket string `help:"Remote bucket"`
PublicUrl string `help:"Public base URL of tile endpoint for TileJSON e.g. https://example.com/tiles/"`
} `cmd:"" help:"Run an HTTP proxy server for Z/X/Y tiles."`

Download struct {
Expand Down Expand Up @@ -125,7 +125,7 @@ func main() {
logger.Fatalf("Failed to show tile, %v", err)
}
case "serve <path>":
server, err := pmtiles.NewServer(cli.Serve.Bucket, cli.Serve.Path, logger, cli.Serve.CacheSize, cli.Serve.Cors, cli.Serve.PublicHostname)
server, err := pmtiles.NewServer(cli.Serve.Bucket, cli.Serve.Path, logger, cli.Serve.CacheSize, cli.Serve.Cors, cli.Serve.PublicUrl)

if err != nil {
logger.Fatalf("Failed to create new server, %v", err)
Expand Down
36 changes: 18 additions & 18 deletions pmtiles/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,15 @@ type Response struct {
}

type Server struct {
reqs chan Request
bucket Bucket
logger *log.Logger
cacheSize int
cors string
publicHostname string
reqs chan Request
bucket Bucket
logger *log.Logger
cacheSize int
cors string
publicUrl string
}

func NewServer(bucketURL string, prefix string, logger *log.Logger, cacheSize int, cors string, publicHostname string) (*Server, error) {
func NewServer(bucketURL string, prefix string, logger *log.Logger, cacheSize int, cors string, publicUrl string) (*Server, error) {

ctx := context.Background()

Expand All @@ -63,20 +63,20 @@ func NewServer(bucketURL string, prefix string, logger *log.Logger, cacheSize in
return nil, err
}

return NewServerWithBucket(bucket, prefix, logger, cacheSize, cors, publicHostname)
return NewServerWithBucket(bucket, prefix, logger, cacheSize, cors, publicUrl)
}

func NewServerWithBucket(bucket Bucket, prefix string, logger *log.Logger, cacheSize int, cors string, publicHostname string) (*Server, error) {
func NewServerWithBucket(bucket Bucket, prefix string, logger *log.Logger, cacheSize int, cors string, publicUrl string) (*Server, error) {

reqs := make(chan Request, 8)

l := &Server{
reqs: reqs,
bucket: bucket,
logger: logger,
cacheSize: cacheSize,
cors: cors,
publicHostname: publicHostname,
reqs: reqs,
bucket: bucket,
logger: logger,
cacheSize: cacheSize,
cors: cors,
publicUrl: publicUrl,
}

return l, nil
Expand Down Expand Up @@ -234,11 +234,11 @@ func (server *Server) get_tilejson(ctx context.Context, http_headers map[string]
var metadata_map map[string]interface{}
json.Unmarshal(metadata_bytes, &metadata_map)

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

tilejson_bytes, err := CreateTilejson(header, metadata_bytes, server.publicHostname+"/"+name+"/{z}/{x}/{y}"+headerExt(header))
tilejson_bytes, err := CreateTilejson(header, metadata_bytes, server.publicUrl+"/"+name)
if err != nil {
return 500, http_headers, []byte("Error generating tilejson")
}
Expand Down
2 changes: 0 additions & 2 deletions pmtiles/show.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,6 @@ func Show(logger *log.Logger, bucketURL string, key string, show_metadata_only b
// 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 {
Expand Down
27 changes: 22 additions & 5 deletions pmtiles/tilejson.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,29 @@ func CreateTilejson(header HeaderV3, metadata_bytes []byte, tileUrl string) ([]b

tilejson["tilejson"] = "3.0.0"
tilejson["scheme"] = "xyz"
tilejson["tiles"] = []string{tileUrl}

if tileUrl == "" {
tileUrl = "https://example.com"
}

tilejson["tiles"] = []string{tileUrl + "/{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"]

if val, ok := metadata_map["attribution"]; ok {
tilejson["attribution"] = val
}

if val, ok := metadata_map["description"]; ok {
tilejson["description"] = val
}

if val, ok := metadata_map["name"]; ok {
tilejson["name"] = val
}

if val, ok := metadata_map["version"]; ok {
tilejson["version"] = val
}

E7 := 10000000.0
tilejson["bounds"] = []float64{float64(header.MinLonE7) / E7, float64(header.MinLatE7) / E7, float64(header.MaxLonE7) / E7, float64(header.MaxLatE7) / E7}
Expand Down
43 changes: 41 additions & 2 deletions pmtiles/tilejson_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ func TestCreateTilejson(t *testing.T) {
MaxLatE7: 483000000,
CenterLonE7: -1141500000,
CenterLatE7: 481000000,
TileType: Mvt,
}
metadataBytes := []byte(`
{
Expand All @@ -27,7 +28,7 @@ func TestCreateTilejson(t *testing.T) {
"name": "Name",
"version": "1.0"
}`)
tileURL := "https://example.com/tiles.pmtiles/{z}/{x}/{y}"
tileURL := "https://example.com/foo"

// Call the function
tilejsonBytes, err := CreateTilejson(header, metadataBytes, tileURL)
Expand All @@ -46,7 +47,7 @@ func TestCreateTilejson(t *testing.T) {

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{}{"https://example.com/foo/{z}/{x}/{y}.mvt"}, 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"])
Expand All @@ -58,3 +59,41 @@ func TestCreateTilejson(t *testing.T) {
assert.Equal(t, 0.0, tilejson["minzoom"])
assert.Equal(t, 14.0, tilejson["maxzoom"])
}

func TestCreateTilejsonOptionalFields(t *testing.T) {
header := HeaderV3{
MinZoom: 0.0,
MaxZoom: 14.0,
MinLonE7: -1144000000,
MinLatE7: 479000000,
MaxLonE7: -1139000000,
MaxLatE7: 483000000,
CenterLonE7: -1141500000,
CenterLatE7: 481000000,
TileType: Png,
}
metadataBytes := []byte(`
{
}`)

tilejsonBytes, err := CreateTilejson(header, metadataBytes, "")

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

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/{z}/{x}/{y}.png"}, tilejson["tiles"])
assert.NotContains(t, tilejson, "attribution")
assert.NotContains(t, tilejson, "description")
assert.NotContains(t, tilejson, "name")
assert.NotContains(t, tilejson, "version")
}

0 comments on commit ef87ca0

Please sign in to comment.