ENH: Add auto-detection of tile size and return blank PNG of same size (#155)

This commit is contained in:
Brendan Ward 2022-08-30 11:43:42 -07:00 committed by GitHub
parent 85eee9b020
commit c4e7d1f8b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 75 additions and 28 deletions

View File

@ -7,6 +7,8 @@
- added support for specifying host IP address to listen on using the `--host`
option (#138).
- switched basemaps to [Stamen map tiles](http://maps.stamen.com/) (#148)
- added auto-detection of tile size and return blank tile of same size
(if available) for image tilesets when tile is not found (#155).
## 0.8.2

2
go.mod
View File

@ -1,7 +1,7 @@
module github.com/consbio/mbtileserver
require (
github.com/brendan-ward/mbtiles-go v0.1.1-0.20220505234122-e9589dbaa0da
github.com/brendan-ward/mbtiles-go v0.1.1-0.20220830154734-a6cb9b848bab
github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d // indirect
github.com/evalphobia/logrus_sentry v0.8.2
github.com/fsnotify/fsnotify v1.5.1

4
go.sum
View File

@ -68,8 +68,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/brendan-ward/mbtiles-go v0.1.1-0.20220505234122-e9589dbaa0da h1:UTmXm7fCvJbBnxoewyzAUX/FpA1MSnAagCSqtCom9vY=
github.com/brendan-ward/mbtiles-go v0.1.1-0.20220505234122-e9589dbaa0da/go.mod h1:PjU6MOcQtVNQbHT42VnBVLQh4pze1Hy+6AkwRoW4BBY=
github.com/brendan-ward/mbtiles-go v0.1.1-0.20220830154734-a6cb9b848bab h1:/+iZRJ5VeHYqgQQtfiAaP6niUH+2zSfox6QetbqB6eI=
github.com/brendan-ward/mbtiles-go v0.1.1-0.20220830154734-a6cb9b848bab/go.mod h1:PjU6MOcQtVNQbHT42VnBVLQh4pze1Hy+6AkwRoW4BBY=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d h1:S2NE3iHSwP0XV47EEXL8mWmRdEfGscSJ+7EgePNgt0s=

View File

@ -421,7 +421,7 @@ func (ts *Tileset) arcgisTileHandler(w http.ResponseWriter, r *http.Request) {
if data == nil || len(data) <= 1 {
// Return blank PNG for all image types
w.Header().Set("Content-Type", "image/png")
_, err = w.Write(BlankPNG())
_, err = w.Write(BlankPNG(ts.tilesize))
if err != nil {
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)

View File

@ -1,18 +1,37 @@
package handlers
// BlankPNG returns bytes of a blank PNG, for the request handlers to return
// when an image tile is not available.
func BlankPNG() []byte {
return []byte{
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd, 0x49,
0x48, 0x44, 0x52, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1, 0x3,
0x0, 0x0, 0x0, 0x66, 0xbc, 0x3a, 0x25, 0x0, 0x0, 0x0, 0x3, 0x50, 0x4c,
0x54, 0x45, 0x0, 0x0, 0x0, 0xa7, 0x7a, 0x3d, 0xda, 0x0, 0x0, 0x0, 0x1,
0x74, 0x52, 0x4e, 0x53, 0x0, 0x40, 0xe6, 0xd8, 0x66, 0x0, 0x0, 0x0,
0x1f, 0x49, 0x44, 0x41, 0x54, 0x68, 0xde, 0xed, 0xc1, 0x1, 0xd, 0x0,
0x0, 0x0, 0xc2, 0x20, 0xfb, 0xa7, 0x36, 0xc7, 0x37, 0x60, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x71, 0x7, 0x21, 0x0, 0x0, 0x1, 0xa7,
0x57, 0x29, 0xd7, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae,
0x42, 0x60, 0x82,
// BlankPNG returns bytes of a blank PNG of the requested size, falling back to
// 256px if a suitable size is not available.
// Used for the request handlers to return when an image tile is not available.
// Images created with https://png-pixel.com/ then minified using tinypng.com
func BlankPNG(tilesize uint32) []byte {
switch tilesize {
case 512:
return []byte{
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd,
0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x2, 0x0, 0x1,
0x3, 0x0, 0x0, 0x0, 0xce, 0xb6, 0x46, 0xb9, 0x0, 0x0, 0x0, 0x3, 0x50,
0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0xa7, 0x7a, 0x3d, 0xda, 0x0, 0x0,
0x0, 0x1, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x40, 0xe6, 0xd8, 0x66, 0x0,
0x0, 0x0, 0x36, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0xed, 0xc1, 0x1,
0x1, 0x0, 0x0, 0x0, 0x82, 0xa0, 0xfe, 0xaf, 0x6e, 0x88, 0xc0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0xe2, 0xe, 0x82, 0x0, 0x0, 0x1, 0x1, 0xf5, 0x37, 0x5e,
0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
}
default: // 256
return []byte{
0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, 0x0, 0x0, 0x0, 0xd,
0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x1,
0x3, 0x0, 0x0, 0x0, 0x66, 0xbc, 0x3a, 0x25, 0x0, 0x0, 0x0, 0x3, 0x50,
0x4c, 0x54, 0x45, 0x0, 0x0, 0x0, 0xa7, 0x7a, 0x3d, 0xda, 0x0, 0x0,
0x0, 0x1, 0x74, 0x52, 0x4e, 0x53, 0x0, 0x40, 0xe6, 0xd8, 0x66, 0x0,
0x0, 0x0, 0x1f, 0x49, 0x44, 0x41, 0x54, 0x68, 0xde, 0xed, 0xc1, 0x1,
0xd, 0x0, 0x0, 0x0, 0xc2, 0x20, 0xfb, 0xa7, 0x36, 0xc7, 0x37, 0x60,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x71, 0x7, 0x21, 0x0, 0x0,
0x1, 0xa7, 0x57, 0x29, 0xd7, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e,
0x44, 0xae, 0x42, 0x60, 0x82,
}
}
}

View File

@ -1,15 +1,35 @@
package handlers
import (
"encoding/base64"
"encoding/hex"
"testing"
)
func Test_BlankPNG(t *testing.T) {
blankPNG := BlankPNG()
b64 := base64.StdEncoding.EncodeToString(blankPNG)
expected := "iVBORw0KGgoAAAANSUhEUgAAAQAAAAEAAQMAAABmvDolAAAAA1BMVEUAAACnej3aAAAAAXRSTlMAQObYZgAAAB9JREFUaN7twQENAAAAwiD7pzbHN2AAAAAAAAAAAHEHIQAAAadXKdcAAAAASUVORK5CYII="
if b64 != expected {
t.Error("BlankPNG returned unexpected value (base64):", b64)
tests := []struct {
tilesize uint32
data string
}{
{
// tilesize not detected, defaults to 256
tilesize: 0, data: "89504e470d0a1a0a0000000d494844520000010000000100010300000066bc3a2500000003504c5445000000a77a3dda0000000174524e530040e6d8660000001f4944415468deedc1010d000000c220fba736c737600000000000000000710721000001a75729d70000000049454e44ae426082",
},
{
// not available, defaults to 256
tilesize: 128, data: "89504e470d0a1a0a0000000d494844520000010000000100010300000066bc3a2500000003504c5445000000a77a3dda0000000174524e530040e6d8660000001f4944415468deedc1010d000000c220fba736c737600000000000000000710721000001a75729d70000000049454e44ae426082",
},
{
tilesize: 256, data: "89504e470d0a1a0a0000000d494844520000010000000100010300000066bc3a2500000003504c5445000000a77a3dda0000000174524e530040e6d8660000001f4944415468deedc1010d000000c220fba736c737600000000000000000710721000001a75729d70000000049454e44ae426082",
},
{
tilesize: 512, data: "89504e470d0a1a0a0000000d4948445200000200000002000103000000ceb646b900000003504c5445000000a77a3dda0000000174524e530040e6d866000000364944415478daedc1010100000082a0feaf6e88c00000000000000000000000000000000000000000000000000000000000000000e20e8200000101f5375e0000000049454e44ae426082",
},
}
for _, tc := range tests {
blankPNG := hex.EncodeToString(BlankPNG(tc.tilesize))
if blankPNG != tc.data {
t.Error("BlankPNG returned unexpected value for size ", tc.tilesize, " :", blankPNG)
}
}
}

View File

@ -21,6 +21,7 @@ type Tileset struct {
id string
name string
tileformat mbtiles.TileFormat
tilesize uint32
published bool
locked bool
router *http.ServeMux
@ -51,6 +52,7 @@ func newTileset(svc *ServiceSet, filename, id, path string) (*Tileset, error) {
id: id,
name: name,
tileformat: db.GetTileFormat(),
tilesize: db.GetTileSize(),
published: true,
}
@ -136,6 +138,10 @@ func (ts *Tileset) TileJSON(svcURL string, query string) (map[string]interface{}
"name": ts.name,
}
if ts.tilesize > 0 {
out["tilesize"] = ts.tilesize
}
metadata, err := db.ReadMetadata()
if err != nil {
return nil, err
@ -213,7 +219,7 @@ func (ts *Tileset) tileHandler(w http.ResponseWriter, r *http.Request) {
if ts == nil || !ts.published {
// In order to not break any requests from when this tileset was published
// return the appropriate not found handler for the original tile format.
tileNotFoundHandler(w, r, ts.tileformat)
tileNotFoundHandler(w, r, ts.tileformat, ts.tilesize)
return
}
@ -249,7 +255,7 @@ func (ts *Tileset) tileHandler(w http.ResponseWriter, r *http.Request) {
return
}
if data == nil || len(data) <= 1 {
tileNotFoundHandler(w, r, ts.tileformat)
tileNotFoundHandler(w, r, ts.tileformat, ts.tilesize)
return
}
@ -322,13 +328,13 @@ func (ts *Tileset) previewHandler(w http.ResponseWriter, r *http.Request) {
// tileNotFoundHandler is an http.HandlerFunc that writes the default response
// for a non-existing tile of type f to w
func tileNotFoundHandler(w http.ResponseWriter, r *http.Request, f mbtiles.TileFormat) {
func tileNotFoundHandler(w http.ResponseWriter, r *http.Request, f mbtiles.TileFormat, tilesize uint32) {
switch f {
case mbtiles.PNG, mbtiles.JPG, mbtiles.WEBP:
// Return blank PNG for all image types
w.Header().Set("Content-Type", "image/png")
w.WriteHeader(http.StatusOK)
w.Write(BlankPNG())
w.Write(BlankPNG(tilesize))
case mbtiles.PBF:
// Return 204
w.WriteHeader(http.StatusNoContent)