Source code for gsshapy.orm.cif

"""
********************************************************************************
* Name: StreamNetworkModel
* Author: Nathan Swain
* Created On: May 12, 2013
* Copyright: (c) Brigham Young University 2013
* License: BSD 2-Clause
********************************************************************************
"""
from __future__ import unicode_literals

## TODO: Add capability to store RATING_CURVE, RULE_CURVE, and SCHEDULED_RELEASE data

__all__ = ['ChannelInputFile',
           'StreamLink',
           'UpstreamLink',
           'StreamNode',
           'Weir',
           'Culvert',
           'Reservoir',
           'ReservoirPoint',
           'BreakpointCS',
           'Breakpoint',
           'TrapezoidalCS']

from future.utils import iteritems
import logging
import json
from mapkit.sqlatypes import Geometry
from sqlalchemy import ForeignKey, Column
from sqlalchemy.types import Integer, String, Float, Boolean
from sqlalchemy.orm import relationship
import xml.etree.ElementTree as ET

from . import DeclarativeBase
from ..base.geom import GeometricObjectBase
from ..base.file_base import GsshaPyFileObjectBase
from ..lib import parsetools as pt, cif_chunk as cic
from ..lib.parsetools import valueReadPreprocessor as vrp, valueWritePreprocessor as vwp

log = logging.getLogger(__name__)


[docs]class ChannelInputFile(DeclarativeBase, GsshaPyFileObjectBase): """ Object interface for the Channel Input File. The contents of the channel input file is abstracted into several objects including: :class:`.StreamLink`, :class:`.UpstreamLink`, :class:`.StreamNode`, :class:`.Weir`, :class:`.Culvert`, :class:`.Reservoir`, :class:`.ReservoirPoint`, :class:`.BreakpointCS`, :class:`.Breakpoint`, and :class:`.TrapezoidalCS`. See the documentation provided for each object for a more details. See: http://www.gsshawiki.com/Surface_Water_Routing:Channel_Routing """ __tablename__ = 'cif_channel_input_files' tableName = __tablename__ #: Database tablename # Primary and Foreign Keys id = Column(Integer, autoincrement=True, primary_key=True) #: PK # Value Columns alpha = Column(Float) #: FLOAT beta = Column(Float) #: FLOAT theta = Column(Float) #: FLOAT links = Column(Integer) #: INTEGER maxNodes = Column(Integer) #: INTEGER fileExtension = Column(String, default='cif') #: STRING # Relationship Properties projectFile = relationship('ProjectFile', uselist=False, back_populates='channelInputFile') #: RELATIONSHIP streamLinks = relationship('StreamLink', back_populates='channelInputFile') #: RELATIONSHIP linkNodeDatasets = relationship('LinkNodeDatasetFile', back_populates='channelInputFile') #: RELATIONSHIP def __init__(self, alpha=None, beta=None, theta=None, links=None, maxNodes=None): """ Constructor """ GsshaPyFileObjectBase.__init__(self) self.alpha = alpha self.beta = beta self.theta = theta self.links = links self.maxNodes = maxNodes def __eq__(self, other): return (self.alpha == other.alpha and self.beta == other.beta and self.theta == other.theta and self.links == other.links and self.maxNodes == other.maxNodes)
[docs] def getStreamNetworkAsKml(self, session, path=None, documentName='Stream Network', withNodes=False, styles={}): """ Retrieve the stream network visualization in KML format. Args: session (:mod:`sqlalchemy.orm.session.Session`): SQLAlchemy session object bound to PostGIS enabled database path (str, optional): Path to file where KML will be written. Defaults to None. documentName (str, optional): Name of the KML document. This will be the name that appears in the legend. Defaults to 'Stream Network'. withNodes (bool, optional): Include nodes. Defaults to False. styles (dict, optional): Custom styles to apply to KML geometry. Defaults to empty dictionary. Valid keys (styles) include: * lineColor: tuple/list of RGBA integers (0-255) e.g.: (255, 0, 0, 128) * lineWidth: float line width in pixels * nodeIconHref: link to icon image (PNG format) to represent nodes (see: http://kml4earth.appspot.com/icons.html) * nodeIconScale: scale of the icon image Returns: str: KML string """ # Retrieve Stream Links links = self.getFluvialLinks() # Set Default Styles lineColorValue = (255, 255, 0, 0) # Blue lineWidthValue = 2 nodeIconHrefValue = 'http://maps.google.com/mapfiles/kml/paddle/red-circle.png' nodeIconScaleValue = 1.0 if 'lineColor' in styles: if len(styles['lineColor']) < 4: log.warning('lineColor style must be a list or a tuple of four elements containing integer RGBA values.') else: userLineColor = styles['lineColor'] lineColorValue = (userLineColor[3], userLineColor[2], userLineColor[1], userLineColor[0]) if 'lineWidth' in styles: try: float(styles['lineWidth']) lineWidthValue = styles['lineWidth'] except ValueError: log.warning('lineWidth must be a valid number containing the width of the line in pixels.') if 'nodeIconHref' in styles: nodeIconHrefValue = styles['nodeIconHref'] if 'nodeIconScale' in styles: try: float(styles['nodeIconScale']) nodeIconScaleValue = styles['nodeIconScale'] except ValueError: log.warning('nodeIconScaleValue must be a valid number containing the width of the line in pixels.') # Initialize KML Document kml = ET.Element('kml', xmlns='http://www.opengis.net/kml/2.2') document = ET.SubElement(kml, 'Document') docName = ET.SubElement(document, 'name') docName.text = documentName for link in links: placemark = ET.SubElement(document, 'Placemark') placemarkName = ET.SubElement(placemark, 'name') placemarkName.text = str(link.linkNumber) # Create style tag and setup styles styles = ET.SubElement(placemark, 'Style') # Set line style lineStyle = ET.SubElement(styles, 'LineStyle') lineColor = ET.SubElement(lineStyle, 'color') lineColor.text = '%02X%02X%02X%02X' % lineColorValue lineWidth = ET.SubElement(lineStyle, 'width') lineWidth.text = str(lineWidthValue) # Add the geometry to placemark linkKML = link.getAsKml(session) if linkKML: lineString = ET.fromstring(linkKML) placemark.append(lineString) else: log.warning("No geometry found for link with id {0}".format(link.id)) if withNodes: # Create the node styles nodeStyles = ET.SubElement(document, 'Style', id='node_styles') # Hide labels nodeLabelStyle = ET.SubElement(nodeStyles, 'LabelStyle') nodeLabelScale = ET.SubElement(nodeLabelStyle, 'scale') nodeLabelScale.text = str(0) # Style icon nodeIconStyle = ET.SubElement(nodeStyles, 'IconStyle') # Set icon nodeIcon = ET.SubElement(nodeIconStyle, 'Icon') iconHref = ET.SubElement(nodeIcon, 'href') iconHref.text = nodeIconHrefValue # Set icon scale iconScale = ET.SubElement(nodeIconStyle, 'scale') iconScale.text = str(nodeIconScaleValue) for node in link.nodes: # New placemark for each node nodePlacemark = ET.SubElement(document, 'Placemark') nodePlacemarkName = ET.SubElement(nodePlacemark, 'name') nodePlacemarkName.text = str(node.nodeNumber) # Styles for the node nodeStyleUrl = ET.SubElement(nodePlacemark, 'styleUrl') nodeStyleUrl.text = '#node_styles' nodeString = ET.fromstring(node.getAsKml(session)) nodePlacemark.append(nodeString) # Embed node data nodeExtendedData = ET.SubElement(nodePlacemark, 'ExtendedData') nodeNumberData = ET.SubElement(nodeExtendedData, 'Data', name='node_number') nodeNumberValue = ET.SubElement(nodeNumberData, 'value') nodeNumberValue.text = str(node.nodeNumber) nodeLinkNumberData = ET.SubElement(nodeExtendedData, 'Data', name='link_number') nodeLinkNumberValue = ET.SubElement(nodeLinkNumberData, 'value') nodeLinkNumberValue.text = str(link.linkNumber) nodeElevationData = ET.SubElement(nodeExtendedData, 'Data', name='elevation') nodeElevationValue = ET.SubElement(nodeElevationData, 'value') nodeElevationValue.text = str(node.elevation) # Create the data tag extendedData = ET.SubElement(placemark, 'ExtendedData') # Add value to data linkNumberData = ET.SubElement(extendedData, 'Data', name='link_number') linkNumberValue = ET.SubElement(linkNumberData, 'value') linkNumberValue.text = str(link.linkNumber) linkTypeData = ET.SubElement(extendedData, 'Data', name='link_type') linkTypeValue = ET.SubElement(linkTypeData, 'value') linkTypeValue.text = str(link.type) numElementsData = ET.SubElement(extendedData, 'Data', name='number_elements') numElementsValue = ET.SubElement(numElementsData, 'value') numElementsValue.text = str(link.numElements) dxData = ET.SubElement(extendedData, 'Data', name='dx') dxValue = ET.SubElement(dxData, 'value') dxValue.text = str(link.dx) erodeData = ET.SubElement(extendedData, 'Data', name='erode') erodeValue = ET.SubElement(erodeData, 'value') erodeValue.text = str(link.type) subsurfaceData = ET.SubElement(extendedData, 'Data', name='subsurface') subsurfaceValue = ET.SubElement(subsurfaceData, 'value') subsurfaceValue.text = str(link.type) kmlString = ET.tostring(kml) if path: with open(path, 'w') as f: f.write(kmlString) return kmlString
[docs] def getStreamNetworkAsWkt(self, session, withNodes=True): """ Retrieve the stream network geometry in Well Known Text format. Args: session (:mod:`sqlalchemy.orm.session.Session`): SQLAlchemy session object bound to PostGIS enabled database withNodes (bool, optional): Include nodes. Defaults to False. Returns: str: Well Known Text string. """ wkt_list = [] for link in self.streamLinks: wkt_link = link.getAsWkt(session) if wkt_link: wkt_list.append(wkt_link) if withNodes: for node in link.nodes: wkt_node = node.getAsWkt(session) if wkt_node: wkt_list.append(wkt_node) return 'GEOMCOLLECTION ({0})'.format(', '.join(wkt_list))
[docs] def getStreamNetworkAsGeoJson(self, session, withNodes=True): """ Retrieve the stream network geometry in GeoJSON format. Args: session (:mod:`sqlalchemy.orm.session.Session`): SQLAlchemy session object bound to PostGIS enabled database withNodes (bool, optional): Include nodes. Defaults to False. Returns: str: GeoJSON string. """ features_list = [] # Assemble link features for link in self.streamLinks: link_geoJson = link.getAsGeoJson(session) if link_geoJson: link_geometry = json.loads(link.getAsGeoJson(session)) link_properties = {"link_number": link.linkNumber, "type": link.type, "num_elements": link.numElements, "dx": link.dx, "erode": link.erode, "subsurface": link.subsurface} link_feature = {"type": "Feature", "geometry": link_geometry, "properties": link_properties, "id": link.id} features_list.append(link_feature) # Assemble node features if withNodes: for node in link.nodes: node_geoJson = node.getAsGeoJson(session) if node_geoJson: node_geometry = json.loads(node_geoJson) node_properties = {"link_number": link.linkNumber, "node_number": node.nodeNumber, "elevation": node.elevation} node_feature = {"type": "Feature", "geometry": node_geometry, "properties": node_properties, "id": node.id} features_list.append(node_feature) feature_collection = {"type": "FeatureCollection", "features": features_list} return json.dumps(feature_collection)
def _read(self, directory, filename, session, path, name, extension, spatial, spatialReferenceID, replaceParamFile): """ Channel Input File Read from File Method """ # Set file extension property self.fileExtension = extension # Dictionary of keywords/cards and parse function names KEYWORDS = {'ALPHA': cic.cardChunk, 'BETA': cic.cardChunk, 'THETA': cic.cardChunk, 'LINKS': cic.cardChunk, 'MAXNODES': cic.cardChunk, 'CONNECT': cic.connectChunk, 'LINK': cic.linkChunk} links = [] connectivity = [] # Parse file into chunks associated with keywords/cards with open(path, 'r') as f: chunks = pt.chunk(KEYWORDS, f) # Parse chunks associated with each key for key, chunkList in iteritems(chunks): # Parse each chunk in the chunk list for chunk in chunkList: # Call chunk specific parsers for each chunk result = KEYWORDS[key](key, chunk) # Cases if key == 'LINK': # Link handler links.append(self._createLink(result, replaceParamFile)) elif key == 'CONNECT': # Connectivity handler connectivity.append(result) else: # Global variable handler card = result['card'] value = result['values'][0] # Cases if card == 'LINKS': self.links = int(value) elif card == 'MAXNODES': self.maxNodes = int(value) elif card == 'ALPHA': self.alpha = float(vrp(value, replaceParamFile)) elif card == 'BETA': self.beta = float(vrp(value, replaceParamFile)) elif card == 'THETA': self.theta = float(vrp(value, replaceParamFile)) self._createConnectivity(linkList=links, connectList=connectivity) if spatial: self._createGeometry(session, spatialReferenceID) def _write(self, session, openFile, replaceParamFile): """ Channel Input File Write to File Method """ # Write lines openFile.write('GSSHA_CHAN\n') alpha = vwp(self.alpha, replaceParamFile) try: openFile.write('ALPHA%s%.6f\n' % (' ' * 7, alpha)) except: openFile.write('ALPHA%s%s\n' % (' ' * 7, alpha)) beta = vwp(self.beta, replaceParamFile) try: openFile.write('BETA%s%.6f\n' % (' ' * 8, beta)) except: openFile.write('BETA%s%s\n' % (' ' * 8, beta)) theta = vwp(self.theta, replaceParamFile) try: openFile.write('THETA%s%.6f\n' % (' ' * 7, theta)) except: openFile.write('THETA%s%s\n' % (' ' * 7, theta)) openFile.write('LINKS%s%s\n' % (' ' * 7, self.links)) openFile.write('MAXNODES%s%s\n' % (' ' * 4, self.maxNodes)) # Retrieve StreamLinks links = self.getOrderedLinks(session) self._writeConnectivity(links=links, fileObject=openFile) self._writeLinks(links=links, fileObject=openFile, replaceParamFile=replaceParamFile) def _createLink(self, linkResult, replaceParamFile): """ Create GSSHAPY Link Object Method """ link = None # Cases if linkResult['type'] == 'XSEC': # Cross section link handler link = self._createCrossSection(linkResult, replaceParamFile) elif linkResult['type'] == 'STRUCTURE': # Structure link handler link = self._createStructure(linkResult, replaceParamFile) elif linkResult['type'] in ('RESERVOIR', 'LAKE'): # Reservoir/lake handler link = self._createReservoir(linkResult, replaceParamFile) return link def _createConnectivity(self, linkList, connectList): """ Create GSSHAPY Connect Object Method """ # Create StreamLink-Connectivity Pairs for idx, link in enumerate(linkList): connectivity = connectList[idx] # Initialize GSSHAPY UpstreamLink objects for upLink in connectivity['upLinks']: upstreamLink = UpstreamLink(upstreamLinkID=int(upLink)) upstreamLink.streamLink = link link.downstreamLinkID = int(connectivity['downLink']) link.numUpstreamLinks = int(connectivity['numUpLinks']) def _createCrossSection(self, linkResult, replaceParamFile): """ Create GSSHAPY Cross Section Objects Method """ # Extract header variables from link result object header = linkResult['header'] # Initialize GSSHAPY StreamLink object link = StreamLink(linkNumber=int(header['link']), type=header['xSecType'], numElements=header['nodes'], dx=vrp(header['dx'], replaceParamFile), erode=header['erode'], subsurface=header['subsurface']) # Associate StreamLink with ChannelInputFile link.channelInputFile = self # Initialize GSSHAPY TrapezoidalCS or BreakpointCS objects xSection = linkResult['xSection'] # Cases if 'TRAPEZOID' in link.type or 'TRAP' in link.type: # Trapezoid cross section handler # Initialize GSSHPY TrapeziodalCS object trapezoidCS = TrapezoidalCS(mannings_n=vrp(xSection['mannings_n'], replaceParamFile), bottomWidth=vrp(xSection['bottom_width'], replaceParamFile), bankfullDepth=vrp(xSection['bankfull_depth'], replaceParamFile), sideSlope=vrp(xSection['side_slope'], replaceParamFile), mRiver=vrp(xSection['m_river'], replaceParamFile), kRiver=vrp(xSection['k_river'], replaceParamFile), erode=xSection['erode'], subsurface=xSection['subsurface'], maxErosion=vrp(xSection['max_erosion'], replaceParamFile)) # Associate TrapezoidalCS with StreamLink trapezoidCS.streamLink = link elif 'BREAKPOINT' in link.type: # Breakpoint cross section handler # Initialize GSSHAPY BreakpointCS objects breakpointCS = BreakpointCS(mannings_n=vrp(xSection['mannings_n'], replaceParamFile), numPairs=xSection['npairs'], numInterp=vrp(xSection['num_interp'], replaceParamFile), mRiver=vrp(xSection['m_river'], replaceParamFile), kRiver=vrp(xSection['k_river'], replaceParamFile), erode=xSection['erode'], subsurface=xSection['subsurface'], maxErosion=vrp(xSection['max_erosion'], replaceParamFile)) # Associate BreakpointCS with StreamLink breakpointCS.streamLink = link # Create GSSHAPY Breakpoint objects for b in xSection['breakpoints']: breakpoint = Breakpoint(x=b['x'], y=b['y']) # Associate Breakpoint with BreakpointCS breakpoint.crossSection = breakpointCS # Initialize GSSHAPY StreamNode objects for n in linkResult['nodes']: # Initialize GSSHAPY StreamNode object node = StreamNode(nodeNumber=int(n['node']), x=n['x'], y=n['y'], elevation=n['elev']) # Associate StreamNode with StreamLink node.streamLink = link return link def _createStructure(self, linkResult, replaceParamFile): """ Create GSSHAPY Structure Objects Method """ # Constants WEIRS = ('WEIR', 'SAG_WEIR') CULVERTS = ('ROUND_CULVERT', 'RECT_CULVERT') CURVES = ('RATING_CURVE', 'SCHEDULED_RELEASE', 'RULE_CURVE') header = linkResult['header'] # Initialize GSSHAPY StreamLink object link = StreamLink(linkNumber=header['link'], type=linkResult['type'], numElements=header['numstructs']) # Associate StreamLink with ChannelInputFile link.channelInputFile = self # Create Structure objects for s in linkResult['structures']: structType = s['structtype'] # Cases if structType in WEIRS: # Weir type handler # Initialize GSSHAPY Weir object weir = Weir(type=structType, crestLength=vrp(s['crest_length'], replaceParamFile), crestLowElevation=vrp(s['crest_low_elev'], replaceParamFile), dischargeCoeffForward=vrp(s['discharge_coeff_forward'], replaceParamFile), dischargeCoeffReverse=vrp(s['discharge_coeff_reverse'], replaceParamFile), crestLowLocation=vrp(s['crest_low_loc'], replaceParamFile), steepSlope=vrp(s['steep_slope'], replaceParamFile), shallowSlope=vrp(s['shallow_slope'], replaceParamFile)) # Associate Weir with StreamLink weir.streamLink = link elif structType in CULVERTS: # Culvert type handler # Initialize GSSHAPY Culvert object culvert = Culvert(type=structType, upstreamInvert=vrp(s['upinvert'], replaceParamFile), downstreamInvert=vrp(s['downinvert'], replaceParamFile), inletDischargeCoeff=vrp(s['inlet_disch_coeff'], replaceParamFile), reverseFlowDischargeCoeff=vrp(s['rev_flow_disch_coeff'], replaceParamFile), slope=vrp(s['slope'], replaceParamFile), length=vrp(s['length'], replaceParamFile), roughness=vrp(s['rough_coeff'], replaceParamFile), diameter=vrp(s['diameter'], replaceParamFile), width=vrp(s['width'], replaceParamFile), height=vrp(s['height'], replaceParamFile)) # Associate Culvert with StreamLink culvert.streamLink = link elif structType in CURVES: # Curve type handler pass return link def _createReservoir(self, linkResult, replaceParamFile): """ Create GSSHAPY Reservoir Objects Method """ # Extract header variables from link result object header = linkResult['header'] # Cases if linkResult['type'] == 'LAKE': # Lake handler initWSE = vrp(header['initwse'], replaceParamFile) minWSE = vrp(header['minwse'], replaceParamFile) maxWSE = vrp(header['maxwse'], replaceParamFile) numPts = header['numpts'] elif linkResult['type'] == 'RESERVOIR': # Reservoir handler initWSE = vrp(header['res_initwse'], replaceParamFile) minWSE = vrp(header['res_minwse'], replaceParamFile) maxWSE = vrp(header['res_maxwse'], replaceParamFile) numPts = header['res_numpts'] # Initialize GSSHAPY Reservoir object reservoir = Reservoir(initWSE=initWSE, minWSE=minWSE, maxWSE=maxWSE) # Initialize GSSHAPY StreamLink object link = StreamLink(linkNumber=int(header['link']), type=linkResult['type'], numElements=numPts) # Associate StreamLink with ChannelInputFile link.channelInputFile = self # Associate Reservoir with StreamLink reservoir.streamLink = link # Create ReservoirPoint objects for p in linkResult['points']: # Initialize GSSHAPY ReservoirPoint object resPoint = ReservoirPoint(i=p['i'], j=p['j']) # Associate ReservoirPoint with Reservoir resPoint.reservoir = reservoir return link def _createGeometry(self, session, spatialReferenceID): """ Create PostGIS geometric objects """ # Flush the current session session.flush() # Create geometry for each fluvial link for link in self.getFluvialLinks(): # Retrieve the nodes for each link nodes = link.nodes nodeCoordinates = [] # Create geometry for each node for node in nodes: # Assemble coordinates in well known text format coordinates = '{0} {1} {2}'.format(node.x, node.y, node.elevation) nodeCoordinates.append(coordinates) # Create well known text string for point with z coordinate wktPoint = 'POINT Z ({0})'.format(coordinates) # Write SQL statement statement = self._getUpdateGeometrySqlString(geometryID=node.id, tableName=node.tableName, spatialReferenceID=spatialReferenceID, wktString=wktPoint) session.execute(statement) # Assemble line string in well known text format wktLineString = 'LINESTRING Z ({0})'.format(', '.join(nodeCoordinates)) # Write SQL statement statement = self._getUpdateGeometrySqlString(geometryID=link.id, tableName=link.tableName, spatialReferenceID=spatialReferenceID, wktString=wktLineString) session.execute(statement) def _writeConnectivity(self, links, fileObject): """ Write Connectivity Lines to File Method """ for link in links: linkNum = link.linkNumber downLink = link.downstreamLinkID numUpLinks = link.numUpstreamLinks upLinks = '' for upLink in link.upstreamLinks: upLinks = '{}{:>5}'.format(upLinks, str(upLink.upstreamLinkID)) line = 'CONNECT{:>5}{:>5}{:>5}{}\n'.format(linkNum, downLink, numUpLinks, upLinks) fileObject.write(line) fileObject.write('\n') def _writeLinks(self, links, fileObject, replaceParamFile): """ Write Link Lines to File Method """ for link in links: linkType = link.type fileObject.write('LINK %s\n' % link.linkNumber) # Cases if 'TRAP' in linkType or 'TRAPEZOID' in linkType or 'BREAKPOINT' in linkType: self._writeCrossSectionLink(link, fileObject, replaceParamFile) elif linkType == 'STRUCTURE': self._writeStructureLink(link, fileObject, replaceParamFile) elif linkType in ('RESERVOIR', 'LAKE'): self._writeReservoirLink(link, fileObject, replaceParamFile) else: log.error('OOPS: CIF LINE 417') # THIS SHOULDN'T HAPPEN fileObject.write('\n') def _writeReservoirLink(self, link, fileObject, replaceParamFile): """ Write Reservoir/Lake Link to File Method """ fileObject.write('%s\n' % link.type) # Retrieve reservoir reservoir = link.reservoir # Reservoir parameters initWSE = vwp(reservoir.initWSE, replaceParamFile) minWSE = vwp(reservoir.minWSE, replaceParamFile) maxWSE = vwp(reservoir.maxWSE, replaceParamFile) numElements = link.numElements # Cases if link.type == 'LAKE': # Lake handler try: fileObject.write('INITWSE %.6f\n' % initWSE) except: fileObject.write('INITWSE %s\n' % initWSE) try: fileObject.write('MINWSE %.6f\n' % minWSE) except: fileObject.write('MINWSE %s\n' % minWSE) try: fileObject.write('MAXWSE %.6f\n' % maxWSE) except: fileObject.write('MAXWSE %s\n' % maxWSE) fileObject.write('NUMPTS %s\n' % numElements) elif link.type == 'RESERVOIR': # Reservoir handler try: fileObject.write('RES_INITWSE %.6f\n' % initWSE) except: fileObject.write('RES_INITWSE %s\n' % initWSE) try: fileObject.write('RES_MINWSE %.6f\n' % minWSE) except: fileObject.write('RES_MINWSE %s\n' % minWSE) try: fileObject.write('RES_MAXWSE %.6f\n' % maxWSE) except: fileObject.write('RES_MAXWSE %s\n' % maxWSE) fileObject.write('RES_NUMPTS %s\n' % numElements) # Retrieve reservoir points points = reservoir.reservoirPoints for idx, point in enumerate(points): if ((idx + 1) % 10) != 0: fileObject.write('%s %s ' % (point.i, point.j)) else: fileObject.write('%s %s\n' % (point.i, point.j)) if (link.numElements % 10) != 0: fileObject.write('\n') def _writeStructureLink(self, link, fileObject, replaceParamFile): """ Write Structure Link to File Method """ fileObject.write('%s\n' % link.type) fileObject.write('NUMSTRUCTS %s\n' % link.numElements) # Retrieve lists of structures weirs = link.weirs culverts = link.culverts # Write weirs to file for weir in weirs: fileObject.write('STRUCTTYPE %s\n' % weir.type) # Check for replacement vars crestLength = vwp(weir.crestLength, replaceParamFile) crestLowElevation = vwp(weir.crestLowElevation, replaceParamFile) dischargeCoeffForward = vwp(weir.dischargeCoeffForward, replaceParamFile) dischargeCoeffReverse = vwp(weir.dischargeCoeffReverse, replaceParamFile) crestLowLocation = vwp(weir.crestLowLocation, replaceParamFile) steepSlope = vwp(weir.steepSlope, replaceParamFile) shallowSlope = vwp(weir.shallowSlope, replaceParamFile) if weir.crestLength != None: try: fileObject.write('CREST_LENGTH %.6f\n' % crestLength) except: fileObject.write('CREST_LENGTH %s\n' % crestLength) if weir.crestLowElevation != None: try: fileObject.write('CREST_LOW_ELEV %.6f\n' % crestLowElevation) except: fileObject.write('CREST_LOW_ELEV %s\n' % crestLowElevation) if weir.dischargeCoeffForward != None: try: fileObject.write('DISCHARGE_COEFF_FORWARD %.6f\n' % dischargeCoeffForward) except: fileObject.write('DISCHARGE_COEFF_FORWARD %s\n' % dischargeCoeffForward) if weir.dischargeCoeffReverse != None: try: fileObject.write('DISCHARGE_COEFF_REVERSE %.6f\n' % dischargeCoeffReverse) except: fileObject.write('DISCHARGE_COEFF_REVERSE %s\n' % dischargeCoeffReverse) if weir.crestLowLocation != None: fileObject.write('CREST_LOW_LOC %s\n' % crestLowLocation) if weir.steepSlope != None: try: fileObject.write('STEEP_SLOPE %.6f\n' % steepSlope) except: fileObject.write('STEEP_SLOPE %s\n' % steepSlope) if weir.shallowSlope != None: try: fileObject.write('SHALLOW_SLOPE %.6f\n' % shallowSlope) except: fileObject.write('SHALLOW_SLOPE %s\n' % shallowSlope) # Write culverts to file for culvert in culverts: fileObject.write('STRUCTTYPE %s\n' % culvert.type) # Check for replacement vars upstreamInvert = vwp(culvert.upstreamInvert, replaceParamFile) downstreamInvert = vwp(culvert.downstreamInvert, replaceParamFile) inletDischargeCoeff = vwp(culvert.inletDischargeCoeff, replaceParamFile) reverseFlowDischargeCoeff = vwp(culvert.reverseFlowDischargeCoeff, replaceParamFile) slope = vwp(culvert.slope, replaceParamFile) length = vwp(culvert.length, replaceParamFile) roughness = vwp(culvert.roughness, replaceParamFile) diameter = vwp(culvert.diameter, replaceParamFile) width = vwp(culvert.width, replaceParamFile) height = vwp(culvert.height, replaceParamFile) if culvert.upstreamInvert != None: try: fileObject.write('UPINVERT %.6f\n' % upstreamInvert) except: fileObject.write('UPINVERT %s\n' % upstreamInvert) if culvert.downstreamInvert != None: try: fileObject.write('DOWNINVERT %.6f\n' % downstreamInvert) except: fileObject.write('DOWNINVERT %s\n' % downstreamInvert) if culvert.inletDischargeCoeff != None: try: fileObject.write('INLET_DISCH_COEFF %.6f\n' % inletDischargeCoeff) except: fileObject.write('INLET_DISCH_COEFF %s\n' % inletDischargeCoeff) if culvert.reverseFlowDischargeCoeff != None: try: fileObject.write('REV_FLOW_DISCH_COEFF %.6f\n' % reverseFlowDischargeCoeff) except: fileObject.write('REV_FLOW_DISCH_COEFF %s\n' % reverseFlowDischargeCoeff) if culvert.slope != None: try: fileObject.write('SLOPE %.6f\n' % slope) except: fileObject.write('SLOPE %s\n' % slope) if culvert.length != None: try: fileObject.write('LENGTH %.6f\n' % length) except: fileObject.write('LENGTH %s\n' % length) if culvert.roughness != None: try: fileObject.write('ROUGH_COEFF %.6f\n' % roughness) except: fileObject.write('ROUGH_COEFF %s\n' % roughness) if culvert.diameter != None: try: fileObject.write('DIAMETER %.6f\n' % diameter) except: fileObject.write('DIAMETER %s\n' % diameter) if culvert.width != None: try: fileObject.write('WIDTH %.6f\n' % width) except: fileObject.write('WIDTH %s\n' % width) if culvert.height != None: try: fileObject.write('HEIGHT %.6f\n' % height) except: fileObject.write('HEIGHT %s\n' % height) def _writeCrossSectionLink(self, link, fileObject, replaceParamFile): """ Write Cross Section Link to File Method """ linkType = link.type # Write cross section link header dx = vwp(link.dx, replaceParamFile) try: fileObject.write('DX %.6f\n' % dx) except: fileObject.write('DX %s\n' % dx) fileObject.write('%s\n' % linkType) fileObject.write('NODES %s\n' % link.numElements) for node in link.nodes: # Write node information fileObject.write('NODE %s\n' % node.nodeNumber) fileObject.write('X_Y %.6f %.6f\n' % (node.x, node.y)) fileObject.write('ELEV %.6f\n' % node.elevation) if node.nodeNumber == 1: # Write cross section information after first node fileObject.write('XSEC\n') # Cases if 'TRAPEZOID' in linkType or 'TRAP' in linkType: # Retrieve cross section xSec = link.trapezoidalCS # Write cross section properties mannings_n = vwp(xSec.mannings_n, replaceParamFile) bottomWidth = vwp(xSec.bottomWidth, replaceParamFile) bankfullDepth = vwp(xSec.bankfullDepth, replaceParamFile) sideSlope = vwp(xSec.sideSlope, replaceParamFile) try: fileObject.write('MANNINGS_N %.6f\n' % mannings_n) except: fileObject.write('MANNINGS_N %s\n' % mannings_n) try: fileObject.write('BOTTOM_WIDTH %.6f\n' % bottomWidth) except: fileObject.write('BOTTOM_WIDTH %s\n' % bottomWidth) try: fileObject.write('BANKFULL_DEPTH %.6f\n' % bankfullDepth) except: fileObject.write('BANKFULL_DEPTH %s\n' % bankfullDepth) try: fileObject.write('SIDE_SLOPE %.6f\n' % sideSlope) except: fileObject.write('SIDE_SLOPE %s\n' % sideSlope) # Write optional cross section properties self._writeOptionalXsecCards(fileObject=fileObject, xSec=xSec, replaceParamFile=replaceParamFile) elif 'BREAKPOINT' in linkType: # Retrieve cross section xSec = link.breakpointCS # Write cross section properties mannings_n = vwp(xSec.mannings_n, replaceParamFile) try: fileObject.write('MANNINGS_N %.6f\n' % mannings_n) except: fileObject.write('MANNINGS_N %s\n' % mannings_n) fileObject.write('NPAIRS %s\n' % xSec.numPairs) fileObject.write('NUM_INTERP %s\n' % vwp(xSec.numInterp, replaceParamFile)) # Write optional cross section properties self._writeOptionalXsecCards(fileObject=fileObject, xSec=xSec, replaceParamFile=replaceParamFile) # Write breakpoint lines for bp in xSec.breakpoints: fileObject.write('X1 %.6f %.6f\n' % (bp.x, bp.y)) else: log.error('OOPS: MISSED A CROSS SECTION TYPE. CIF LINE 580. {0}'.format(linkType)) def _writeOptionalXsecCards(self, fileObject, xSec, replaceParamFile): """ Write Optional Cross Section Cards to File Method """ if xSec.erode: fileObject.write('ERODE\n') if xSec.maxErosion != None: fileObject.write('MAX_EROSION %.6f\n' % xSec.maxErosion) if xSec.subsurface: fileObject.write('SUBSURFACE\n') if xSec.mRiver != None: mRiver = vwp(xSec.mRiver, replaceParamFile) try: fileObject.write('M_RIVER %.6f\n' % mRiver) except: fileObject.write('M_RIVER %s\n' % mRiver) if xSec.kRiver != None: kRiver = vwp(xSec.kRiver, replaceParamFile) try: fileObject.write('K_RIVER %.6f\n' % kRiver) except: fileObject.write('K_RIVER %s\n' % kRiver) def _getUpdateGeometrySqlString(self, geometryID, tableName, spatialReferenceID, wktString): statement = """ UPDATE {0} SET geometry=ST_GeomFromText('{1}', {2}) WHERE id={3}; """.format(tableName, wktString, spatialReferenceID, geometryID) return statement
[docs]class StreamNode(DeclarativeBase, GeometricObjectBase): """ Object containing the stream node data in the channel network. Stream nodes represent the computational unit of GSSHA stream networks. Each stream link must consist of two or more stream nodes. This object inherits several methods from the :class:`gsshapy.orm.GeometricObjectBase` base class for generating geometric visualizations. See: http://www.gsshawiki.com/Surface_Water_Routing:Channel_Routing#5.1.4.1.4.2.1.4_Node_information """ __tablename__ = 'cif_nodes' tableName = __tablename__ #: Database tablename # Public Table Metadata tableName = __tablename__ geometryColumnName = 'geometry' # Primary and Foreign Keys id = Column(Integer, autoincrement=True, primary_key=True) #: PK linkID = Column(Integer, ForeignKey('cif_links.id')) #: FK # Value Columns nodeNumber = Column(Integer) #: INTEGER x = Column(Float) #: FLOAT y = Column(Float) #: FLOAT elevation = Column(Float) #: FLOAT geometry = Column(Geometry) #: GEOMETRY # Relationship Properties streamLink = relationship('StreamLink', back_populates='nodes') #: RELATIONSHIP datasets = relationship('NodeDataset', back_populates='node') #: RELATIONSHIP def __init__(self, nodeNumber, x, y, elevation): """ Constructor """ self.nodeNumber = nodeNumber self.x = x self.y = y self.elevation = elevation def __repr__(self): return '<Node: NodeNumber=%s, X=%s, Y=%s, Elevation=%s>' % ( self.nodeNumber, self.x, self.y, self.elevation) def __eq__(self, other): return (self.nodeNumber == other.nodeNumber and self.x == other.x and self.y == other.y and self.elevation == other.elevation)
[docs]class Weir(DeclarativeBase): """ Object containing data that defines a weir structure for a stream link. See: http://www.gsshawiki.com/Surface_Water_Routing:Channel_Routing#5.1.4.1.4.2_-_Structure_channel_links """ __tablename__ = 'cif_weirs' tableName = __tablename__ #: Database tablename # Primary and Foreign Keys id = Column(Integer, autoincrement=True, primary_key=True) #: PK linkID = Column(Integer, ForeignKey('cif_links.id')) #: FK # Value Columns type = Column(String) #: STRING crestLength = Column(Float) #: FLOAT crestLowElevation = Column(Float) #: FLOAT dischargeCoeffForward = Column(Float) #: FLOAT dischargeCoeffReverse = Column(Float) #: FLOAT crestLowLocation = Column(Float) #: FLOAT steepSlope = Column(Float) #: FLOAT shallowSlope = Column(Float) #: FLOAT # Relationship Properties streamLink = relationship('StreamLink', back_populates='weirs') #: RELATIONSHIP def __init__(self, type, crestLength, crestLowElevation, dischargeCoeffForward, dischargeCoeffReverse, crestLowLocation, steepSlope, shallowSlope): """ Constructor """ self.type = type self.crestLength = crestLength self.crestLowElevation = crestLowElevation self.dischargeCoeffForward = dischargeCoeffForward self.dischargeCoeffReverse = dischargeCoeffReverse self.crestLowLocation = crestLowLocation self.steepSlope = steepSlope self.shallowSlope = shallowSlope def __repr__(self): return '<Weir: Type=%s, CrestLenght=%s, CrestLowElevation=%s, DischargeCoeffForward=%s, DischargeCoeffReverse=%s, CrestLowLocation=%s, SteepSlope=%s, ShallowSlope=%s>' % ( self.type, self.crestLength, self.crestLowElevation, self.dischargeCoeffForward, self.dischargeCoeffReverse, self.crestLowLocation, self.steepSlope, self.shallowSlope) def __eq__(self, other): return (self.type == other.type and self.crestLength == other.crestLength and self.crestLowElevation == other.crestLowElevation and self.dischargeCoeffForward == other.dischargeCoeffForward and self.dischargeCoeffReverse == other.dischargeCoeffReverse and self.crestLowLocation == other.crestLowLocation and self.steepSlope == other.steepSlope and self.shallowSlope == other.shallowSlope)
[docs]class Culvert(DeclarativeBase): """ Object containing a culvert structure data for a stream link. See: http://www.gsshawiki.com/Surface_Water_Routing:Channel_Routing#5.1.4.1.4.2_-_Structure_channel_links """ __tablename__ = 'cif_culverts' tableName = __tablename__ #: Database tablename # Primary and Foreign Keys id = Column(Integer, autoincrement=True, primary_key=True) #: PK linkID = Column(Integer, ForeignKey('cif_links.id')) #: FK # Value Columns type = Column(String) #: STRING upstreamInvert = Column(Float) #: FLOAT downstreamInvert = Column(Float) #: FLOAT inletDischargeCoeff = Column(Float) #: FLOAT reverseFlowDischargeCoeff = Column(Float) #: FLOAT slope = Column(Float) #: FLOAT length = Column(Float) #: FLOAT roughness = Column(Float) #: FLOAT diameter = Column(Float) #: FLOAT width = Column(Float) #: FLOAT height = Column(Float) #: FLOAT # Relationship Properties streamLink = relationship('StreamLink', back_populates='culverts') #: RELATIONSHIP def __init__(self, type, upstreamInvert, downstreamInvert, inletDischargeCoeff, reverseFlowDischargeCoeff, slope, length, roughness, diameter, width, height): """ Constructor """ self.type = type self.upstreamInvert = upstreamInvert self.downstreamInvert = downstreamInvert self.inletDischargeCoeff = inletDischargeCoeff self.reverseFlowDischargeCoeff = reverseFlowDischargeCoeff self.slope = slope self.length = length self.roughness = roughness self.diameter = diameter self.width = width self.height = height def __repr__(self): return '<Culvert: Type=%s, UpstreamInvert=%s, DownstreamInvert=%s, InletDischargeCoeff=%s, ReverseFlowDischargeCoeff=%s, Slope=%s, Length=%s, Roughness=%s, Diameter=%s, Width=%s, Height=%s>' % ( self.type, self.upstreamInvert, self.downstreamInvert, self.inletDischargeCoeff, self.reverseFlowDischargeCoeff, self.slope, self.length, self.roughness, self.diameter, self.width, self.height) def __eq__(self, other): return (self.type == other.type and self.upstreamInvert == other.upstreamInvert and self.downstreamInvert == other.downstreamInvert and self.inletDischargeCoeff == other.inletDischargeCoeff and self.reverseFlowDischargeCoeff == other.reverseFlowDischargeCoeff and self.slope == other.slope and self.length == other.length and self.roughness == other.roughness and self.diameter == other.diameter and self.width == other.width and self.height == other.height)
[docs]class Reservoir(DeclarativeBase): """ Object containing a data that defines a reservoir for a stream link. See: http://www.gsshawiki.com/Surface_Water_Routing:Channel_Routing#5.1.4.1.4.3_-_Reservoir_channel_links """ __tablename__ = 'cif_reservoirs' tableName = __tablename__ #: Database tablename # Primary and Foreign Keys id = Column(Integer, autoincrement=True, primary_key=True) #: PK linkID = Column(Integer, ForeignKey('cif_links.id')) #: FK # Value Columns initWSE = Column(Float) #: FLOAT minWSE = Column(Float) #: FLOAT maxWSE = Column(Float) #: FLOAT # Relationship Properties streamLink = relationship('StreamLink', back_populates='reservoir') #: RELATIONSHIP reservoirPoints = relationship('ReservoirPoint', back_populates='reservoir') #: RELATIONSHIP def __init__(self, initWSE, minWSE, maxWSE): """ Constructor """ self.initWSE = initWSE self.minWSE = minWSE self.maxWSE = maxWSE def __repr__(self): return '<Reservoir: InitialWSE=%s, MinWSE=%s, MaxWSE=%s>' % (self.initWSE, self.minWSE, self.maxWSE) def __eq__(self, other): return (self.initWSE == other.initWSE and self.minWSE == other.minWSE and self.maxWSE == other.maxWSE)
[docs]class ReservoirPoint(DeclarativeBase): """ Object containing the cells/points that define the maximum inundation area of a reservoir. See: http://www.gsshawiki.com/Surface_Water_Routing:Channel_Routing# """ __tablename__ = 'cif_reservoir_points' tableName = __tablename__ #: Database tablename # Primary and Foreign Keys id = Column(Integer, autoincrement=True, primary_key=True) #: PK reservoirID = Column(Integer, ForeignKey('cif_reservoirs.id')) #: FK # Value Columns i = Column(Integer) #: INTEGER j = Column(Integer) #: INTEGER # Relationship Properties reservoir = relationship('Reservoir', back_populates='reservoirPoints') #: RELATIONSHIP def __init__(self, i, j): """ Constructor """ self.i = i self.j = j def __repr__(self): return '<ReservoirPoint: CellI=%s, CellJ=%s>' % (self.i, self.j) def __eq__(self, other): return (self.i == other.i and self.j == other.j)
[docs]class BreakpointCS(DeclarativeBase): """ Object containing breakpoint type cross section data for fluvial stream links. See: http://www.gsshawiki.com/Surface_Water_Routing:Channel_Routing#5.1.4.1.4.2.1.2_Natural_cross-section """ __tablename__ = 'cif_breakpoint' tableName = __tablename__ #: Database tablename # Primary and Foreign Keys id = Column(Integer, autoincrement=True, primary_key=True) #: PK linkID = Column(Integer, ForeignKey('cif_links.id')) #: FK # Value Columns mannings_n = Column(Float) #: FLOAT numPairs = Column(Integer) #: INTEGER numInterp = Column(Integer) #:INTEGER mRiver = Column(Float) #: FLOAT kRiver = Column(Float) #: FLOAT erode = Column(Boolean) #: BOOLEAN subsurface = Column(Boolean) #: BOOLEAN maxErosion = Column(Float) #: FLOAT # Relationship Properties streamLink = relationship('StreamLink', back_populates='breakpointCS') #: RELATIONSHIP breakpoints = relationship('Breakpoint', back_populates='crossSection') #: RELATIONSHIP def __init__(self, mannings_n, numPairs, numInterp, mRiver, kRiver, erode, subsurface, maxErosion): """ Constructor """ self.mannings_n = mannings_n self.numPairs = numPairs self.numInterp = numInterp self.mRiver = mRiver self.kRiver = kRiver self.erode = erode self.subsurface = subsurface self.maxErosion = maxErosion def __repr__(self): return '<BreakpointCrossSection: Mannings-n=%s, NumPairs=%s, NumInterp=%s, M-River=%s, K-River=%s, Erode=%s, Subsurface=%s, MaxErosion=%s>' % ( self.mannings_n, self.numPairs, self.numInterp, self.mRiver, self.kRiver, self.erode, self.subsurface, self.maxErosion) def __eq__(self, other): return (self.mannings_n == other.mannings_n and self.numPairs == other.numPairs and self.numInterp == other.numInterp and self.mRiver == other.mRiver and self.kRiver == other.kRiver and self.erode == other.erode and self.subsurface == other.subsurface and self.maxErosion == other.maxErosion)
[docs]class Breakpoint(DeclarativeBase): """ Object used to define points in a :class:`.BreakpointCS` object. See: http://www.gsshawiki.com/Surface_Water_Routing:Channel_Routing#5.1.4.1.4.2.1.2_Natural_cross-section """ __tablename__ = 'cif_bcs_points' tableName = __tablename__ #: Database tablename # Primary and Foreign Keys id = Column(Integer, autoincrement=True, primary_key=True) #: PK crossSectionID = Column(Integer, ForeignKey('cif_breakpoint.id')) #: FK # Value Columns x = Column(Float) #: FLOAT y = Column(Float) #: FLOAT # Relationship Properties crossSection = relationship('BreakpointCS', back_populates='breakpoints') #: RELATIONSHIP def __init__(self, x, y): """ Constructor """ self.x = x self.y = y def __repr__(self): return '<Breakpoint: X=%s, Y=%s>' % (self.x, self.y) def __eq__(self, other): return (self.x == other.x, self.y == other.y)
[docs]class TrapezoidalCS(DeclarativeBase): """ Object containing trapezoidal type cross section data for fluvial stream links. See: http://www.gsshawiki.com/Surface_Water_Routing:Channel_Routing#5.1.4.1.4.2.1.1_Trapezoidal_cross-section """ __tablename__ = 'cif_trapezoid' tableName = __tablename__ #: Database tablename # Primary and Foreign Keys id = Column(Integer, autoincrement=True, primary_key=True) #: PK linkID = Column(Integer, ForeignKey('cif_links.id')) #: FK # Value Columns mannings_n = Column(Float) #: FLOAT bottomWidth = Column(Float) #: FLOAT bankfullDepth = Column(Float) #: FLOAT sideSlope = Column(Float) #: FLOAT mRiver = Column(Float) #: FLOAT kRiver = Column(Float) #: FLOAT erode = Column(Boolean) #: BOOLEAN subsurface = Column(Boolean) #: BOOLEAN maxErosion = Column(Float) #: BOOLEAN # Relationship Properties streamLink = relationship('StreamLink', back_populates='trapezoidalCS') #: RELATIONSHIP def __init__(self, mannings_n, bottomWidth, bankfullDepth, sideSlope, mRiver, kRiver, erode, subsurface, maxErosion): """ Constructor """ self.mannings_n = mannings_n self.bottomWidth = bottomWidth self.bankfullDepth = bankfullDepth self.sideSlope = sideSlope self.mRiver = mRiver self.kRiver = kRiver self.erode = erode self.subsurface = subsurface self.maxErosion = maxErosion def __repr__(self): return '<TrapezoidalCS: Mannings-n=%s, BottomWidth=%s, BankfullDepth=%s, SideSlope=%s, M-River=%s, K-River=%s, Erode=%s, Subsurface=%s, MaxErosion=%s>' % ( self.mannings_n, self.bottomWidth, self.bankfullDepth, self.sideSlope, self.mRiver, self.kRiver, self.erode, self.subsurface, self.maxErosion) def __eq__(self, other): return (self.mannings_n == other.mannings_n and self.bottomWidth == other.bottomWidth and self.bankfullDepth == other.bankfullDepth and self.sideSlope == other.sideSlope and self.mRiver == other.mRiver and self.kRiver == other.kRiver and self.erode == other.erode and self.subsurface == other.subsurface and self.maxErosion == other.maxErosion)