def main(): # Parse command line arguments parser = argparse.ArgumentParser(description=__doc__) utils.add_standard_command_options(parser) parser.add_argument( "-f", "--format", action="store", dest="format", default="ESRI Shapefile", help="Format of road network" ) parser.add_argument("--inRoads", required=True, action="store", dest="inRoads", help="Input road network") parser.add_argument("--outRoads", required=True, action="store", dest="outRoads", help="Output road network") parser.add_argument( "--buildings", required=True, action="store", dest="buildings", help="Input building roof contours (3D)" ) parser.add_argument("--topo", required=True, action="store", dest="topo", help="Input raster DEM") parser.add_argument( "--split", type=int, action="store", dest="split", help="Threshold in changed road direction (degrees)" + " for when to split road", ) args = parser.parse_args() if not path.exists(args.topo): log.error("Input raster does not exist") sys.exit(1) if not path.exists(args.buildings): log.error("Input building contours does not exist") sys.exit(1) return 1 log.info("Reading DEM") topo = readGDAL(args.topo, bandIndex=1)[0] # Opening driver for road networks try: driver = ogr.GetDriverByName(args.format) except: log.error("Invalid format for road networks, check ogr documentation") sys.exit(1) if args.split is None: log.info("Do not split roads") splitLimit = None else: splitLimit = float(args.split) log.info("Split roads that change direction" + " more than %f" % splitLimit) # extract extent from topo raster xmin = topo.xll ymin = topo.yll xmax = topo.xur() ymax = topo.yur() # Calculate dimensions of spatial index ncols = int((xmax - xmin) / CELLSIZE) nrows = int((ymax - ymin) / CELLSIZE) # Init spatial index of building contours spatInd = SpatialIndex(xmin, ymin, nrows, ncols, CELLSIZE) log.info("Reading and indexing building contours") # Read buildings and store using spatial indices spatInd.indexBuildingContours(args.buildings) # open road network shape-file log.info("Reading road network") inRoadFile = driver.Open(args.inRoads, update=0) if path.exists(args.outRoads): driver.DeleteDataSource(args.outRoads) outRoadFile = driver.CreateDataSource(args.outRoads) if inRoadFile is None: log.error("Could not open file with input road network") sys.exit(1) if outRoadFile is None: log.error("Could not open file with output road network") sys.exit(1) # Get layer definition and first feature of input road network inRoadLayer = inRoadFile.GetLayer() inRoadLayerDefn = inRoadLayer.GetLayerDefn() outRoadLayer = outRoadFile.CreateLayer("first_layer", geom_type=inRoadLayer.GetGeomType()) # create fields on output road file for fieldInd in range(inRoadLayerDefn.GetFieldCount()): fieldDefn = inRoadLayerDefn.GetFieldDefn(fieldInd) outRoadLayer.CreateField(fieldDefn) outRoadLayerDefn = outRoadLayer.GetLayerDefn() fieldNames = [outRoadLayerDefn.GetFieldDefn(i).GetName() for i in range(outRoadLayerDefn.GetFieldCount())] log.info("Adding attributes to road feature (if missing)") # Add attributes for street canyon geometry if "BHGT1" not in fieldNames: outRoadLayer.CreateField(ogr.FieldDefn("BHGT1", ogr.OFTInteger)) if "BHGT2" not in fieldNames: outRoadLayer.CreateField(ogr.FieldDefn("BHGT2", ogr.OFTInteger)) if "BHGT1W" not in fieldNames: outRoadLayer.CreateField(ogr.FieldDefn("BHGT1W", ogr.OFTInteger)) if "BHGT2W" not in fieldNames: outRoadLayer.CreateField(ogr.FieldDefn("BHGT2W", ogr.OFTInteger)) if "BANG1" not in fieldNames: outRoadLayer.CreateField(ogr.FieldDefn("BANG1", ogr.OFTInteger)) if "BANG2" not in fieldNames: outRoadLayer.CreateField(ogr.FieldDefn("BANG2", ogr.OFTInteger)) if "BDIST" not in fieldNames: outRoadLayer.CreateField(ogr.FieldDefn("BDIST", ogr.OFTInteger)) if "BSECT" not in fieldNames: fieldDefn = ogr.FieldDefn("BSECT", ogr.OFTString) fieldDefn.SetWidth(40) outRoadLayer.CreateField(fieldDefn) if "BSECTW" not in fieldNames: fieldDefn = ogr.FieldDefn("BSECTW", ogr.OFTString) fieldDefn.SetWidth(40) outRoadLayer.CreateField(fieldDefn) fig1 = plt.figure(1) ax1 = plt.subplot(111) ax1.axis("equal") if PLOTIND > 0: spatInd.plot(ax1) roadInd = 0 noGeom = 0 nsplit = 0 # get first road feature inRoadFeature = inRoadLayer.GetNextFeature() # Loop over all roads log.info("Finding nearest facades, setting heights...") pg = ProgressBar(inRoadLayer.GetFeatureCount(), sys.stdout) while inRoadFeature: pg.update(roadInd) outRoadFeatures = splitRoad(inRoadFeature, splitLimit, outRoadLayer.GetLayerDefn()) if len(outRoadFeatures) > 1: log.debug("Raod split into %s parts" % len(outRoadFeatures)) nsplit += len(outRoadFeatures) - 1 for outRoadFeature in outRoadFeatures: intersections = [] outRoadGeom = outRoadFeature.GetGeometryRef() road = Road(outRoadGeom) if outRoadGeom is None or outRoadGeom.GetPointCount() == 0 or not spatInd.inside(road): noGeom += 1 maxHeight1 = None maxHeight2 = None avgDist = None bAngle1 = None bAngle2 = None avgHeight1 = None avgHeight2 = None else: sumHeight1 = 0 sumHeight2 = 0 maxHeight1 = 0 maxHeight2 = 0 sumDist = 0 # Define crossections along the road, # Defined by start and endpoints at both side of the road cs1List, cs2List = road.defineCrossSections() nCS = len(cs1List) log.debug("Defined %i cross sections" % nCS) # Check intersections with building contours for all cross-sections for csInd in range(nCS): cs1 = cs1List[csInd] cs2 = cs2List[csInd] cs1MidPoint = cs1.P0 + 0.5 * (cs1.P1 - cs1.P0) buildingSegments = spatInd.getBuildingSegments(cs1MidPoint[0], cs1MidPoint[1]) log.debug("Calculating intersection") if PLOTIND == roadInd and CSIND == csInd: dist1, Pint1 = getIntersectingFacade(ax1, cs1, buildingSegments, True) else: dist1, Pint1 = getIntersectingFacade(ax1, cs1, buildingSegments, False) if Pint1 is None: log.debug("No intersection on side 1") height1 = 0 dist1 = MAXDIST else: log.debug("Intersection1 in (%f, %f, %f)" % (Pint1[0], Pint1[1], Pint1[2])) height1 = spatInd.getBuildingHeight(Pint1[0], Pint1[1], Pint1[2], topo) + HEIGHTCORR intersections.append(Pint1[:2]) if PLOTIND == roadInd and csInd == CSIND: plotSegments(ax1, buildingSegments, color="red", width=2.0) row, col = spatInd.getInd(cs1MidPoint[0], cs1MidPoint[1]) spatInd.plotCell(ax1, row, col, color="purple", width=2.0) plotSegments(ax1, [cs1List[csInd]], color="pink", style="-", width=1.0) plt.draw() cs2MidPoint = cs2.P0 + 0.5 * (cs2.P1 - cs2.P0) buildingSegments = spatInd.getBuildingSegments(cs2MidPoint[0], cs2MidPoint[1]) if PLOTIND == roadInd and csInd == CSIND: plotSegments(ax1, buildingSegments, color="red", width=2.0) row, col = spatInd.getInd(cs2MidPoint[0], cs2MidPoint[1]) spatInd.plotCell(ax1, row, col, color="brown", width=2.0) plotSegments(ax1, [cs2List[csInd]], color="red", style="-", width=1.0) plt.draw() log.debug("Calculating intersection") if PLOTIND == roadInd and CSIND == csInd: dist2, Pint2 = getIntersectingFacade(ax1, cs2, buildingSegments, True) else: dist2, Pint2 = getIntersectingFacade(ax1, cs2, buildingSegments, False) if Pint2 is None: log.debug("No intersection on side 2") height2 = 0 else: log.debug("Intersection2 in (%f, %f, %f)" % (Pint2[0], Pint2[1], Pint2[2])) height2 = spatInd.getBuildingHeight(Pint2[0], Pint2[1], Pint2[2], topo) + HEIGHTCORR intersections.append(Pint2[:2]) sumHeight1 += height1 sumHeight2 += height2 sumDist += dist1 + dist2 maxHeight1 = int(max(height1, maxHeight1)) maxHeight2 = int(max(height2, maxHeight2)) if PLOTIND == roadInd and CSIND == csInd: if Pint1 is not None: ax1.text(Pint1[0], Pint1[1], "Distance=%f" % dist1) if Pint2 is not None: ax1.text(Pint2[0], Pint2[1], "Distance=%f" % dist2) avgHeight1 = int(sumHeight1 / float(nCS)) avgHeight2 = int(sumHeight2 / float(nCS)) # averaging over both sides of street # distance refers to between facades on opposite sides avgDist = int(round(sumDist / float(nCS))) bAngle1, bAngle2 = road.normalAngles() if PLOTIND > 0: plotSegments(ax1, road.getSegments(), color="grey", width=0.3) if PLOTIND == roadInd: plotSegments(ax1, road.getSegments(), color="black", width=2.0) plotSegments(ax1, cs1List, color="green", style="--", width=0.5) plotSegments(ax1, cs2List, color="green", style="--", width=0.5) X = [intersect[0] for intersect in intersections] Y = [intersect[1] for intersect in intersections] if len(X) > 0: ax1.plot(X, Y, "*") plt.title("Road %i, cross-section %i" % (PLOTIND, CSIND)) plt.draw() # building height as list of sectors bsect = bheight2sect(avgHeight1, avgHeight2, bAngle1) bsectw = bheight2sect(maxHeight1, maxHeight2, bAngle1) outRoadFeature.SetField("BSECT", bsect) outRoadFeature.SetField("BSECTW", bsectw) outRoadFeature.SetField("BHGT1", avgHeight1) outRoadFeature.SetField("BHGT2", avgHeight2) outRoadFeature.SetField("BHGT1W", maxHeight1) outRoadFeature.SetField("BHGT2W", maxHeight2) outRoadFeature.SetField("BANG1", bAngle1) outRoadFeature.SetField("BANG2", bAngle2) outRoadFeature.SetField("BDIST", avgDist) outRoadLayer.CreateFeature(outRoadFeature) outRoadFeature.Destroy() inRoadFeature.Destroy() inRoadFeature = inRoadLayer.GetNextFeature() roadInd += 1 inRoads = inRoadLayer.GetFeatureCount() outRoads = outRoadLayer.GetFeatureCount() # close datasource for building contours inRoadFile.Destroy() outRoadFile.Destroy() pg.finished() if PLOTIND > 0: plt.show() log.info("Read %i roads, wrote %i roads (created %i by splitting)" % (inRoads, outRoads, nsplit)) if noGeom > 0: log.warning("Found %i roads without geometry" % noGeom) log.info("Finished")
def main(): # -----------Setting up and unsing option parser---------------- parser = OptionParser(usage=usage, version=version) parser.add_option("-d", "--doc", action="store_true", dest="doc", help="Prints more detailed documentation and exit") parser.add_option("-v", "--verbose", action="store_const", const=logging.INFO, dest="loglevel", default=logging.WARNING, help="Produce verbose output") parser.add_option("--no-progress", action="store_const", dest="progressStream", const=None, default=sys.stdout, help="turn off the progress bar") parser.add_option("-i", "--input", action="store", dest="infileName", help="Input raster") parser.add_option("--bandIndex", action="store", dest="bandIndex", help="Band index to read from", default=1) parser.add_option("-o", "--output", action="store", dest="output", default=None, help="Output file") parser.add_option("--solidName", default="TOPO", action="store", dest="solidName", help="Name of solid in STL-output") parser.add_option("--precision", default=6, action="store", dest="precision", help="Precision of coordinates in STL-output") parser.add_option("--buffer", action="store", dest="buffer", help="Add buffer distance to topo, " + "damping out differences in elevation") (options, args) = parser.parse_args() # configuring logging FORMAT = '%(levelname)s %(message)s' logging.basicConfig(format=FORMAT) # ------------Process and validate options--------------- if options.doc: print __doc__ sys.exit() if len(args) > 0: parser.error("Incorrect number of arguments") # validate infile path if options.infileName is not None: inFilePath = options.infileName if not path.exists(inFilePath): log.error("Input raster does not exist") sys.exit(1) else: log.error("No input file specified") sys.exit(1) # validate outfile path if options.output is None: log.error("No output file specified") sys.exit(1) if options.buffer is not None: bufferDist = float(options.buffer) # Assure that gdal is present if not __gdal_loaded__: raise OSError("Function readGDAL needs GDAL with python bindings") # register all of the raster drivers gdal.AllRegister() ds = gdal.Open(inFilePath, GA_ReadOnly) if ds is None: log.error('Could not open ' + inFilePath) sys.exit(1) ncols = ds.RasterXSize nrows = ds.RasterYSize # nbands = ds.RasterCount # Info used for georeferencing geoTransform = ds.GetGeoTransform() xul = geoTransform[0] # top left x cellsizeX = geoTransform[1] # w-e pixel resolution rot1 = geoTransform[2] # rotation, 0 if image is "north up" yul = geoTransform[3] # top left y rot2 = geoTransform[4] # rotation, 0 if image is "north up" cellsizeY = geoTransform[5] # n-s pixel resolution # proj = ds.GetProjection() # Calculate lower left corner xll = xul yll = yul + nrows * cellsizeY # cellsizeY should be a negative value # Rotated rasters not handled...yet if rot1 != 0 or rot2 != 0: log.error('Handling of rotated rasters are not implemented yet') sys.exit(1) bandIndex = int(options.bandIndex) band = ds.GetRasterBand(bandIndex) nodata = band.GetNoDataValue() # If no nodata value is present in raster, set to -9999 for completeness if nodata is None: nodata = -9999 # Processing of data is made for blocks of the following size # two rows are processed at a time since triangles # are created between cell centres. procXBlockSize = ncols procYBlockSize = 2 with open(options.output, 'w') as stlFile: pg = ProgressBar(nrows, options.progressStream) stlFile.write("solid " + options.solidName + "\n") # avgHeight = 0 # write original cells to STL # Loop over blocks of raster for i in range(0, nrows - 1, 1): pg.update(i) data = band.ReadAsArray(xoff=0, yoff=i, win_xsize=procXBlockSize, win_ysize=procYBlockSize) if nodata in data: log.error("Nodata value found in raster," + " this should be interpolated before" + " converting to STL") sys.exit(1) # newCellsizeY is negative blockYll = yul + (procYBlockSize + i) * cellsizeY blockXll = xll block2STL(data, stlFile, blockXll, blockYll, cellsizeX, cellsizeY, nodata, int(options.precision)) # avgHeight += np.mean(data) # avgHeight /= (nrows - 1) # write buffer cells to STL leftBoundary = band.ReadAsArray(xoff=0, yoff=0, win_xsize=1, win_ysize=nrows) rightBoundary = band.ReadAsArray(xoff=ncols - 1, yoff=0, win_xsize=1, win_ysize=nrows) topBoundary = band.ReadAsArray(xoff=0, yoff=0, win_xsize=procXBlockSize, win_ysize=1) bottomBoundary = band.ReadAsArray(xoff=0, yoff=nrows - 1, win_xsize=procXBlockSize, win_ysize=1) avgHeight = 0.25 * (leftBoundary.mean() + rightBoundary.mean() + topBoundary.mean() + bottomBoundary.mean()) if options.buffer is not None: # overlap of one cell since triangles are created between # cell centres bufferCells = int(bufferDist / cellsizeX) + 1 leftBuffer = leftBoundary[:] for i in range(1, bufferCells): dist = i * cellsizeX leftBuffer = np.hstack((damp(leftBoundary, avgHeight, dist, bufferDist), leftBuffer)) block2STL(leftBuffer, stlFile, xll - (bufferCells - 1) * cellsizeX, yll, cellsizeX, cellsizeY, nodata, int(options.precision)) rightBuffer = rightBoundary[:] for i in range(1, bufferCells): dist = i * cellsizeX rightBuffer = np.hstack((rightBuffer, damp(rightBoundary, avgHeight, dist, bufferDist))) block2STL(rightBuffer, stlFile, xll + (ncols - 1) * cellsizeX, yll, cellsizeX, cellsizeY, nodata, int(options.precision)) topBuffer = topBoundary[:] for i in range(1, bufferCells): dist = abs(i * cellsizeY) topBuffer = np.vstack((damp(topBoundary, avgHeight, dist, bufferDist), topBuffer)) block2STL(topBuffer, stlFile, xll, yul + cellsizeY, cellsizeX, cellsizeY, nodata, int(options.precision)) bottomBuffer = bottomBoundary[:] for i in range(1, bufferCells): dist = abs(i * cellsizeY) bottomBuffer = np.vstack((bottomBuffer, damp(bottomBoundary, avgHeight, dist, bufferDist))) block2STL(bottomBuffer, stlFile, xll, yll + (bufferCells - 1) * cellsizeY, cellsizeX, cellsizeY, nodata, int(options.precision)) # Add corner buffers cornerBlock = np.ones((bufferCells, bufferCells)) # upper right blockXll = xll + (ncols - 1) * cellsizeX blockYll = yul + cellsizeY startHeight = rightBoundary[0, 0] cornerPoint = (xll + (ncols - 0.5) * cellsizeX, yul + 0.5 * cellsizeY) for col in range(bufferCells): for row in range(bufferCells): centre = getCentreCoords(row, col, blockXll, blockYll, cellsizeX, cellsizeY, bufferCells, bufferCells) dist = (np.sqrt( pow(centre[0] - cornerPoint[0], 2) + pow(centre[1] - cornerPoint[1], 2))) # dist = min(row, col) * cellsizeX cornerBlock[row, col] = damp(startHeight, avgHeight, dist, bufferDist) cornerBlock[:, 0] = topBuffer[:, -1] cornerBlock[-1, :] = rightBuffer[0, :] block2STL(cornerBlock, stlFile, blockXll, blockYll, cellsizeX, cellsizeY, nodata, int(options.precision)) # lower left blockXll = xll - (bufferCells - 1) * cellsizeX blockYll = yll + (bufferCells - 1) * cellsizeY startHeight = leftBoundary[-1, 0] cornerPoint = (xll + 0.5 * cellsizeX, yll - 0.5 * cellsizeY) for col in range(bufferCells): for row in range(bufferCells): centre = getCentreCoords(row, col, blockXll, blockYll, cellsizeX, cellsizeY, bufferCells, bufferCells) dist = (np.sqrt( pow(centre[0] - cornerPoint[0], 2) + pow(centre[1] - cornerPoint[1], 2))) cornerBlock[row, col] = damp(startHeight, avgHeight, dist, bufferDist) cornerBlock[:, -1] = bottomBuffer[:, 0] cornerBlock[0, :] = leftBuffer[-1, :] block2STL(cornerBlock, stlFile, blockXll, blockYll, cellsizeX, cellsizeY, nodata, int(options.precision)) # upper left blockXll = xll - (bufferCells - 1) * cellsizeX blockYll = yul + cellsizeY startHeight = leftBoundary[0, 0] cornerPoint = (xll + 0.5 * cellsizeX, yul + 0.5 * cellsizeY) for col in range(bufferCells): for row in range(bufferCells): centre = getCentreCoords(row, col, blockXll, blockYll, cellsizeX, cellsizeY, bufferCells, bufferCells) dist = (np.sqrt( pow(centre[0] - cornerPoint[0], 2) + pow(centre[1] - cornerPoint[1], 2))) cornerBlock[row, col] = damp(startHeight, avgHeight, dist, bufferDist) cornerBlock[:, -1] = topBuffer[:, 0] cornerBlock[-1, :] = leftBuffer[0, :] block2STL(cornerBlock, stlFile, blockXll, blockYll, cellsizeX, cellsizeY, nodata, int(options.precision)) # lower right blockXll = xll + (ncols - 1) * cellsizeX blockYll = yll + (bufferCells - 1) * cellsizeY startHeight = rightBoundary[-1, 0] cornerPoint = (xll + (ncols - 0.5) * cellsizeX, yll - 0.5 * cellsizeY) for col in range(bufferCells): for row in range(bufferCells): centre = getCentreCoords(row, col, blockXll, blockYll, cellsizeX, cellsizeY, bufferCells, bufferCells) dist = (np.sqrt( pow(centre[0] - cornerPoint[0], 2) + pow(centre[1] - cornerPoint[1], 2))) cornerBlock[row, col] = damp(startHeight, avgHeight, dist, bufferDist) cornerBlock[:, 0] = bottomBuffer[:, -1] cornerBlock[0, :] = rightBuffer[-1, :] block2STL(cornerBlock, stlFile, blockXll, blockYll, cellsizeX, cellsizeY, nodata, int(options.precision)) stlFile.write("endsolid %s\n" % options.solidName) pg.finished()
def indexBuildingContours(self, buildingFile): """ Read building contours from shape file and create a spatial indexing. The indexing is implemented as a nested dict cols={row1,row2,row3}, where row1..n = {cell1,cell2,...,celln}, and cell1...n = [contour_1,contour_2,...,contour_n] Extent of index is set to extent of the used DEM (topo) @param buildingFile: shapefile with 3D building contours """ # open building contours shape-file log.info("Reading building contours") # Init reading of shape-files using OGR shapeDriver = ogr.GetDriverByName("ESRI Shapefile") buildings = shapeDriver.Open(buildingFile, update=0) if buildings is None: log.error("Could not open file with building contours") return 1 buildingsLayer = buildings.GetLayer() self.ind = {} # set up progress indicator pg = ProgressBar(buildingsLayer.GetFeatureCount(), sys.stdout) contourInd = 0 # Counter for building contour buildHeights = [] # List to store building height for plotting noGeom = 0 # Counter for invalid features without geometry reference # Read and process building contours contour = buildingsLayer.GetNextFeature() # get first feature while contour: if log.level == 0: pg.update(contourInd) log.debug("Contour %i" % contourInd) contourGeom = contour.GetGeometryRef() if contourGeom is None or contourGeom.GetPointCount() == 0: noGeom += 1 else: for i in range(1, contourGeom.GetPointCount()): x1 = contourGeom.GetX(i - 1) y1 = contourGeom.GetY(i - 1) z1 = contourGeom.GetZ(i - 1) x2 = contourGeom.GetX(i) y2 = contourGeom.GetY(i) z2 = contourGeom.GetZ(i) P1 = np.array([x1, y1, z1]) P2 = np.array([x2, y2, z2]) try: row, col = self.getInd((x2 + x1) / 2.0, (y1 + y2) / 2.0) except ValueError: # outside extent row = col = None if row is not None and col is not None: if row not in self.ind: self.ind[row] = {} self.ind[row][col] = [] elif col not in self.ind[row]: self.ind[row][col] = [] self.ind[row][col].append(Segment(P1, P2)) contour.Destroy() contour = buildingsLayer.GetNextFeature() contourInd += 1 # close datasource for building contours buildings.Destroy() pg.finished() if noGeom > 0: log.warning("Found %i building contours without geometry" % noGeom)
def main(): # -----------Setting up and unsing option parser----------------------- parser = OptionParser(usage=usage, version=version) parser.add_option("-d", "--doc", action="store_true", dest="doc", help="Prints more detailed documentation and exit") parser.add_option("-v", action="store_const", const=logging.DEBUG, dest="loglevel", default=get_loglevel(), help="Produce verbose output") parser.add_option("--no-progress", action="store_const", dest="progressStream", const=None, default=sys.stdout, help="turn off the progress bar") parser.add_option("-o", "--output", action="store", dest="outfileName", default=None, help="Output file") parser.add_option("-i", "--input", action="store", dest="infileName", help="Input raster") parser.add_option("--bbox", action="store", dest="bbox", help="Only read data within bbox," + " --box <\"x1,y1,x2,y2\"") parser.add_option("--reclassify", action="store", dest="classTable", help="Tab-separated table with code " + "and z0 value for each landuse class") parser.add_option("--reclassFromColumn", action="store", dest="reclassFromColumn", help="Header of reclass table column " + "containing values to " + "reclass (default is to use first column of classTable)") parser.add_option("--reclassToColumn", action="store", dest="reclassToColumn", help="Header of reclass table column containing codes," + " default is to use first column (default is to use " + "second column of classTable)") parser.add_option("--resample", action="store", dest="cellFactor", help="Resample grid by dividing cellsize with a" + " factor. Factor <1 results in refinement." + " Reprojection uses temporary refinement") parser.add_option("--resamplingMethod", action="store", dest="resamplingMethod", help="For cellFactor > 1: " + "Choose between 'mean', 'sum', 'majority' or" + " 'count', default is sum," "For cellFactor < 1, choose between: " + "'keepTotal' and 'keepValue', default is 'keepTotal'") parser.add_option("--summarize", action="store_true", dest="summarize", help="Print a summary of the input grid properties") parser.add_option("--bandIndex", action="store", dest="bandIndex", help="Band index to read from", default=1) parser.add_option("--dataType", action="store", dest="dataType", help="Output raster/shape data type", default=None) parser.add_option("--toShape", action="store_true", dest="toShape", help="output as shape file", default=None) parser.add_option("--fieldName", metavar='FIELD', action="store", dest="fieldName", help="write data in shape file to FIELD," + " default is 'value'", default=None) parser.add_option("--filter", action="store", dest="filter", help="Filter out data equal or below" + " limit in shape output", default=None) parser.add_option("-t", "--template", action="store", dest="template", help="Header for output raster when reprojecting") parser.add_option("--fromProj", dest="fromProj", help="Input raster proj4 definition string") parser.add_option("--toProj", dest="toProj", help="Output raster proj4 definition string") (options, args) = parser.parse_args() #------------Setting up logging capabilities ----------- # Setup logging logging.basicConfig( format='%(levelname)s:%(name)s: %(message)s', level=options.loglevel, ) log = logging.getLogger(__name__) #------------Process and validate options--------------- if options.doc: print doc sys.exit() if len(args) > 0: parser.error("Incorrect number of arguments") #validate infile path if options.infileName is not None: inFilePath = path.abspath(options.infileName) if not path.exists(inFilePath): log.error("Input raster %s does not exist" % options.infileName) sys.exit(1) else: parser.error("No input data specified") #validate outfile path if options.outfileName is not None: outFilePath = path.abspath(options.outfileName) if options.toShape and ".shp" not in outFilePath: parser.error("Output shape has to to be specified with" + " .shp extension") else: outFilePath = None #Validate fieldName option if options.toShape: if options.fieldName == "": parser.error("fieldName can't be an empty string") fieldName = options.fieldName or "value" elif options.fieldName is not None: parser.error("fieldName option only allowed together" + " with shape output") # Validate filter option and convert filter to numeric value if present if not options.toShape and options.filter is not None: parser.error("Filter option only allowed together with shape output") elif options.toShape: if options.filter is not None: filter = float(options.filter) else: filter = None # read and process reclass table file if options.classTable is not None: reclassFromColumn = options.reclassFromColumn reclassToColumn = options.reclassToColumn if reclassFromColumn is not None and reclassToColumn is not None: desc = [{"id": reclassFromColumn, "type": float}, {"id": reclassToColumn, "type": float}] classTable = datatable.DataTable(desc=desc) classTable.read(options.classTable) else: classTable = datatable.DataTable() classTable.read(options.classTable) reclassFromColumn = classTable.desc[0]["id"] reclassToColumn = classTable.desc[1]["id"] classTable.convertCol(reclassFromColumn, float) classTable.convertCol(reclassToColumn, float) classTable.setKeys([reclassFromColumn]) log.debug("Successfully read landuse class table") classDict = {} for row in classTable.data: classDict[row[classTable.colIndex[reclassFromColumn]] ] = row[classTable.colIndex[reclassToColumn]] if options.cellFactor is not None: cellFactor = float(options.cellFactor) if cellFactor > 1 and \ options.resamplingMethod not in ('sum', 'mean', 'majority', 'count', None): log.error( "Invalid resampling method, valid options for grid " + "coarsening are 'sum' " + "and 'mean' and 'majority', " + "specified %s" % options.resamplingMethod ) sys.exit(1) elif cellFactor < 1 and \ options.resamplingMethod not in ('keepTotal', 'keepValue', None): log.error( "Invalid resampling method, valid options for grid " + "coarsening are 'keepTotal' and 'keepValue'" + ", specified %s" % resamplingMethod) sys.exit(1) # setting default resampling methods if cellFactor > 1 and \ options.resamplingMethod is None: resamplingMethod = 'sum' elif options.resamplingMethod is None: resamplingMethod = 'keepTotal' else: resamplingMethod = options.resamplingMethod if options.resamplingMethod == 'majority' or \ options.resamplingMethod == 'count' and \ options.toProj is not None: log.error( "Resampling method " + "%s not possible to " % options.resamplingMethod + "combine with reprojection") sys.exit(1) # Assure that gdal is present if not __gdal_loaded__: raise OSError("Function readGDAL needs GDAL with python bindings") # register all of the raster drivers gdal.AllRegister() ds = gdal.Open(inFilePath, GA_ReadOnly) if ds is None: print 'Could not open ' + filename sys.exit(1) ncols = ds.RasterXSize nrows = ds.RasterYSize nbands = ds.RasterCount # Info used for georeferencing geoTransform = ds.GetGeoTransform() xul = geoTransform[0] # top left x cellsizeX = geoTransform[1] # w-e pixel resolution rot1 = geoTransform[2] # rotation, 0 if image is "north up" yul = geoTransform[3] # top left y rot2 = geoTransform[4] # rotation, 0 if image is "north up" cellsizeY = geoTransform[5] # n-s pixel resolution proj = osr.SpatialReference(ds.GetProjection()) # Calculate lower left corner xll = xul yll = yul + nrows * cellsizeY # cellsizeY should be a negative value # Rotated rasters not handled...yet if rot1 != 0 or rot2 != 0: print 'Rotated rasters are not supported by pyAirviro.geo.raster' sys.exit(1) if abs(cellsizeX) != abs(cellsizeY): print('Non-homogenous cellsizes are not' + ' supported by pyAirviro.geo.raster') sys.exit(1) bandIndex = int(options.bandIndex) band = ds.GetRasterBand(bandIndex) nodata = band.GetNoDataValue() # If no nodata value is present in raster, set to -9999 for completeness if nodata is None: nodata = -9999 # Read data from a window defined by option --bbox <"x1,y1,x2,y2"> if options.bbox is not None: xur = xll + ncols * cellsizeX try: x1, y1, x2, y2 = map(float, options.bbox.split(",")) except: log.error("Invalid value for option --bbox <\"x1,y1,x2,y2\">") sys.exit(1) # Check if totally outside raster extent if x2 < xll or y2 < yll or x1 > xur or y1 > yul: log.error("Trying to extract outside grid boundaries") sys.exit(1) # Limit bbox to raster extent if x1 < xll: x1 = xll if x2 > xur: x2 = xur if y1 < yll: y1 = yll if y2 > yul: y2 = yul # estimate min and max of rows and cols colmin = int((x1 - xll) / cellsizeX) colmaxdec = (x2 - xll) / float(cellsizeX) rowmin = int((yul - y2) / abs(cellsizeY)) rowmaxdec = (yul - y1) / float(abs(cellsizeY)) if (colmaxdec - int(colmaxdec)) > 0: colmax = int(colmaxdec) else: colmax = int(colmaxdec - 1) if (rowmaxdec - int(rowmaxdec)) > 0: rowmax = int(rowmaxdec) else: rowmax = int(rowmaxdec - 1) nrows = rowmax - rowmin + 1 ncols = colmax - colmin + 1 xll = xll + colmin * cellsizeX yll = yul + (rowmax + 1) * cellsizeY # cellsizeY is negative yul = yll - nrows * cellsizeY else: rowmin = 0 colmin = 0 # process option for resampling if options.cellFactor is not None: cellFactor = float(cellFactor) else: cellFactor = 1 if cellFactor >= 1: procYBlockSize = int(cellFactor) else: procYBlockSize = 1 if options.toProj is None: # Set output raster dimensions and cellsize newNcols = int(ncols / cellFactor) newNrows = int(nrows / cellFactor) newCellsizeX = cellsizeX * cellFactor newCellsizeY = cellsizeY * cellFactor # is a negative value as before newXll = xll newYll = yll newYul = yul newNodata = nodata else: # Create coordinate transform if options.fromProj is None: src_srs = proj else: src_srs = osr.SpatialReference() src_srs.ImportFromProj4(options.fromProj) tgt_srs = osr.SpatialReference() tgt_srs.ImportFromProj4(options.toProj) import pdb;pdb.set_trace() coordTrans = osr.CoordinateTransformation(src_srs, tgt_srs) if options.template is None: # estimate extent from input newNcols = ncols newNrows = nrows newCellsizeX = cellsizeX newCellsizeY = cellsizeY newNodata = nodata newXll, newYll, z = coordTrans.TransformPoint(xll, yll) newXll = int(newXll) newYll = int(newYll) newYul = newYll - newNrows * newCellsizeY else: header = open(options.template, "r").read() newNcols = int( re.compile("ncols\s*([0-9]*)").search(header).group(1)) newNrows = int( re.compile("nrows\s*([0-9]*)").search(header).group(1)) newCellsizeX = float( re.compile("cellsize\s*([0-9]*)").search(header).group(1)) newCellsizeY = -1 * newCellsizeX try: newNodata = float( re.compile( "NODATA_value\s*(.*?)\n" ).search(header).group(1) ) except AttributeError: newNodata = float( re.compile( "nodata_value\s*(.*?)\n" ).search(header).group(1) ) newXll = float( re.compile("xllcorner\s*(.*?)\n").search(header).group(1)) newYll = float( re.compile("yllcorner\s*(.*?)\n").search(header).group(1)) newYul = newYll - newNrows * newCellsizeY # Processing of data is made for blocks of the following size # Important - blocks are set to cover all columns for simpler processing # This might not always be optimal for read/write speed procXBlockSize = ncols # process option for dataType if options.toShape: dataTypes, defaultDataType = ogrDataTypes, 'Real' else: dataTypes, defaultDataType = gdalDataTypes, 'Float32' try: dataType = dataTypes[options.dataType or defaultDataType] except KeyError: log.error( "Unknown datatype choose between: %s" % ",".join(dataTypes.keys())) sys.exit(1) # Create and configure output raster data source if not options.toShape and outFilePath is not None: # Creates a raster dataset with 1 band mem_ds = gdal.GetDriverByName('MEM').Create(outFilePath, newNcols, newNrows, 1, dataType) if mem_ds is None: print "Error: could not create output raster" sys.exit(1) outGeotransform = [newXll, newCellsizeX, 0, newYul, 0, newCellsizeY] mem_ds.SetGeoTransform(outGeotransform) if options.toProj is not None: mem_ds.SetProjection(tgt_srs.ExportToWkt()) else: mem_ds.SetProjection(proj) outBand = mem_ds.GetRasterBand(1) outBand.SetNoDataValue(newNodata) # Set nodata-value if options.toProj is not None: outArray = np.zeros((newNrows, newNcols)) nvals = np.zeros((newNrows, newNcols)) outDef = {"ncols": newNcols, "nrows": newNrows, "xll": newXll, "yll": newYll, "yul": newYll - newNrows * newCellsizeY, "xur": newXll + newNcols * newCellsizeX, "cellsize": newCellsizeX } # Create and inititialize output vector data source if options.toShape: shapeDriver = ogr.GetDriverByName('ESRI Shapefile') if path.exists(outFilePath): shapeDriver.DeleteDataSource(outFilePath) shapeFile = shapeDriver.CreateDataSource(outFilePath) if shapeFile is None: log.error("Could not open output shapefile %s" % outFilePath) sys.exit(1) layer = shapeFile.CreateLayer(outFilePath, geom_type=ogr.wkbPolygon) fieldDefn = ogr.FieldDefn(fieldName, dataType) layer.CreateField(fieldDefn) # inititialize input grid summary inputGridSummary = {"sum": 0, "mean": 0, "nnodata": 0, "nnegative": 0, "xll": xll, "yll": yll, "ncols": ncols, "nrows": nrows, "cellsizeX": cellsizeX, "cellsizeY": cellsizeY, "nodatavalue": nodata} outputGridSummary = {"sum": 0, "mean": 0, "nnodata": 0, "nnegative": 0, "xll": newXll, "yll": newYll, "ncols": newNcols, "nrows": newNrows, "cellsizeX": newCellsizeX, "cellsizeY": newCellsizeY, "nodatavalue": newNodata} # Loop over block of raster (at least one row in each block) rowsOffset = 0 errDict = {} pg = ProgressBar(nrows, options.progressStream) for i in range(0, nrows, int(procYBlockSize)): pg.update(i) data = band.ReadAsArray(xoff=colmin, yoff=rowmin + i, win_xsize=procXBlockSize, win_ysize=procYBlockSize) if options.summarize: inputGridSummary = updateGridSummary(data, inputGridSummary, nodata) if options.classTable is not None: try: data = reclassBlock(data, classDict, errDict) except IOError as e: log.error(str(e)) sys.exit(1) if options.cellFactor is not None: try: data = resampleBlock(data[:, :], cellFactor, resamplingMethod, nodata) except ValueError as err: log.error(err.message) sys.exit(1) except IOError as err: log.error(err.message) sys.exit(1) if options.toProj is not None: blockDef = {"nrows": int(procYBlockSize / cellFactor), "ncols": int(procXBlockSize / cellFactor), "xll": xll + (colmin) * cellsizeX, "yul": yll - (nrows - rowmin - i) * cellsizeY, "cellsize": cellsizeX * cellFactor, "nodata": nodata} reprojectBlock(outArray, data, cellFactor, blockDef, outDef, coordTrans, nvals) if outFilePath is not None: if options.toShape: blockYll = yul + i * newCellsizeY # newCellsizeY is negative blockXll = xll block2vector(data, layer, blockXll, blockYll, newCellsizeX, newCellsizeY, nodata, fieldName, filter) elif options.toProj is None: outBand.WriteArray(data, 0, rowsOffset) # Write block to raster outBand.FlushCache() # Write data to disk if options.summarize: outputGridSummary = updateGridSummary(data, outputGridSummary, nodata) rowsOffset += int(procYBlockSize / cellFactor) # Update offset if options.toShape: shapeFile.Destroy() if not options.toShape and options.toProj is not None: if options.resamplingMethod is not None and resamplingMethod == "mean": outArray = np.where(nvals > 0, outArray / nvals, outArray) outBand.WriteArray(outArray, 0, 0) outBand.FlushCache() # Write data to disk if options.outfileName is not None and not options.toShape: outputDriver = ds.GetDriver() output_ds = outputDriver.CreateCopy(options.outfileName, mem_ds, 0) output_ds = None mem_ds = None pg.finished() if options.summarize: print "\nInput raster summary" printGridSummary(inputGridSummary, prefix="input") print "\nOutput raster summary" printGridSummary(outputGridSummary, prefix="output") if errDict != {}: print "Errors/warnings during processing:" for err, nerr in errDict.items(): print "%s err in %i cells" % (err, nerr)
def main(): #-----------Setting up and unsing option parser----------------------- parser = OptionParser(usage=usage, version=version) parser.add_option("-d", "--doc", action="store_true", dest="doc", help="Prints more detailed documentation and exit") parser.add_option("-l", "--loglevel", action="store", dest="loglevel", default=2, help="Sets the loglevel (0-3 where 3=full logging)") parser.add_option("--no-progress", action="store_const", dest="progressStream", const=None, default=sys.stdout, help="turn off the progress bar") parser.add_option("-o", "--output", action="store", dest="outfileName", default=None, help="Output file") parser.add_option("-i", "--input", action="store", dest="infileName", help="Input raster") parser.add_option("--bandIndex", action="store", dest="bandIndex", help="Band index to read from", default=1) parser.add_option("--dataType", action="store", dest="dataType", help="Output data type", default=None) parser.add_option("--featureType", action="store", dest="featureType", help="Type of feature ('poly' or 'point')," + " default='point'", default='point') parser.add_option("--fieldName", metavar='FIELD', action="store", dest="fieldName", help="write data in shape file to FIELD," + " default='value'", default='value') parser.add_option("--filter", action="store", dest="filter", help="Filter out data equal or below" + " limit in shape output", default=None) (options, args) = parser.parse_args() #------------Setting up logging capabilities ----------- rootLogger = logger.RootLogger(int(options.loglevel)) global log log = rootLogger.getLogger(sys.argv[0]) #------------Process and validate options--------------- if options.doc: print __doc__ sys.exit() if len(args) > 0: parser.error("Incorrect number of arguments") #validate infile path if options.infileName is not None: inFilePath = options.infileName if not path.exists(inFilePath): log.error("Input raster does not exist") sys.exit(1) else: log.error("No input file specified") sys.exit(1) #validate outfile path if options.outfileName is not None: outFilePath = path.abspath(options.outfileName) if ".shp" not in outFilePath: log.error("Output shape must have .shp extension") sys.exit(1) else: log.error("No output file specified") sys.exit(1) #Validate fieldName option fieldName = options.fieldName if fieldName == "": parser.error("fieldName can't be an empty string") #Validate filter option and convert filter to numeric value if present if options.filter is not None: filter = float(options.filter) else: filter = None #validate featureType if options.featureType not in ('point', 'poly'): log.error("Invalid feature type, must be either 'point' or 'poly'") sys.exit(1) #Assure that gdal is present if not __gdal_loaded__: raise OSError("Function readGDAL needs GDAL with python bindings") # register all of the raster drivers gdal.AllRegister() ds = gdal.Open(inFilePath, GA_ReadOnly) if ds is None: log.error('Could not open ' + inFilePath) sys.exit(1) ncols = ds.RasterXSize nrows = ds.RasterYSize nbands = ds.RasterCount #Info used for georeferencing geoTransform = ds.GetGeoTransform() xul = geoTransform[0] # top left x cellsizeX = geoTransform[1] # w-e pixel resolution rot1 = geoTransform[2] # rotation, 0 if image is "north up" yul = geoTransform[3] # top left y rot2 = geoTransform[4] # rotation, 0 if image is "north up" cellsizeY = geoTransform[5] # n-s pixel resolution proj = ds.GetProjection() #Calculate lower left corner xll = xul yll = yul + nrows * cellsizeY # cellsizeY should be a negative value #Rotated rasters not handled...yet if rot1 != 0 or rot2 != 0: log.error('Handling of rotated rasters are not implemented yet') sys.exit(1) bandIndex = int(options.bandIndex) band = ds.GetRasterBand(bandIndex) nodata = band.GetNoDataValue() #If no nodata value is present in raster, set to -9999 for completeness if nodata is None: nodata = -9999 rowmin = 0 colmin = 0 #Processing of data is made for blocks of the following size #Important - blocks are set to cover all columns for simpler processing #This might not always be optimal for read/write speed procXBlockSize = ncols procYBlockSize = 1 #process option for dataType dataTypes, defaultDataType = ogrDataTypes, 'Real' try: dataType = dataTypes[options.dataType or defaultDataType] except KeyError: log.error( "Unknown datatype choose between: %s" % ",".join(dataTypes.keys())) sys.exit(1) #Create and inititialize output vector data source shapeDriver = ogr.GetDriverByName('ESRI Shapefile') if path.exists(outFilePath): shapeDriver.DeleteDataSource(outFilePath) shapeFile = shapeDriver.CreateDataSource(outFilePath) if shapeFile is None: log.error("Could not open output shapefile %s" % outFilePath) sys.exit(1) if options.featureType == "point": layer = shapeFile.CreateLayer(outFilePath, geom_type=ogr.wkbPoint) else: layer = shapeFile.CreateLayer(outFilePath, geom_type=ogr.wkbPolygon) fieldDefn = ogr.FieldDefn(fieldName, dataType) layer.CreateField(fieldDefn) #Loop over block of raster (at least one row in each block) rowsOffset = 0 pg = ProgressBar(nrows, options.progressStream) for i in range(0, nrows, procYBlockSize): pg.update(i) data = band.ReadAsArray(xoff=colmin, yoff=rowmin + i, win_xsize=procXBlockSize, win_ysize=procYBlockSize) blockYll = yul + i * cellsizeY # newCellsizeY is negative blockXll = xll if options.featureType == 'point': block2point(data, layer, blockXll, blockYll, cellsizeX, cellsizeY, nodata, fieldName, filter) else: block2poly(data, layer, blockXll, blockYll, cellsizeX, cellsizeY, nodata, fieldName, filter) rowsOffset += procYBlockSize # Update offset shapeFile.Destroy() pg.finished()
def main(): # -----------Setting up and unsing option parser---------------- parser = OptionParser(usage=usage, version=version) parser.add_option("-d", "--doc", action="store_true", dest="doc", help="Prints more detailed documentation and exit") parser.add_option("-v", "--verbose", action="store_const", const=logging.INFO, dest="loglevel", default=logging.WARNING, help="Produce verbose output") parser.add_option("--no-progress", action="store_const", dest="progressStream", const=None, default=sys.stdout, help="turn off the progress bar") parser.add_option("-i", "--input", action="store", dest="infileName", help="Input raster") parser.add_option("--bandIndex", action="store", dest="bandIndex", help="Band index to read from", default=1) parser.add_option("-o", "--output", action="store", dest="output", default=None, help="Output file") parser.add_option("--solidName", default="TOPO", action="store", dest="solidName", help="Name of solid in STL-output") parser.add_option("--precision", default=6, action="store", dest="precision", help="Precision of coordinates in STL-output") parser.add_option("--buffer", action="store", dest="buffer", help="Add buffer distance to topo, " + "damping out differences in elevation") (options, args) = parser.parse_args() # configuring logging FORMAT = '%(levelname)s %(message)s' logging.basicConfig(format=FORMAT) # ------------Process and validate options--------------- if options.doc: print __doc__ sys.exit() if len(args) > 0: parser.error("Incorrect number of arguments") # validate infile path if options.infileName is not None: inFilePath = options.infileName if not path.exists(inFilePath): log.error("Input raster does not exist") sys.exit(1) else: log.error("No input file specified") sys.exit(1) # validate outfile path if options.output is None: log.error("No output file specified") sys.exit(1) if options.buffer is not None: bufferDist = float(options.buffer) # Assure that gdal is present if not __gdal_loaded__: raise OSError("Function readGDAL needs GDAL with python bindings") # register all of the raster drivers gdal.AllRegister() ds = gdal.Open(inFilePath, GA_ReadOnly) if ds is None: log.error('Could not open ' + inFilePath) sys.exit(1) ncols = ds.RasterXSize nrows = ds.RasterYSize # nbands = ds.RasterCount # Info used for georeferencing geoTransform = ds.GetGeoTransform() xul = geoTransform[0] # top left x cellsizeX = geoTransform[1] # w-e pixel resolution rot1 = geoTransform[2] # rotation, 0 if image is "north up" yul = geoTransform[3] # top left y rot2 = geoTransform[4] # rotation, 0 if image is "north up" cellsizeY = geoTransform[5] # n-s pixel resolution # proj = ds.GetProjection() # Calculate lower left corner xll = xul yll = yul + nrows * cellsizeY # cellsizeY should be a negative value # Rotated rasters not handled...yet if rot1 != 0 or rot2 != 0: log.error('Handling of rotated rasters are not implemented yet') sys.exit(1) bandIndex = int(options.bandIndex) band = ds.GetRasterBand(bandIndex) nodata = band.GetNoDataValue() # If no nodata value is present in raster, set to -9999 for completeness if nodata is None: nodata = -9999 # Processing of data is made for blocks of the following size # two rows are processed at a time since triangles # are created between cell centres. procXBlockSize = ncols procYBlockSize = 2 with open(options.output, 'w') as stlFile: pg = ProgressBar(nrows, options.progressStream) stlFile.write("solid " + options.solidName + "\n") # avgHeight = 0 # write original cells to STL # Loop over blocks of raster for i in range(0, nrows - 1, 1): pg.update(i) data = band.ReadAsArray(xoff=0, yoff=i, win_xsize=procXBlockSize, win_ysize=procYBlockSize) if nodata in data: log.error("Nodata value found in raster," + " this should be interpolated before" + " converting to STL") sys.exit(1) # newCellsizeY is negative blockYll = yul + (procYBlockSize + i) * cellsizeY blockXll = xll block2STL(data, stlFile, blockXll, blockYll, cellsizeX, cellsizeY, nodata, int(options.precision)) # avgHeight += np.mean(data) # avgHeight /= (nrows - 1) # write buffer cells to STL leftBoundary = band.ReadAsArray( xoff=0, yoff=0, win_xsize=1, win_ysize=nrows ) rightBoundary = band.ReadAsArray( xoff=ncols - 1, yoff=0, win_xsize=1, win_ysize=nrows ) topBoundary = band.ReadAsArray( xoff=0, yoff=0, win_xsize=procXBlockSize, win_ysize=1 ) bottomBoundary = band.ReadAsArray( xoff=0, yoff=nrows - 1, win_xsize=procXBlockSize, win_ysize=1 ) avgHeight = 0.25 * ( leftBoundary.mean() + rightBoundary.mean() + topBoundary.mean() + bottomBoundary.mean() ) if options.buffer is not None: # overlap of one cell since triangles are created between # cell centres bufferCells = int(bufferDist / cellsizeX) + 1 leftBuffer = leftBoundary[:] for i in range(1, bufferCells): dist = i * cellsizeX leftBuffer = np.hstack(( damp(leftBoundary, avgHeight, dist, bufferDist), leftBuffer)) block2STL(leftBuffer, stlFile, xll - (bufferCells - 1) * cellsizeX, yll, cellsizeX, cellsizeY, nodata, int(options.precision)) rightBuffer = rightBoundary[:] for i in range(1, bufferCells): dist = i * cellsizeX rightBuffer = np.hstack(( rightBuffer, damp(rightBoundary, avgHeight, dist, bufferDist))) block2STL(rightBuffer, stlFile, xll + (ncols - 1) * cellsizeX, yll, cellsizeX, cellsizeY, nodata, int(options.precision)) topBuffer = topBoundary[:] for i in range(1, bufferCells): dist = abs(i * cellsizeY) topBuffer = np.vstack(( damp(topBoundary, avgHeight, dist, bufferDist), topBuffer) ) block2STL(topBuffer, stlFile, xll, yul + cellsizeY, cellsizeX, cellsizeY, nodata, int(options.precision)) bottomBuffer = bottomBoundary[:] for i in range(1, bufferCells): dist = abs(i * cellsizeY) bottomBuffer = np.vstack(( bottomBuffer, damp(bottomBoundary, avgHeight, dist, bufferDist)) ) block2STL(bottomBuffer, stlFile, xll, yll + (bufferCells - 1) * cellsizeY, cellsizeX, cellsizeY, nodata, int(options.precision)) # Add corner buffers cornerBlock = np.ones((bufferCells, bufferCells)) # upper right blockXll = xll + (ncols - 1) * cellsizeX blockYll = yul + cellsizeY startHeight = rightBoundary[0, 0] cornerPoint = (xll + (ncols - 0.5) * cellsizeX, yul + 0.5 * cellsizeY) for col in range(bufferCells): for row in range(bufferCells): centre = getCentreCoords(row, col, blockXll, blockYll, cellsizeX, cellsizeY, bufferCells, bufferCells) dist = (np.sqrt(pow(centre[0] - cornerPoint[0], 2) + pow(centre[1] - cornerPoint[1], 2))) # dist = min(row, col) * cellsizeX cornerBlock[row, col] = damp(startHeight, avgHeight, dist, bufferDist) cornerBlock[:, 0] = topBuffer[:, -1] cornerBlock[-1, :] = rightBuffer[0, :] block2STL(cornerBlock, stlFile, blockXll, blockYll, cellsizeX, cellsizeY, nodata, int(options.precision)) # lower left blockXll = xll - (bufferCells - 1) * cellsizeX blockYll = yll + (bufferCells - 1) * cellsizeY startHeight = leftBoundary[-1, 0] cornerPoint = (xll + 0.5 * cellsizeX, yll - 0.5 * cellsizeY) for col in range(bufferCells): for row in range(bufferCells): centre = getCentreCoords(row, col, blockXll, blockYll, cellsizeX, cellsizeY, bufferCells, bufferCells) dist = (np.sqrt(pow(centre[0] - cornerPoint[0], 2) + pow(centre[1] - cornerPoint[1], 2))) cornerBlock[row, col] = damp(startHeight, avgHeight, dist, bufferDist) cornerBlock[:, -1] = bottomBuffer[:, 0] cornerBlock[0, :] = leftBuffer[-1, :] block2STL(cornerBlock, stlFile, blockXll, blockYll, cellsizeX, cellsizeY, nodata, int(options.precision)) # upper left blockXll = xll - (bufferCells - 1) * cellsizeX blockYll = yul + cellsizeY startHeight = leftBoundary[0, 0] cornerPoint = (xll + 0.5 * cellsizeX, yul + 0.5 * cellsizeY) for col in range(bufferCells): for row in range(bufferCells): centre = getCentreCoords(row, col, blockXll, blockYll, cellsizeX, cellsizeY, bufferCells, bufferCells) dist = (np.sqrt(pow(centre[0] - cornerPoint[0], 2) + pow(centre[1] - cornerPoint[1], 2))) cornerBlock[row, col] = damp(startHeight, avgHeight, dist, bufferDist) cornerBlock[:, -1] = topBuffer[:, 0] cornerBlock[-1, :] = leftBuffer[0, :] block2STL(cornerBlock, stlFile, blockXll, blockYll, cellsizeX, cellsizeY, nodata, int(options.precision)) # lower right blockXll = xll + (ncols - 1) * cellsizeX blockYll = yll + (bufferCells - 1) * cellsizeY startHeight = rightBoundary[-1, 0] cornerPoint = (xll + (ncols - 0.5) * cellsizeX, yll - 0.5 * cellsizeY) for col in range(bufferCells): for row in range(bufferCells): centre = getCentreCoords(row, col, blockXll, blockYll, cellsizeX, cellsizeY, bufferCells, bufferCells) dist = (np.sqrt(pow(centre[0] - cornerPoint[0], 2) + pow(centre[1] - cornerPoint[1], 2))) cornerBlock[row, col] = damp(startHeight, avgHeight, dist, bufferDist) cornerBlock[:, 0] = bottomBuffer[:, -1] cornerBlock[0, :] = rightBuffer[-1, :] block2STL(cornerBlock, stlFile, blockXll, blockYll, cellsizeX, cellsizeY, nodata, int(options.precision)) stlFile.write("endsolid %s\n" % options.solidName) pg.finished()
def main(): #-----------Setting up and unsing option parser----------------------- parser = OptionParser(usage=usage, version=version) parser.add_option("-d", "--doc", action="store_true", dest="doc", help="Prints more detailed documentation and exit") parser.add_option("-l", "--loglevel", action="store", dest="loglevel", default=2, help="Sets the loglevel (0-3 where 3=full logging)") parser.add_option("--no-progress", action="store_const", dest="progressStream", const=None, default=sys.stdout, help="turn off the progress bar") parser.add_option("-o", "--output", action="store", dest="outfileName", default=None, help="Output file") parser.add_option("-i", "--input", action="store", dest="infileName", help="Input raster") parser.add_option("--bandIndex", action="store", dest="bandIndex", help="Band index to read from", default=1) parser.add_option("--dataType", action="store", dest="dataType", help="Output data type", default=None) parser.add_option("--featureType", action="store", dest="featureType", help="Type of feature ('poly' or 'point')," + " default='point'", default='point') parser.add_option("--fieldName", metavar='FIELD', action="store", dest="fieldName", help="write data in shape file to FIELD," + " default='value'", default='value') parser.add_option("--filter", action="store", dest="filter", help="Filter out data equal or below" + " limit in shape output", default=None) (options, args) = parser.parse_args() #------------Setting up logging capabilities ----------- rootLogger = logger.RootLogger(int(options.loglevel)) global log log = rootLogger.getLogger(sys.argv[0]) #------------Process and validate options--------------- if options.doc: print __doc__ sys.exit() if len(args) > 0: parser.error("Incorrect number of arguments") #validate infile path if options.infileName is not None: inFilePath = options.infileName if not path.exists(inFilePath): log.error("Input raster does not exist") sys.exit(1) else: log.error("No input file specified") sys.exit(1) #validate outfile path if options.outfileName is not None: outFilePath = path.abspath(options.outfileName) if ".shp" not in outFilePath: log.error("Output shape must have .shp extension") sys.exit(1) else: log.error("No output file specified") sys.exit(1) #Validate fieldName option fieldName = options.fieldName if fieldName == "": parser.error("fieldName can't be an empty string") #Validate filter option and convert filter to numeric value if present if options.filter is not None: filter = float(options.filter) else: filter = None #validate featureType if options.featureType not in ('point', 'poly'): log.error("Invalid feature type, must be either 'point' or 'poly'") sys.exit(1) #Assure that gdal is present if not __gdal_loaded__: raise OSError("Function readGDAL needs GDAL with python bindings") # register all of the raster drivers gdal.AllRegister() ds = gdal.Open(inFilePath, GA_ReadOnly) if ds is None: log.error('Could not open ' + inFilePath) sys.exit(1) ncols = ds.RasterXSize nrows = ds.RasterYSize nbands = ds.RasterCount #Info used for georeferencing geoTransform = ds.GetGeoTransform() xul = geoTransform[0] # top left x cellsizeX = geoTransform[1] # w-e pixel resolution rot1 = geoTransform[2] # rotation, 0 if image is "north up" yul = geoTransform[3] # top left y rot2 = geoTransform[4] # rotation, 0 if image is "north up" cellsizeY = geoTransform[5] # n-s pixel resolution proj = ds.GetProjection() #Calculate lower left corner xll = xul yll = yul + nrows * cellsizeY # cellsizeY should be a negative value #Rotated rasters not handled...yet if rot1 != 0 or rot2 != 0: log.error('Handling of rotated rasters are not implemented yet') sys.exit(1) bandIndex = int(options.bandIndex) band = ds.GetRasterBand(bandIndex) nodata = band.GetNoDataValue() #If no nodata value is present in raster, set to -9999 for completeness if nodata is None: nodata = -9999 rowmin = 0 colmin = 0 #Processing of data is made for blocks of the following size #Important - blocks are set to cover all columns for simpler processing #This might not always be optimal for read/write speed procXBlockSize = ncols procYBlockSize = 1 #process option for dataType dataTypes, defaultDataType = ogrDataTypes, 'Real' try: dataType = dataTypes[options.dataType or defaultDataType] except KeyError: log.error("Unknown datatype choose between: %s" % ",".join(dataTypes.keys())) sys.exit(1) #Create and inititialize output vector data source shapeDriver = ogr.GetDriverByName('ESRI Shapefile') if path.exists(outFilePath): shapeDriver.DeleteDataSource(outFilePath) shapeFile = shapeDriver.CreateDataSource(outFilePath) if shapeFile is None: log.error("Could not open output shapefile %s" % outFilePath) sys.exit(1) if options.featureType == "point": layer = shapeFile.CreateLayer(outFilePath, geom_type=ogr.wkbPoint) else: layer = shapeFile.CreateLayer(outFilePath, geom_type=ogr.wkbPolygon) fieldDefn = ogr.FieldDefn(fieldName, dataType) layer.CreateField(fieldDefn) #Loop over block of raster (at least one row in each block) rowsOffset = 0 pg = ProgressBar(nrows, options.progressStream) for i in range(0, nrows, procYBlockSize): pg.update(i) data = band.ReadAsArray(xoff=colmin, yoff=rowmin + i, win_xsize=procXBlockSize, win_ysize=procYBlockSize) blockYll = yul + i * cellsizeY # newCellsizeY is negative blockXll = xll if options.featureType == 'point': block2point(data, layer, blockXll, blockYll, cellsizeX, cellsizeY, nodata, fieldName, filter) else: block2poly(data, layer, blockXll, blockYll, cellsizeX, cellsizeY, nodata, fieldName, filter) rowsOffset += procYBlockSize # Update offset shapeFile.Destroy() pg.finished()
def main(): # Parse command line arguments parser = argparse.ArgumentParser(description=__doc__) utils.add_standard_command_options(parser) parser.add_argument("-f", "--format", action="store", dest="format", default="ESRI Shapefile", help="Format of road network") parser.add_argument("--inRoads", required=True, action="store", dest="inRoads", help="Input road network") parser.add_argument("--outRoads", required=True, action="store", dest="outRoads", help="Output road network") parser.add_argument("--buildings", required=True, action="store", dest="buildings", help="Input building roof contours (3D)") parser.add_argument("--topo", required=True, action="store", dest="topo", help="Input raster DEM") parser.add_argument("--split", type=int, action="store", dest="split", help="Threshold in changed road direction (degrees)" + " for when to split road") args = parser.parse_args() if not path.exists(args.topo): log.error("Input raster does not exist") sys.exit(1) if not path.exists(args.buildings): log.error("Input building contours does not exist") sys.exit(1) return 1 log.info("Reading DEM") topo = readGDAL(args.topo, bandIndex=1)[0] # Opening driver for road networks try: driver = ogr.GetDriverByName(args.format) except: log.error("Invalid format for road networks, check ogr documentation") sys.exit(1) if args.split is None: log.info("Do not split roads") splitLimit = None else: splitLimit = float(args.split) log.info("Split roads that change direction" + " more than %f" % splitLimit) # extract extent from topo raster xmin = topo.xll ymin = topo.yll xmax = topo.xur() ymax = topo.yur() # Calculate dimensions of spatial index ncols = int((xmax - xmin) / CELLSIZE) nrows = int((ymax - ymin) / CELLSIZE) # Init spatial index of building contours spatInd = SpatialIndex(xmin, ymin, nrows, ncols, CELLSIZE) log.info("Reading and indexing building contours") # Read buildings and store using spatial indices spatInd.indexBuildingContours(args.buildings) # open road network shape-file log.info("Reading road network") inRoadFile = driver.Open(args.inRoads, update=0) if path.exists(args.outRoads): driver.DeleteDataSource(args.outRoads) outRoadFile = driver.CreateDataSource(args.outRoads) if inRoadFile is None: log.error("Could not open file with input road network") sys.exit(1) if outRoadFile is None: log.error("Could not open file with output road network") sys.exit(1) # Get layer definition and first feature of input road network inRoadLayer = inRoadFile.GetLayer() inRoadLayerDefn = inRoadLayer.GetLayerDefn() outRoadLayer = outRoadFile.CreateLayer("first_layer", geom_type=inRoadLayer.GetGeomType()) # create fields on output road file for fieldInd in range(inRoadLayerDefn.GetFieldCount()): fieldDefn = inRoadLayerDefn.GetFieldDefn(fieldInd) outRoadLayer.CreateField(fieldDefn) outRoadLayerDefn = outRoadLayer.GetLayerDefn() fieldNames = [ outRoadLayerDefn.GetFieldDefn(i).GetName() for i in range(outRoadLayerDefn.GetFieldCount()) ] log.info("Adding attributes to road feature (if missing)") # Add attributes for street canyon geometry if "BHGT1" not in fieldNames: outRoadLayer.CreateField(ogr.FieldDefn("BHGT1", ogr.OFTInteger)) if "BHGT2" not in fieldNames: outRoadLayer.CreateField(ogr.FieldDefn("BHGT2", ogr.OFTInteger)) if "BHGT1W" not in fieldNames: outRoadLayer.CreateField(ogr.FieldDefn("BHGT1W", ogr.OFTInteger)) if "BHGT2W" not in fieldNames: outRoadLayer.CreateField(ogr.FieldDefn("BHGT2W", ogr.OFTInteger)) if "BANG1" not in fieldNames: outRoadLayer.CreateField(ogr.FieldDefn("BANG1", ogr.OFTInteger)) if "BANG2" not in fieldNames: outRoadLayer.CreateField(ogr.FieldDefn("BANG2", ogr.OFTInteger)) if "BDIST" not in fieldNames: outRoadLayer.CreateField(ogr.FieldDefn("BDIST", ogr.OFTInteger)) if "BSECT" not in fieldNames: fieldDefn = ogr.FieldDefn("BSECT", ogr.OFTString) fieldDefn.SetWidth(40) outRoadLayer.CreateField(fieldDefn) if "BSECTW" not in fieldNames: fieldDefn = ogr.FieldDefn("BSECTW", ogr.OFTString) fieldDefn.SetWidth(40) outRoadLayer.CreateField(fieldDefn) fig1 = plt.figure(1) ax1 = plt.subplot(111) ax1.axis('equal') if PLOTIND > 0: spatInd.plot(ax1) roadInd = 0 noGeom = 0 nsplit = 0 # get first road feature inRoadFeature = inRoadLayer.GetNextFeature() # Loop over all roads log.info("Finding nearest facades, setting heights...") pg = ProgressBar(inRoadLayer.GetFeatureCount(), sys.stdout) while inRoadFeature: pg.update(roadInd) outRoadFeatures = splitRoad(inRoadFeature, splitLimit, outRoadLayer.GetLayerDefn()) if len(outRoadFeatures) > 1: log.debug("Raod split into %s parts" % len(outRoadFeatures)) nsplit += len(outRoadFeatures) - 1 for outRoadFeature in outRoadFeatures: intersections = [] outRoadGeom = outRoadFeature.GetGeometryRef() road = Road(outRoadGeom) if outRoadGeom is None or \ outRoadGeom.GetPointCount() == 0 or not spatInd.inside(road): noGeom += 1 maxHeight1 = None maxHeight2 = None avgDist = None bAngle1 = None bAngle2 = None avgHeight1 = None avgHeight2 = None else: sumHeight1 = 0 sumHeight2 = 0 maxHeight1 = 0 maxHeight2 = 0 sumDist = 0 # Define crossections along the road, # Defined by start and endpoints at both side of the road cs1List, cs2List = road.defineCrossSections() nCS = len(cs1List) log.debug("Defined %i cross sections" % nCS) # Check intersections with building contours for all cross-sections for csInd in range(nCS): cs1 = cs1List[csInd] cs2 = cs2List[csInd] cs1MidPoint = cs1.P0 + 0.5 * (cs1.P1 - cs1.P0) buildingSegments = spatInd.getBuildingSegments( cs1MidPoint[0], cs1MidPoint[1]) log.debug("Calculating intersection") if PLOTIND == roadInd and CSIND == csInd: dist1, Pint1 = getIntersectingFacade( ax1, cs1, buildingSegments, True) else: dist1, Pint1 = getIntersectingFacade( ax1, cs1, buildingSegments, False) if Pint1 is None: log.debug("No intersection on side 1") height1 = 0 dist1 = MAXDIST else: log.debug("Intersection1 in (%f, %f, %f)" % (Pint1[0], Pint1[1], Pint1[2])) height1 = spatInd.getBuildingHeight( Pint1[0], Pint1[1], Pint1[2], topo) + HEIGHTCORR intersections.append(Pint1[:2]) if PLOTIND == roadInd and csInd == CSIND: plotSegments(ax1, buildingSegments, color='red', width=2.0) row, col = spatInd.getInd(cs1MidPoint[0], cs1MidPoint[1]) spatInd.plotCell(ax1, row, col, color="purple", width=2.0) plotSegments(ax1, [cs1List[csInd]], color="pink", style="-", width=1.0) plt.draw() cs2MidPoint = cs2.P0 + 0.5 * (cs2.P1 - cs2.P0) buildingSegments = spatInd.getBuildingSegments( cs2MidPoint[0], cs2MidPoint[1]) if PLOTIND == roadInd and csInd == CSIND: plotSegments(ax1, buildingSegments, color='red', width=2.0) row, col = spatInd.getInd(cs2MidPoint[0], cs2MidPoint[1]) spatInd.plotCell(ax1, row, col, color="brown", width=2.0) plotSegments(ax1, [cs2List[csInd]], color="red", style="-", width=1.0) plt.draw() log.debug("Calculating intersection") if PLOTIND == roadInd and CSIND == csInd: dist2, Pint2 = getIntersectingFacade( ax1, cs2, buildingSegments, True) else: dist2, Pint2 = getIntersectingFacade( ax1, cs2, buildingSegments, False) if Pint2 is None: log.debug("No intersection on side 2") height2 = 0 else: log.debug("Intersection2 in (%f, %f, %f)" % (Pint2[0], Pint2[1], Pint2[2])) height2 = spatInd.getBuildingHeight( Pint2[0], Pint2[1], Pint2[2], topo) + HEIGHTCORR intersections.append(Pint2[:2]) sumHeight1 += height1 sumHeight2 += height2 sumDist += dist1 + dist2 maxHeight1 = int(max(height1, maxHeight1)) maxHeight2 = int(max(height2, maxHeight2)) if PLOTIND == roadInd and CSIND == csInd: if Pint1 is not None: ax1.text(Pint1[0], Pint1[1], "Distance=%f" % dist1) if Pint2 is not None: ax1.text(Pint2[0], Pint2[1], "Distance=%f" % dist2) avgHeight1 = int(sumHeight1 / float(nCS)) avgHeight2 = int(sumHeight2 / float(nCS)) # averaging over both sides of street # distance refers to between facades on opposite sides avgDist = int(round(sumDist / float(nCS))) bAngle1, bAngle2 = road.normalAngles() if PLOTIND > 0: plotSegments(ax1, road.getSegments(), color='grey', width=0.3) if PLOTIND == roadInd: plotSegments(ax1, road.getSegments(), color='black', width=2.0) plotSegments(ax1, cs1List, color="green", style="--", width=0.5) plotSegments(ax1, cs2List, color="green", style="--", width=0.5) X = [intersect[0] for intersect in intersections] Y = [intersect[1] for intersect in intersections] if len(X) > 0: ax1.plot(X, Y, "*") plt.title("Road %i, cross-section %i" % (PLOTIND, CSIND)) plt.draw() # building height as list of sectors bsect = bheight2sect(avgHeight1, avgHeight2, bAngle1) bsectw = bheight2sect(maxHeight1, maxHeight2, bAngle1) outRoadFeature.SetField("BSECT", bsect) outRoadFeature.SetField("BSECTW", bsectw) outRoadFeature.SetField("BHGT1", avgHeight1) outRoadFeature.SetField("BHGT2", avgHeight2) outRoadFeature.SetField("BHGT1W", maxHeight1) outRoadFeature.SetField("BHGT2W", maxHeight2) outRoadFeature.SetField("BANG1", bAngle1) outRoadFeature.SetField("BANG2", bAngle2) outRoadFeature.SetField("BDIST", avgDist) outRoadLayer.CreateFeature(outRoadFeature) outRoadFeature.Destroy() inRoadFeature.Destroy() inRoadFeature = inRoadLayer.GetNextFeature() roadInd += 1 inRoads = inRoadLayer.GetFeatureCount() outRoads = outRoadLayer.GetFeatureCount() # close datasource for building contours inRoadFile.Destroy() outRoadFile.Destroy() pg.finished() if PLOTIND > 0: plt.show() log.info("Read %i roads, wrote %i roads (created %i by splitting)" % (inRoads, outRoads, nsplit)) if noGeom > 0: log.warning("Found %i roads without geometry" % noGeom) log.info("Finished")
def indexBuildingContours(self, buildingFile): """ Read building contours from shape file and create a spatial indexing. The indexing is implemented as a nested dict cols={row1,row2,row3}, where row1..n = {cell1,cell2,...,celln}, and cell1...n = [contour_1,contour_2,...,contour_n] Extent of index is set to extent of the used DEM (topo) @param buildingFile: shapefile with 3D building contours """ # open building contours shape-file log.info("Reading building contours") # Init reading of shape-files using OGR shapeDriver = ogr.GetDriverByName('ESRI Shapefile') buildings = shapeDriver.Open(buildingFile, update=0) if buildings is None: log.error("Could not open file with building contours") return 1 buildingsLayer = buildings.GetLayer() self.ind = {} # set up progress indicator pg = ProgressBar(buildingsLayer.GetFeatureCount(), sys.stdout) contourInd = 0 # Counter for building contour buildHeights = [] # List to store building height for plotting noGeom = 0 # Counter for invalid features without geometry reference # Read and process building contours contour = buildingsLayer.GetNextFeature() # get first feature while contour: if log.level == 0: pg.update(contourInd) log.debug("Contour %i" % contourInd) contourGeom = contour.GetGeometryRef() if contourGeom is None or contourGeom.GetPointCount() == 0: noGeom += 1 else: for i in range(1, contourGeom.GetPointCount()): x1 = contourGeom.GetX(i - 1) y1 = contourGeom.GetY(i - 1) z1 = contourGeom.GetZ(i - 1) x2 = contourGeom.GetX(i) y2 = contourGeom.GetY(i) z2 = contourGeom.GetZ(i) P1 = np.array([x1, y1, z1]) P2 = np.array([x2, y2, z2]) try: row, col = self.getInd((x2 + x1) / 2.0, (y1 + y2) / 2.0) except ValueError: # outside extent row = col = None if row is not None and col is not None: if row not in self.ind: self.ind[row] = {} self.ind[row][col] = [] elif col not in self.ind[row]: self.ind[row][col] = [] self.ind[row][col].append(Segment(P1, P2)) contour.Destroy() contour = buildingsLayer.GetNextFeature() contourInd += 1 # close datasource for building contours buildings.Destroy() pg.finished() if noGeom > 0: log.warning("Found %i building contours without geometry" % noGeom)
def main(): # -----------Setting up and unsing option parser----------------------- parser = OptionParser(usage=usage, version=version) parser.add_option("-d", "--doc", action="store_true", dest="doc", help="Prints more detailed documentation and exit") parser.add_option("-v", action="store_const", const=logging.DEBUG, dest="loglevel", default=get_loglevel(), help="Produce verbose output") parser.add_option("--no-progress", action="store_const", dest="progressStream", const=None, default=sys.stdout, help="turn off the progress bar") parser.add_option("-o", "--output", action="store", dest="outfileName", default=None, help="Output file") parser.add_option("-i", "--input", action="store", dest="infileName", help="Input raster") parser.add_option("--bbox", action="store", dest="bbox", help="Only read data within bbox," + " --box <\"x1,y1,x2,y2\"") parser.add_option("--reclassify", action="store", dest="classTable", help="Tab-separated table with code " + "and z0 value for each landuse class") parser.add_option("--reclassFromColumn", action="store", dest="reclassFromColumn", help="Header of reclass table column " + "containing values to " + "reclass (default is to use first column of classTable)") parser.add_option("--reclassToColumn", action="store", dest="reclassToColumn", help="Header of reclass table column containing codes," + " default is to use first column (default is to use " + "second column of classTable)") parser.add_option("--resample", action="store", dest="cellFactor", help="Resample grid by dividing cellsize with a" + " factor. Factor <1 results in refinement." + " Reprojection uses temporary refinement") parser.add_option("--resamplingMethod", action="store", dest="resamplingMethod", help="For cellFactor > 1: " + "Choose between 'mean', 'sum', 'majority' or" + " 'count', default is sum," "For cellFactor < 1, choose between: " + "'keepTotal' and 'keepValue', default is 'keepTotal'") parser.add_option("--summarize", action="store_true", dest="summarize", help="Print a summary of the input grid properties") parser.add_option("--bandIndex", action="store", dest="bandIndex", help="Band index to read from", default=1) parser.add_option("--dataType", action="store", dest="dataType", help="Output raster/shape data type", default=None) parser.add_option("--toShape", action="store_true", dest="toShape", help="output as shape file", default=None) parser.add_option("--fieldName", metavar='FIELD', action="store", dest="fieldName", help="write data in shape file to FIELD," + " default is 'value'", default=None) parser.add_option("--filter", action="store", dest="filter", help="Filter out data equal or below" + " limit in shape output", default=None) parser.add_option("-t", "--template", action="store", dest="template", help="Header for output raster when reprojecting") parser.add_option("--fromProj", dest="fromProj", help="Input raster proj4 definition string") parser.add_option("--toProj", dest="toProj", help="Output raster epsg or proj4 definition string") (options, args) = parser.parse_args() # ------------Setting up logging capabilities ----------- # Setup logging logging.basicConfig( format='%(levelname)s:%(name)s: %(message)s', level=options.loglevel, ) log = logging.getLogger(__name__) # ------------Process and validate options--------------- if options.doc: print __doc__ sys.exit(0) if len(args) > 0: parser.error("Incorrect number of arguments") # validate infile path if options.infileName is not None: inFilePath = path.abspath(options.infileName) if not path.exists(inFilePath): log.error("Input raster %s does not exist" % options.infileName) sys.exit(1) else: parser.error("No input data specified") # validate outfile path if options.outfileName is not None: outFilePath = path.abspath(options.outfileName) if options.toShape and ".shp" not in outFilePath: parser.error("Output shape has to to be specified with" + " .shp extension") else: outFilePath = None # Validate fieldName option if options.toShape: if options.fieldName == "": parser.error("fieldName can't be an empty string") fieldName = options.fieldName or "value" elif options.fieldName is not None: parser.error("fieldName option only allowed together" + " with shape output") # Validate filter option and convert filter to numeric value if present if not options.toShape and options.filter is not None: parser.error("Filter option only allowed together with shape output") elif options.toShape: if options.filter is not None: filter = float(options.filter) else: filter = None # read and process reclass table file if options.classTable is not None: reclassFromColumn = options.reclassFromColumn reclassToColumn = options.reclassToColumn if reclassFromColumn is not None and reclassToColumn is not None: desc = [{ "id": reclassFromColumn, "type": float }, { "id": reclassToColumn, "type": float }] classTable = datatable.DataTable(desc=desc) classTable.read(options.classTable) else: classTable = datatable.DataTable() classTable.read(options.classTable) reclassFromColumn = classTable.desc[0]["id"] reclassToColumn = classTable.desc[1]["id"] classTable.convertCol(reclassFromColumn, float) classTable.convertCol(reclassToColumn, float) classTable.setKeys([reclassFromColumn]) log.debug("Successfully read landuse class table") classDict = {} for row in classTable.data: classDict[row[classTable.colIndex[reclassFromColumn]]] = row[ classTable.colIndex[reclassToColumn]] if options.cellFactor is not None: cellFactor = float(options.cellFactor) if cellFactor > 1 and \ options.resamplingMethod not in ('sum', 'mean', 'majority', 'count', None): log.error("Invalid resampling method, valid options for grid " + "coarsening are 'sum' " + "and 'mean' and 'majority', " + "specified %s" % options.resamplingMethod) sys.exit(1) elif cellFactor < 1 and \ options.resamplingMethod not in ('keepTotal', 'keepValue', None): log.error("Invalid resampling method, valid options for grid " + "coarsening are 'keepTotal' and 'keepValue'" + ", specified %s" % resamplingMethod) sys.exit(1) # setting default resampling methods if cellFactor > 1 and \ options.resamplingMethod is None: resamplingMethod = 'sum' elif options.resamplingMethod is None: resamplingMethod = 'keepTotal' else: resamplingMethod = options.resamplingMethod if options.resamplingMethod == 'majority' or \ options.resamplingMethod == 'count' and \ options.toProj is not None: log.error("Resampling method " + "%s not possible to " % options.resamplingMethod + "combine with reprojection") sys.exit(1) # Assure that gdal is present if not __gdal_loaded__: raise OSError("Function readGDAL needs GDAL with python bindings") # register all of the raster drivers gdal.AllRegister() ds = gdal.Open(inFilePath, GA_ReadOnly) if ds is None: print 'Could not open ' + inFilePath sys.exit(1) ncols = ds.RasterXSize nrows = ds.RasterYSize nbands = ds.RasterCount # Info used for georeferencing geoTransform = ds.GetGeoTransform() xul = geoTransform[0] # top left x cellsizeX = geoTransform[1] # w-e pixel resolution rot1 = geoTransform[2] # rotation, 0 if image is "north up" yul = geoTransform[3] # top left y rot2 = geoTransform[4] # rotation, 0 if image is "north up" cellsizeY = geoTransform[5] # n-s pixel resolution proj = osr.SpatialReference(ds.GetProjection()) # Calculate lower left corner xll = xul yll = yul + nrows * cellsizeY # cellsizeY should be a negative value # Calculate upper right corner xur = xul + cellsizeX * ncols yur = yul # Rotated rasters not handled...yet if rot1 != 0 or rot2 != 0: print 'Rotated rasters are not supported by pyAirviro.geo.raster' sys.exit(1) if abs(cellsizeX) != abs(cellsizeY): print('Non-homogenous cellsizes are not' + ' supported by pyAirviro.geo.raster') sys.exit(1) bandIndex = int(options.bandIndex) band = ds.GetRasterBand(bandIndex) nodata = band.GetNoDataValue() # If no nodata value is present in raster, set to -9999 for completeness if nodata is None: nodata = -9999 # Read data from a window defined by option --bbox <"x1,y1,x2,y2"> if options.bbox is not None: try: x1, y1, x2, y2 = map(float, options.bbox.split(",")) except: log.error("Invalid value for option --bbox <\"x1,y1,x2,y2\">") sys.exit(1) # Check if totally outside raster extent if x2 < xll or y2 < yll or x1 > xur or y1 > yul: log.error("Trying to extract outside grid boundaries") sys.exit(1) # Limit bbox to raster extent if x1 < xll: x1 = xll if x2 > xur: x2 = xur if y1 < yll: y1 = yll if y2 > yul: y2 = yul # estimate min and max of rows and cols colmin = int((x1 - xll) / cellsizeX) colmaxdec = (x2 - xll) / float(cellsizeX) rowmin = int((yul - y2) / abs(cellsizeY)) rowmaxdec = (yul - y1) / float(abs(cellsizeY)) if (colmaxdec - int(colmaxdec)) > 0: colmax = int(colmaxdec) else: colmax = int(colmaxdec - 1) if (rowmaxdec - int(rowmaxdec)) > 0: rowmax = int(rowmaxdec) else: rowmax = int(rowmaxdec - 1) nrows = rowmax - rowmin + 1 ncols = colmax - colmin + 1 xll = xll + colmin * cellsizeX yll = yul + (rowmax + 1) * cellsizeY # cellsizeY is negative yul = yll - nrows * cellsizeY yur = yul xur = xll + ncols * cellsizeX else: rowmin = 0 colmin = 0 # process option for resampling if options.cellFactor is not None: cellFactor = float(cellFactor) else: cellFactor = 1 if cellFactor >= 1: procYBlockSize = int(cellFactor) else: procYBlockSize = 1 if options.toProj is None: # Set output raster dimensions and cellsize newNcols = int(ncols / cellFactor) newNrows = int(nrows / cellFactor) newCellsizeX = cellsizeX * cellFactor newCellsizeY = cellsizeY * cellFactor # is a negative value as before newXll = xll newYll = yll newYul = yul newNodata = nodata else: # Create coordinate transform if options.fromProj is None: src_srs = proj else: src_srs = osr.SpatialReference() src_srs.ImportFromProj4(options.fromProj) tgt_srs = osr.SpatialReference() if options.toProj.startswith("epsg"): tgt_srs.ImportFromEPSG(int(options.toProj[5:])) else: tgt_srs.ImportFromProj4(options.toProj) coordTrans = osr.CoordinateTransformation(src_srs, tgt_srs) newXur = None newYur = None if options.template is None: # estimate extent from input newNcols = int(ncols / cellFactor) newNrows = int(nrows / cellFactor) newXll, newYll, z = coordTrans.TransformPoint(xll, yll) newXur, newYur, z = coordTrans.TransformPoint(xur, yur) newCellsizeX = (newXur - newXll) / float(newNcols) newCellsizeY = (newYur - newYll) / float(newNrows) newNodata = nodata newXll = newXll newYll = newYll newYul = newYur else: header = open(options.template, "r").read() newNcols = int( re.compile("ncols\s*([0-9]*)").search(header).group(1)) newNrows = int( re.compile("nrows\s*([0-9]*)").search(header).group(1)) newCellsizeX = float( re.compile("cellsize\s*([0-9]*)").search(header).group(1)) newCellsizeY = -1 * newCellsizeX try: newNodata = float( re.compile("NODATA_value\s*(.*?)\n").search(header).group( 1)) except AttributeError: newNodata = float( re.compile("nodata_value\s*(.*?)\n").search(header).group( 1)) newXll = float( re.compile("xllcorner\s*(.*?)\n").search(header).group(1)) newYll = float( re.compile("yllcorner\s*(.*?)\n").search(header).group(1)) newYul = newYll - newNrows * newCellsizeY # Processing of data is made for blocks of the following size # Important - blocks are set to cover all columns for simpler processing # This might not always be optimal for read/write speed procXBlockSize = ncols # process option for dataType if options.toShape: dataTypes, defaultDataType = ogrDataTypes, 'Real' else: dataTypes, defaultDataType = gdalDataTypes, 'Float32' try: dataType = dataTypes[options.dataType or defaultDataType] except KeyError: log.error("Unknown datatype choose between: %s" % ",".join(dataTypes.keys())) sys.exit(1) # Create and configure output raster data source if not options.toShape and outFilePath is not None: # Creates a raster dataset with 1 band mem_ds = gdal.GetDriverByName('MEM').Create(outFilePath, newNcols, newNrows, 1, dataType) if mem_ds is None: print "Error: could not create output raster" sys.exit(1) outGeotransform = [newXll, newCellsizeX, 0, newYul, 0, newCellsizeY] mem_ds.SetGeoTransform(outGeotransform) if options.toProj is not None: mem_ds.SetProjection(tgt_srs.ExportToWkt()) elif isinstance(proj, osr.SpatialReference): mem_ds.SetProjection(proj.ExportToProj4()) else: mem_ds.SetProjection(proj) outBand = mem_ds.GetRasterBand(1) outBand.SetNoDataValue(newNodata) # Set nodata-value if options.toProj is not None: outArray = np.zeros((newNrows, newNcols)) nvals = np.zeros((newNrows, newNcols)) outDef = { "ncols": newNcols, "nrows": newNrows, "xll": newXll, "yll": newYll, "yul": newYll - newNrows * newCellsizeY, "xur": newXll + newNcols * newCellsizeX, "cellsize": newCellsizeX } # Create and inititialize output vector data source if options.toShape: shapeDriver = ogr.GetDriverByName('ESRI Shapefile') if path.exists(outFilePath): shapeDriver.DeleteDataSource(outFilePath) shapeFile = shapeDriver.CreateDataSource(outFilePath) if shapeFile is None: log.error("Could not open output shapefile %s" % outFilePath) sys.exit(1) layer = shapeFile.CreateLayer(outFilePath, geom_type=ogr.wkbPolygon) fieldDefn = ogr.FieldDefn(fieldName, dataType) layer.CreateField(fieldDefn) # inititialize input grid summary inputGridSummary = { "sum": 0, "mean": 0, "nnodata": 0, "nnegative": 0, "xll": xll, "yll": yll, "ncols": ncols, "nrows": nrows, "cellsizeX": cellsizeX, "cellsizeY": cellsizeY, "nodatavalue": nodata } outputGridSummary = { "sum": 0, "mean": 0, "nnodata": 0, "nnegative": 0, "xll": newXll, "yll": newYll, "ncols": newNcols, "nrows": newNrows, "cellsizeX": newCellsizeX, "cellsizeY": newCellsizeY, "nodatavalue": newNodata } # Loop over block of raster (at least one row in each block) rowsOffset = 0 errDict = {} pg = ProgressBar(nrows, options.progressStream) for i in range(0, nrows, int(procYBlockSize)): pg.update(i) data = band.ReadAsArray(xoff=colmin, yoff=rowmin + i, win_xsize=procXBlockSize, win_ysize=procYBlockSize) if options.summarize: inputGridSummary = updateGridSummary(data, inputGridSummary, nodata) if options.classTable is not None: try: data = reclassBlock(data, classDict, errDict) except IOError as e: log.error(str(e)) sys.exit(1) if options.cellFactor is not None: try: data = resampleBlock(data[:, :], cellFactor, resamplingMethod, nodata) except ValueError as err: log.error(err.message) sys.exit(1) except IOError as err: log.error(err.message) sys.exit(1) if options.toProj is not None: blockDef = { "nrows": int(procYBlockSize / cellFactor), "ncols": int(procXBlockSize / cellFactor), "xll": xll + (colmin) * cellsizeX, "yul": yll - (nrows - rowmin - i) * cellsizeY, "cellsize": cellsizeX * cellFactor, "nodata": nodata } reprojectBlock(outArray, data, cellFactor, blockDef, outDef, coordTrans, nvals) if outFilePath is not None: if options.toShape: blockYll = yul + i * newCellsizeY # newCellsizeY is negative blockXll = xll block2vector(data, layer, blockXll, blockYll, newCellsizeX, newCellsizeY, nodata, fieldName, filter) elif options.toProj is None: outBand.WriteArray(data, 0, rowsOffset) # Write block to raster outBand.FlushCache() # Write data to disk if options.summarize: outputGridSummary = updateGridSummary(data, outputGridSummary, nodata) rowsOffset += int(procYBlockSize / cellFactor) # Update offset if options.toShape: shapeFile.Destroy() if not options.toShape and options.toProj is not None: if options.resamplingMethod is not None and resamplingMethod == "mean": outArray = np.where(nvals > 0, outArray / nvals, outArray) outBand.WriteArray(outArray, 0, 0) outBand.FlushCache() # Write data to disk if options.outfileName is not None and not options.toShape: outputDriver = ds.GetDriver() output_ds = outputDriver.CreateCopy(options.outfileName, mem_ds, 0) output_ds = None mem_ds = None pg.finished() if options.summarize: print "\nInput raster summary" printGridSummary(inputGridSummary, prefix="input") print "\nOutput raster summary" printGridSummary(outputGridSummary, prefix="output") if errDict != {}: print "Errors/warnings during processing:" for err, nerr in errDict.items(): print "%s err in %i cells" % (err, nerr)