-
Notifications
You must be signed in to change notification settings - Fork 0
/
slicer.go
184 lines (158 loc) · 5.74 KB
/
slicer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
package wgs84tiler
import (
"fmt"
"image"
"image/color"
"math"
"os"
"strconv"
"sync"
"time"
"github.com/disintegration/imaging"
)
// WGS84Bounds boundaries of the image in WSG84 world
//
// Example :
// myBounds = WGS84Bounds{top: 48.8687073004617, right: 2.15657022586739, left: 2.14840505163567,bottom: 48.8651234503015}
type WGS84Bounds struct {
Top float64 // top holds the top coordinate in WGS84 latitude
Right float64 // holds the right coordinate in WGS84 longitude
Left float64 // left holds the coordinate in WGS84 longitude
Bottom float64 // bottom holds the coordinate in WGS84 latitude
}
type dimension struct {
width int
height int
}
// tilebounds wgs84Bounds in tile coordinate
type tilebounds struct {
top int
bottom int
left int
right int
}
// shift offset image in pixels from top and left tiles
type shift struct {
left int
top int
}
// portion of the original image to extract
type extract struct {
width int
height int
left int
top int
}
// TILESIZE the slippy tilesize value in pixels
const TILESIZE = 256
var virgin = imaging.New(TILESIZE, TILESIZE, color.NRGBA{128, 128, 128, 0})
func getTargetImageSize(imageSource image.Image, wgs84Bounds WGS84Bounds, zoom int) dimension {
imageSouceBounds := imageSource.Bounds()
x1 := tile2long(long2tile(wgs84Bounds.Left, zoom), zoom)
x2 := tile2long(long2tile(wgs84Bounds.Left, zoom)+1, zoom)
lngperpx := (x2 - x1) / float64(TILESIZE)
fileSizeInLng := wgs84Bounds.Right - wgs84Bounds.Left
newWidth := math.Ceil(fileSizeInLng / lngperpx)
newHeight := math.Ceil(newWidth * float64(imageSouceBounds.Max.Y) / float64(imageSouceBounds.Max.X))
return dimension{width: int(newWidth), height: int(newHeight)}
}
func getTargetTilesBounds(wgs84Bounds WGS84Bounds, zoom int) (tilebounds, shift) {
// bounds in tiles
var tilebounds tilebounds
tilebounds.top = lat2tile(wgs84Bounds.Top, zoom)
tilebounds.bottom = lat2tile(wgs84Bounds.Bottom, zoom)
tilebounds.left = long2tile(wgs84Bounds.Left, zoom)
tilebounds.right = long2tile(wgs84Bounds.Right, zoom)
// tiles in coord
tileTopLat := tile2lat(tilebounds.top, zoom)
tileTopLatNext := tile2lat(tilebounds.top+1, zoom)
tileLeftLng := tile2long(tilebounds.left, zoom)
tileLeftLngNext := tile2long(tilebounds.left+1, zoom)
var tileshift shift
tileshift.top = int(((wgs84Bounds.Top - tileTopLat) / (tileTopLatNext - tileTopLat)) * float64(TILESIZE))
tileshift.left = int(((wgs84Bounds.Left - tileLeftLng) / (tileLeftLngNext - tileLeftLng)) * float64(TILESIZE))
return tilebounds, tileshift
}
// SliceIt : analyse image and command the slicing
func SliceIt(imageSource image.Image, wgs84Bounds WGS84Bounds, zoom int, outputDir string) (nbtiles int, tps time.Duration) {
start := time.Now()
targetImageSize := getTargetImageSize(imageSource, wgs84Bounds, zoom)
targetTilesBounds, targetTileShift := getTargetTilesBounds(wgs84Bounds, zoom)
resizedImage := imaging.Resize(imageSource, targetImageSize.width, targetImageSize.height, imaging.Lanczos)
sX := 0
sY := 0
nbOfSlices := (targetTilesBounds.right - targetTilesBounds.left + 1) * (targetTilesBounds.bottom - targetTilesBounds.top + 1)
var wg sync.WaitGroup
wg.Add(nbOfSlices)
for tileX := targetTilesBounds.left; tileX <= targetTilesBounds.right; tileX++ {
for tileY := targetTilesBounds.top; tileY <= targetTilesBounds.bottom; tileY++ {
// for each slice initialize extract and shift
var sliceExtract image.Rectangle
var sliceShift image.Point
var width = TILESIZE
var height = TILESIZE
var top = 0
var left = 0
if tileX == targetTilesBounds.left && tileX == targetTilesBounds.right {
//first and last tile of the row
width = targetImageSize.width
sliceShift.X = targetTileShift.left
} else if tileX == targetTilesBounds.left {
//first tile of the row
width = TILESIZE - targetTileShift.left
sliceShift.X = targetTileShift.left
} else if tileX == targetTilesBounds.right {
//last tile of the row
width = targetImageSize.width - sX*TILESIZE + targetTileShift.left
left = sX*TILESIZE - targetTileShift.left
} else {
//middle tile of the row
left = sX*TILESIZE - targetTileShift.left - 1
}
if tileY == targetTilesBounds.top && tileY == targetTilesBounds.bottom {
//first and last tile of the column
height = targetImageSize.height
sliceShift.Y = targetTileShift.top
} else if tileY == targetTilesBounds.top {
//first tile of the column
height = TILESIZE - targetTileShift.top
sliceShift.Y = targetTileShift.top
} else if tileY == targetTilesBounds.bottom {
//last tile of the column
height = targetImageSize.height - sY*TILESIZE + targetTileShift.top
top = sY*TILESIZE - targetTileShift.top
} else {
//middle tile of the column
top = sY*TILESIZE - targetTileShift.top - 1
}
sliceExtract.Min = image.Pt(left, top)
sliceExtract.Max = image.Pt(left+width, top+height)
tile := Tile{x: tileX, y: tileY}
go func() {
defer wg.Done()
makeTheSlice(resizedImage, tile, zoom, sliceExtract, sliceShift, outputDir)
}()
sY++
}
sX++
sY = 0
}
wg.Wait()
nbtiles = nbOfSlices
elapsed := time.Since(start)
fmt.Printf("%v tiles in %v \n", nbtiles, elapsed)
return nbtiles, elapsed
}
func makeTheSlice(imageSource image.Image, tile Tile, zoom int, sliceExtract image.Rectangle, sliceShift image.Point, outputDir string) {
var path = outputDir + strconv.Itoa(zoom) + "/" + strconv.Itoa(tile.x)
var file = strconv.Itoa(tile.y) + ".png"
var fulldest = path + "/" + file
_ = os.MkdirAll(path, os.ModePerm)
part := imaging.Crop(imageSource, sliceExtract)
originalContent, err := imaging.Open(fulldest)
if err != nil {
originalContent = virgin
}
dst := imaging.Paste(originalContent, part, image.Pt(sliceShift.X, sliceShift.Y))
err = imaging.Save(dst, fulldest)
}