diff --git a/cmd/mtwebmapper/main.go b/cmd/mtwebmapper/main.go index 90bddcd..f859242 100644 --- a/cmd/mtwebmapper/main.go +++ b/cmd/mtwebmapper/main.go @@ -7,71 +7,12 @@ package main import ( "flag" "fmt" - "image" - "image/color" - "image/png" "log" "net/http" - "os" - "path/filepath" - "strconv" - - "bitbucket.org/s_l_teichmann/mtredisalize/common" "github.com/gorilla/mux" ) -func blowUp(src image.Image) *image.RGBA { - - // TODO: Fast path for image.RGBA - - dst := image.NewRGBA(image.Rect(0, 0, 256, 256)) - - // fix point numbers x:8 - dx, dy := src.Bounds().Dx(), src.Bounds().Dy() - - bx, by := src.Bounds().Min.X<<8, src.Bounds().Min.Y<<8 - - py := by - for y := 0; y < 256; y++ { - sy := (py >> 8) & 0xff - ox := -1 - px := bx - var col color.Color - for x := 0; x < 256; x++ { - sx := (px >> 8) & 0xff - if sx != ox { // Minimize interface indirection access. - ox = sx - col = src.At(sx, sy) - } - dst.Set(x, y, col) - px += dx - } - py += dy - } - - return dst -} - -func toUint(s string) uint { - var err error - var x int - if x, err = strconv.Atoi(s); err != nil { - log.Printf("WARN: Cannot convert to int: %s", err) - return 0 - } - return uint(x) -} - -func createETag(path string) (etag string, err error) { - var fi os.FileInfo - if fi, err = os.Stat(path); err != nil { - return - } - etag = fmt.Sprintf("%x-%x", fi.ModTime().Unix(), fi.Size()) - return -} - func main() { var ( port int @@ -91,93 +32,10 @@ func main() { flag.Parse() r := mux.NewRouter() - r.HandleFunc("/map/{z:[0-9]+}/{x:[0-9]+}/{y:[0-9]+}.png", func(rw http.ResponseWriter, r *http.Request) { - - vars := mux.Vars(r) - xs := vars["x"] - ys := vars["y"] - zs := vars["z"] - - x, y, z := toUint(xs), toUint(ys), toUint(zs) - if z < 9 { - filename := fmt.Sprintf("%d/%d/%d.png", z, x, y) - http.ServeFile(rw, r, filepath.Join(mapDir, filename)) - return - } - - if z > 16 { - z = 16 - } - tx := x >> (z - 8) - ty := y >> (z - 8) - - baseTile := filepath.Join(mapDir, fmt.Sprintf("8/%d/%d.png", tx, ty)) - - var etag string - var err error - - if ifNoneMatch := r.Header.Get("If-None-Match"); ifNoneMatch != "" { - if etag, err = createETag(baseTile); err != nil { - http.NotFound(rw, r) - return - } - if ifNoneMatch == etag { - http.Error(rw, http.StatusText(http.StatusNotModified), http.StatusNotModified) - return - } - } - img := common.LoadPNG(baseTile) - - type subImage interface { - SubImage(image.Rectangle) image.Image - } - var si subImage - var ok bool - if si, ok = img.(subImage); !ok { - // Should not happen. - http.Error(rw, - http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) - return - } - - rx := x & ^(^uint(0) << (z - 8)) - ry := y & ^(^uint(0) << (z - 8)) - - parts := uint(1) << (z - 8) - - w := uint(256) / parts - xo := w * rx - yo := w * (parts - 1 - ry) - //fmt.Printf("x %d\n", x) - //fmt.Printf("y %d\n", y) - //fmt.Printf("z %d\n", z) - //fmt.Printf("z %d\n", z) - //fmt.Printf("parts %d\n", parts) - //fmt.Printf("w %d\n", w) - //fmt.Printf("xo %d\n", xo) - //fmt.Printf("yo %d\n", yo) - //fmt.Printf("rx %d\n", rx) - //fmt.Printf("ry %d\n", ry) - - img = si.SubImage(image.Rect(int(xo), int(yo), int(xo+w), int(yo+w))) - img = blowUp(img) - - rw.Header().Set("Content-Type", "image/png") - if etag == "" { - if etag, err = createETag(baseTile); err != nil { - log.Printf("Cannot create ETag: %s", baseTile) - } else { - rw.Header().Set("ETag", etag) - } - } else { - rw.Header().Set("ETag", etag) - } - - if err = png.Encode(rw, img); err != nil { - log.Printf("WARN: encoding image failed: %s", err) - return - } - }) + r.HandleFunc("/map/{z:[0-9]+}/{x:[0-9]+}/{y:[0-9]+}.png", + func(rw http.ResponseWriter, r *http.Request) { + createTiles(rw, r, mapDir) + }) r.PathPrefix("/").Handler(http.FileServer(http.Dir(webDir))) http.Handle("/", r) diff --git a/cmd/mtwebmapper/subbaseline.go b/cmd/mtwebmapper/subbaseline.go new file mode 100644 index 0000000..e8ef808 --- /dev/null +++ b/cmd/mtwebmapper/subbaseline.go @@ -0,0 +1,158 @@ +// Copyright 2014 by Sascha L. Teichmann +// Use of this source code is governed by the MIT license +// that can be found in the LICENSE file. + +package main + +import ( + "fmt" + "image" + "image/color" + "image/png" + "log" + "net/http" + "os" + "path/filepath" + "strconv" + + "bitbucket.org/s_l_teichmann/mtredisalize/common" + "github.com/gorilla/mux" +) + +func blowUp(src image.Image) *image.RGBA { + + // TODO: Fast path for image.RGBA + + dst := image.NewRGBA(image.Rect(0, 0, 256, 256)) + + // fix point numbers x:8 + dx, dy := src.Bounds().Dx(), src.Bounds().Dy() + + bx, by := src.Bounds().Min.X<<8, src.Bounds().Min.Y<<8 + + py := by + for y := 0; y < 256; y++ { + sy := (py >> 8) & 0xff + ox := -1 + px := bx + var col color.Color + for x := 0; x < 256; x++ { + sx := (px >> 8) & 0xff + if sx != ox { // Minimize interface indirection access. + ox = sx + col = src.At(sx, sy) + } + dst.Set(x, y, col) + px += dx + } + py += dy + } + + return dst +} + +func createTiles(rw http.ResponseWriter, r *http.Request, mapDir string) { + vars := mux.Vars(r) + xs := vars["x"] + ys := vars["y"] + zs := vars["z"] + + x, y, z := toUint(xs), toUint(ys), toUint(zs) + if z < 9 { + filename := fmt.Sprintf("%d/%d/%d.png", z, x, y) + http.ServeFile(rw, r, filepath.Join(mapDir, filename)) + return + } + + if z > 16 { + z = 16 + } + tx := x >> (z - 8) + ty := y >> (z - 8) + + baseTile := filepath.Join(mapDir, fmt.Sprintf("8/%d/%d.png", tx, ty)) + + var etag string + var err error + + if ifNoneMatch := r.Header.Get("If-None-Match"); ifNoneMatch != "" { + if etag, err = createETag(baseTile); err != nil { + http.NotFound(rw, r) + return + } + if ifNoneMatch == etag { + http.Error(rw, http.StatusText(http.StatusNotModified), http.StatusNotModified) + return + } + } + img := common.LoadPNG(baseTile) + + type subImage interface { + SubImage(image.Rectangle) image.Image + } + var si subImage + var ok bool + if si, ok = img.(subImage); !ok { + // Should not happen. + http.Error(rw, + http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) + return + } + + rx := x & ^(^uint(0) << (z - 8)) + ry := y & ^(^uint(0) << (z - 8)) + + parts := uint(1) << (z - 8) + + w := uint(256) / parts + xo := w * rx + yo := w * (parts - 1 - ry) + //fmt.Printf("x %d\n", x) + //fmt.Printf("y %d\n", y) + //fmt.Printf("z %d\n", z) + //fmt.Printf("z %d\n", z) + //fmt.Printf("parts %d\n", parts) + //fmt.Printf("w %d\n", w) + //fmt.Printf("xo %d\n", xo) + //fmt.Printf("yo %d\n", yo) + //fmt.Printf("rx %d\n", rx) + //fmt.Printf("ry %d\n", ry) + + img = si.SubImage(image.Rect(int(xo), int(yo), int(xo+w), int(yo+w))) + img = blowUp(img) + + rw.Header().Set("Content-Type", "image/png") + if etag == "" { + if etag, err = createETag(baseTile); err != nil { + log.Printf("Cannot create ETag: %s", baseTile) + } else { + rw.Header().Set("ETag", etag) + } + } else { + rw.Header().Set("ETag", etag) + } + + if err = png.Encode(rw, img); err != nil { + log.Printf("WARN: encoding image failed: %s", err) + return + } +} + +func createETag(path string) (etag string, err error) { + var fi os.FileInfo + if fi, err = os.Stat(path); err != nil { + return + } + etag = fmt.Sprintf("%x-%x", fi.ModTime().Unix(), fi.Size()) + return +} + +func toUint(s string) uint { + var err error + var x int + if x, err = strconv.Atoi(s); err != nil { + log.Printf("WARN: Cannot convert to int: %s", err) + return 0 + } + return uint(x) +}