def extract_flowlines(self, source, destination, HUC8, verbose = True): """Extracts flowlines from the source datafile to the destination using the HUC8 for the query.""" # open the flowline file if verbose: print('reading the flowline file\n') shapefile = Reader(source, shapeType = 3) records = shapefile.records() # figure out which field codes are the Reach code and comid reach_index = shapefile.fields.index(['REACHCODE', 'C', 14, 0]) - 1 # go through the reach indices, add add them to the list of flowlines # if in the watershed; also make a list of the corresponding comids if verbose: print('searching for flowlines in the watershed\n') indices = [] i = 0 for record in records: if record[reach_index][:8] == HUC8: indices.append(i) i+=1 if len(indices) == 0: if verbose: print('error: query returned no values') raise # write the data from the HUC8 to a new shapefile w = Writer(shapeType = 3) for field in shapefile.fields: w.field(*field) for i in indices: shape = shapefile.shape(i) w.poly(shapeType = 3, parts = [shape.points]) record = records[i] # little work around for blank GNIS_ID and GNIS_NAME values if isinstance(record[3], bytes): record[3] = record[3].decode('utf-8') if isinstance(record[4], bytes): record[4] = record[4].decode('utf-8') w.record(*record) w.save(destination) if verbose: l = len(indices) print('queried {} flowlines from original shapefile\n'.format(l))
def extract_catchments(self, source, destination, flowlinefile, verbose = True, ): """ Extracts the catchments from the source data file to the destination using the list of comids for the query. """ # make a list of the comids comids = self.get_comids(flowlinefile) # open the catchment shapefile if verbose: print('reading the catchment shapefile\n') shapefile = Reader(source) # get the index of the feature id, which links to the flowline comid featureid_index = shapefile.fields.index(['FEATUREID', 'N', 9, 0]) - 1 # go through the comids from the flowlines and add the corresponding # catchment to the catchment list if verbose: print('searching the catchments in the watershed\n') records = shapefile.records() indices = [] i = 0 for record in records: if record[featureid_index] in comids: indices.append(i) i+=1 if len(indices) == 0: print('query returned no values, returning\n') raise # create the new shapefile if verbose: print('writing the new catchment shapefile\n') w = Writer() for field in shapefile.fields: w.field(*field) for i in indices: shape = shapefile.shape(i) w.poly(shapeType = 5, parts = [shape.points]) w.record(*records[i]) w.save(destination)
def set_metadata(self, gagefile, ): """ Opens the gage file with the station metadata. """ # metadata for stations self.gages = [] self.day1s = [] self.dayns = [] self.drains = [] self.states = [] self.sites = [] self.nwiss = [] self.aves = [] self.names = [] gagereader = Reader(gagefile, shapeType = 1) # get the fields with pertinent info day1_index = gagereader.fields.index(['DAY1', 'N', 19, 0]) - 1 dayn_index = gagereader.fields.index(['DAYN', 'N', 19, 0]) - 1 drain_index = gagereader.fields.index(['DA_SQ_MILE', 'N', 19, 2]) - 1 HUC8_index = gagereader.fields.index(['HUC', 'C', 8, 0]) - 1 state_index = gagereader.fields.index(['STATE', 'C', 2, 0]) - 1 site_index = gagereader.fields.index(['SITE_NO', 'C', 15, 0]) - 1 nwis_index = gagereader.fields.index(['NWISWEB', 'C', 75, 0]) - 1 ave_index = gagereader.fields.index(['AVE', 'N', 19, 3]) - 1 name_index = gagereader.fields.index(['STATION_NM', 'C', 60, 0]) - 1 # iterate through the records for r in gagereader.records(): gage = r[site_index] day1 = r[day1_index] dayn = r[dayn_index] drain = r[drain_index] state = r[state_index] nwis = r[nwis_index] ave = r[ave_index] name = r[name_index] site = r[site_index] self.gages.append(gage) self.day1s.append(day1) self.dayns.append(dayn) self.drains.append(drain) self.states.append(state) self.sites.append(site) self.nwiss.append(nwis) self.aves.append(ave) self.names.append(name)
def get_comids(self, flowlinefile): """Finds the comids from the flowline file.""" # open the file shapefile = Reader(flowlinefile) # find the index of the comids comid_index = shapefile.fields.index(['COMID', 'N', 9, 0]) - 1 # make a list of the comids comids = [r[comid_index] for r in shapefile.records()] return comids
def climate(self, HUC8, s, e, verbose = True, ): subbasinfile = '{}/subbasin_catchments'.format(self.hydrography) climatedata = '{}/{}/climate'.format(self.output, HUC8) # make a directory for the climate data and time series if not os.path.isdir(climatedata): os.mkdir(climatedata) # use the Climateprocessor to get the data climateprocessor = ClimateProcessor() climateprocessor.download_shapefile(subbasinfile, s, e, climatedata, space = 0.5) # make directories for hourly and daily aggregated timeseries hourly = '{}/hourly'.format(climatedata) daily = '{}/daily'.format(climatedata) if not os.path.isdir(hourly): os.mkdir(hourly) if not os.path.isdir(daily): os.mkdir(daily) # aggregate the daily GSOD tmin, tmax, dewpoint, and wind data tmin = '{}/tmin'.format(daily) tmax = '{}/tmax'.format(daily) dewt = '{}/dewpoint'.format(daily) wind = '{}/wind'.format(daily) if not os.path.isfile(tmin): ts = s, 1440, climateprocessor.aggregate('GSOD', 'tmin', s, e) with open(tmin, 'wb') as f: pickle.dump(ts, f) if not os.path.isfile(tmax): ts = s, 1440, climateprocessor.aggregate('GSOD', 'tmax', s, e) with open(tmax, 'wb') as f: pickle.dump(ts, f) if not os.path.isfile(dewt): ts = s, 1440, climateprocessor.aggregate('GSOD','dewpoint', s,e) with open(dewt, 'wb') as f: pickle.dump(ts, f) if not os.path.isfile(wind): ts = s, 1440, climateprocessor.aggregate('GSOD', 'wind', s, e) with open(wind, 'wb') as f: pickle.dump(ts, f) # aggregate the daily GHCND snowfall and snowdepth data snowfall = '{}/snowfall'.format(daily) snowdepth = '{}/snowdepth'.format(daily) if not os.path.isfile(snowfall): ts = s, 1440, climateprocessor.aggregate('GHCND','snowfall', s, e) with open(snowfall, 'wb') as f: pickle.dump(ts, f) if not os.path.isfile(snowdepth): ts = s, 1440,climateprocessor.aggregate('GHCND','snowdepth', s, e) with open(snowdepth, 'wb') as f: pickle.dump(ts, f) # find stations with pan evaporation data from GHCND evapstations = [] for k, v in climateprocessor.metadata.ghcndstations.items(): # check if the station has any evaporation data if v['evap'] > 0: # open up the file and get the data with open(k, 'rb') as f: station = pickle.load(f) data = station.make_timeseries('evaporation', s, e) # ignore datasets with no observations during the period observations = [v for v in data if v is not None] if len(observations) > 0: evapstations.append(k) # aggregate the hourly NSRDB metstat data hsolar = '{}/solar'.format(hourly) if not os.path.isfile(hsolar): ts = s, 60, climateprocessor.aggregate('NSRDB', 'metstat', s, e) with open(hsolar, 'wb') as f: pickle.dump(ts, f) # aggregate the hourly solar to daily dsolar = '{}/solar'.format(daily) if not os.path.isfile(dsolar): with open(hsolar, 'rb') as f: t, tstep, data = pickle.load(f) ts = s, 1440, [sum(data[i:i+24]) / 24 for i in range(0, 24 * (e-s).days, 24)] with open(dsolar, 'wb') as f: pickle.dump(ts, f) # aggregate the hourly precipitation for each subbasin using IDWA precip = '{}/hourlyprecipitation'.format(climatedata) if not os.path.isdir(precip): os.mkdir(precip) # use the subbasin shapefile to get the location of the centroids sf = Reader(subbasinfile) # index of the comid, latitude, and longitude records comid_index = [f[0] for f in sf.fields].index('ComID') - 1 lon_index = [f[0] for f in sf.fields].index('CenX') - 1 lat_index = [f[0] for f in sf.fields].index('CenY') - 1 elev_index = [f[0] for f in sf.fields].index('AvgElevM') - 1 area_index = [f[0] for f in sf.fields].index('AreaSqKm') - 1 # iterate through the shapefile records and aggregate the timeseries for i in range(len(sf.records())): record = sf.record(i) comid = record[comid_index] lon = record[lon_index] lat = record[lat_index] # check if the aggregated time series exists or calculate it subbasinprecip = '{}/{}'.format(precip, comid) if not os.path.isfile(subbasinprecip): if verbose: i = comid, lon, lat print('aggregating timeseries for comid ' + '{} at {}, {}\n'.format(*i)) p = climateprocessor.aggregate('precip3240', 'precip', s, e, method = 'IDWA', longitude = lon, latitude = lat) ts = s, 60, p with open(subbasinprecip, 'wb') as f: pickle.dump(ts, f) # make a directory for the evapotranspiration time series evapotranspiration = '{}/evapotranspiration'.format(climatedata) if not os.path.isdir(evapotranspiration): os.mkdir(evapotranspiration) # use the ETCalculator to calculate the ET time series etcalculator = ETCalculator() # get the centroid of the watershed from the subbasin shapefile areas = [r[area_index] for r in sf.records()] xs = [r[lon_index] for r in sf.records()] ys = [r[lat_index] for r in sf.records()] zs = [r[elev_index] for r in sf.records()] # get the areal-weighted averages lon = sum([a * x for a, x in zip(areas, xs)]) / sum(areas) lat = sum([a * y for a, y in zip(areas, ys)]) / sum(areas) elev = sum([a * z for a, z in zip(areas, zs)]) / sum(areas) # add them to the ETCalculator etcalculator.add_location(lon, lat, elev) # check if the daily RET exists; otherwise calculate it dRET = '{}/dailyRET'.format(evapotranspiration) if not os.path.isfile(dRET): # add the daily time series to the calculator with open(tmin, 'rb') as f: t, tstep, data = pickle.load(f) etcalculator.add_timeseries('tmin', tstep, t, data) with open(tmax, 'rb') as f: t, tstep, data = pickle.load(f) etcalculator.add_timeseries('tmax', tstep, t, data) with open(dewt, 'rb') as f: t, tstep, data = pickle.load(f) etcalculator.add_timeseries('dewpoint', tstep, t, data) with open(wind, 'rb') as f: t, tstep, data = pickle.load(f) etcalculator.add_timeseries('wind', tstep, t, data) with open(dsolar, 'rb') as f: t, tstep, data = pickle.load(f) etcalculator.add_timeseries('solar', tstep, t, data) # calculate the daily RET etcalculator.penman_daily(s, e) ts = s, 1440, etcalculator.daily['RET'][1] with open(dRET, 'wb') as f: pickle.dump(ts, f) # disaggregate the daily temperature time series to hourly hourlytemp = '{}/temperature'.format(hourly) if not os.path.isfile(hourlytemp): if etcalculator.daily['tmin'] is None: with open(tmin, 'rb') as f: t, tstep, data = pickle.load(f) etcalculator.add_timeseries('tmin', tstep, t, data) if etcalculator.daily['tmax'] is None: with open(tmax, 'rb') as f: t, tstep, data = pickle.load(f) etcalculator.add_timeseries('tmax', tstep, t, data) data = etcalculator.interpolate_temperatures(s, e) tstep = 60 ts = t, tstep, data with open(hourlytemp, 'wb') as f: pickle.dump(ts, f) etcalculator.add_timeseries('temperature', tstep, t, data) # disaggregate the dewpoint and wind speed time series to hourly hourlydewt = '{}/dewpoint'.format(hourly) if not os.path.isfile(hourlydewt): if etcalculator.daily['dewpoint'] is None: with open(dewt, 'rb') as f: t, tstep, data = pickle.load(f) else: t, data = etcalculator.daily['dewpoint'] tstep = 60 data = [v for v in data for i in range(24)] ts = t, tstep, data with open(hourlydewt, 'wb') as f: pickle.dump(ts, f) etcalculator.add_timeseries('dewpoint', tstep, t, data) hourlywind = '{}/wind'.format(hourly) if not os.path.isfile(hourlywind): if etcalculator.daily['wind'] is None: with open(wind, 'rb') as f: t, tstep, data = pickle.load(f) else: t, data = etcalculator.daily['wind'] tstep = 60 data = [v for v in data for i in range(24)] ts = t, tstep, data with open(hourlywind, 'wb') as f: pickle.dump(ts, f) etcalculator.add_timeseries('wind', tstep, t, data) # check if the hourly RET exists; otherwise calculate it hRET = '{}/hourlyRET'.format(evapotranspiration) if not os.path.isfile(hRET): required = 'temperature', 'solar', 'dewpoint', 'wind' for tstype in required: if etcalculator.hourly[tstype] is None: name = '{}/{}'.format(hourly, tstype) with open(name, 'rb') as f: t, tstep, data = pickle.load(f) etcalculator.add_timeseries(tstype, tstep, t, data) # calculate and save the hourly RET etcalculator.penman_hourly(s, e) ts = s, 60, etcalculator.hourly['RET'][1] with open(hRET, 'wb') as f: pickle.dump(ts, f) # add the daily time series for the plot required = 'tmin', 'tmax', 'dewpoint', 'wind', 'solar' for tstype in required: if etcalculator.daily[tstype] is None: name = '{}/{}'.format(daily, tstype) with open(name, 'rb') as f: t, tstep, data = pickle.load(f) etcalculator.add_timeseries(tstype, tstep, t, data) # aggregate the hourly to daily for plotting hRET = etcalculator.hourly['RET'][1] dRET = [sum(hRET[i:i+24]) for i in range(0, len(hRET), 24)] etcalculator.add_timeseries('RET', 'daily', s, dRET) name = '{}/referenceET'.format(evapotranspiration) etcalculator.plotET(stations = evapstations, output = name, show = False) name = '{}/dayofyearET'.format(evapotranspiration) etcalculator.plotdayofyear(stations = evapstations, output = name, show = False) # calculate hourly PET for different land use categories lucs = ('corn', 'soybeans', 'grains', 'alfalfa', 'fallow', 'pasture', 'wetlands', 'others', ) colors = ('yellow', 'green', 'brown', 'lime', 'gray', 'orange', 'blue', 'black', ) pdates = (datetime.datetime(2000, 4, 15), datetime.datetime(2000, 5, 15), datetime.datetime(2000, 4, 15), datetime.datetime(2000, 5, 15), datetime.datetime(2000, 3, 1), datetime.datetime(2000, 3, 1), datetime.datetime(2000, 3, 1), datetime.datetime(2000, 3, 1), ) ems = (30, 20, 20, 10, 10, 10, 10, 10, ) gs = (50, 30, 30, 10, 10, 10, 10, 10, ) fs = (60, 60, 60, 120, 240, 240, 240, 240, ) ls = (40, 30, 40, 10, 10, 10, 10, 10, ) Kis = (0.30, 0.40, 0.30, 0.30, 0.30, 0.30, 1.00, 1.00, ) Kms = (1.15, 1.15, 1.15, 0.95, 0.30, 0.85, 1.20, 1.00, ) Kls = (0.40, 0.55, 0.40, 0.90, 0.30, 0.30, 1.00, 1.00, ) # add the hourly RET time series if it isn't present if etcalculator.hourly['RET'] is None: with open(hRET, 'rb') as f: t, tstep, data = pickle.load(f) etcalculator.add_timeseries('RET', tstep, t, data) # iterate through the land use categories and calculate PET for i in zip(lucs, colors, pdates, ems, gs, fs, ls, Kis, Kms, Kls): crop, c, plant, emergence, growth, full, late, Ki, Km, Kl = i # add the information and calculate the PET time series etcalculator.add_crop(crop, plant, emergence, growth, full, late, Ki, Km, Kl, ) etcalculator.hourly_PET(crop, s, e) # get the PET time series t, PET = etcalculator.hourlyPETs[crop] ts = t, 60, PET # save it name = '{}/{}'.format(evapotranspiration, crop) with open(name, 'wb') as f: pickle.dump(ts, f)
def build_watershed(self, subbasinfile, flowfile, outletfile, damfile, gagefile, landfiles, VAAfile, years, HUC8, filename, plotname = None, ): # create a dictionary to store subbasin data subbasins = {} # create a dictionary to keep track of subbasin inlets inlets = {} # read in the flow plane data into an instance of the FlowPlane class sf = Reader(subbasinfile, shapeType = 5) comid_index = sf.fields.index(['ComID', 'N', 9, 0]) - 1 len_index = sf.fields.index(['PlaneLenM', 'N', 8, 2]) - 1 slope_index = sf.fields.index(['PlaneSlope', 'N', 9, 6]) - 1 area_index = sf.fields.index(['AreaSqKm', 'N', 10, 2]) - 1 cx_index = sf.fields.index(['CenX', 'N', 12, 6]) - 1 cy_index = sf.fields.index(['CenY', 'N', 12, 6]) - 1 elev_index = sf.fields.index(['AvgElevM', 'N', 8, 2]) - 1 for record in sf.records(): comid = '{}'.format(record[comid_index]) length = record[len_index] slope = record[slope_index] tot_area = record[area_index] centroid = [record[cx_index], record[cy_index]] elevation = record[elev_index] subbasin = Subbasin(comid) subbasin.add_flowplane(length, slope, centroid, elevation) subbasins[comid] = subbasin # read in the flowline data to an instance of the Reach class sf = Reader(flowfile) outcomid_index = sf.fields.index(['OutComID', 'N', 9, 0]) - 1 gnis_index = sf.fields.index(['GNIS_NAME', 'C', 65, 0]) - 1 reach_index = sf.fields.index(['REACHCODE', 'C', 8, 0]) - 1 incomid_index = sf.fields.index(['InletComID', 'N', 9, 0]) - 1 maxelev_index = sf.fields.index(['MaxElev', 'N', 9, 2]) - 1 minelev_index = sf.fields.index(['MinElev', 'N', 9, 2]) - 1 slopelen_index = sf.fields.index(['SlopeLenKM', 'N', 6, 2]) - 1 slope_index = sf.fields.index(['Slope', 'N', 8, 5]) - 1 inflow_index = sf.fields.index(['InFlowCFS', 'N', 8, 3]) - 1 outflow_index = sf.fields.index(['OutFlowCFS', 'N', 8, 3]) - 1 velocity_index = sf.fields.index(['VelFPS', 'N', 7, 4]) - 1 traveltime_index = sf.fields.index(['TravTimeHR', 'N', 8, 2]) - 1 for record in sf.records(): outcomid = '{}'.format(record[outcomid_index]) gnis = record[gnis_index] reach = record[reach_index] incomid = '{}'.format(record[incomid_index]) maxelev = record[maxelev_index] / 100 minelev = record[minelev_index] / 100 slopelen = record[slopelen_index] slope = record[slope_index] inflow = record[inflow_index] outflow = record[outflow_index] velocity = record[velocity_index] traveltime = record[traveltime_index] if isinstance(gnis, bytes): gnis = '' subbasin = subbasins[outcomid] flow = (inflow + outflow) / 2 subbasin.add_reach(gnis, maxelev, minelev, slopelen, flow = flow, velocity = velocity, traveltime = traveltime) inlets[outcomid] = incomid # open up the outlet file and see if the subbasin has a gage or dam sf = Reader(outletfile) records = sf.records() comid_index = sf.fields.index(['COMID', 'N', 9, 0]) - 1 nid_index = sf.fields.index(['NIDID', 'C', 7, 0]) - 1 nwis_index = sf.fields.index(['SITE_NO', 'C', 15, 0]) - 1 nids = {'{}'.format(r[comid_index]):r[nid_index] for r in records if isinstance(r[nid_index], str)} nwiss = {'{}'.format(r[comid_index]):r[nwis_index] for r in records if r[nwis_index] is not None} # open up the dam file and read in the information for the dams sf = Reader(damfile) records = sf.records() name_index = sf.fields.index(['DAM_NAME', 'C', 65, 0]) - 1 nid_index = sf.fields.index(['NIDID', 'C', 7, 0]) - 1 long_index = sf.fields.index(['LONGITUDE', 'N', 19, 11]) - 1 lat_index = sf.fields.index(['LATITUDE', 'N', 19, 11]) - 1 river_index = sf.fields.index(['RIVER', 'C', 65, 0]) - 1 owner_index = sf.fields.index(['OWN_NAME', 'C', 65, 0]) - 1 type_index = sf.fields.index(['DAM_TYPE', 'C', 10, 0]) - 1 purp_index = sf.fields.index(['PURPOSES', 'C', 254, 0]) - 1 year_index = sf.fields.index(['YR_COMPL', 'C', 10, 0]) - 1 high_index = sf.fields.index(['NID_HEIGHT', 'N', 19, 11]) - 1 mstor_index = sf.fields.index(['MAX_STOR', 'N', 19, 11]) - 1 nstor_index = sf.fields.index(['NORMAL_STO', 'N', 19, 11]) - 1 area_index = sf.fields.index(['SURF_AREA', 'N', 19, 11]) - 1 # iterate through the subbasins and see if they have a dam for comid, subbasin in subbasins.items(): if comid in nids: # if the subbasin has a dam, find the data info in the file nid = nids[comid] r = records[[r[nid_index] for r in records].index(nid)] subbasin.add_dam(nid, r[name_index], r[long_index], r[lat_index], r[river_index], r[owner_index], r[type_index], r[purp_index], r[year_index], r[high_index], r[mstor_index], r[nstor_index], r[area_index], ) # read in the landuse data from the csv files for year in years: csvfile = '{}/{}landuse.csv'.format(landfiles, year) with open(csvfile, 'r') as f: reader = csv.reader(f) rows = [r for r in reader] # organize the data comids = [r[0] for r in rows[3:]] categories = rows[2][2:] emptys = [r[1] for r in rows[3:]] data = [r[2:] for r in rows[3:]] for comid, subbasin in subbasins.items(): i = comids.index(comid) subbasin.add_landuse(year, categories, data[i]) # create an instance of the Watershed class watershed = Watershed(HUC8, subbasins) # open up the flowline VAA file to use to establish mass linkages with open(VAAfile, 'rb') as f: flowlines = pickle.load(f) # create a dictionary to connect the comids to hydroseqs hydroseqs = {'{}'.format(flowlines[f].comid): flowlines[f].hydroseq for f in flowlines} # establish the mass linkages using a dictionary "updown" and a list of # head water subbasins updown = {} for comid, subbasin in watershed.subbasins.items(): # get the flowline instance for the outlet comid flowline = flowlines[hydroseqs[comid]] # check if the subbasin is a watershed inlet or a headwater source inlet = hydroseqs[inlets[comid]] if flowlines[inlet].up in flowlines: i = '{}'.format(flowlines[flowlines[inlet].up].comid) subbasin.add_inlet(i) elif flowlines[inlet].up != 0: watershed.add_inlet(comid) else: watershed.add_headwater(comid) # check if the subbasin is a watershed outlet, and if it is not # then find the downstream reach if flowline.down in flowlines: flowline = flowlines[flowline.down] while '{}'.format(flowline.comid) not in subbasins: flowline = flowlines[flowline.down] updown[comid] = '{}'.format(flowline.comid) else: updown[comid] = 0 watershed.add_outlet('{}'.format(comid)) # add the updown dictionary to show mass linkage in the reaches watershed.add_mass_linkage(updown) with open(filename, 'wb') as f: pickle.dump(watershed, f) if plotname is not None and not os.path.isfile(plotname + '.png'): self.plot_mass_flow(watershed, plotname)
careas[c] = f.variables["area_" + c][:] # find valid fpus tarea = 100 * (111.2 / 2) ** 2 * cos(pi * lats / 180) tarea = resize(tarea, (nlons, nlats)).T validfpus = [] for i in range(nfpu): hareafpu = harea[fpumap == fpu[i]].sum() tareafpu = tarea[fpumap == fpu[i]].sum() if hareafpu / tareafpu > percent / 100.0: validfpus.append(fpu[i]) # load shape file r = Reader(shapefile) shapes = r.shapes() records = r.records() models = ["epic", "gepic", "lpj-guess", "lpjml", "pdssat", "pegasus"] # exclude image gcms = ["gfdl-esm2m", "hadgem2-es", "ipsl-cm5a-lr", "miroc-esm-chem", "noresm1-m"] crops = ["maize", "wheat", "soy", "rice"] if crop == "all" else [crop] co2s = ["co2", "noco2"] hadgemidx = gcms.index("hadgem2-es") nm, ng, ncr, nco2 = len(models), len(gcms), len(crops), len(co2s) # variables sh = (nm, ng, ncr, 3, nfpu, nco2) dy26arr = masked_array(zeros(sh), mask=ones(sh)) dy85arr = masked_array(zeros(sh), mask=ones(sh)) with nc(infile) as f:
def calculate_landuse(self, rasterfile, shapefile, aggregatefile, attribute, csvfile = None, ): """ Calculates the land use for the given year for the "attribute" feature attribute in the polygon shapefile using the aggregate mapping provided in the "aggregatefile." """ # make sure the files exist for f in rasterfile, shapefile + '.shp', aggregatefile: if not os.path.isfile(f): print('error, {} does not exist\n'.format(f)) raise # read the aggregate file self.read_aggregatefile(aggregatefile) # open the shapefile sf = Reader(shapefile, shapeType = 5) attributes = [f[0] for f in sf.fields] try: index = attributes.index(attribute) - 1 except: print('error: attribute ' + '{} is not in the shapefile fields'.format(attribute)) raise # iterate through the shapes, get the fractions and save them for i in range(len(sf.records())): points = numpy.array(sf.shape(i).points) record = sf.record(i) k = record[index] # store the results self.landuse[k] = {r:0 for r in self.order} try: values, origin = get_raster_in_poly(rasterfile, points, verbose = False) values = values.flatten() values = values[values.nonzero()] tot_pixels = len(values) # count the number of pixels of each land use type for v in numpy.unique(values): # find all the indices for each pixel value pixels = numpy.argwhere(values == v) # normalize by the total # of pixels f = len(values[pixels]) / tot_pixels # add the landuse to the aggregated value self.landuse[k][self.groups[v]] += f # work around for small shapes except: self.landuse[k][self.groups[0]] = 1 if csvfile is not None: self.make_csv(attribute, csvfile) return self.landuse
def plot_landuse(self, landuse, catchments, attribute, categoryfile, output = None, datatype = 'raw', overwrite = False, pixels = 1000, border = 0.02, lw = 0.5, show = False, verbose = True, vverbose = False ): """ Makes a plot of the landuse of a catchment shapefile on top of a raster landuse file. """ if self.order is None: print('error: no landuse aggregation file information provided\n') raise self.read_categoryfile(categoryfile) if verbose: print('generating a {} land use plot\n'.format(datatype)) # make the figure fig = pyplot.figure() subplot = fig.add_subplot(111, aspect = 'equal') subplot.tick_params(axis = 'both', which = 'major', labelsize = 11) # add the title if datatype == 'results': title = 'Land Use Fractions' else: title = 'Raw Land Use Data' subplot.set_title(title, size = 14) # open the shapefile and get the bounding box s = Reader(catchments, shapeType = 5) xmin, ymin, xmax, ymax = s.bbox # get the index of the field for the attribute matching index = [f[0] for f in s.fields].index(attribute) - 1 # set up a custom colormap using the rgbs supplied in the aggregate file color_table = [(self.reds[g] / 255, self.greens[g] / 255, self.blues[g] / 255) for g in self.order] cmap = colors.ListedColormap(color_table) # provide the cutoff boundaries for the mapping of values to the table bounds = [i-0.5 for i in range(len(self.order)+1)] # create a norm to map the bounds to the colors norm = colors.BoundaryNorm(bounds, cmap.N) # get the pixel width and origin w = (xmax - xmin) / pixels # calculate the image array height and the height of a pixel height = int(numpy.ceil((ymax - ymin) / (xmax - xmin)) * pixels) h = (ymax - ymin) / height # set up the image array image_array = numpy.zeros((height, pixels), dtype = 'uint8') # get the land use fraction for each category if datatype == 'results': # iterate through the shapes and make patches for i in range(len(s.records())): comid = s.record(i)[index] points = numpy.array(s.shape(i).points) # convert the shape to pixel coordinates pixel_polygon = [(get_pixel(x, xmin, w), get_pixel(y, ymin, h)) for x, y in points] # make a PIL image to use as a mask rasterpoly = Image.new('L', (pixels, height), 1) rasterize = ImageDraw.Draw(rasterpoly) # rasterize the polygon rasterize.polygon(pixel_polygon, 0) # convert the PIL array to numpy boolean to use as a mask mask = 1 - numpy.array(rasterpoly) # get the total number of pixels in the shape tot = mask.sum() # iterate from left to right and get the fraction of the total # area inside the shape as a function of x (takes into account # the depth) fractions = [column.sum() / tot for column in mask.transpose()] area_cdf = [sum(fractions[:i+1]) for i in range(len(fractions))] # convert the land use fractions into a land use cdf fractions = [self.landuse[comid][g] for g in self.order] land_cdf = [sum(fractions[:i+1]) for i in range(len(fractions))] # use the area cdf to determine the break points for the land # use patches. note this array does not account for the masking # of the patch. thus there are n+1 vertical bands. the first # and last are the "empty" (first in the aggregate file). in # between the break points are determined from the area cdf. color_array = numpy.zeros(len(mask[0]), dtype = 'uint8') # find the break point for each band by looping through the land # ues cdf and filling from left to right i = 0 for p, n in zip(land_cdf, range(len(self.order))): # move from left to right nuntil the area_cdf exceeds # the land area cdf while area_cdf[i] <= p: color_array[i] = n if i < len(area_cdf) - 1: i += 1 else: break # multiply the color band array by the mask to get the img sub_img = mask * color_array # add the new mask to the watershed image image_array = image_array + sub_img # add a patch for the shape boundary subplot.add_patch(self.make_patch(points, (1,0,0,0), width=lw)) # show the bands bbox = s.bbox[0], s.bbox[2], s.bbox[1], s.bbox[3] im = subplot.imshow(image_array, extent = bbox, origin = 'upper left', interpolation = 'nearest', cmap = cmap, norm = norm) # adjust the plot bounding box xmin, xmax = xmin-border * (xmax-xmin), xmax + border * (xmax-xmin) ymin, ymax = ymin-border * (ymax-ymin), ymax + border * (ymax-ymin) else: # adjust the plot bounding box xmin, xmax = xmin-border * (xmax-xmin), xmax + border * (xmax-xmin) ymin, ymax = ymin-border * (ymax-ymin), ymax + border * (ymax-ymin) # pixel width in latitude pw = (xmax - xmin) / pixels # calculate the image height in pixels ny = int(numpy.ceil((ymax - ymin) / (xmax - xmin) * pixels)) # note the height of pixels = width of pixels # and image width in pixels is "pixels" xs = numpy.array([xmin + (i + 0.5) * pw for i in range(pixels)]) ys = numpy.array([ymin + (i + 0.5) * pw for i in range(ny)]) # set up an array of values for the image zs = numpy.zeros((ny, pixels)) for i in range(len(ys)): ps = [(x, ys[i]) for x in xs] zs[i, :] = numpy.array(get_raster(landuse, ps, quiet = True)) zs = zs.astype(int) tot = zs.size for v in numpy.unique(zs): group = self.groups[v] i = self.order.index(group) zs[numpy.where(zs == v)] = i # plot the grid im = subplot.imshow(zs, interpolation = 'nearest', origin = 'upper left', extent = [xmin, xmax, ymin, ymax], norm = norm, cmap = cmap, ) # add patch for the shape boundary for shape in s.shapes(): points = numpy.array(shape.points) subplot.add_patch(self.make_patch(points, (1,0,0,0), width=0.5)) # add the legend using a dummy box to make patches for the legend dummybox = [[0,0], [0,1], [1,1], [1,0], [0,0]] handles, labels = [], [] for group, color in zip(self.order[1:], color_table[1:]): p = self.make_patch(dummybox, facecolor = color, width = 0) handles.append(subplot.add_patch(p)) labels.append(group) leg = subplot.legend(handles, labels, bbox_to_anchor = (1.0, 0.5), loc = 'center left', title = 'Land Use Categories') legtext = leg.get_texts() pyplot.setp(legtext, fontsize = 10) subplot.set_position([0.125, 0.1, 0.6, 0.8]) # add the labels and set the limits subplot.set_xlabel('Longitude, Decimal Degrees', size = 13) subplot.set_ylabel('Latitude, Decimal Degrees', size = 13) subplot.set_xlim([xmin, xmax]) subplot.set_ylim([ymin, ymax]) subplot.xaxis.set_major_locator(ticker.MaxNLocator(8)) subplot.yaxis.set_major_locator(ticker.MaxNLocator(8)) subplot.xaxis.set_major_formatter(ticker.FormatStrFormatter('%.2f')) subplot.yaxis.set_major_formatter(ticker.FormatStrFormatter('%.2f')) # show it if output is not None: pyplot.savefig(output) if show: pyplot.show() pyplot.clf() pyplot.close()
def plot_landuse(self, landuse, catchments, attribute, output=None, datatype='raw', overwrite=False, pixels=1000, border=0.02, lw=0.5, show=False, verbose=True, vverbose=False): """ Makes a plot of the landuse of a catchment shapefile on top of a raster landuse file. """ if verbose: print('generating land use plot\n') # make the figure fig = pyplot.figure() subplot = fig.add_subplot(111, aspect='equal') subplot.tick_params(axis='both', which='major', labelsize=11) # add the title if datatype == 'results': title = 'Land Use Fractions' else: title = 'Raw Land Use Data' subplot.set_title(title, size=14) # open the shapefile and get the bounding box s = Reader(catchments, shapeType=5) xmin, ymin, xmax, ymax = s.bbox # get the index of the field for the attribute matching index = [f[0] for f in s.fields].index(attribute) - 1 # set up a custom colormap using the rgbs supplied in the aggregate file color_table = [(self.reds[g] / 255, self.greens[g] / 255, self.blues[g] / 255) for g in self.order] cmap = colors.ListedColormap(color_table) # provide the cutoff boundaries for the mapping of values to the table bounds = [i - 0.5 for i in range(len(self.order) + 1)] # create a norm to map the bounds to the colors norm = colors.BoundaryNorm(bounds, cmap.N) # get the pixel width and origin w = (xmax - xmin) / pixels # calculate the image array height and the height of a pixel height = int(numpy.ceil((ymax - ymin) / (xmax - xmin)) * pixels) h = (ymax - ymin) / height # set up the image array image_array = numpy.zeros((height, pixels), dtype='uint8') # get the land use fraction for each category if datatype == 'results': # iterate through the shapes and make patches for i in range(len(s.records())): comid = s.record(i)[index] points = numpy.array(s.shape(i).points) # convert the shape to pixel coordinates pixel_polygon = [(get_pixel(x, xmin, w), get_pixel(y, ymin, h)) for x, y in points] # make a PIL image to use as a mask rasterpoly = Image.new('L', (pixels, height), 1) rasterize = ImageDraw.Draw(rasterpoly) # rasterize the polygon rasterize.polygon(pixel_polygon, 0) # convert the PIL array to numpy boolean to use as a mask mask = 1 - numpy.array(rasterpoly) # get the total number of pixels in the shape tot = mask.sum() # iterate from left to right and get the fraction of the total # area inside the shape as a function of x (takes into account # the depth) fractions = [column.sum() / tot for column in mask.transpose()] area_cdf = [ sum(fractions[:i + 1]) for i in range(len(fractions)) ] # convert the land use fractions into a land use cdf fractions = [self.landuse[comid][g] for g in self.order] land_cdf = [ sum(fractions[:i + 1]) for i in range(len(fractions)) ] # use the area cdf to determine the break points for the land # use patches. note this array does not account for the masking # of the patch. thus there are n+1 vertical bands. the first # and last are the "empty" (first in the aggregate file). in # between the break points are determined from the area cdf. color_array = numpy.zeros(len(mask[0]), dtype='uint8') # find the break point for each band by looping through the land # ues cdf and filling from left to right i = 0 for p, n in zip(land_cdf, range(len(self.order))): # move from left to right nuntil the area_cdf exceeds # the land area cdf while area_cdf[i] <= p: color_array[i] = n if i < len(area_cdf) - 1: i += 1 else: break # multiply the color band array by the mask to get the img sub_img = mask * color_array # add the new mask to the watershed image image_array = image_array + sub_img # add a patch for the shape boundary subplot.add_patch( self.make_patch(points, (1, 0, 0, 0), width=lw)) # show the bands bbox = s.bbox[0], s.bbox[2], s.bbox[1], s.bbox[3] im = subplot.imshow(image_array, extent=bbox, origin='upper left', interpolation='nearest', cmap=cmap, norm=norm) # adjust the plot bounding box xmin, xmax = xmin - border * (xmax - xmin), xmax + border * (xmax - xmin) ymin, ymax = ymin - border * (ymax - ymin), ymax + border * (ymax - ymin) else: # adjust the plot bounding box xmin, xmax = xmin - border * (xmax - xmin), xmax + border * (xmax - xmin) ymin, ymax = ymin - border * (ymax - ymin), ymax + border * (ymax - ymin) # pixel width in latitude pw = (xmax - xmin) / pixels # calculate the image height in pixels ny = int(numpy.ceil((ymax - ymin) / (xmax - xmin) * pixels)) # note the height of pixels = width of pixels # and image width in pixels is "pixels" xs = numpy.array([xmin + (i + 0.5) * pw for i in range(pixels)]) ys = numpy.array([ymin + (i + 0.5) * pw for i in range(ny)]) # set up an array of values for the image zs = numpy.zeros((ny, pixels)) for i in range(len(ys)): ps = [(x, ys[i]) for x in xs] zs[i, :] = numpy.array(get_raster(landuse, ps, quiet=True)) zs = zs.astype(int) tot = zs.size for v in numpy.unique(zs): group = self.groups[v] i = self.order.index(group) zs[numpy.where(zs == v)] = i # plot the grid im = subplot.imshow( zs, interpolation='nearest', origin='upper left', extent=[xmin, xmax, ymin, ymax], norm=norm, cmap=cmap, ) # add patch for the shape boundary for shape in s.shapes(): points = numpy.array(shape.points) subplot.add_patch( self.make_patch(points, (1, 0, 0, 0), width=0.5)) # add the legend using a dummy box to make patches for the legend dummybox = [[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]] handles, labels = [], [] for group, color in zip(self.order[1:], color_table[1:]): p = self.make_patch(dummybox, facecolor=color, width=0) handles.append(subplot.add_patch(p)) labels.append(group) leg = subplot.legend(handles, labels, bbox_to_anchor=(1.0, 0.5), loc='center left', title='Land Use Categories') legtext = leg.get_texts() pyplot.setp(legtext, fontsize=10) subplot.set_position([0.125, 0.1, 0.6, 0.8]) # add the labels and set the limits subplot.set_xlabel('Longitude, Decimal Degrees', size=13) subplot.set_ylabel('Latitude, Decimal Degrees', size=13) subplot.set_xlim([xmin, xmax]) subplot.set_ylim([ymin, ymax]) subplot.xaxis.set_major_locator(ticker.MaxNLocator(8)) subplot.yaxis.set_major_locator(ticker.MaxNLocator(8)) subplot.xaxis.set_major_formatter(ticker.FormatStrFormatter('%.2f')) subplot.yaxis.set_major_formatter(ticker.FormatStrFormatter('%.2f')) # show it if output is not None: pyplot.savefig(output) if show: pyplot.show() pyplot.clf() pyplot.close()
def plot_HUC8(self, flowfile, cfile, bfile, VAAfile, elevfile, patchcolor = None, resolution = 400, colormap = 'gist_earth', grid = False, title = None, verbose = True, output = None, show = False, ): """Makes a plot of the raw NHDPlus data.""" if verbose: print('generating plot of the watershed\n') fig = pyplot.figure() subplot = fig.add_subplot(111, aspect = 'equal') subplot.tick_params(axis = 'both', which = 'major', labelsize = 10) # add the title if title is not None: subplot.set_title(title, fontsize = 14) if patchcolor is None: facecolor = (1,0,0,0.) else: facecolor = patchcolor # open up and show the boundary b = Reader(bfile, shapeType = 5) boundary = b.shape(0) points = numpy.array(boundary.points) subplot.add_patch(self.make_patch(points, facecolor, width = 0.5)) # open up and show the catchments c = Reader(cfile, shapeType = 5) extent = self.get_boundaries(c.shapes(), space = 0.02) xmin, ymin, xmax, ymax = extent # figure out how far one foot is on the map points_per_width = 72 * 8 ft_per_km = 3280.84 scale_factor = (points_per_width / self.get_distance([xmin, ymin], [xmax, ymin]) / ft_per_km) # make patches of the catchment area for i in range(len(c.records())): catchment = c.shape(i) points = numpy.array(catchment.points) subplot.add_patch(self.make_patch(points, facecolor, width = 0.1)) # get the flowline attributes, make an "updown" dictionary to follow # flow, and change the keys to comids with open(VAAfile, 'rb') as f: flowlineVAAs = pickle.load(f) updown = {} for f in flowlineVAAs: if flowlineVAAs[f].down in flowlineVAAs: updown[flowlineVAAs[f].comid] = \ flowlineVAAs[flowlineVAAs[f].down].comid flowlineVAAs = {flowlineVAAs[f].comid:flowlineVAAs[f] for f in flowlineVAAs} # open up and show the flowfiles f = Reader(flowfile, shapeType = 3) comid_index = f.fields.index(['COMID', 'N', 9, 0]) - 1 all_comids = [r[comid_index] for r in f.records()] # get the flows and velocities from the dictionary widths = [] comids = [] for comid in all_comids: if comid in flowlineVAAs: flow = flowlineVAAs[comid].flow velocity = flowlineVAAs[comid].velocity # estimate flow width (ft) assuming triangular 90 d channel comids.append(comid) widths.append(numpy.sqrt(4 * flow / velocity)) # convert widths in feet to points on the figure; exaggerated by 10 widths = [w * scale_factor * 20 for w in widths] # get the flowline and the corresponding catchment for comid, w in zip(comids, widths): i = all_comids.index(comid) flowline = numpy.array(f.shape(i).points) # plot it subplot.plot(flowline[:, 0], flowline[:, 1], 'b', lw = w) subplot.set_xlabel('Longitude, Decimal Degrees', size = 13) subplot.set_ylabel('Latitude, Decimal Degrees', size = 13) # add the NED raster im = self.add_raster(subplot, elevfile, resolution, extent, colormap, 100) divider = make_axes_locatable(subplot) cax = divider.append_axes('right', size = 0.16, pad = 0.16) colorbar = fig.colorbar(im, cax = cax, orientation = 'vertical') colorbar.set_label('Elevation, m', size = 12) cbax = pyplot.axes(colorbar.ax) for t in cbax.get_yaxis().get_majorticklabels(): t.set_fontsize(10) subplot.xaxis.set_major_locator(ticker.MultipleLocator(0.2)) subplot.yaxis.set_major_locator(ticker.MultipleLocator(0.2)) if grid: subplot.xaxis.grid(True, 'minor', linestyle = '-', linewidth = 0.5) subplot.yaxis.grid(True, 'minor', linestyle = '-', linewidth = 0.5) # show it pyplot.tight_layout() if output is not None: pyplot.savefig(output) if show: pyplot.show() pyplot.close() pyplot.clf()
# weighted average (IDWA) to interpolate between the stations at a given point # using the "method," "latitude," and "longitude" keyword arguments. the # result is the same as the previous example. as before, the subbasin_catchments # shapefile will be used that contains the centroid for each aggregation. sf = Reader(filename) # index of the comid, latitude, and longitude records comid_index = [f[0] for f in sf.fields].index('ComID') - 1 lon_index = [f[0] for f in sf.fields].index('CenX') - 1 lat_index = [f[0] for f in sf.fields].index('CenY') - 1 # iterate through the shapefile records and aggregate the timeseries for i in range(len(sf.records())): record = sf.record(i) comid = record[comid_index] lon = record[lon_index] lat = record[lat_index] i = comid, lon, lat print('aggregating timeseries for comid {} at {}, {}\n'.format(*i)) precipitation = processor.aggregate('precip3240', 'precip', start, end, method = 'IDWA', longitude = lon, latitude = lat) mean = sum(precipitation) / (end - start).days * 365.25
name_index = sf.fields.index(['DAM_NAME', 'C', 65, 0]) - 1 nid_index = sf.fields.index(['NIDID', 'C', 7, 0]) - 1 lon_index = sf.fields.index(['LONGITUDE', 'N', 19, 11]) - 1 lat_index = sf.fields.index(['LATITUDE', 'N', 19, 11]) - 1 river_index = sf.fields.index(['RIVER', 'C', 65, 0]) - 1 owner_index = sf.fields.index(['OWN_NAME', 'C', 65, 0]) - 1 type_index = sf.fields.index(['DAM_TYPE', 'C', 10, 0]) - 1 purp_index = sf.fields.index(['PURPOSES', 'C', 254, 0]) - 1 year_index = sf.fields.index(['YR_COMPL', 'C', 10, 0]) - 1 high_index = sf.fields.index(['NID_HEIGHT', 'N', 19, 11]) - 1 mstor_index = sf.fields.index(['MAX_STOR', 'N', 19, 11]) - 1 nstor_index = sf.fields.index(['NORMAL_STO', 'N', 19, 11]) - 1 area_index = sf.fields.index(['SURF_AREA', 'N', 19, 11]) - 1 # iterate through the records and get whatever information is needed for r in sf.records(): name = r[name_index] nidid = r[nid_index] lon = r[lon_index] lat = r[lat_index] pur = r[purp_index] print('Dam name: ', name) print('NID ID: ', nidid) print('Longitude: ', lon) print('Latitude: ', lat) print('Primary Purpose:', pur) print('')
def plot_climate(HUC8, sfile, bfile, pfile = None, efile = None, tfile = None, snowfile = None, centroids = True, radius = None, patchcolor = None, solarfile = None, windfile = None, output = None, show = False, verbose = True): """Makes a plot of all the hourly precipitation stations of a watershed defined by "bfile" with subbasin defined by "sfile" from the source precipitation shapefile "pfile".""" if verbose: print('generating plot of watershed %s NCDC stations\n' % HUC8) fig = pyplot.figure() subplot = fig.add_subplot(111, aspect = 'equal') subplot.tick_params(axis = 'both', which = 'major', labelsize = 10) # add the title description = 'Climate Data Stations' title = 'Cataloging Unit %s\n%s' % (HUC8, description) subplot.set_title(title, fontsize = 14) # open up and show the catchments if patchcolor is None: facecolor = (1,0,0,0.) else: facecolor = patchcolor b = Reader(bfile, shapeType = 5) points = np.array(b.shape(0).points) subplot.add_patch(make_patch(points, facecolor = facecolor, width = 1.)) extent = get_boundaries(b.shapes(), space = 0.02) xmin, ymin, xmax, ymax = extent # add the subbasin file s = Reader(sfile, shapeType = 5) # make patches of the subbasins for i in range(len(s.records())): shape = s.shape(i) points = np.array(shape.points) subplot.add_patch(make_patch(points, facecolor, width = 0.15)) plots = [] # keep track of the scatterplots names = [] # keep track of names for the legend # add the subbasin centroids if centroids: cx_index = s.fields.index(['CenX', 'N', 12, 6]) - 1 cy_index = s.fields.index(['CenY', 'N', 12, 6]) - 1 centroids = [[r[cx_index], r[cy_index]] for r in s.records()] xs, ys = zip(*centroids) cplot = subplot.scatter(xs, ys, marker = '+', c = 'pink', s = 15) plots.append(cplot) names.append('Centroids') # add a circle showing around subbasin "radius" showing the gages within # the radius for a given subbasin if radius is not None: comid_index = s.fields.index(['ComID', 'N', 9, 0]) - 1 cx_index = s.fields.index(['CenX', 'N', 12, 6]) - 1 cy_index = s.fields.index(['CenY', 'N', 12, 6]) - 1 area_index = s.fields.index(['AreaSqKm', 'N', 10, 2]) - 1 comids = ['{}'.format(r[comid_index]) for r in s.records()] cxs = [r[cx_index] for r in s.records()] cys = [r[cy_index] for r in s.records()] areas = [r[area_index] for r in s.records()] try: i = comids.index(radius) except: i = 0 c = [cxs[i], cys[i]] radii = [math.sqrt(a / math.pi) for a in areas] # scale kms to degrees km = get_distance([xmin, ymin], [xmax, ymax]) deg = math.sqrt((xmin - xmax)**2 + (ymax - ymin)**2) r = sum(radii) / len(radii) * deg / km * 5 circle = pyplot.Circle(c, radius = r, edgecolor = 'black', facecolor = 'yellow', alpha = 0.5) subplot.add_patch(circle) subplot.scatter(c[0], c[1], marker = '+', c = 'black') # add the precipitation gage points if pfile is not None: with open(pfile, 'rb') as f: precips = pickle.load(f) gage_points = [(p.longitude, p.latitude) for p in precips.values()] x1, y1 = zip(*gage_points) plots.append(subplot.scatter(x1, y1, marker = 'o', c = 'b')) names.append('Precipitation') # add the pan evaporation points if efile is not None: with open(efile, 'rb') as f: evaps = pickle.load(f) gage_points = [(e.longitude, e.latitude) for e in evaps.values()] x2, y2 = zip(*gage_points) eplot = subplot.scatter(x2, y2, s = evaps, marker = 'o', c = 'g') plots.append(eplot) names.append('Pan Evaporation') # add the temperature station points if tfile is not None: with open(tfile, 'rb') as f: temps = pickle.load(f) gage_points = [(t.longitude, t.latitude) for t in temps.values()] x2, y2 = zip(*gage_points) plots.append(subplot.scatter(x2, y2, marker = 's', c = 'red')) names.append('Temperature') # add the snowdepth station points if snowfile is not None: with open(snowfile, 'rb') as f: snows = pickle.load(f) snow_points = [(s.longitude, s.latitude) for s in snows.values()] x2, y2 = zip(*snow_points) plots.append(subplot.scatter(x2, y2, marker = 'o', c = 'gray', alpha = 0.5)) names.append('Snow') # add the solar radiation files if solarfile is not None: with open(solarfile, 'rb') as f: solar = pickle.load(f) points = [(s.longitude, s.latitude) for s in solar.values()] x2, y2 = zip(*points) plots.append(subplot.scatter(x2, y2, marker = 'o', c = 'orange')) names.append('Solar') # add the wind files if windfile is not None: with open(windfile, 'rb') as f: wind = pickle.load(f) points = [(w.longitude, w.latitude) for w in wind.values()] x2, y2 = zip(*points) plots.append(subplot.scatter(x2, y2, marker = 'o', c = 'pink')) names.append('Wind') # add a legend leg = subplot.legend(plots, names, loc = 'upper center', ncol = 3, bbox_to_anchor = (0.5, -0.15)) legtext = leg.get_texts() pyplot.setp(legtext, fontsize = 10) #subplot.set_position([0.125, 0.1, 0.6, 0.8]) # set the labels subplot.set_xlabel('Longitude, Decimal Degrees', size = 13) subplot.set_ylabel('Latitude, Decimal Degrees', size = 13) # show it if output is not None: pyplot.savefig(output) if show: pyplot.show() pyplot.clf() pyplot.close()
def merge_shapes(inputfile, outputfile = None, overwrite = False, verbose = True, vverbose = False): """Merges all the shapes in a shapefile into a single shape.""" if outputfile is None: output = '{}/merged'.format(os.getcwd()) if os.path.isfile(outputfile + '.shp') and not overwrite: if verbose: print('combined watershed shapefile %s exists' % outputfile) return if verbose: print('combining shapes from {}\n'.format(inputfile) + 'this may take a while...\n') # start by copying the projection files shutil.copy(inputfile + '.prj', outputfile + '.prj') # load the catchment and flowline shapefiles r = Reader(inputfile, shapeType = 5) n = len(r.records()) try: shapes = [] records = [] bboxes = [] for i in range(n): shape = r.shape(i) record = r.record(i) shape_list = format_shape(shape.points) for sh in shape_list: shapes.append(sh) records.append(record) bboxes.append(shape.bbox) try: combined = combine_shapes(shapes, bboxes, verbose = vverbose) except: if verbose: print('trying alternate trace method') combined = combine_shapes(shapes, bboxes, skip = True, verbose = vverbose) except: if verbose: print('trying alternate trace method') shapes = [] records = [] bboxes = [] for i in range(n): shape = r.shape(i) record = r.record(i) shape_list = format_shape(shape.points, omit = True) for sh in shape_list: shapes.append(sh) records.append(record) bboxes.append(shape.bbox) try: combined = combine_shapes(shapes, bboxes, verbose = vverbose) except: if verbose: print('trying alternate trace method') combined = combine_shapes(shapes, bboxes, skip = True, verbose = vverbose) # create the new file with the merged shapes w = Writer(shapeType = 5) w.poly(shapeType = 5, parts = [combined]) # copy the fields from the original and then the first record; note this # can be adapted as needed for field in r.fields: w.field(*field) w.record(*r.record(0)) w.save(outputfile) if verbose: print('successfully combined shapes from %s to %s\n' % (inputfile, outputfile))
import csv, pandas from shapefile import Reader sf = 'C:/HSPF_data/07080106/hydrography/subbasin_catchments' # read the areas from the shapefile into a lookup dictionary r = Reader(sf) comid_index = [f[0] for f in r.fields].index('ComID') - 1 area_index = [f[0] for f in r.fields].index('AreaSqKm') - 1 areas = {row[comid_index]: row[area_index] for row in r.records()} # directory to the land use data p = 'C:/HSPF_new/07080106/landuse' # store the results in a data structure rows = [['Year']] for y in range(2000,2011): # expand the structure for the next file rows.append([y]) # land use csv file for the year (contains the fractions for each comid)
def calculate_landuse( self, rasterfile, shapefile, aggregatefile, attribute, csvfile=None, ): """ Calculates the land use for the given year for the "attribute" feature attribute in the polygon shapefile using the aggregate mapping provided in the "aggregatefile." """ # make sure the files exist for f in rasterfile, shapefile + '.shp', aggregatefile: if not os.path.isfile(f): print('error, {} does not exist\n'.format(f)) raise # read the aggregate file self.read_aggregatefile(aggregatefile) # open the shapefile sf = Reader(shapefile, shapeType=5) attributes = [f[0] for f in sf.fields] try: index = attributes.index(attribute) - 1 except: print('error: attribute ' + '{} is not in the shapefile fields'.format(attribute)) raise # iterate through the shapes, get the fractions and save them for i in range(len(sf.records())): points = numpy.array(sf.shape(i).points) record = sf.record(i) k = record[index] # store the results self.landuse[k] = {r: 0 for r in self.order} try: values, origin = get_raster_in_poly(rasterfile, points, verbose=False) values = values.flatten() values = values[values.nonzero()] tot_pixels = len(values) # count the number of pixels of each land use type for v in numpy.unique(values): # find all the indices for each pixel value pixels = numpy.argwhere(values == v) # normalize by the total # of pixels f = len(values[pixels]) / tot_pixels # add the landuse to the aggregated value self.landuse[k][self.groups[v]] += f # work around for small shapes except: self.landuse[k][self.groups[0]] = 1 if csvfile is not None: self.make_csv(attribute, csvfile) return self.landuse
def extract_HUC8(self, HUC8, output, gagefile = 'gagestations', verbose = True): """Extracts the USGS gage stations for a watershed from the gage station shapefile into a shapefile for the 8-digit hydrologic unit code of interest. """ # make sure the metadata exist locally self.download_metadata() # make sure the output destination exists if not os.path.isdir(output): os.mkdir(output) sfile = '{}/{}'.format(output, gagefile) if not os.path.isfile(sfile + '.shp'): # copy the projection shutil.copy(self.NWIS + '.prj', sfile + '.prj') # read the file gagereader = Reader(self.NWIS, shapeType = 1) gagerecords = gagereader.records() # pull out the HUC8 record to parse the dataset HUC8_index = gagereader.fields.index(['HUC', 'C', 8, 0]) - 1 # iterate through the field and find gages in the watershed its = HUC8, sfile print('extracting gage stations in {} to {}\n'.format(*its)) gage_indices = [] i = 0 for record in gagerecords: if record[HUC8_index] == HUC8: gage_indices.append(i) i+=1 # write the data from the HUC8 to a new shapefile w = Writer(shapeType = 1) for field in gagereader.fields: w.field(*field) for i in gage_indices: point = gagereader.shape(i).points[0] w.point(*point) w.record(*gagerecords[i]) w.save(sfile) if verbose: print('successfully extracted NWIS gage stations\n') elif verbose: print('gage station file {} exists\n'.format(sfile)) self.set_metadata(sfile)
# use the ETCalculator to estimate the evapotranspiration time series calculator = ETCalculator() # some of the parameters in the Penman-Monteith Equation depend on the # geographic location so get the average longitude, latitude, and elevation sf = Reader(filename) # make a list of the fields for each shape fields = [f[0] for f in sf.fields] # get the area, centroid and elevation of each shape areas = [r[fields.index("AreaSqKm") - 1] for r in sf.records()] xs = [r[fields.index("CenX") - 1] for r in sf.records()] ys = [r[fields.index("CenY") - 1] for r in sf.records()] zs = [r[fields.index("AvgElevM") - 1] for r in sf.records()] # get the areal-weighted averages lon = sum([a * x for a, x in zip(areas, xs)]) / sum(areas) lat = sum([a * y for a, y in zip(areas, ys)]) / sum(areas) elev = sum([a * z for a, z in zip(areas, zs)]) / sum(areas) # add the information to the calculator calculator.add_location(lon, lat, elev) # use the daily tmin and tmax time series to the calculator to get hourly temps
# solar radiation in W/m2; these are the units supplied by the other classes # in PyHSPF already so no manipulation is needed # some of the parameters in the Penman-Monteith Equation depend on the # geographic location so let's use the information in the shapefile to # provide the average longitude, latitude, and elevation sf = Reader(filename) # make a list of the fields for each shape fields = [f[0] for f in sf.fields] # get the area, centroid and elevation of each shape areas = [r[fields.index('AreaSqKm') - 1] for r in sf.records()] xs = [r[fields.index('CenX') - 1] for r in sf.records()] ys = [r[fields.index('CenY') - 1] for r in sf.records()] zs = [r[fields.index('AvgElevM') - 1] for r in sf.records()] # get the areal-weighted averages lon = sum([a * x for a, x in zip(areas, xs)]) / sum(areas) lat = sum([a * y for a, y in zip(areas, ys)]) / sum(areas) elev = sum([a * z for a, z in zip(areas, zs)]) / sum(areas) # add the information to the calculator calculator.add_location(lon, lat, elev) # it is pretty trivial to get the corresponding reference evapotranspiration
extractor.download_gagedata(gageid, start, end, output = gageid) # need to know the reach length; so find the location of the gage, then find # the flowline in the shapefile and use the record info to get the length # first use the NWIS metadata file to get the latitude and longitude of the gage reader = Reader('{}/USGS_Streamgages-NHD_Locations.shp'.format(NWIS)) # find the record index for the NWIS gage ids i = [f[0] for f in reader.fields].index('SITE_NO') - 1 # find the index of the gage j = [r[i] for r in reader.records()].index(gageid) # use the index to get the latitude and longitude of the station x, y = reader.shape(j).points[0] print('location of gage {}: {:.4f}, {:.4f}\n'.format(gageid, x, y)) # open the flowline shapefile to supply reach length (miles or kilometers # depending on the unit system) reader = Reader(flowfile) # find shapes with a bounding box encompassing the gage to narrow the search print('searching for the closest flowline to the gage\n')
def plot_gage_subbasin(self, hspfmodel, folder): """Makes a plot of the subbasin area.""" subbasinfile = '{}/subbasins'.format(folder) boundaryfile = '{}/boundary'.format(folder) flowfile = '{}/flowlines'.format(folder) combinedfile = '{}/combined'.format(folder) watershedplot = '{}/watershed.png'.format(folder) # make a shapefile of the subbasins for the watershed f = '{0}/{1}/{1}subbasins'.format(self.directory, self.HUC8) for out in (subbasinfile, boundaryfile, flowfile, combinedfile): if not os.path.isfile(out + '.prj'): shutil.copy(f + '.prj', out + '.prj') if not os.path.isfile(subbasinfile + '.shp'): subshapes = [] subrecords = [] for subbasin in hspfmodel.subbasins: f = '{0}/{1}/{2}/combined'.format(self.directory, self.HUC8, subbasin) s = Reader(f, shapeType = 5) subshapes.append(s.shape(0).points) subrecords.append(s.record(0)) w = Writer(shapeType = 5) for field in s.fields: w.field(*field) for record in subrecords: w.record(*record) for shape in subshapes: w.poly(shapeType = 5, parts = [shape]) w.save(subbasinfile) if not os.path.isfile(combinedfile + '.shp'): fshapes = [] frecords = [] for subbasin in hspfmodel.subbasins: f = '{0}/{1}/{2}/combined_flowline'.format(self.directory, self.HUC8, subbasin) r = Reader(f, shapeType = 3) fshapes.append(r.shape(0).points) frecords.append(r.record(0)) w = Writer(shapeType = 3) for field in r.fields: w.field(*field) for record in frecords: w.record(*record) for shape in fshapes: w.poly(shapeType = 3, parts = [shape]) w.save(combinedfile) # merge the shapes into a watershed if not os.path.exists(boundaryfile + '.shp'): merge_shapes(subbasinfile, outputfile = boundaryfile) # make a flowline file for the subbasins for the watershed if not os.path.isfile(flowfile + '.shp'): shapes = [] records = [] for subbasin in hspfmodel.subbasins: f = '{0}/{1}/{2}/flowlines'.format(self.directory, self.HUC8, subbasin) r = Reader(f, shapeType = 3) for shape in r.shapes(): shapes.append(shape.points) for record in r.records(): records.append(record) w = Writer(shapeType = 3) for field in r.fields: w.field(*field) for record in records: w.record(*record) for shape in shapes: w.poly(shapeType = 3, parts = [shape]) w.save(flowfile) if not os.path.isfile(watershedplot): plot_gage_subbasin(folder, self.HUC8, self.gageid, hspfmodel, output = watershedplot)
# use the ETCalculator to estimate the evapotranspiration time series calculator = ETCalculator() # some of the parameters in the Penman-Monteith Equation depend on the # geographic location so get the average longitude, latitude, and elevation sf = Reader(filename) # make a list of the fields for each shape fields = [f[0] for f in sf.fields] # get the area, centroid and elevation of each shape areas = [r[fields.index('AreaSqKm') - 1] for r in sf.records()] xs = [r[fields.index('CenX') - 1] for r in sf.records()] ys = [r[fields.index('CenY') - 1] for r in sf.records()] zs = [r[fields.index('AvgElevM') - 1] for r in sf.records()] # get the areal-weighted averages lon = sum([a * x for a, x in zip(areas, xs)]) / sum(areas) lat = sum([a * y for a, y in zip(areas, ys)]) / sum(areas) elev = sum([a * z for a, z in zip(areas, zs)]) / sum(areas) # add the information to the calculator calculator.add_location(lon, lat, elev) # use the daily tmin and tmax time series to the calculator to get hourly temps