def createChannelUnitsJSON(topoDataFolder, visitID, jsonFilePath): topo = TopoData(topoDataFolder, visitID) topo.loadlayers() shpCU = Shapefile(topo.ChannelUnits) dUnits = {} feats = shpCU.featuresToShapely() for aFeat in feats: origTier1 = aFeat['fields']['Tier1'] origTier2 = aFeat['fields']['Tier2'] UtNum = aFeat['fields']['UnitNumber'] finalTier1 = None for tier1 in dUnitDefs.keys(): if tier1.lower() == origTier1.lower(): finalTier1 = tier1 break if not finalTier1: print('No Tier 1 channel unit type found') dUnits[UtNum] = (finalTier1, origTier2, 1) writeChannelUnitsToJSON(jsonFilePath, dUnits)
def calc(self, shpIslands): """ :param shpIslands: :return: """ self.metrics = { 'Count': 0, 'Area': 0.0, 'QualifyingCount': 0, 'QualifyingArea': 0.0 } if not path.isfile(shpIslands): return shp = Shapefile(shpIslands) feats = shp.featuresToShapely() if feats: for aFeat in feats: self.metrics['Count'] += 1 self.metrics['Area'] += aFeat['geometry'].area if 'IsValid' in shp.fields: if aFeat['fields']['IsValid'] == 1: self.metrics['QualifyingCount'] += 1 self.metrics['QualifyingArea'] += aFeat[ 'geometry'].area
def vectorToCSV(shapefilePath, outputCSVPath): print("Writing vector {0} to {1}").format(os.path.basename(shapefilePath), outputCSVPath) clShp = Shapefile(shapefilePath) clList = clShp.featuresToShapely() with open(outputCSVPath, 'w') as csvfile: # write the header row csvfile.write('x,y\n') for aLine in clList: for aPoint in aLine['geometry'].coords: csvfile.write('{0},{1}\n'.format(aPoint[0], aPoint[1]))
def calc(self, sThalwegshp, sDepthRaster, sWaterSurfaceRaster, fDist, visitMetrics): if not path.isfile(sThalwegshp): raise MissingException("Thalweg shapefile missing") if not path.isfile(sDepthRaster): raise MissingException("Depth raster missing") if not path.isfile(sWaterSurfaceRaster): raise MissingException("Surface raster missing") wettedMainstemLength = visitMetrics['Wetted']['Centerline']['MainstemLength'] if wettedMainstemLength is None: raise MissingException("No wetted mainstem length found in visit metrics") sfile = Shapefile(sThalwegshp).featuresToShapely() if len(sfile) < 1: raise DataException("Thalweg shapefile has no features") thalweg = sfile[0]['geometry'] depthRaster = Raster(sDepthRaster) waterSurfaceRaster = Raster(sWaterSurfaceRaster) samplepts = ThalwegMetrics.interpolateRasterAlongLine(thalweg, fDist) results = ThalwegMetrics.lookupRasterValues(samplepts, depthRaster)['values'] # Get the elevation at the first (downstream) point on the Thalweg dsElev = waterSurfaceRaster.getPixelVal(thalweg.coords[0]) usElev = waterSurfaceRaster.getPixelVal(thalweg.coords[-1]) if (np.isnan(dsElev)): raise DataException('nodata detected in the raster for downstream point on the thalweg') elif np.isnan(usElev): raise DataException('nodata detected in the raster for upstream point on the thalweg') waterSurfaceGradientRatio = (usElev - dsElev) / thalweg.length waterSurfaceGradientPC = waterSurfaceGradientRatio * 100.0 # Thalweg straight length and sinuosity firstPoint = Point(thalweg.coords[0]) lastPoint = Point(thalweg.coords[-1]) straightLength = firstPoint.distance(lastPoint) sinuosity = thalweg.length / straightLength self.metrics = { 'Min': np.nanmin(results), 'Max': np.nanmax(results), 'Mean': np.mean(results), 'StDev': np.std(results), 'Count': np.count_nonzero(results), 'Length': thalweg.length, 'WSGradientRatio': waterSurfaceGradientRatio, 'WSGradientPC': waterSurfaceGradientPC, 'Sinuosity': sinuosity, 'CV': 0.0, 'ThalwegToCenterlineRatio': thalweg.length / wettedMainstemLength # , 'Values': results.data } if self.metrics['StDev'] != 0 and self.metrics['Mean'] != 0: self.metrics['CV'] = self.metrics['StDev'] / self.metrics['Mean']
def _getMainstemGeometry(self, shpCenterline): """ :param shpCenterline: :return: """ mainstem = None shp = Shapefile(shpCenterline) feats = shp.featuresToShapely() if 'Channel' in shp.fields: for featureIndex in range(0, feats.count()): if shp.fields[featureIndex] == 'Main': mainstem = shp.features[featureIndex]['geometry'] break else: mainstem = shp.features[0]['geometry'][0] return mainstem
def processSurveyExtent(projects, outputFolder): spatialRef = None unionPoly = None intersectPoly = None for proj in projects: extentPath = proj['project'].getpath('SurveyExtent') vid = proj['visit'] if not os.path.isfile(extentPath): print("Warning: Missing Survey Extent ShapeFile for Visit {0}").format(vid) continue seShp = Shapefile(extentPath) seList = seShp.featuresToShapely() spatialRef = seShp.spatialRef for sePolygon in seList: if unionPoly: unionPoly = unionPoly.union(sePolygon['geometry']) else: unionPoly = sePolygon['geometry'] if intersectPoly: intersectPoly = intersectPoly.intersection(sePolygon['geometry']) else: intersectPoly = sePolygon['geometry'] if not unionPoly: raise DataException("No survey extent union polygon derived from visits") if not intersectPoly: raise DataException("No survey extent intersect polygon derived from visits") unionPath = os.path.join(outputFolder, "SurveyExtentUnion.shp") print("Writing survey extent union to {0}").format(unionPath) writeShapeToShapeFile(unionPath, unionPoly, spatialRef) intersectPath = os.path.join(outputFolder, "SurveyExtentIntersect.shp") print("Writing survey extent intersection to {0}").format(intersectPath) writeShapeToShapeFile(intersectPath, intersectPoly, spatialRef)
def writeShapeToShapeFile(outputPath, aPolygon, spatialRef): # Write the polygon to ShapeFile (note: ShapeFile handles deleting existing file) outShape = Shapefile() outShape.create(outputPath, spatialRef, geoType=ogr.wkbPolygon) outShape.createField("ID", ogr.OFTInteger) # The main centerline gets written first featureDefn = outShape.layer.GetLayerDefn() outFeature = ogr.Feature(featureDefn) ogrPolygon = ogr.CreateGeometryFromJson(json.dumps(mapping(aPolygon))) outFeature.SetGeometry(ogrPolygon) outFeature.SetField('ID', 1) outShape.layer.CreateFeature(outFeature)
def export_cad_files(project_xml, out_path): """exports dxf files containing tin components of topo tin and Topographic Survey Points, Lines and Survey Extent""" log = Logger("CADExport") # Load Topo project log.info("Load Topo project") project = topoproject.TopoProject(project_xml) # TIN stuff log.info("Beginning TIN Work") tin = TIN(project.getpath("TopoTin")) dict_tinlayers = {} dict_tinlayers["tin_points"] = { "layer_type": "POINT", "Features": [feat for feat in tin.nodes.values()] } dict_tinlayers["tin_lines"] = { "layer_type": "POLYLINE", "Features": [feat['geometry'] for feat in tin.breaklines.values()] } #, "linetype_field":"LineType"} dict_tinlayers["tin_area"] = { "layer_type": "POLYGON", "Features": [feat for feat in tin.hull_polygons.values()] } out_tin_dxf = export_as_dxf(dict_tinlayers, os.path.join(out_path, "TopoTin.dxf")) # Topo Stuff log.info("Beginning Topo Work") shpTopo = Shapefile(project.getpath("Topo_Points")) shpEOW = Shapefile(project.getpath("EdgeofWater_Points")) shpCP = Shapefile(project.getpath("Control_Points")) shpBL = Shapefile(project.getpath("Breaklines")) if project.layer_exists( "Breaklines") else None shpExtent = Shapefile(project.getpath("Survey_Extent")) dict_topolayers = {} dict_topolayers["Topo_Points"] = { "layer_type": "POINT", "Features": [feat['geometry'] for feat in shpTopo.featuresToShapely()] } dict_topolayers["EdgeofWater_Points"] = { "layer_type": "POINT", "Features": [feat['geometry'] for feat in shpEOW.featuresToShapely()] } dict_topolayers["Control_Points"] = { "layer_type": "POINT", "Features": [feat['geometry'] for feat in shpCP.featuresToShapely()] } dict_topolayers["Breaklines"] = { "layer_type": "POLYLINE", "Features": [feat['geometry'] for feat in shpBL.featuresToShapely()] } if shpBL else None dict_topolayers["Survey_Extent"] = { "layer_type": "POLYGON", "Features": [feat['geometry'] for feat in shpExtent.featuresToShapely()] } out_topo_dxf = export_as_dxf( dict_topolayers, os.path.join(out_path, "SurveyTopography.dxf")) out_topo_csv = exportAsCSV( shpTopo.featuresToShapely() + shpEOW.featuresToShapely(), os.path.join(out_path, "SurveyTopographyPoints.csv")) out_control_csv = exportAsCSV( shpCP.featuresToShapely(), os.path.join(out_path, "ControlNetworkPoints.csv")) topo_rs_project = riverscapes.Project(project_xml) out_project = riverscapes.Project() out_project.create("CHaMP_Survey_CAD_Export", "CAD_Export", __version__) out_project.addProjectMetadata( "Watershed", topo_rs_project.ProjectMetadata["Watershed"]) # find previous meta tags for tagname, tags in { "Site": ["Site", "SiteName"], "Visit": ["Visit", "VisitID"], "Year": ["Year", "FieldSeason"], "Watershed": ["Watershed", "Watershed"] }.items(): if tags[0] in topo_rs_project.ProjectMetadata or tags[ 1] in topo_rs_project.ProjectMetadata: out_project.addProjectMetadata( tagname, topo_rs_project.ProjectMetadata[tags[0]] if tags[0] in topo_rs_project.ProjectMetadata else topo_rs_project.ProjectMetadata[tags[1]]) else: raise DataException("Missing project metadata") out_realization = riverscapes.Realization("CAD_Export") out_realization.name = "CHaMP Survey CAD Export" out_realization.productVersion = out_project.projectVersion ds = [] ds.append( out_project.addInputDataset("TopoTin", "tin", None, None, "TIN", project.get_guid("TopoTin"))) ds.append( out_project.addInputDataset("Topo_Points", "topo_points", None, guid=project.get_guid("Topo_Points"))) ds.append( out_project.addInputDataset( "EdgeofWater_Points", "eow_points", None, guid=project.get_guid("EdgeofWater_Points"))) ds.append( out_project.addInputDataset("Control_Points", "control_ponts", None, guid=project.get_guid("Control_Points"))) if shpBL: ds.append( out_project.addInputDataset("Breaklines", "breaklines", None, guid=project.get_guid("Breaklines"))) ds.append( out_project.addInputDataset("Survey_Extent", "survey_extent", None, guid=project.get_guid("Survey_Extent"))) for inputds in ds: out_realization.inputs[inputds.name] = inputds.id ds_tin_dxf = riverscapes.Dataset() ds_tin_dxf.create("TIN_DXF", "TopoTin.dxf") ds_tin_dxf.id = 'tin_dxf' ds_topo_dxf = riverscapes.Dataset() ds_topo_dxf.create("Topo_DXF", "SurveyTopography.dxf") ds_topo_dxf.id = 'topo_dxf' ds_topo_csv = riverscapes.Dataset() ds_topo_csv.create("Topo_CSV", "SurveyTopographyPoints.csv", "CSV") ds_topo_csv.id = 'topo_csv' ds_con_csv = riverscapes.Dataset() ds_con_csv.create("Control_CSV", "ControlNetworkPoints.csv", "CSV") ds_con_csv.id = 'control_csv' out_realization.outputs.update({ "TIN_DXF": ds_tin_dxf, "Topo_DXF": ds_topo_dxf, "Topo_CSV": ds_topo_csv, "Control_CSV": ds_con_csv }) out_project.addRealization(out_realization) out_project.writeProjectXML(os.path.join(out_path, "project.rs.xml")) return 0
def get_shapefile(self): return Shapefile(self.filename)
def channelUnitScraper(outputDir, watersheds): visitData = {} for watershed, apiName in watersheds.items(): visitData[watershed] = {} loadVisitData(watershed, apiName, visitData[watershed]) for watershed, visits in visitData.items(): outPath = os.path.join(outputDir, watershed.replace(' ', '') + ".shp") outShape = None featureID = 0 for visitID, visit in visits.items(): if len(visit['ChannelUnits']) < 1: continue try: dirpath = tempfile.mkdtemp() # Open the visit channel unit shapefile. # Need the spatial reference from one of the visits to create the output watershed shapefile try: fileJSON, projPath = downloadUnzipTopo(visitID, dirpath) topo = TopoProject(projPath) cuPath = topo.getpath('ChannelUnits') except (DataException, MissingException), e: print("Error retrieving channel units ShapeFile for visit {0}").format(visitID) continue try: shpCU = Shapefile(cuPath) except Exception as e: print("Error OGR opening channel unit ShapeFile for visit {0}").format(visitID) continue if not outShape: # Create new ShapeFile for this watershed outShape = Shapefile() outShape.create(outPath, shpCU.spatialRef, geoType=ogr.wkbPolygon) outShape.createField("ID", ogr.OFTInteger) outShape.createField("Watershed", ogr.OFTString) outShape.createField("Site", ogr.OFTString) outShape.createField("VisitID", ogr.OFTInteger) outShape.createField("SampleYear", ogr.OFTInteger) outShape.createField("Org", ogr.OFTString) outShape.createField("UnitNumber", ogr.OFTInteger) outShape.createField("UnitArea", ogr.OFTReal) outShape.createField("Tier1", ogr.OFTString) outShape.createField("Tier2", ogr.OFTString) outShape.createField("AvgSiteWid", ogr.OFTReal) outShape.createField("ReachLen", ogr.OFTReal) # Loop over all channel unit polygons for this visit feats = shpCU.featuresToShapely() for aFeat in feats: featureID += 1 cuNumber = aFeat['fields']['UnitNumber'] featureDefn = outShape.layer.GetLayerDefn() outFeature = ogr.Feature(featureDefn) outFeature.SetField('ID', featureID) outFeature.SetField('Watershed', visit['Watershed']) outFeature.SetField('Site', visit['Site']) outFeature.SetField('VisitID', visitID) outFeature.SetField('SampleYear', visit['SampleYear']) outFeature.SetField('Org', visit['Organization']) outFeature.SetField('AvgSiteWid', visit['AverageSiteWidth']) outFeature.SetField('ReachLen', visit['TotalReachLength']) outFeature.SetField('UnitNumber', cuNumber) outFeature.SetField('Tier1', visit['ChannelUnits'][cuNumber]['Tier1']) outFeature.SetField('Tier2', visit['ChannelUnits'][cuNumber]['Tier2']) outFeature.SetField('UnitArea', aFeat['geometry'].area) outFeature.SetGeometry(ogr.CreateGeometryFromJson(json.dumps(mapping(aFeat['geometry'])))) outShape.layer.CreateFeature(outFeature) finally:
def calc(self, crosssections, waterExtent, demPath, stationInterval): # Save space by only loading the desired fields from the ShapeFile. # We also need the 'Channel' and 'IsValid' fields if they exist. desiredFields = list(CrossSectionMetrics.dMetricTypes.keys()) desiredFields.append('IsValid') # Open the cross section ShapeFile & build a list of all features with a dictionary of the desired fields clShp = Shapefile(crosssections) lChannels = ['Main'] if not clShp.loaded: return if "Channel" in clShp.fields: desiredFields.append('Channel') lChannels.append('Side') # Older Cross Section Layers don't have the width and depth fields calculated. # So if all the necessary metric fields are present then continue to load # ShapeFile features. Otherwise we need to calculate the topometrics from scratch bMetricCalculationNeeded = False for aMetric in desiredFields: if not aMetric in clShp.fields: bMetricCalculationNeeded = True break allFeatures = [] if bMetricCalculationNeeded: # Retrieve the water extent polygon exterior rivershp = Shapefile(waterExtent) polyRiverShapeFeats = rivershp.featuresToShapely() # Try and find a channel shape. Thers's a lot of variance here. if len(polyRiverShapeFeats ) == 0 or 'geometry' not in polyRiverShapeFeats[0]: raise DataException("No features in crosssection shape file") # If there's only one shape then just use it elif len(polyRiverShapeFeats) == 1: polyRiverShape = polyRiverShapeFeats[0]['geometry'] # If there's more than one shape then see if else: channelShapes = [ feat['geometry'] for feat in polyRiverShapeFeats if feat['fields']['ExtentType'] == 'Channel' ] if len(channelShapes) == 0: raise DataException( "No features in crosssection shape file") polyRiverShape = channelShapes[0] # Calculate the topometrics from scratch for a single cross section shpXS = Shapefile(crosssections) demRaster = Raster(demPath) if shpXS.loaded: for aFeat in shpXS.featuresToShapely(): # Calculate the topometrics for this cross section. They will be stored on the aFeat dict under key 'topometrics' calcXSMetrics(aFeat, polyRiverShape, demRaster, stationInterval) # Build the all features dictionary that would be expect had the topometrics already # existed in the XS shapefile and simply got loaded. This is a combination of the new topometrics # and also the existing fields on the XS ShapeFile. singleXSMetrics = copy.deepcopy(aFeat['topometrics']) singleXSMetrics.update(aFeat['fields']) allFeatures.append(singleXSMetrics) # Destroying the raster object appears to prevent warning messages on Windows demRaster = None else: allFeatures = clShp.attributesToList(desiredFields) # For simple ShapeFiles, make every feature part of the main channel, and # set every feature as valid. This helps keep code below generic for x in allFeatures: if 'Channel' not in x: x['Channel'] = 'Main' if 'IsValid' not in x: x['IsValid'] = 1 for channelName in lChannels: # Filter the list of features to just those in this channel # PGB - 24 Apr 2017 - observed NULL values in 'Channel' ShapeFile field in Harold results. # Cast field contents to string to avoid crash here. channelFeatures = [ x for x in allFeatures if str(x['Channel']).lower() == channelName.lower() ] # Filter the list of features to just those that the crew considered valid validFeatures = [x for x in channelFeatures if x['IsValid'] != 0] # Filter the features to just those with a length that is within 4 standard deviations of mean wetted width channelStatistics = getStatistics(channelFeatures, 'WetWidth') autoFeatures = None if channelStatistics['StdDev'] is not None: wetWidthThreshold = channelStatistics['StdDev'] * 4 autoFeatures = [ x for x in channelFeatures if abs(x['WetWidth'] - channelStatistics['Mean']) < wetWidthThreshold ] # Loop over each desired metric and calculate the statistics for each filtering type for metricName, bestFiltering in CrossSectionMetrics.dMetricTypes.items( ): populateChannelStatistics(self.metrics[channelName], 'None', metricName, channelFeatures) populateChannelStatistics(self.metrics[channelName], 'Crew', metricName, validFeatures) if channelStatistics['StdDev'] is not None: populateChannelStatistics(self.metrics[channelName], 'Auto', metricName, autoFeatures) self.metrics[channelName]['Best'] = self.metrics[channelName][ bestFiltering] # The topometrics for the whole channel are always the results for 'Main'. # For complex ShapeFiles this will be just the results for the main channel. # For simple, single threaded, ShapeFiles this will all cross sections. self.metrics['Channel'] = self.metrics['Main']
def concatenateControlPoints(projects, outputFolder): spatialRef = None cpList = [] for proj in projects: controlPointsPath = proj['project'].getpath('Control_Points') vid = proj['visit'] cpShp = Shapefile(controlPointsPath) visitCPList = cpShp.featuresToShapely() spatialRef = cpShp.spatialRef for aPoint in visitCPList: cpItems = {} cpItems['geometry'] = aPoint['geometry'] cpItems['fields'] = {} cpItems['fields']['VisitID'] = int(vid) cpItems['fields']['PointNum'] = getFieldValue(aPoint['fields'], ['Point_Numb', 'POINT_NUMB', 'PointNumb', 'POINT_NUM', 'POINT']) cpItems['fields']['Code'] = getFieldValue(aPoint['fields'], ['DESCRIPTIO', 'Descriptio', 'Code']) cpItems['fields']['Type'] = getFieldValue(aPoint['fields'], ['Type', 'TYPE']) cpList.append(cpItems) outputPath = os.path.join(outputFolder, "AllVisitControlPoints.shp") print("Writing combined control points to {0}").format(outputPath) outShape = Shapefile() outShape.create(outputPath, spatialRef, geoType=ogr.wkbPointZM) outShape.createField("ID", ogr.OFTInteger) outShape.createField("VisitID", ogr.OFTInteger) outShape.createField("PointNum", ogr.OFTString) outShape.createField("Code", ogr.OFTString) outShape.createField("Type", ogr.OFTString) id = 1 for aCP in cpList: featureDefn = outShape.layer.GetLayerDefn() outFeature = ogr.Feature(featureDefn) ogrPolygon = ogr.CreateGeometryFromJson(json.dumps(mapping(aCP['geometry']))) outFeature.SetGeometry(ogrPolygon) outFeature.SetField('ID', id) id += 1 for fieldName, fieldValue in aCP['fields'].items(): outFeature.SetField(fieldName, fieldValue) outShape.layer.CreateFeature(outFeature)
def calc(self, shpCUPath, shpThalweg, rasDepth, visitMetrics, dUnits, unitDefs): if not path.isfile(shpCUPath): raise MissingException("Channel units file not found") if not path.isfile(shpThalweg): raise MissingException("Thalweg shape file not found") if not path.isfile(rasDepth): raise MissingException("Depth raster file not found") siteLength = visitMetrics['Wetted']['Centerline']['MainstemLength'] if siteLength is None: raise DataException("No valid site length found in visit metrics") # Give us a fresh template with 0's in the value positions self.metrics = self._templateMaker(0, unitDefs) dResultsChannelSummary = self.metrics['ResultsChannelSummary'] dResultsTier1 = self.metrics['ResultsTier1'] dResultsTier2 = self.metrics['ResultsTier2'] resultsCU = self.metrics['resultsCU'] #Load the Thalweg feature thalweg = Shapefile(shpThalweg).featuresToShapely() thalwegLine = thalweg[0]['geometry'] # Load the depth raster depthRaster = Raster(rasDepth) # Load the channel unit polygons and calculate the total area # The channel units should be clipped to the wetted extent and so this # can be used as the site area wetted shpCU = Shapefile(shpCUPath) arrCU = depthRaster.rasterMaskLayer(shpCUPath, "UnitNumber") feats = shpCU.featuresToShapely() for aFeat in feats: dResultsChannelSummary['Main']['Area'] += aFeat['geometry'].area # Loop over each channel unit and calculate topometrics for aFeat in feats: nCUNumber = int(aFeat['fields']['UnitNumber']) if nCUNumber not in dUnits: self.log.error("Channel Unit: '{0}' not present in the aux data.".format(nCUNumber)) # Keep it general for the exception so we can aggregate them raise DataException("The Channel Unit ShapeFile contains a unit number that is not present in the aux data.") tier1Name = dUnits[nCUNumber][0] tier2Name = dUnits[nCUNumber][1] nSegment = dUnits[nCUNumber][2] #print("Channel Unit Number {0}, Segment {1}, Tier 1 - {2}, Tier 2 - {3}").format(nCUNumber, nSegment, tier1Name, tier2Name) unitMetrics = {} resultsCU.append(unitMetrics) unitMetrics['ChannelUnitNumber'] = nCUNumber unitMetrics['Area'] = aFeat['geometry'].area unitMetrics['Tier1'] = tier1Name unitMetrics['Tier2'] = tier2Name unitMetrics['Length'] = None unitMetrics['ResidualDepth'] = None unitMetrics['DepthAtThalwegExit'] = None unitMetrics['ThalwegIntersect'] = 0 # Get the depth raster for this unit as variable so we can check # whether it is entirely masked below. depArr = depthRaster.array[arrCU == nCUNumber] if depArr.count() == 0: unitMetrics['MaxDepth'] = 0 unitMetrics['Volume'] = 0 else: unitMetrics['MaxDepth'] = np.max(depArr) unitMetrics['Volume'] = np.sum(depthRaster.array[arrCU == nCUNumber]) * (depthRaster.cellWidth**2) if nSegment != 1: dSideChannelSummary = dResultsChannelSummary['SideChannelSummary'] dMain = dResultsChannelSummary['Main'] # Side channel summary captures both small and large side channels dSideChannelSummary['Area'] += aFeat['geometry'].area dSideChannelSummary['Count'] += 1 dSideChannelSummary['Percent'] = 100 * dSideChannelSummary['Area'] / dMain['Area'] dSideChannelSummary['Volume'] += unitMetrics['Volume'] if 'side' in tier1Name.lower(): dSmallSideChannel = dResultsChannelSummary['SmallSideChannel'] dSmallSideChannel['Area'] += aFeat['geometry'].area dSmallSideChannel['Count'] += 1 dSmallSideChannel['Percent'] = 100 * dSmallSideChannel['Area'] / dMain['Area'] dSmallSideChannel['Volume'] += unitMetrics['Volume'] else: dLargeSideChannel = dResultsChannelSummary['LargeSideChannel'] dLargeSideChannel['Area'] += aFeat['geometry'].area dLargeSideChannel['Count'] += 1 dLargeSideChannel['Percent'] = 100 * dLargeSideChannel['Area'] / dMain['Area'] dLargeSideChannel['Volume'] += unitMetrics['Volume'] if tier1Name is None: raise DataException("tier1Name cannot be 'None'") if 'side' in tier1Name.lower(): dResultsChannelSummary['ChannelUnitBreakdown']['SmallSideChannel'] += 1 else: dResultsChannelSummary['ChannelUnitBreakdown']['Other'] += 1 if (thalwegLine.intersects(aFeat['geometry'])): cuThalwegLine = thalwegLine.intersection(aFeat['geometry']) exitPoint = None if cuThalwegLine.type == 'LineString': exitPoint = cuThalwegLine.coords[0] else: exitPoint = cuThalwegLine[0].coords[0] # Retrieve a list of points along the Thalweg in the channel unit thalwegPoints = ChannelUnitMetrics.interpolatePointsAlongLine(cuThalwegLine, 0.13) thalwegDepths = ChannelUnitMetrics.lookupRasterValuesAtPoints(thalwegPoints, depthRaster) unitMetrics['MaxDepth'] = np.nanmax(thalwegDepths['values']) unitMetrics['DepthAtThalwegExit'] = depthRaster.getPixelVal(exitPoint) unitMetrics['ResidualDepth'] = unitMetrics['MaxDepth'] - unitMetrics['DepthAtThalwegExit'] unitMetrics['Length'] = cuThalwegLine.length unitMetrics['ThalwegIntersect'] = 1 # Tier 1 and tier 2 topometrics. Note that metric dictionary keys are used for XML tags & require cleaning tier1NameClean = getCleanTierName(tier1Name) self._calcTierLevelMetrics(dResultsTier1[tier1NameClean], tier1Name, unitMetrics, siteLength, dResultsChannelSummary['Main']['Area']) tier2NameClean = getCleanTierName(tier2Name) self._calcTierLevelMetrics(dResultsTier2[tier2NameClean], tier2Name, unitMetrics, siteLength, dResultsChannelSummary['Main']['Area']) # Calculate the average of the channel unit max depths for each tier 1 and tier 2 type for tierKey, tierMetrics in { 'Tier1': dResultsTier1, 'Tier2': dResultsTier2}.items(): for tierName, metricDict in tierMetrics.items(): maxDepthList = [aResult['MaxDepth'] for aResult in resultsCU if getCleanTierName(aResult[tierKey]) == tierName] if len(maxDepthList) > 0: metricDict['AvgMaxDepth'] = np.average(maxDepthList) # Convert the sum of residual depth and depth at thalweg exist # to average residual depth for each tier 1 and tier 2 type for tierMetricDict in [dResultsTier1, dResultsTier2]: for tierName, tierMetrics in tierMetricDict.items(): # channel unit types that don't occur should retain the value None for Residual Depth and Depth at Thalweg exit if tierMetrics['Count'] > 0 and tierMetrics['ThalwegIntersectCount'] > 0: for metricName in ['ResidualDepth', 'DepthAtThalwegExit']: if tierMetrics[metricName] is not None and tierMetrics[metricName] != 0: tierMetrics[metricName] = tierMetrics[metricName] / tierMetrics['ThalwegIntersectCount'] else: tierMetrics[metricName] = 0