예제 #1
0
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
예제 #2
0
    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