From acd2e0c8d040ba3f136cdff728b2f1831b4bcaac Mon Sep 17 00:00:00 2001 From: NatureFreshMilk Date: Thu, 17 Jan 2019 11:20:03 +0100 Subject: [PATCH] working tile stitching --- tiledb/accessor.go | 1 + tiledb/sqlite.go | 11 +++ tilerenderer/renderer.go | 130 ++++++++++++++++++++++++++++++++-- tilerenderer/renderer_test.go | 15 +++- 4 files changed, 150 insertions(+), 7 deletions(-) diff --git a/tiledb/accessor.go b/tiledb/accessor.go index eecda5d..3e20c53 100644 --- a/tiledb/accessor.go +++ b/tiledb/accessor.go @@ -14,4 +14,5 @@ type DBAccessor interface { Migrate() error GetTile(pos coords.TileCoords) (*Tile, error) SetTile(tile *Tile) error + RemoveTile(pos coords.TileCoords) error } diff --git a/tiledb/sqlite.go b/tiledb/sqlite.go index 2440cbc..9d6be10 100644 --- a/tiledb/sqlite.go +++ b/tiledb/sqlite.go @@ -90,6 +90,17 @@ func (db *Sqlite3Accessor) SetTile(tile *Tile) error { return err } +const removeTileQuery = ` +delete from tiles +where x = ? and y = ? and zoom = ? and layerid = ? +` + +func (db *Sqlite3Accessor) RemoveTile(pos coords.TileCoords) error { + _, err := db.db.Exec(removeTileQuery, pos.X, pos.Y, pos.Zoom, pos.LayerId) + return err +} + + func NewSqliteAccessor(filename string) (*Sqlite3Accessor, error) { db, err := sql.Open("sqlite3", filename) if err != nil { diff --git a/tilerenderer/renderer.go b/tilerenderer/renderer.go index e28fcba..98c0e0c 100644 --- a/tilerenderer/renderer.go +++ b/tilerenderer/renderer.go @@ -1,29 +1,147 @@ package tilerenderer import ( + "errors" "image" + "bytes" + "image/png" + "image/draw" "mapserver/coords" "mapserver/mapblockrenderer" + "mapserver/layerconfig" + "mapserver/tiledb" ) type TileRenderer struct { mapblockrenderer *mapblockrenderer.MapBlockRenderer + layers []layerconfig.Layer + tdb tiledb.DBAccessor } -func NewTileRenderer(mapblockrenderer *mapblockrenderer.MapBlockRenderer) *TileRenderer { +func NewTileRenderer(mapblockrenderer *mapblockrenderer.MapBlockRenderer, + tdb tiledb.DBAccessor, + layers []layerconfig.Layer) *TileRenderer { + return &TileRenderer{ mapblockrenderer: mapblockrenderer, + layers: layers, + tdb: tdb, } } -//TODO layerConfig -func (tr *TileRenderer) Render(tc coords.TileCoords) (*image.NRGBA, error) { +const ( + IMG_SIZE = 256 +) + +func (tr *TileRenderer) Render(tc coords.TileCoords) ([]byte, error) { + + //Check cache + tile, err := tr.tdb.GetTile(tc) + if err != nil { + return nil, err + } + + if tile == nil { + //No tile in db + img, err := tr.RenderImage(tc) + + if err != nil { + return nil, err + } + + buf := new(bytes.Buffer) + png.Encode(buf, img) + + return buf.Bytes(), nil + } + + return tile.Data, nil +} + +func (tr *TileRenderer) RenderImage(tc coords.TileCoords) (*image.NRGBA, error) { + + cachedtile, err := tr.tdb.GetTile(tc) + if err != nil { + return nil, err + } + + if cachedtile != nil { + reader := bytes.NewReader(cachedtile.Data) + cachedimg, err := png.Decode(reader) + if err != nil { + return nil, err + } + + return cachedimg.(*image.NRGBA), nil + } + + var layer *layerconfig.Layer + + for _, l := range(tr.layers) { + if l.Id == tc.LayerId { + layer = &l + } + } + + if layer == nil { + return nil, errors.New("No layer found") + } + if tc.Zoom == 13 { //max zoomed in on mapblock level - //mbr := coords.GetMapBlockRangeFromTile(tc, 0) - //return tr.mapblockrenderer.Render() + mbr := coords.GetMapBlockRangeFromTile(tc, 0) + mbr.Pos1.Y = layer.From + mbr.Pos2.Y = layer.To + return tr.mapblockrenderer.Render(mbr.Pos1, mbr.Pos2) } - return nil, nil + if tc.Zoom > 13 || tc.Zoom < 1 { + return nil, errors.New("Invalid zoom") + } + + //zoom 1-12 + quads := tc.GetZoomedQuadrantsFromTile() + + upperLeft, err := tr.RenderImage(quads.UpperLeft) + if err != nil { + return nil, err + } + + upperRight, err := tr.RenderImage(quads.UpperRight) + if err != nil { + return nil, err + } + + lowerLeft, err := tr.RenderImage(quads.LowerLeft) + if err != nil { + return nil, err + } + + lowerRight, err := tr.RenderImage(quads.LowerRight) + if err != nil { + return nil, err + } + + img := image.NewNRGBA( + image.Rectangle{ + image.Point{0, 0}, + image.Point{IMG_SIZE, IMG_SIZE}, + }, + ) + + rect := image.Rect(0, 0, 128, 128) + draw.Draw(img, rect, upperLeft, image.ZP, draw.Src) + + rect = image.Rect(128, 0, 256, 128) + draw.Draw(img, rect, upperRight, image.ZP, draw.Src) + + rect = image.Rect(0, 128, 128, 256) + draw.Draw(img, rect, lowerLeft, image.ZP, draw.Src) + + rect = image.Rect(128, 128, 256, 256) + draw.Draw(img, rect, lowerRight, image.ZP, draw.Src) + + + return img, nil } diff --git a/tilerenderer/renderer_test.go b/tilerenderer/renderer_test.go index e704cae..6cfec79 100644 --- a/tilerenderer/renderer_test.go +++ b/tilerenderer/renderer_test.go @@ -9,8 +9,11 @@ import ( "mapserver/testutils" "mapserver/mapblockrenderer" "mapserver/coords" + "mapserver/layerconfig" + "mapserver/tiledb" "os" "testing" + "bytes" ) func TestTileRender(t *testing.T) { @@ -20,6 +23,7 @@ func TestTileRender(t *testing.T) { if err != nil { panic(err) } + defer os.Remove(tmpfile.Name()) testutils.CreateTestDatabase(tmpfile.Name()) @@ -42,7 +46,13 @@ func TestTileRender(t *testing.T) { r := mapblockrenderer.NewMapBlockRenderer(cache, c) - tr := NewTileRenderer(r) + tiletmpfile, err := ioutil.TempFile("", "TestTileRenderTiles.*.sqlite") + defer os.Remove(tiletmpfile.Name()) + + tdb, _ := tiledb.NewSqliteAccessor(tiletmpfile.Name()) + tdb.Migrate() + + tr := NewTileRenderer(r, tdb, layerconfig.DefaultLayers) if tr == nil { panic("no renderer") @@ -59,4 +69,7 @@ func TestTileRender(t *testing.T) { if data == nil { panic("no data") } + + f, _ := os.Create("../output/0_0_12.png") + bytes.NewReader(data).WriteTo(f) }