def buildmaptile(args): (log, name, tilex, tiley, elxsize, elysize, lcarr, tifgeotrans, elgeoxform, wantCL) = args (xminarr, xmaxarr, yminarr, ymaxarr) = lcarr yamlfile = file(os.path.join('Regions', name, 'Region.yaml')) self = yaml.load(yamlfile) yamlfile.close() offsetx = tilex * Region.prepTileSize offsety = tiley * Region.prepTileSize sizex = min(Region.prepTileSizeOverlap, elxsize - offsetx) sizey = min(Region.prepTileSizeOverlap, elysize - offsety) srs = osr.SpatialReference() srs.ImportFromProj4(Region.t_srs) driver = gdal.GetDriverByName("GTiff") lctif = os.path.join(self.mapsdir, '%s.tif' % (self.lclayer)) lcextentsWhole = self.utmextents['landcover'] elfile = os.path.join(self.mapsdir, '%s-new.tif' % (self.ellayer)) oifile = os.path.join(self.mapsdir, '%s-new.tif' % (self.oilayer)) # GeoTIFF Image Tile # eight bands: landcover, elevation, bathy, crust, oR, oG, oB, oA # data type is GDT_Int16 (elevation can be negative) log.log_debug(1,"Creating output image tile %d, %d with offset " \ "(%d, %d) and size (%d, %d)" % \ (tilex, tiley, offsetx, offsety, sizex, sizey)) tilename = os.path.join(self.regiondir, 'Tile_%04d_%04d.tif' % (tilex, tiley)) mapds = driver.Create(tilename, sizex, sizey, len(Region.rasters), GDT_Int16) mapds.SetProjection(srs.ExportToWkt()) # overall map transform should match elevation map transform geoxform = [elgeoxform[0] + offsetx * elgeoxform[1], elgeoxform[1], elgeoxform[2], \ elgeoxform[3] + offsety * elgeoxform[5], elgeoxform[4], elgeoxform[5]] mapds.SetGeoTransform(geoxform) # modify elarray and save it as raster band 2 log.log_debug(2,"Adjusting and storing elevation to GeoTIFF...") elds = gdal.Open(elfile, GA_ReadOnly) elarray = elds.GetRasterBand(1).ReadAsArray(offsetx, offsety, sizex, sizey) elds = None actualel = ((elarray - self.trim)/self.vscale)+self.sealevel mapds.GetRasterBand(Region.rasters['elevation']).WriteArray(actualel) elarray = None actualel = None # generate crust and save it as raster band 4 log.log_debug(2,"Adjusting and storing crust to GeoTIFF...") newcrust = Crust(sizex, sizey, wantCL=wantCL) crustarray = newcrust() mapds.GetRasterBand(Region.rasters['crust']).WriteArray(crustarray) crustarray = None newcrust = None # Compute new geographic tile extents for landcover lcextents = {} border = self.scale * self.maxdepth lcxsize = elxsize + 2 * border # Total LC region size lcysize = elysize + 2 * border lctxsize = sizex + 2 * border # Current LC tile size lctysize = sizey + 2 * border lcextents['xmin'] = lcextentsWhole['xmin'] + \ (offsetx / float(lcxsize)) * \ (lcextentsWhole['xmax'] - lcextentsWhole['xmin']) lcextents['xmax'] = lcextentsWhole['xmin'] + \ ((offsetx + lctxsize) / float(lcxsize)) * \ (lcextentsWhole['xmax'] - lcextentsWhole['xmin']) lcextents['ymax'] = lcextentsWhole['ymax'] - \ (offsety / float(lcysize)) * \ (lcextentsWhole['ymax'] - lcextentsWhole['ymin']) lcextents['ymin'] = lcextentsWhole['ymax'] - \ ((offsety + lctysize) / float(lcysize)) * \ (lcextentsWhole['ymax'] - lcextentsWhole['ymin']) # Compute new array tile extents for landcover lcxminarr = int(xminarr + (offsetx / float(lcxsize)) * (xmaxarr - xminarr)) lcxmaxarr = int(xminarr + ((offsetx + lctxsize) / float(lcxsize)) * (xmaxarr - xminarr)) lcyminarr = int(yminarr + (offsety / float(lcysize)) * (ymaxarr - yminarr)) lcymaxarr = int(yminarr + ((offsety + lctysize) / float(lcysize)) * (ymaxarr - yminarr)) # read landcover array log.log_debug(2,"Warping and storing landcover and bathy data...") log.log_debug(3,"LC extents: %s" % str(lcextents)) log.log_debug(3,"LC window: %s => %s" % (str([xminarr, xmaxarr, yminarr, ymaxarr]), \ str([lcxminarr, lcyminarr, lcxmaxarr-lcxminarr, lcymaxarr-lcyminarr]))) tifds = gdal.Open(lctif, GA_ReadOnly) tifband = tifds.GetRasterBand(1) values = tifband.ReadAsArray(lcxminarr, lcyminarr, lcxmaxarr-lcxminarr, lcymaxarr-lcyminarr) tifnodata = tifband.GetNoDataValue() #nodata is treated as water, which is 11 if (tifnodata == None): tifnodata = 0 values[values == tifnodata] = 11 values = values.flatten() tifband = None tifds = None # 2. a new array of original scale coordinates must be created # The following two lines should work fine still because x and y are offset into the array tifxrange = [tifgeotrans[0] + tifgeotrans[1] * x for x in xrange(lcxminarr, lcxmaxarr)] tifyrange = [tifgeotrans[3] + tifgeotrans[5] * y for y in xrange(lcyminarr, lcymaxarr)] coords = numpy.array([(x, y) for y in tifyrange for x in tifxrange]) # 3. a new array of goal scale coordinates must be made # landcover extents are used for the bathy depth array # yes, it's confusing. sorry. depthxlen = int((lcextents['xmax']-lcextents['xmin'])/self.scale) depthylen = int((lcextents['ymax']-lcextents['ymin'])/self.scale) depthxrange = [lcextents['xmin'] + self.scale * x for x in xrange(depthxlen)] depthyrange = [lcextents['ymax'] - self.scale * y for y in xrange(depthylen)] depthbase = numpy.array([(x, y) for y in depthyrange for x in depthxrange]) # 4. an inverse distance tree must be built from that lcCLIDT = CLIDT(coords, values, depthbase, wantCL=wantCL) # 5. the desired output comes from that inverse distance tree deptharray = lcCLIDT() deptharray.resize((depthylen, depthxlen)) lcCLIDT = None # 6. Finish and store the band lcarray = deptharray[self.maxdepth:-1*self.maxdepth, self.maxdepth:-1*self.maxdepth] geotrans = [ lcextents['xmin'], self.scale, 0, lcextents['ymax'], 0, -1 * self.scale ] projection = srs.ExportToWkt() bathyarray = getBathy(deptharray, self.maxdepth, geotrans, projection) mapds.GetRasterBand(Region.rasters['bathy']).WriteArray(bathyarray) # perform terrain translation # NB: figure out why this doesn't work up above lcpid = self.lclayer[:3] if lcpid in Terrain.translate: trans = Terrain.translate[lcpid] for key in trans: lcarray[lcarray == key] = trans[key] for value in numpy.unique(lcarray).flat: if value not in Terrain.terdict: self.warn("tile has bad value: " + str(value)) mapds.GetRasterBand(Region.rasters['landcover']).WriteArray(lcarray) # read and store ortho arrays tifds = gdal.Open(oifile, GA_ReadOnly) for band in xrange(1,5): # That is, [1 2 3 4] log.log_debug(2,"Cropping and storing ortho data (%s band)..." % "-rgba"[band]) tifband = tifds.GetRasterBand(band) #values = tifband.ReadAsArray(oixminarr, oiyminarr, (oixmaxarr - oixminarr), (oiymaxarr - oiyminarr)) values = tifband.ReadAsArray(offsetx, offsety, sizex, sizey) tifnodata = tifband.GetNoDataValue() if (tifnodata == None): tifnodata = 0 values[values == tifnodata] = -1 #Signal missing tifband = None mapds.GetRasterBand(band+Region.rasters['orthor']-1).WriteArray(values) values = None tifds = None # close the dataset and append it to the tile list mapds = None return tilename
def buildmap(self, wantCL=True): """Use downloaded files and other parameters to build multi-raster map.""" # warp elevation data into new format # NB: can't do this to landcover until mode algorithm is supported elvrt = os.path.join(self.mapsdir, self.ellayer, '%s.vrt' % (self.ellayer)) elfile = os.path.join(self.mapsdir, self.ellayer, '%s.tif' % (self.ellayer)) elextents = self.albersextents['elevation'] warpcmd = 'gdalwarp -q -multi -t_srs "%s" -tr %d %d -te %d %d %d %d -r cubic %s %s -srcnodata "-340282346638529993179660072199368212480.000" -dstnodata 0' % (Region.t_srs, self.scale, self.scale, elextents['xmin'], elextents['ymin'], elextents['xmax'], elextents['ymax'], elvrt, elfile) try: os.remove(elfile) except OSError: pass # NB: make this work on Windows too! os.system("%s" % warpcmd) elds = gdal.Open(elfile, GA_ReadOnly) elgeotrans = elds.GetGeoTransform() elband = elds.GetRasterBand(1) elarray = elband.ReadAsArray(0, 0, elds.RasterXSize, elds.RasterYSize) (elysize, elxsize) = elarray.shape # update sealevel, trim and vscale elmin = elband.GetMinimum() elmax = elband.GetMaximum() if elmin is None or elmax is None: (elmin, elmax) = elband.ComputeRasterMinMax(False) elmin = int(elmin) elmax = int(elmax) elband = None elds = None # sealevel depends upon elmin minsealevel = 2 # if minimum elevation is below sea level, add extra space if (elmin < 0): minsealevel += int(-1.0*elmin/self.scale) maxsealevel = Region.tileheight - Region.headroom oldsealevel = self.sealevel if (oldsealevel > maxsealevel or oldsealevel < minsealevel): print "warning: sealevel value %d outside %d-%d range" % (oldsealevel, minsealevel, maxsealevel) self.sealevel = int(min(max(oldsealevel, minsealevel), maxsealevel)) # maxdepth depends upon sealevel minmaxdepth = 1 maxmaxdepth = self.sealevel - 1 oldmaxdepth = self.maxdepth if (oldmaxdepth > maxmaxdepth or oldmaxdepth < minmaxdepth): print "warning: maxdepth value %d outside %d-%d range" % (oldmaxdepth, minmaxdepth, maxmaxdepth) self.maxdepth = int(min(max(oldmaxdepth, minmaxdepth), maxmaxdepth)) # trim depends upon elmin (if elmin < 0, trim == 0) mintrim = Region.trim maxtrim = max(elmin, mintrim) oldtrim = self.trim if (oldtrim > maxtrim or oldtrim < mintrim): print "warning: trim value %d outside %d-%d range" % (oldtrim, mintrim, maxtrim) self.trim = int(min(max(oldtrim, mintrim), maxtrim)) # vscale depends on sealevel, trim and elmax # NB: no maximum vscale, the sky's the limit (hah!) eltrimmed = elmax - self.trim elroom = Region.tileheight - Region.headroom - self.sealevel minvscale = ceil(eltrimmed / elroom) oldvscale = self.vscale if (oldvscale < minvscale): print "warning: vscale value %d smaller than minimum value %d" % (oldvscale, minvscale) self.vscale = int(max(oldvscale, minvscale)) # GeoTIFF # four bands: landcover, elevation, bathy, crust # data type is GDT_Int16 (elevation can be negative) driver = gdal.GetDriverByName("GTiff") mapds = driver.Create(self.mapname, elxsize, elysize, len(Region.rasters), GDT_Int16) # overall map transform should match elevation map transform mapds.SetGeoTransform(elgeotrans) srs = osr.SpatialReference() srs.ImportFromProj4(Region.t_srs) mapds.SetProjection(srs.ExportToWkt()) # modify elarray and save it as raster band 2 actualel = ((elarray - self.trim)/self.vscale)+self.sealevel mapds.GetRasterBand(Region.rasters['elevation']).WriteArray(actualel) elarray = None actualel = None # generate crust and save it as raster band 4 newcrust = Crust(mapds.RasterXSize, mapds.RasterYSize, wantCL=wantCL) crustarray = newcrust() mapds.GetRasterBand(Region.rasters['crust']).WriteArray(crustarray) crustarray = None newcrust = None # read landcover array lcvrt = os.path.join(self.mapsdir, self.lclayer, '%s.vrt' % (self.lclayer)) lcfile = os.path.join(self.mapsdir, self.lclayer, '%s.tif' % (self.lclayer)) # here are the things that need to happen lcextents = self.albersextents['landcover'] # if True, use new code, if False, use gdalwarp if True: # 1. the new file must be read into an array and flattened vrtds = gdal.Open(lcvrt, GA_ReadOnly) vrtgeotrans = vrtds.GetGeoTransform() vrtband = vrtds.GetRasterBand(1) values = vrtband.ReadAsArray(0, 0, vrtds.RasterXSize, vrtds.RasterYSize) # nodata is treated as water, which is 11 vrtnodata = vrtband.GetNoDataValue() if (vrtnodata == None): vrtnodata = 0 values[values == vrtnodata] = 11 values = values.flatten() vrtband = None # 2. a new array of original scale coordinates must be created vrtxrange = [vrtgeotrans[0] + vrtgeotrans[1] * x for x in xrange(vrtds.RasterXSize)] vrtyrange = [vrtgeotrans[3] + vrtgeotrans[5] * y for y in xrange(vrtds.RasterYSize)] vrtds = None coords = numpy.array([(x, y) for y in vrtyrange for x in vrtxrange]) # 3. a new array of goal scale coordinates must be made # landcover extents are used for the bathy depth array # yes, it's confusing. sorry. depthxlen = int((lcextents['xmax']-lcextents['xmin'])/self.scale) depthylen = int((lcextents['ymax']-lcextents['ymin'])/self.scale) depthxrange = [lcextents['xmin'] + self.scale * x for x in xrange(depthxlen)] depthyrange = [lcextents['ymax'] - self.scale * y for y in xrange(depthylen)] depthbase = numpy.array([(x, y) for y in depthyrange for x in depthxrange]) # 4. an inverse distance tree must be built from that lcCLIDT = CLIDT(coords, values, depthbase, wantCL=wantCL) # 5. the desired output comes from that inverse distance tree deptharray = lcCLIDT() deptharray.resize((depthylen, depthxlen)) lcCLIDT = None else: warpcmd = 'gdalwarp -q -multi -t_srs "%s" -tr %d %d -te %d %d %d %d -r near %s %s' % (Region.t_srs, self.scale, self.scale, lcextents['xmin'], lcextents['ymin'], lcextents['xmax'], lcextents['ymax'], lcvrt, lcfile) try: os.remove(lcfile) except OSError: pass # NB: make this work on Windows too! os.system("%s" % warpcmd) lcds = gdal.Open(lcfile, GA_ReadOnly) lcband = lcds.GetRasterBand(1) # depth array is entire landcover region, landcover array is subset deptharray = lcband.ReadAsArray(0, 0, lcds.RasterXSize, lcds.RasterYSize) lcarray = deptharray[self.maxdepth:-1*self.maxdepth, self.maxdepth:-1*self.maxdepth] geotrans = [ lcextents['xmin'], self.scale, 0, lcextents['ymax'], 0, -1 * self.scale ] projection = srs.ExportToWkt() bathyarray = getBathy(deptharray, self.maxdepth, geotrans, projection) mapds.GetRasterBand(Region.rasters['bathy']).WriteArray(bathyarray) # perform terrain translation # NB: figure out why this doesn't work up above lcpid = self.lclayer[:3] if lcpid in Terrain.translate: trans = Terrain.translate[lcpid] for key in trans: lcarray[lcarray == key] = 1000+trans[key] lcarray[lcarray > 1000] -= 1000 for value in numpy.unique(lcarray).flat: if value not in Terrain.terdict: print "bad value: ", value mapds.GetRasterBand(Region.rasters['landcover']).WriteArray(lcarray) # close the dataset mapds = None