From 5ee3731a27e2b94b09b89fa5003370b7ec6608e0 Mon Sep 17 00:00:00 2001 From: "Sascha L. Teichmann" Date: Sun, 8 May 2016 12:33:17 +0200 Subject: [PATCH 01/21] Started with experimental hashing of base tiles to avoid redundant write operations and pyramid updates. --- common/basetilehash.go | 42 +++++++++++++++++++++++++++++++++++ common/image.go | 50 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 common/basetilehash.go diff --git a/common/basetilehash.go b/common/basetilehash.go new file mode 100644 index 0000000..506c464 --- /dev/null +++ b/common/basetilehash.go @@ -0,0 +1,42 @@ +// Copyright 2016 by Sascha L. Teichmann +// Use of this source code is governed by the MIT license +// that can be found in the LICENSE file. + +package common + +import ( + "bytes" + "image" + "sync" +) + +type btPos struct { + x int + y int +} + +type BaseTileHash struct { + // XXX: Maybe use some kind of LRU cache instead? + hashes map[btPos][]byte + sync.Mutex +} + +func NewBaseTileHash() *BaseTileHash { + return &BaseTileHash{hashes: map[btPos][]byte{}} +} + +func (bth *BaseTileHash) Update(x, y int, img image.Image) bool { + hash := SHA1Image(img) + key := btPos{x, y} + bth.Lock() + defer bth.Unlock() + if old, found := bth.hashes[key]; found { + equals := bytes.Equal(old, hash) + if !equals { + bth.hashes[key] = hash + } + return !equals + } + bth.hashes[key] = hash + return true +} diff --git a/common/image.go b/common/image.go index dcf3211..45f2a01 100644 --- a/common/image.go +++ b/common/image.go @@ -7,6 +7,7 @@ package common import ( "bufio" "bytes" + "crypto/sha1" "errors" "image" "image/color" @@ -110,3 +111,52 @@ func LoadPNG(path string, bg color.RGBA) image.Image { } return img } + +func sha1rgba(img *image.RGBA) []byte { + hash := sha1.New() + w, h := img.Rect.Dx()*4, img.Rect.Dy() + + pos := img.PixOffset(img.Rect.Min.X, img.Rect.Min.Y) + + for ; h > 0; h, pos = h-1, pos+img.Stride { + hash.Write(img.Pix[pos : pos+w]) + } + + return hash.Sum(nil) +} + +func sha1uniform(img *image.Uniform) []byte { + r, g, b, a := img.C.RGBA() + return sha1.New().Sum([]byte{ + byte(r >> 16), + byte(g >> 16), + byte(b >> 16), + byte(a >> 16)}) +} + +func SHA1Image(img image.Image) []byte { + + switch i := img.(type) { + case *image.RGBA: + return sha1rgba(i) + case *image.Uniform: + return sha1uniform(i) + } + + log.Println("WARN: slow path for SHA1Image") + + hash := sha1.New() + bounds := img.Bounds() + var pix [4]byte + for x := bounds.Min.X; x <= bounds.Max.X; x++ { + for y := bounds.Min.Y; y <= bounds.Max.Y; y++ { + r, g, b, a := img.At(x, y).RGBA() + pix[0] = byte(r >> 16) + pix[1] = byte(g >> 16) + pix[2] = byte(b >> 16) + pix[3] = byte(a >> 16) + hash.Write(pix[:]) + } + } + return hash.Sum(nil) +} From 4bed73d9e34b7208c307201290759e64e832635e Mon Sep 17 00:00:00 2001 From: "Sascha L. Teichmann" Date: Sun, 8 May 2016 15:13:40 +0200 Subject: [PATCH 02/21] Prepare tiles updater in webmapper to have unupdated base tiles. --- cmd/mtwebmapper/tilesupdater.go | 54 ++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/cmd/mtwebmapper/tilesupdater.go b/cmd/mtwebmapper/tilesupdater.go index 1e91a3a..5cd8cc1 100644 --- a/cmd/mtwebmapper/tilesupdater.go +++ b/cmd/mtwebmapper/tilesupdater.go @@ -48,6 +48,11 @@ type xz struct { Z int16 } +type xzc struct { + xz + canceled bool +} + type xzm struct { P xz Mask uint16 @@ -150,21 +155,40 @@ func (tu *tileUpdater) ServeHTTP(rw http.ResponseWriter, r *http.Request) { rw.WriteHeader(http.StatusOK) } +func extractChanges(changes map[xz]bool) []xzc { + chs := make([]xzc, len(changes)) + var i int + for ch := range changes { + chs[i] = xzc{ch, false} + i++ + } + return chs +} + +func activeChanges(changes []xzc) map[xz]bool { + chs := make(map[xz]bool) + for i := range changes { + if !changes[i].canceled { + chs[changes[i].xz] = true + } + } + return chs +} + func (tu *tileUpdater) doUpdates() { for { - var changes map[xz]bool tu.cond.L.Lock() for len(tu.changes) == 0 { tu.cond.Wait() } - changes = tu.changes + changes := extractChanges(tu.changes) tu.changes = map[xz]bool{} tu.cond.L.Unlock() baseDir := filepath.Join(tu.mapDir, "8") - jobs := make(chan xz) + jobs := make(chan *xzc) var done sync.WaitGroup for i, n := 0, common.Min(tu.workers, len(changes)); i < n; i++ { @@ -182,17 +206,25 @@ func (tu *tileUpdater) doUpdates() { go tu.updateBaseTiles(jobs, btc, &done) } - parentJobs := make(map[xz]uint16) - - for c := range changes { + for i := range changes { //log.Printf("job: %+v\n", c) - jobs <- c - pxz := c.parent() - parentJobs[pxz.P] |= pxz.Mask + jobs <- &changes[i] } close(jobs) done.Wait() + actChs := activeChanges(changes) + + if len(actChs) == 0 { + continue + } + + parentJobs := make(map[xz]uint16) + for c := range actChs { + pxz := c.parent() + parentJobs[pxz.P] |= pxz.Mask + } + for level := 7; level >= 0; level-- { pJobs := make(chan xzm) for i, n := 0, common.Min(len(parentJobs), tu.workers); i < n; i++ { @@ -211,7 +243,7 @@ func (tu *tileUpdater) doUpdates() { } if tu.btu != nil { - tu.btu.BaseTilesUpdated(changes) + tu.btu.BaseTilesUpdated(actChs) } } } @@ -294,7 +326,7 @@ func (tu *tileUpdater) updatePyramidTile(scratch, resized *image.RGBA, level int } func (tu *tileUpdater) updateBaseTiles( - jobs chan xz, + jobs chan *xzc, btc *common.BaseTileCreator, done *sync.WaitGroup) { defer btc.Close() From 0900bd16ce1bed161f34b16aef0ad3613b2de19f Mon Sep 17 00:00:00 2001 From: "Sascha L. Teichmann" Date: Sun, 8 May 2016 15:38:50 +0200 Subject: [PATCH 03/21] Finished logic to supress pyramid regeneration in mtwebmapper. Untested. --- cmd/mtseeder/baselevel.go | 2 +- cmd/mtwebmapper/tilesupdater.go | 10 ++++++++-- common/basetilecreator.go | 33 +++++++++++++++++++++------------ 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/cmd/mtseeder/baselevel.go b/cmd/mtseeder/baselevel.go index cbd8ebf..7c4c839 100644 --- a/cmd/mtseeder/baselevel.go +++ b/cmd/mtseeder/baselevel.go @@ -68,7 +68,7 @@ func createBaseLevel( btc := common.NewBaseTileCreator( client, colors, bg, int16(yMin), int16(yMax), - transparent, baseDir, false) + transparent, baseDir, nil) go createTiles(btc, jobs, &done) } diff --git a/cmd/mtwebmapper/tilesupdater.go b/cmd/mtwebmapper/tilesupdater.go index 5cd8cc1..4d360e0 100644 --- a/cmd/mtwebmapper/tilesupdater.go +++ b/cmd/mtwebmapper/tilesupdater.go @@ -177,6 +177,8 @@ func activeChanges(changes []xzc) map[xz]bool { func (tu *tileUpdater) doUpdates() { + bth := common.NewBaseTileHash() + for { tu.cond.L.Lock() for len(tu.changes) == 0 { @@ -201,7 +203,7 @@ func (tu *tileUpdater) doUpdates() { btc := common.NewBaseTileCreator( client, tu.colors, tu.bg, tu.yMin, tu.yMax, - tu.transparent, baseDir, true) + tu.transparent, baseDir, bth.Update) done.Add(1) go tu.updateBaseTiles(jobs, btc, &done) } @@ -334,8 +336,12 @@ func (tu *tileUpdater) updateBaseTiles( for job := range jobs { xz := job.dequantize() //log.Printf("%d/%d %d/%d", x, z, job.X, job.Z) - if err := btc.CreateTile(xz.X-1, xz.Z-1, int(job.X), int(job.Z)); err != nil { + updated, err := btc.CreateTile(xz.X-1, xz.Z-1, int(job.X), int(job.Z)) + if err != nil { log.Printf("WARN: create tile failed: %s\n", err) } + if !updated { + job.canceled = true + } } } diff --git a/common/basetilecreator.go b/common/basetilecreator.go index 35ea2f5..1b290f5 100644 --- a/common/basetilecreator.go +++ b/common/basetilecreator.go @@ -5,6 +5,7 @@ package common import ( + "image" "image/color" "io/ioutil" "log" @@ -49,6 +50,8 @@ var tileDepths = [...][2]int16{ var BackgroundColor = color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff} +type BaseTileUpdateFunc func(x, y int, img image.Image) bool + type BaseTileCreator struct { client *RedisClient colors *Colors @@ -57,7 +60,7 @@ type BaseTileCreator struct { yMin int16 yMax int16 baseDir string - update bool + update BaseTileUpdateFunc emptyImage []byte bg color.RGBA } @@ -69,7 +72,7 @@ func NewBaseTileCreator( yMin, yMax int16, transparent bool, baseDir string, - update bool) *BaseTileCreator { + update BaseTileUpdateFunc) *BaseTileCreator { renderer := NewRenderer(tileWidth, tileHeight, transparent) yMin, yMax = Order16(yMin, yMax) return &BaseTileCreator{ @@ -88,7 +91,7 @@ func (btc *BaseTileCreator) Close() error { return btc.client.Close() } -func (btc *BaseTileCreator) CreateTile(x, z int16, i, j int) error { +func (btc *BaseTileCreator) CreateTile(x, z int16, i, j int) (bool, error) { btc.renderer.Reset() btc.renderer.SetPos(x, z) btc.yOrder.Reset() @@ -128,7 +131,7 @@ func (btc *BaseTileCreator) CreateTile(x, z int16, i, j int) error { c2.Z = area.Z2 + z query := Cuboid{P1: c1, P2: c2} if err := btc.client.QueryCuboid(query, drawBlock); err != nil { - return err + return false, err } if err := btc.yOrder.Drain(btc.colors); err != nil { log.Printf("WARN: rendering block failed: %s\n", err) @@ -140,28 +143,34 @@ func (btc *BaseTileCreator) CreateTile(x, z int16, i, j int) error { path := filepath.Join(btc.baseDir, strconv.Itoa(i), strconv.Itoa(j)+".png") // Empty images are likely to be produced during seeding. - if !btc.update && btc.renderer.IsEmpty() { + if btc.update == nil && btc.renderer.IsEmpty() { // To avoid redundant encoding cache the resulting empty image. if btc.emptyImage == nil { var err error m := BackgroundImage((tileWidth-2)*16, (tileHeight-2)*16, btc.bg) if btc.emptyImage, err = EncodeToMem(m); err != nil { - return err + return false, err } } //log.Printf("Writing empty (%d, %d) to file %s\n", x, z, path) - return ioutil.WriteFile(path, btc.emptyImage, 0666) + return true, ioutil.WriteFile(path, btc.emptyImage, 0666) } image := btc.renderer.CreateShadedImage( 16, 16, (tileWidth-2)*16, (tileHeight-2)*16, btc.colors, btc.bg) - log.Printf("Writing (%d, %d) to file %s\n", x, z, path) - - if !btc.update { - return SaveAsPNG(path, image) + if btc.update == nil { + log.Printf("Writing (%d, %d) to file %s.\n", x, z, path) + return true, SaveAsPNG(path, image) } - return SaveAsPNGAtomic(path, image) + if btc.update(i, j, image) { + log.Printf("Writing (%d, %d) to file %s.\n", x, z, path) + return true, SaveAsPNGAtomic(path, image) + } + + log.Printf("File %s does not change.\n", path) + + return false, nil } From 792aae99d7793b3e76eb472fcfa5dfb82e46ce51 Mon Sep 17 00:00:00 2001 From: "Sascha L. Teichmann" Date: Sun, 8 May 2016 17:22:04 +0200 Subject: [PATCH 04/21] Simplified tile hash. --- common/basetilehash.go | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/common/basetilehash.go b/common/basetilehash.go index 506c464..19e0146 100644 --- a/common/basetilehash.go +++ b/common/basetilehash.go @@ -10,32 +10,27 @@ import ( "sync" ) -type btPos struct { - x int - y int -} - type BaseTileHash struct { // XXX: Maybe use some kind of LRU cache instead? - hashes map[btPos][]byte + hashes map[struct{ x, y int }][]byte sync.Mutex } func NewBaseTileHash() *BaseTileHash { - return &BaseTileHash{hashes: map[btPos][]byte{}} + return &BaseTileHash{hashes: map[struct{ x, y int }][]byte{}} } func (bth *BaseTileHash) Update(x, y int, img image.Image) bool { hash := SHA1Image(img) - key := btPos{x, y} + key := struct{ x, y int }{x, y} bth.Lock() defer bth.Unlock() if old, found := bth.hashes[key]; found { - equals := bytes.Equal(old, hash) - if !equals { + if !bytes.Equal(old, hash) { bth.hashes[key] = hash + return true } - return !equals + return false } bth.hashes[key] = hash return true From 398c52f8a5a2b94364dd44a984a1004d9de22fe7 Mon Sep 17 00:00:00 2001 From: "Sascha L. Teichmann" Date: Sun, 8 May 2016 17:33:51 +0200 Subject: [PATCH 05/21] Removed unnecessary general code. --- common/basetilecreator.go | 5 ++--- common/basetilehash.go | 4 +--- common/image.go | 39 ++------------------------------------- 3 files changed, 5 insertions(+), 43 deletions(-) diff --git a/common/basetilecreator.go b/common/basetilecreator.go index 1b290f5..a53ba05 100644 --- a/common/basetilecreator.go +++ b/common/basetilecreator.go @@ -5,7 +5,6 @@ package common import ( - "image" "image/color" "io/ioutil" "log" @@ -50,7 +49,7 @@ var tileDepths = [...][2]int16{ var BackgroundColor = color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff} -type BaseTileUpdateFunc func(x, y int, img image.Image) bool +type BaseTileUpdateFunc func(x, y int, hash []byte) bool type BaseTileCreator struct { client *RedisClient @@ -165,7 +164,7 @@ func (btc *BaseTileCreator) CreateTile(x, z int16, i, j int) (bool, error) { return true, SaveAsPNG(path, image) } - if btc.update(i, j, image) { + if btc.update(i, j, SHA1Image(image)) { log.Printf("Writing (%d, %d) to file %s.\n", x, z, path) return true, SaveAsPNGAtomic(path, image) } diff --git a/common/basetilehash.go b/common/basetilehash.go index 19e0146..4adbefa 100644 --- a/common/basetilehash.go +++ b/common/basetilehash.go @@ -6,7 +6,6 @@ package common import ( "bytes" - "image" "sync" ) @@ -20,8 +19,7 @@ func NewBaseTileHash() *BaseTileHash { return &BaseTileHash{hashes: map[struct{ x, y int }][]byte{}} } -func (bth *BaseTileHash) Update(x, y int, img image.Image) bool { - hash := SHA1Image(img) +func (bth *BaseTileHash) Update(x, y int, hash []byte) bool { key := struct{ x, y int }{x, y} bth.Lock() defer bth.Unlock() diff --git a/common/image.go b/common/image.go index 45f2a01..c97ab5f 100644 --- a/common/image.go +++ b/common/image.go @@ -112,7 +112,8 @@ func LoadPNG(path string, bg color.RGBA) image.Image { return img } -func sha1rgba(img *image.RGBA) []byte { +func SHA1Image(img *image.RGBA) []byte { + hash := sha1.New() w, h := img.Rect.Dx()*4, img.Rect.Dy() @@ -124,39 +125,3 @@ func sha1rgba(img *image.RGBA) []byte { return hash.Sum(nil) } - -func sha1uniform(img *image.Uniform) []byte { - r, g, b, a := img.C.RGBA() - return sha1.New().Sum([]byte{ - byte(r >> 16), - byte(g >> 16), - byte(b >> 16), - byte(a >> 16)}) -} - -func SHA1Image(img image.Image) []byte { - - switch i := img.(type) { - case *image.RGBA: - return sha1rgba(i) - case *image.Uniform: - return sha1uniform(i) - } - - log.Println("WARN: slow path for SHA1Image") - - hash := sha1.New() - bounds := img.Bounds() - var pix [4]byte - for x := bounds.Min.X; x <= bounds.Max.X; x++ { - for y := bounds.Min.Y; y <= bounds.Max.Y; y++ { - r, g, b, a := img.At(x, y).RGBA() - pix[0] = byte(r >> 16) - pix[1] = byte(g >> 16) - pix[2] = byte(b >> 16) - pix[3] = byte(a >> 16) - hash.Write(pix[:]) - } - } - return hash.Sum(nil) -} From fad16a58e88f6b3587a6590461569525108b9786 Mon Sep 17 00:00:00 2001 From: "Sascha L. Teichmann" Date: Sun, 8 May 2016 17:43:48 +0200 Subject: [PATCH 06/21] Write tile coord if it is unchanged not the file path to the base tile. --- common/basetilecreator.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/basetilecreator.go b/common/basetilecreator.go index a53ba05..ee70a79 100644 --- a/common/basetilecreator.go +++ b/common/basetilecreator.go @@ -169,7 +169,7 @@ func (btc *BaseTileCreator) CreateTile(x, z int16, i, j int) (bool, error) { return true, SaveAsPNGAtomic(path, image) } - log.Printf("File %s does not change.\n", path) + log.Printf("(%d, %d) is unchanged.\n", x, z) return false, nil } From b59f849d73c11a3b617512762b1ea5c90fcc52b0 Mon Sep 17 00:00:00 2001 From: "Sascha L. Teichmann" Date: Sun, 8 May 2016 18:03:13 +0200 Subject: [PATCH 07/21] Make go vet happy. --- common/image.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/image.go b/common/image.go index c97ab5f..b2feeee 100644 --- a/common/image.go +++ b/common/image.go @@ -45,7 +45,7 @@ func nextSuffix() string { func EncodeToMem(img image.Image) ([]byte, error) { var buf bytes.Buffer - enc := png.Encoder{png.BestCompression} + enc := png.Encoder{CompressionLevel: png.BestCompression} if err := enc.Encode(&buf, img); err != nil { return nil, err } From da2a6a82d4b409bb4a5a96539ca0cc7e23913d7b Mon Sep 17 00:00:00 2001 From: "Sascha L. Teichmann" Date: Sun, 8 May 2016 22:30:00 +0200 Subject: [PATCH 08/21] Simplified tiles update processing and forwarding. --- cmd/mtwebmapper/forwardupdates.go | 12 ++++-------- cmd/mtwebmapper/tilesupdater.go | 12 ++++++------ 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/cmd/mtwebmapper/forwardupdates.go b/cmd/mtwebmapper/forwardupdates.go index c2b0dea..d492d9d 100644 --- a/cmd/mtwebmapper/forwardupdates.go +++ b/cmd/mtwebmapper/forwardupdates.go @@ -27,7 +27,7 @@ type connection struct { } type msg struct { - tiles map[xz]bool + tiles []xz pls []*player } @@ -57,12 +57,8 @@ func (wsf *websocketForwarder) run() { } encMsg := map[string]interface{}{} - if tiles := message.tiles; tiles != nil { - xzs := make([]xz, 0, len(tiles)) - for xz := range tiles { - xzs = append(xzs, xz) - } - encMsg["tiles"] = xzs + if message.tiles != nil { + encMsg["tiles"] = message.tiles } if message.pls != nil { @@ -101,7 +97,7 @@ func (wsf *websocketForwarder) ServeHTTP(rw http.ResponseWriter, r *http.Request c.reader() } -func (wsf *websocketForwarder) BaseTilesUpdated(changes map[xz]bool) { +func (wsf *websocketForwarder) BaseTilesUpdated(changes []xz) { wsf.broadcast <- msg{tiles: changes} } diff --git a/cmd/mtwebmapper/tilesupdater.go b/cmd/mtwebmapper/tilesupdater.go index 4d360e0..8b09a0e 100644 --- a/cmd/mtwebmapper/tilesupdater.go +++ b/cmd/mtwebmapper/tilesupdater.go @@ -25,7 +25,7 @@ import ( ) type baseTilesUpdates interface { - BaseTilesUpdated(map[xz]bool) + BaseTilesUpdated([]xz) } type tileUpdater struct { @@ -165,11 +165,11 @@ func extractChanges(changes map[xz]bool) []xzc { return chs } -func activeChanges(changes []xzc) map[xz]bool { - chs := make(map[xz]bool) +func activeChanges(changes []xzc) []xz { + chs := make([]xz, 0, len(changes)) for i := range changes { if !changes[i].canceled { - chs[changes[i].xz] = true + chs = append(chs, changes[i].xz) } } return chs @@ -222,8 +222,8 @@ func (tu *tileUpdater) doUpdates() { } parentJobs := make(map[xz]uint16) - for c := range actChs { - pxz := c.parent() + for i := range actChs { + pxz := actChs[i].parent() parentJobs[pxz.P] |= pxz.Mask } From 496a60aa291eb8b1bf979da7188f42c2b718e380 Mon Sep 17 00:00:00 2001 From: "Sascha L. Teichmann" Date: Sun, 8 May 2016 23:07:19 +0200 Subject: [PATCH 09/21] Simplified mask embedding in parent calculation of pyramid tiles. --- cmd/mtwebmapper/tilesupdater.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/cmd/mtwebmapper/tilesupdater.go b/cmd/mtwebmapper/tilesupdater.go index 8b09a0e..5150394 100644 --- a/cmd/mtwebmapper/tilesupdater.go +++ b/cmd/mtwebmapper/tilesupdater.go @@ -54,7 +54,7 @@ type xzc struct { } type xzm struct { - P xz + xz Mask uint16 } @@ -70,8 +70,8 @@ func (c xz) parent() xzm { xp, xr := c.X>>1, uint16(c.X&1) zp, zr := c.Z>>1, uint16(c.Z&1) return xzm{ - P: xz{X: xp, Z: zp}, - Mask: 1 << (zr<<1 | xr)} + xz{X: xp, Z: zp}, + 1 << (zr<<1 | xr)} } func newTileUpdater( @@ -224,7 +224,7 @@ func (tu *tileUpdater) doUpdates() { parentJobs := make(map[xz]uint16) for i := range actChs { pxz := actChs[i].parent() - parentJobs[pxz.P] |= pxz.Mask + parentJobs[pxz.xz] |= pxz.Mask } for level := 7; level >= 0; level-- { @@ -235,9 +235,9 @@ func (tu *tileUpdater) doUpdates() { } ppJobs := make(map[xz]uint16) for c, mask := range parentJobs { - pJobs <- xzm{P: c, Mask: mask} + pJobs <- xzm{c, mask} pxz := c.parent() - ppJobs[pxz.P] |= pxz.Mask + ppJobs[pxz.xz] |= pxz.Mask } close(pJobs) done.Wait() @@ -291,8 +291,8 @@ func (tu *tileUpdater) updatePyramidTile(scratch, resized *image.RGBA, level int origPath := filepath.Join( tu.mapDir, strconv.Itoa(level), - strconv.Itoa(int(j.P.X)), - strconv.Itoa(int(j.P.Z))+".png") + strconv.Itoa(int(j.X)), + strconv.Itoa(int(j.Z))+".png") sr := resized.Bounds() levelDir := strconv.Itoa(level + 1) @@ -300,7 +300,7 @@ func (tu *tileUpdater) updatePyramidTile(scratch, resized *image.RGBA, level int if j.Mask&(1< Date: Sun, 8 May 2016 23:12:29 +0200 Subject: [PATCH 10/21] Removed obsolete comment. --- cmd/mtwebmapper/tilesupdater.go | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd/mtwebmapper/tilesupdater.go b/cmd/mtwebmapper/tilesupdater.go index 5150394..f22cbca 100644 --- a/cmd/mtwebmapper/tilesupdater.go +++ b/cmd/mtwebmapper/tilesupdater.go @@ -209,7 +209,6 @@ func (tu *tileUpdater) doUpdates() { } for i := range changes { - //log.Printf("job: %+v\n", c) jobs <- &changes[i] } close(jobs) From 4c24a016074316d31af4690fa57a232e84a26ab5 Mon Sep 17 00:00:00 2001 From: "Sascha L. Teichmann" Date: Sun, 8 May 2016 23:20:51 +0200 Subject: [PATCH 11/21] Use empty structs instead of bools in changes. --- cmd/mtwebmapper/tilesupdater.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cmd/mtwebmapper/tilesupdater.go b/cmd/mtwebmapper/tilesupdater.go index f22cbca..cc44edb 100644 --- a/cmd/mtwebmapper/tilesupdater.go +++ b/cmd/mtwebmapper/tilesupdater.go @@ -29,7 +29,7 @@ type baseTilesUpdates interface { } type tileUpdater struct { - changes map[xz]bool + changes map[xz]struct{} btu baseTilesUpdates mapDir string redisAddress string @@ -89,7 +89,7 @@ func newTileUpdater( mapDir: mapDir, redisAddress: redisAddress, ips: ips, - changes: map[xz]bool{}, + changes: map[xz]struct{}{}, colors: colors, bg: bg, yMin: int16(yMin), @@ -146,7 +146,7 @@ func (tu *tileUpdater) ServeHTTP(rw http.ResponseWriter, r *http.Request) { if len(newChanges) > 0 { tu.cond.L.Lock() for _, c := range newChanges { - tu.changes[c.quantize()] = true + tu.changes[c.quantize()] = struct{}{} } tu.cond.L.Unlock() tu.cond.Signal() @@ -155,7 +155,7 @@ func (tu *tileUpdater) ServeHTTP(rw http.ResponseWriter, r *http.Request) { rw.WriteHeader(http.StatusOK) } -func extractChanges(changes map[xz]bool) []xzc { +func extractChanges(changes map[xz]struct{}) []xzc { chs := make([]xzc, len(changes)) var i int for ch := range changes { @@ -185,7 +185,7 @@ func (tu *tileUpdater) doUpdates() { tu.cond.Wait() } changes := extractChanges(tu.changes) - tu.changes = map[xz]bool{} + tu.changes = map[xz]struct{}{} tu.cond.L.Unlock() baseDir := filepath.Join(tu.mapDir, "8") From 5d1be98ae56b117712c975756bfbd7640bffb537 Mon Sep 17 00:00:00 2001 From: "Sascha L. Teichmann" Date: Sun, 8 May 2016 23:27:11 +0200 Subject: [PATCH 12/21] Cosmetics. --- cmd/mtwebmapper/tilesupdater.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/mtwebmapper/tilesupdater.go b/cmd/mtwebmapper/tilesupdater.go index cc44edb..a4f9d2f 100644 --- a/cmd/mtwebmapper/tilesupdater.go +++ b/cmd/mtwebmapper/tilesupdater.go @@ -179,6 +179,8 @@ func (tu *tileUpdater) doUpdates() { bth := common.NewBaseTileHash() + baseDir := filepath.Join(tu.mapDir, "8") + for { tu.cond.L.Lock() for len(tu.changes) == 0 { @@ -188,8 +190,6 @@ func (tu *tileUpdater) doUpdates() { tu.changes = map[xz]struct{}{} tu.cond.L.Unlock() - baseDir := filepath.Join(tu.mapDir, "8") - jobs := make(chan *xzc) var done sync.WaitGroup From 69ee5af388787e2c64fb5802103671cbe68088bd Mon Sep 17 00:00:00 2001 From: "Sascha L. Teichmann" Date: Tue, 10 May 2016 18:57:14 +0200 Subject: [PATCH 13/21] Implemented a simple LRU cache with max 256 entries for base tile hasing. TODO: Improve test. --- cmd/mtwebmapper/web/index.html | 2 +- common/basetilehash.go | 68 ++++++++++++++++++++++++++++++---- common/basetilehash_test.go | 27 ++++++++++++++ 3 files changed, 89 insertions(+), 8 deletions(-) create mode 100644 common/basetilehash_test.go diff --git a/cmd/mtwebmapper/web/index.html b/cmd/mtwebmapper/web/index.html index 85345af..dd26a91 100644 --- a/cmd/mtwebmapper/web/index.html +++ b/cmd/mtwebmapper/web/index.html @@ -48,7 +48,7 @@