Commit 3b2cd474 authored by Hoek, Steven's avatar Hoek, Steven
Browse files

The classes in these modules are now equipped to handle also multiband rasters

parent a3f4cc3e
......@@ -30,6 +30,10 @@ class StripManager():
self.__yll = rg.yll
self.__ncols = rg.ncols
self.__nrows = rg.nrows
if hasattr(rg, "nbands"):
self.__nbands = rg.nbands
else:
self.__nbands = 1
self.__currow = 0
self.__depth = stripheight
self.__datatype = rg.datatype
......@@ -60,18 +64,26 @@ class StripManager():
if depth > 0:
# In case buffer contains rows, prepare to add them at the beginning of the output raster
nbrows = 0
if (not buffer is None): nbrows = len(buffer)
if (not buffer is None):
if self.__nbands == 1:
nbrows = len(buffer)
else:
nbrows = buffer.shape[1]
else: depth += rowoverlap
# Prepare a new InMemoryRaster which will be able to hold the needed data
yll = self.__yll + (self.__nrows - self.__currow - depth) * self.__rg.dy
imr = InMemoryRaster("dummy_file.ext", None, self.__datatype)
imr.open('w', self.__ncols, nbrows + depth, self.__xll, yll, self.__rg.dy, self.__rg.nodatavalue)
imr.open('w', self.__ncols, nbrows + depth, self.__nbands, self.__xll, yll, self.__rg.dy, self.__rg.nodatavalue)
# If relevant, add the buffer rows first
for i in range(nbrows):
imr.writenext(buffer[i, :])
if self.__nbands == 1:
for i in range(nbrows):
imr.writenext(buffer[i, :])
else:
for i in range(nbrows):
imr.writenext(buffer[:, i, :])
# Retrieve enough lines from the source raster to fill the new InMemoryRaster
for i in range(depth):
try:
......
......@@ -59,6 +59,10 @@ class TileManager():
self.__yll = rg.yll
self.__ncols = rg.ncols
self.__nrows = rg.nrows
if hasattr(rg, "nbands"):
self.__nbands = rg.nbands
else:
self.__nbands = 1
self.__datatype = rg.datatype
self.__stripmanager = StripManager(rg, tileheight)
self.__stripmanager.stripheight = tileheight
......@@ -91,31 +95,53 @@ class TileManager():
# In case buffer contains rows, prepare to add them at the beginning of the output raster
if self.__curcol + width + self.__coloverlap <= self.__ncols:
width += self.__coloverlap
data = self.__curstrip.data[:, self.__curcol:self.__curcol + width]
if self.__nbands == 1:
data = self.__curstrip.data[:, self.__curcol:self.__curcol + width]
else:
data = self.__curstrip.data[:, :, self.__curcol:self.__curcol + width]
# Check whether extra columns have to be added
if (data.shape[1] < self.__width + self.__coloverlap) and self.__addedges:
extradata = np.empty((data.shape[0], self.__width + self.__coloverlap - data.shape[1]), dtype=data.dtype)
extradata.fill(self.__rg.nodatavalue)
data = np.append(data, extradata, 1)
# Establish width, height and the coordinates of the lower left point
width = data.shape[1]
if self.__nbands == 1:
width = data.shape[1]
else:
width = data.shape[2]
if (width < self.__width + self.__coloverlap) and self.__addedges:
if self.__nbands == 1:
extradata = np.empty((data.shape[0], self.__width + self.__coloverlap - data.shape[1]), dtype=data.dtype)
extradata.fill(self.__rg.nodatavalue)
data = np.append(data, extradata, 1)
else:
extradata = np.empty((self.__nbands, data.shape[1], self.__width + self.__coloverlap - data.shape[2]), dtype=data.dtype)
extradata.fill(self.__rg.nodatavalue)
data = np.append(data, extradata, 2)
# Adjust width, height and the coordinates of the lower left point
if self.__nbands == 1:
width = data.shape[1]
else:
width = data.shape[2]
height = self.__curstrip.nrows
xll = self.__xll + (self.__curcol * self.__rg.dx)
yll = self.__curstrip.yll
# Check whether extra rows have to be added
if (self.__rowidx == maxrowno) and (height < self.__depth) and self.__addedges:
extradata = np.empty((self.__depth - height + self.__rowoverlap, data.shape[1]), dtype=data.dtype)
extradata.fill(self.__rg.nodatavalue)
data = np.append(data, extradata, 0)
if self.__nbands == 1:
extradata = np.empty((self.__depth - height + self.__rowoverlap, data.shape[1]), dtype=data.dtype)
extradata.fill(self.__rg.nodatavalue)
data = np.append(data, extradata, 0)
else:
extradata = np.empty((self.__nbands, self.__depth - height + self.__rowoverlap, data.shape[2]), dtype=data.dtype)
extradata.fill(self.__rg.nodatavalue)
data = np.append(data, extradata, 1)
# Adjust y-coordinate and then also height of the lower left point
yll = yll - (self.__depth - height + self.__rowoverlap) * self.__rg.dy
height = self.__depth + self.__rowoverlap
# Now that we have retrieved enough particulars, prepare the result
imr = Tile("dummy_file.ext", data, self.__datatype)
imr.open('w', width, height, xll, yll, self.__rg.dy, self.__rg.nodatavalue)
imr.open('w', width, height, self.__nbands, xll, yll, self.__rg.dy, self.__rg.nodatavalue)
self.__curcol += self.__width
self.__colidx += 1
imr.rowidx = self.__rowidx
......@@ -130,13 +156,17 @@ class TileManager():
try:
# If there's row overlap, keep the last few lines of the strip in a buffer
if (self.__rowoverlap > 0) and (not self.__curstrip is None):
rowbuf = self.__curstrip.data[-1 * self.__rowoverlap:, :]
if self.__nbands == 1:
rowbuf = self.__curstrip.data[-1 * self.__rowoverlap:, :]
else:
rowbuf = self.__curstrip.data[:, -1 * self.__rowoverlap:, :]
self.__curstrip = self.__stripmanager.next(rowbuf, self.__rowoverlap)
self.__curcol = 0
self.__colidx = -1
self.__rowidx += 1
result = True
except:
except Exception as e:
print(e)
raise StopIteration
finally:
return result
......@@ -191,6 +221,11 @@ class TileManager():
@property
def rowoverlap(self):
return self.__rowoverlap
@property
def coloverlap(self):
return self.__coloverlap
class TileIterator:
def __init__(self, tilemanager):
......@@ -204,7 +239,7 @@ class TileIterator:
curcol = self.__tilemanager.curcol
width = self.__tilemanager.tilewidth
ncols = self.__tilemanager.ncols
tilesleft = ceil((ncols - curcol) / width)
tilesleft = ceil((ncols - self.__tilemanager.coloverlap - curcol) / width)
if tilesleft > 0:
result = self.__tilemanager.next()
else:
......@@ -224,6 +259,7 @@ class TileStitcher():
'''
__ncols = 1
__nrows = 1
__nbands = 1 # default
__tilewidth = 1
__tileheight = 1
__coloverlap = 0
......@@ -249,6 +285,7 @@ class TileStitcher():
if not option in ['average', 'topleft', 'bottomright']:
raise ValueError("Invalid option: %s!" % option)
self.__option = option
self.__nbands = 1
def add(self, filename, separator="_"):
# Initialise
......@@ -266,7 +303,7 @@ class TileStitcher():
def process(self, RasterClass, datatype, outputraster):
'''
After all the tiles have been added, this method can be invoked with an outputraster
After all the tiles have been added, this method can be invoked with an output raster
which is still closed!
'''
# Check whether output raster is already open
......@@ -291,11 +328,13 @@ class TileStitcher():
# 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))
data = None
if self.__option == "average":
# We'll need to store some values temporarily
colbuffer = np.empty((self.__tileheight, self.__coloverlap))
rowbuffer = np.empty((self.__rowoverlap, self.__tilewidth * self.__nrows))
# Now loop over the tiles. The output raster will be written strip by strip
for i in range(minrowidx, maxrowidx + 1):
for k in range(mincolidx, maxcolidx + 1):
# Get hold of the right tile
......@@ -304,6 +343,32 @@ class TileStitcher():
if not os.path.exists(fn): raise ValueError("File %s does not exist" % fn)
rg = RasterClass(fn, datatype)
rg.open('r')
# Check the number of bands
if hasattr(rg, "nbands"): nbands = rg.nbands
else: nbands = 1
if i == minrowidx and k == mincolidx:
self.__nbands = nbands
print("First tile detected. It has %s bands ..." % nbands)
else:
if nbands != self.__nbands:
errmsg = "Tile detected with %s bands, but %s expected!"
raise Exception(errmsg % (nbands, self.__nbands))
# Check the size of the opened raster
if (rg.ncols < self.__tilewidth + self.__coloverlap):
raise Exception("Tile represented by file %s has unexpected width!" % fn)
if (rg.nrows < self.__tileheight + self.__rowoverlap):
raise Exception("Tile represented by file %s has unexpected height!" % fn)
# Prepare an array for the data
if data is None:
stripheight = self.__tileheight + self.__rowoverlap
stripwidth = self.__ncols * self.__tilewidth + self.__coloverlap
if self.__nbands == 1:
data = np.empty((stripheight, stripwidth))
else:
data = np.empty((self.__nbands, stripheight, stripwidth))
# Initialise the array data further
if (i == minrowidx) and (k == mincolidx):
......@@ -318,44 +383,90 @@ class TileStitcher():
else:
# Check the coordinates
eps = 0.00001
stripheight = self.__tileheight + self.__rowoverlap
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))
B = abs(rg.yll + stripheight * rg.dy + (i-minrowidx)*self.__tileheight*rg.dy - yul) > eps
if A or B:
errmsg = "Tile with row index %s and column index %s is not georeferenced correctly!"
raise Exception(errmsg % (i, k))
# Add the data from the tile at the right location
for j in range(rg.nrows - self.__rowoverlap):
# Add the data from the tile at the correct location in the data array
if (i < maxrowidx): numlines = self.__tileheight
else: numlines = self.__tileheight + self.__rowoverlap
for j in range(numlines):
# Get the next line
line = rg.next()
if (self.__option == "average") and (k > mincolidx):
# TODO: use the data from the column buffer to do the calculations
pass
data[j, k*self.__tilewidth:(k+1)*self.__tilewidth] = line[0:self.__tilewidth]
# Add data from the line, but leave the data from the overlap out
if self.__nbands == 1:
data[j, k*self.__tilewidth:(k+1)*self.__tilewidth] = line[0:self.__tilewidth]
else:
data[:, j, k*self.__tilewidth:(k+1)*self.__tilewidth] = line[:, 0:self.__tilewidth]
if self.__option == "average":
# Fill the column buffer with new data
colbuffer[j, 0:self.__coloverlap] = line[self.__tilewidth:self.__tilewidth + self.__coloverlap]
# If option average has been selected, keep the values from the overlapping edge in a buffer
if self.__option == "average":
rg.reset()
for j in range(self.__rowoverlap):
line = rg.next()
rowbuffer[j, k*self.__tilewidth:(k+1)*self.__tilewidth] = line[0:self.__tilewidth]
if self.__nbands == 1:
rowbuffer[j, k*self.__tilewidth:(k+1)*self.__tilewidth] = line[0:self.__tilewidth]
else:
rowbuffer[:, 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):
# When dealing with the last tile of a strip, we may add the data from the overlapping columns if appropriate
rg.reset()
ovlpcol1 = (k+1) * self.__tilewidth # first column of overlap
if (i < maxrowidx): numlines = self.__tileheight
else: numlines = self.__tileheight + self.__rowoverlap
for j in range(numlines):
line = rg.next()
if self.__nbands == 1:
data[j, ovlpcol1 : ovlpcol1 + self.__coloverlap] = line[self.__tilewidth : self.__tilewidth + self.__coloverlap]
else:
data[:, j, ovlpcol1 : ovlpcol1 + self.__coloverlap] = line[:, self.__tilewidth : self.__tilewidth + self.__coloverlap]
# Take appropriate action when the last tile of the first strip has been processed
if (i == minrowidx):
ncols = self.__ncols * self.__tilewidth
nrows = self.__nrows * self.__tileheight
# First row: open the file for writing!
ncols = self.__ncols * self.__tilewidth + self.__coloverlap # Note: overlap is added!
nrows = self.__nrows * self.__tileheight + self.__rowoverlap # Idem
nbands = self.__nbands
yll = yul - nrows * rg.dy
outputraster.open('w', ncols, nrows, xul, yll, rg.cellsize, rg.nodatavalue)
# TODO: if option average has been selected, use data in the row buffer to do the calculations
for j in range(self.__tileheight):
line = data[j, :]
outputraster.open('w', ncols, nrows, nbands, xul, yll, rg.cellsize, rg.nodatavalue)
if self.__option == "average":
# TODO: if option average has been selected, use data in the row buffer to do the calculations
# TODO in case of last tile, tileheight and / or line length may have to be adjusted
pass
# Now write the lines
if (i < maxrowidx): numlines = self.__tileheight
else: numlines = self.__tileheight + self.__rowoverlap
for j in range(numlines):
if self.__nbands == 1:
line = data[j, :]
else:
line = data[:, j, :]
outputraster.writenext(line)
# Prepare for the next loop
# Prepare to process the next tile
rg.close()
# Prepare for the next loop
# Prepare to process the next strip
data.fill(nodatavalue)
# Clean up
print("Finished stitching the tiles together!")
if not outputraster is None:
outputraster.close()
\ 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