Commit 0a21764a authored by Hoek, Steven's avatar Hoek, Steven
Browse files

Tile stitcher was added to the module tilelib

parent c485e672
...@@ -41,6 +41,7 @@ class RioRaster(Raster, GridEnvelope2D): ...@@ -41,6 +41,7 @@ class RioRaster(Raster, GridEnvelope2D):
print('File path cannot be an empty string (method __init__).') print('File path cannot be an empty string (method __init__).') = os.path.basename(filepath); = os.path.basename(filepath);
self.folder = os.path.dirname(filepath); self.folder = os.path.dirname(filepath);
self.datatype = datatype[0]
# overrides same method of Raster # overrides same method of Raster
def getWorldFileExt(self): def getWorldFileExt(self):
...@@ -8,6 +8,7 @@ from ..toolbox.striplib import StripManager ...@@ -8,6 +8,7 @@ from ..toolbox.striplib import StripManager
from ..formats.const import constants as const from ..formats.const import constants as const
from math import ceil from math import ceil
import numpy as np import numpy as np
import os.path
__author__ = "Steven B. Hoek" __author__ = "Steven B. Hoek"
...@@ -212,4 +213,129 @@ class TileIterator: ...@@ -212,4 +213,129 @@ class TileIterator:
result = result =
else: else:
raise StopIteration raise StopIteration
return result return result
\ No newline at end of file
class TileStitcher():
Class that can join tiles together again - even if they have overlap. Assumption is that the last part
of the file names - before the extension - contain row and column indices which are separated by under-
scores or another separator. Assumption is that all tiles are of the same width and height and all have
the same overlaps.
__ncols = 1
__nrows = 1
__tilewidth = 1
__tileheight = 1
__coloverlap = 0
__rowoverlpa = 0
__tilelist = []
__option = "topleft"
def __init__(self, tilewidth, tileheight, coloverlap=0, rowoverlap=0, option="bottomright"):
Option "bottomright" is the default, meaning that the values from overlapping edges coming from the left and from
the top are basically ignored. In other words: values from the tiles located to the right and below are used in
the output raster. For option "topleft", values from the overlapping edges belonging to tiles located to the left
and above are rather used instead of the values belonging to the tiles located to the right and below.For option
"average" the values in the output raster are obtained by taking the average of all the values contained in the
overlapping edges.
self.__tilewidth = tilewidth
self.__tileheight = tileheight
self.__coloverlap = coloverlap
self.__rowoverlap = rowoverlap
self.__option = option # or average or topleft
def add(self, filename, separator="_"):
# Initialise
if not os.path.exists(filename):
raise ValueError("File %s does not exist!" % filename)
# Assume that the last 2 underscores in the filename are to separate 2-digit row and column indices (zero-based)
basename = os.path.splitext(os.path.basename(filename))[0]
if basename.count(separator) < 2:
raise ValueError("Filename %s does not contain enough separator characters!" % filename)
parts = basename.split("_")
rowidx = int(parts[-2])
colidx = int(parts[-1])
self.__tilelist.append({"rowidx": rowidx, "colidx":colidx, "filename":filename})
def process(self, RasterClass, datatype, outputraster):
After all the tiles have been added, this method can be invoked with an outputraster
which is still closed!
# Check whether output raster is already open
if (outputraster.nrows != 1) and (outputraster.ncols != 1):
raise Warning("Output raster seems to be open already!")
# Determine minimum and maximum rowidx etc.
tmplist = [t["rowidx"] for t in self.__tilelist]
minrowidx, maxrowidx = min(tmplist), max(tmplist)
self.__nrows = maxrowidx - minrowidx + 1
# Check that the list of tiles is complete
for i in range(self.__nrows):
tiles = list(filter(lambda t: t["rowidx"] == i, self.__tilelist))
if len(tiles) < (maxrowidx - minrowidx + 1):
raise ValueError("Row of tiles with index %s is not complete!" % i)
# Determine minimum and maximum colidx etc.
tmplist = [t["colidx"] for t in self.__tilelist]
mincolidx, maxcolidx = min(tmplist), max(tmplist)
self.__ncols = maxcolidx - mincolidx + 1
# Now loop over the tiles; in principle i and k should be zero-based
xul, yul = 0.0, 0.0
data = np.empty((self.__tileheight, self.__ncols * self.__tilewidth))
for i in range(minrowidx, maxrowidx + 1):
for k in range(mincolidx, maxcolidx + 1):
# Get hold of the right tile
tile = list(filter(lambda t: t["rowidx"] == i and t["colidx"] == k, self.__tilelist))[0]
fn = tile["filename"]
if not os.path.exists(fn): raise ValueError("File %s does not exist" % fn)
rg = RasterClass(fn, datatype)'r')
# Initialise the array data further
if (i == minrowidx) and (k == mincolidx):
data = data.astype(rg.datatype)
nodatavalue = rg.nodatavalue
# Check that the tiles do indeed land at the right place in the tile grid
if (i == minrowidx) and (k == mincolidx):
# Get the coordinates of the upper left corner
xul, yul = rg.xll, rg.yll + (self.__tileheight + self.__rowoverlap) * rg.dy
# Check the coordinates
eps = 0.00001
A = abs(rg.xll - (k-mincolidx)*self.__tilewidth*rg.dx - xul) > eps
B = abs(rg.yll + (self.__tileheight + self.__rowoverlap)*rg.dy + (i-minrowidx)*self.__tileheight*rg.dy - yul) > eps
if A or B: raise Exception("Tile with row index %s and column index %s is not georeferenced correctly!" % (i, k))
# Add the data from the tile at the right location
for j in range(rg.nrows - self.__rowoverlap):
line =
data[j, k*self.__tilewidth:(k+1)*self.__tilewidth] = line[0:self.__tilewidth]
# When the last column is filled, write the strip to disk
if (k == maxcolidx):
if (i == minrowidx):
ncols = self.__ncols * self.__tilewidth
nrows = self.__nrows * self.__tileheight
yll = yul - nrows * rg.dy'w', ncols, nrows, xul, yll, rg.cellsize, rg.nodatavalue)
for j in range(self.__tileheight):
line = data[j, :]
# Prepare for the next loop
# Prepare for the next loop
if not outputraster is None:
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment