"""
********************************************************************************
* Name: PrecipitationModel
* Author: Nathan Swain
* Created On: Mar 6, 2013
* Copyright: (c) Brigham Young University 2013
* License: BSD 2-Clause
********************************************************************************
"""
from __future__ import unicode_literals
__all__ = ['PrecipFile',
'PrecipEvent',
'PrecipValue',
'PrecipGage']
from future.utils import iteritems
from sqlalchemy import ForeignKey, Column, Table
from sqlalchemy.types import Integer, DateTime, String, Float
from sqlalchemy.orm import relationship
from . import DeclarativeBase
from ..base.file_base import GsshaPyFileObjectBase
from ..lib import pivot, parsetools as pt, gag_chunk as gak
gag_assoc_event_gage = Table('gag_assoc_event_gage', DeclarativeBase.metadata,
Column('gageID', Integer, ForeignKey('gag_coord.id')),
Column('eventID', Integer, ForeignKey('gag_events.id')))
[docs]class PrecipFile(DeclarativeBase, GsshaPyFileObjectBase):
"""
Object interface for the Precipitation Input File.
The contents of the precipitation file are abstracted into three types of objects including: :class:`.PrecipEvent`,
:class:`.PrecipValue`, and :class:`.PrecipGage`. One precipitation file can consist of multiple events and each event
can have several gages and a time series of values for each gage.
See: http://www.gsshawiki.com/Precipitation:Spatially_and_Temporally_Varied_Precipitation
"""
__tablename__ = 'gag_precipitation_files'
tableName = __tablename__ #: Database tablename
# Primary and Foreign Keys
id = Column(Integer, autoincrement=True, primary_key=True) #: PK
# Value Columns
fileExtension = Column(String, default='gag') #: STRING
# Relationship Properties
precipEvents = relationship('PrecipEvent', back_populates='precipFile') #: RELATIONSHIP
projectFile = relationship('ProjectFile', uselist=False, back_populates='precipFile') #: RELATIONSHIP
def __init__(self):
"""
Constructor
"""
GsshaPyFileObjectBase.__init__(self)
def _read(self, directory, filename, session, path, name, extension, spatial, spatialReferenceID, replaceParamFile):
"""
Precipitation Read from File Method
"""
# Set file extension property
self.fileExtension = extension
# Dictionary of keywords/cards and parse function names
KEYWORDS = ('EVENT',)
# 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:
result = gak.eventChunk(key, chunk)
self._createGsshaPyObjects(result)
# Add this PrecipFile to the database session
session.add(self)
def _write(self, session, openFile, replaceParamFile):
"""
Precipitation File Write to File Method
"""
# Retrieve the events associated with this PrecipFile
events = self.precipEvents
# Write each event to file
for event in events:
openFile.write('EVENT "%s"\nNRGAG %s\nNRPDS %s\n' % (event.description, event.nrGag, event.nrPds))
if event.nrGag > 0:
values = event.values
valList = []
# Convert PrecipValue objects into a list of dictionaries, valList,
# so that it is compatible with the pivot function.
for value in values:
valList.append({'ValueType': value.valueType,
'DateTime': value.dateTime,
'Gage': value.gage.id,
'Value': value.value})
# Pivot using the function found at:
# code.activestate.com/recipes/334695
pivotedValues = pivot.pivot(valList, ('DateTime', 'ValueType'), ('Gage',), 'Value')
## TODO: Create custom pivot function that can work with sqlalchemy
## objects explicitly without the costly conversion.
# Create an empty set for obtaining a list of unique gages
gages = session.query(PrecipGage). \
filter(PrecipGage.event == event). \
order_by(PrecipGage.id). \
all()
for gage in gages:
openFile.write('COORD %s %s "%s"\n' % (gage.x, gage.y, gage.description))
# Write the value rows out to file
for row in pivotedValues:
# Extract the PrecipValues
valString = ''
# Retreive a list of sorted keys. This assumes the values are
# read into the database in order
keys = sorted([key for key in row if key != 'DateTime' and key != 'ValueType'])
# String all of the values together into valString
for key in keys:
if key != 'DateTime' and key != 'ValueType':
valString = '%s %.3f' % (valString, row[key])
# Write value line to file with appropriate formatting
openFile.write('%s %.4d %.2d %.2d %.2d %.2d%s\n' % (
row['ValueType'],
row['DateTime'].year,
row['DateTime'].month,
row['DateTime'].day,
row['DateTime'].hour,
row['DateTime'].minute,
valString))
def _createGsshaPyObjects(self, eventChunk):
"""
Create GSSHAPY PrecipEvent, PrecipValue, and PrecipGage Objects Method
"""
## TODO: Add Support for RADAR file format type values
# Create GSSHAPY PrecipEvent
event = PrecipEvent(description=eventChunk['description'],
nrGag=eventChunk['nrgag'],
nrPds=eventChunk['nrpds'])
# Associate PrecipEvent with PrecipFile
event.precipFile = self
gages = []
for coord in eventChunk['coords']:
# Create GSSHAPY PrecipGage object
gage = PrecipGage(description=coord['description'],
x=coord['x'],
y=coord['y'])
# Associate PrecipGage with PrecipEvent
gage.event = event
# Append to gages list for association with PrecipValues
gages.append(gage)
for valLine in eventChunk['valLines']:
for index, value in enumerate(valLine['values']):
# Create GSSHAPY PrecipValue object
val = PrecipValue(valueType=valLine['type'],
dateTime=valLine['dateTime'],
value=value)
# Associate PrecipValue with PrecipEvent and PrecipGage
val.event = event
val.gage = gages[index]
[docs]class PrecipEvent(DeclarativeBase):
"""
Object containing data for a single precipitation event.
"""
__tablename__ = 'gag_events'
tableName = __tablename__ #: Database tablename
# Primary and Foreign Keys
id = Column(Integer, autoincrement=True, primary_key=True) #: PK
precipFileID = Column(Integer, ForeignKey('gag_precipitation_files.id')) #: FK
# Value Columns
description = Column(String) #: STRING
nrGag = Column(Integer) #: INTEGER
nrPds = Column(Integer) #: INTEGER
# Relationship Properties
values = relationship('PrecipValue', back_populates='event') #: RELATIONSHIP
gages = relationship('PrecipGage', secondary=gag_assoc_event_gage, back_populates='event') #: RELATIONSHIP
precipFile = relationship('PrecipFile', back_populates='precipEvents') #: RELATIONSHIP
def __init__(self, description, nrGag, nrPds):
"""
Constructor
"""
self.description = description
self.nrGag = nrGag
self.nrPds = nrPds
def __repr__(self):
return '<PrecipEvent: Description=%s, NumGages=%s, NumPeriods=%s>' % (self.description, self.nrGag, self.nrPds)
[docs]class PrecipValue(DeclarativeBase):
"""
Object containing data for a single time stamped precipitation value.
"""
__tablename__ = 'gag_values'
tableName = __tablename__ #: Database tablename
# Primary and Foreign Keys
id = Column(Integer, autoincrement=True, primary_key=True) #: PK
eventID = Column(Integer, ForeignKey('gag_events.id')) #: FK
coordID = Column(Integer, ForeignKey('gag_coord.id')) #: FK
# Value Columns
valueType = Column(String) #: STRING
dateTime = Column(DateTime) #: DATETIME
value = Column(Float) #: FLOAT
# Relationship Properties
event = relationship('PrecipEvent', back_populates='values') #: RELATIONSHIP
gage = relationship('PrecipGage', back_populates='values') #: RELATIONSHIP
def __init__(self, valueType, dateTime, value):
"""
Constructor
"""
self.valueType = valueType
self.dateTime = dateTime
self.value = value
def __repr__(self):
return '<PrecipValue: Type=%s, DateTime=%s, Value=%s>' % (self.valueType, self.dateTime, self.value)
[docs]class PrecipGage(DeclarativeBase):
"""
Object containing data for a single precipitation gage or location.
"""
__tablename__ = 'gag_coord'
tableName = __tablename__ #: Database tablename
# Primary and Foreign Keys
id = Column(Integer, autoincrement=True, primary_key=True) #: PK
# Value Columns
description = Column(String) #: STRING
x = Column(Float) #: FLOAT
y = Column(Float) #: FLOAT
# geom = Column(Geometry('POINT')) #: POINT
# Relationship Properties
values = relationship('PrecipValue', back_populates='gage') #: RELATIONSHIP
event = relationship('PrecipEvent', secondary=gag_assoc_event_gage, uselist=False,
back_populates='gages') #: RELATIONSHIP
def __init__(self, description, x, y):
"""
Constructor
"""
self.description = description
self.x = x
self.y = y
# self.geom = 'POINT(%s %s)' % (x, y)
def __repr__(self):
return '<PrecipGage: Description=%s, X=%s, Y=%s>' % (self.description, self.x, self.y)