Repository: climate Updated Branches: refs/heads/master 8f3a16e2c -> d73c3c21c
Initial commit Project: http://git-wip-us.apache.org/repos/asf/climate/repo Commit: http://git-wip-us.apache.org/repos/asf/climate/commit/0cc4b1ea Tree: http://git-wip-us.apache.org/repos/asf/climate/tree/0cc4b1ea Diff: http://git-wip-us.apache.org/repos/asf/climate/diff/0cc4b1ea Branch: refs/heads/master Commit: 0cc4b1eac2eab8ffbc9a5ab20c6b2e8c2f34f17c Parents: 7ab0141 Author: huikyole <[email protected]> Authored: Mon Aug 1 17:36:10 2016 -0700 Committer: huikyole <[email protected]> Committed: Mon Aug 1 17:36:10 2016 -0700 ---------------------------------------------------------------------- ocw/dataset.py | 308 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 204 insertions(+), 104 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/climate/blob/0cc4b1ea/ocw/dataset.py ---------------------------------------------------------------------- diff --git a/ocw/dataset.py b/ocw/dataset.py index f9c344e..509c04a 100644 --- a/ocw/dataset.py +++ b/ocw/dataset.py @@ -25,6 +25,8 @@ Classes: import numpy import logging import datetime as dt +from mpl_toolkits.basemap import Basemap +import netCDF4 import ocw.utils as utils @@ -231,18 +233,35 @@ class Bounds(object): correct functioning. Bounds guarantees that a function receives well formed information without the need to do the validation manually. - Spatial and temporal bounds must follow the following guidelines. + boundary_type may be one of the following: + * 'rectangular' + * 'CORDEX (CORDEX region name)': pre-defined CORDEX boundary + * 'us_states': an array of US states abbreviation is required (ex) us_states = ['CA','NV']) + * 'countries': an array of county names is required (ex) countries = ['United States','Canada','Mexico'] + * 'user': user_mask_file in a netCDF format with two dimensional mask variable is required. + + If boundary_type == 'rectangular', spatial and temporal bounds must follow the following guidelines. * Latitude values must be in the range [-90, 90] * Longitude values must be in the range [-180, 180] * Lat/Lon Min values must be less than the corresponding Lat/Lon Max values. - * Temporal bounds must a valid datetime object + + Temporal bounds must a valid datetime object ''' - def __init__(self, lat_min, lat_max, lon_min, - lon_max, start=None, end=None): + def __init__(self, boundary_type='rectangular', + us_states=None, countries=None, + user_mask_file=None, mask_variable_name=None, longitude_name=None, latitude_name=None, + lat_min=None, lat_max=None, lon_min=None, lon_max=None, + start=None, end=None): '''Default Bounds constructor + :param boundary_type: The type of spatial subset boundary. + :type boundary_type: :mod:`string' + + + :param lat_min: The minimum latitude bound. + :type lat_min: :class:`float` :param lat_min: The minimum latitude bound. :type lat_min: :class:`float` @@ -265,11 +284,7 @@ class Bounds(object): :raises: ValueError ''' - self._lat_min = float(lat_min) - self._lat_max = float(lat_max) - self._lon_min = float(lon_min) - self._lon_max = float(lon_max) - + self.boundary_type = boundary_type if start: self._start = start else: @@ -280,100 +295,185 @@ class Bounds(object): else: self._end = None - @property - def lat_min(self): - return self._lat_min - - @lat_min.setter - def lat_min(self, value): - if not (-90 <= value <= 90 and value < self._lat_max): - error = "Attempted to set lat_min to invalid value: %s" % (value) - logger.error(error) - raise ValueError(error) - - self._lat_min = value - - @property - def lat_max(self): - return self._lat_max - - @lat_max.setter - def lat_max(self, value): - if not (-90 <= value <= 90 and value > self._lat_min): - error = "Attempted to set lat_max to invalid value: %s" % (value) - logger.error(error) - raise ValueError(error) - - self._lat_max = value - - @property - def lon_min(self): - return self._lon_min - - @lon_min.setter - def lon_min(self, value): - if not (-180 <= value <= 180 and value < self._lon_max): - error = "Attempted to set lon_min to invalid value: %s" % (value) - logger.error(error) - raise ValueError(error) - - self._lon_min = value - - @property - def lon_max(self): - return self._lon_max - - @lon_max.setter - def lon_max(self, value): - if not (-180 <= value <= 180 and value > self._lon_min): - error = "Attempter to set lon_max to invalid value: %s" % (value) - logger.error(error) - raise ValueError(error) - - self._lon_max = value - - @property - def start(self): - return self._start - - @start.setter - def start(self, value): - if self._end: - if not (type(value) is dt.datetime and value < self._end): - error = "Attempted to set start to invalid value: %s" % (value) - logger.error(error) - raise ValueError(error) - - self._start = value - - @property - def end(self): - return self._end - - @end.setter - def end(self, value): - if self._start: - if not (type(value) is dt.datetime and value > self._start): - error = "Attempted to set end to invalid value: %s" % (value) - logger.error(error) - raise ValueError(error) - - self._end = value - - def __str__(self): - lat_range = "({}, {})".format(self._lat_min, self._lat_max) - lon_range = "({}, {})".format(self._lon_min, self._lon_max) - temporal_boundaries = "({}, {})".format(self._start, self._end) + @property + def start(self): + return self._start + + @start.setter + def start(self, value): + if self._end: + if not (type(value) is dt.datetime and value < self._end): + error = "Attempted to set start to invalid value: %s" % (value) + logger.error(error) + raise ValueError(error) + + self._start = value + + @property + def end(self): + return self._end + + @end.setter + def end(self, value): + if self._start: + if not (type(value) is dt.datetime and value > self._start): + error = "Attempted to set end to invalid value: %s" % (value) + logger.error(error) + raise ValueError(error) + + if boundary_type == 'us_states': + self.masked_regions = shapefile_boundary(boundary_type, us_states) + if boundary_type == 'countries': + self.masked_regions = shapefile_boundary(boundary_type, countries) + if boundary_type == 'user': + file_object = netCDF4.Dataset(user_mask_file) + self.mask_variable = file_object.variables[mask_variable_name][:] + mask_longitude = file_object.variables[longitude_name][:] + mask_latitude = file_object.variables[latitude_name][:] + if mask_longitude.ndim == 1 and mask_latitude.ndim == 1: + self.mask_longitude, self.mask_latitude = np.meshgrid(mask_longitude, mask_latitude) + elif mask_longitude.ndim == 2 and mask_latitude.ndim == 2: + self.mask_longitude = mask_longitude + self.mask_latitude = mask_latitude + if boundary_type == 'rectangular' or boundary_type[:6].upper() == 'CORDEX': + if boundary_type == 'rectangular': + self._lat_min = float(lat_min) + self._lat_max = float(lat_max) + self._lon_min = float(lon_min) + self._lon_max = float(lon_max) + if boundary_type[:6].upper() == 'CORDEX': + self._lat_min, self._lat_max, self._lon_min, self._lon_max = CORDEX_boundary(boundary_type[6:].replace(" ","").lower()) + + + @property + def lat_min(self): + return self._lat_min + + @lat_min.setter + def lat_min(self, value): + if not (-90 <= value <= 90 and value < self._lat_max): + error = "Attempted to set lat_min to invalid value: %s" % (value) + logger.error(error) + raise ValueError(error) + + self._lat_min = value + + @property + def lat_max(self): + return self._lat_max + + @lat_max.setter + def lat_max(self, value): + if not (-90 <= value <= 90 and value > self._lat_min): + error = "Attempted to set lat_max to invalid value: %s" % (value) + logger.error(error) + raise ValueError(error) + + self._lat_max = value + + @property + def lon_min(self): + return self._lon_min + + @lon_min.setter + def lon_min(self, value): + if not (-180 <= value <= 180 and value < self._lon_max): + error = "Attempted to set lon_min to invalid value: %s" % (value) + logger.error(error) + raise ValueError(error) + + self._lon_min = value + + @property + def lon_max(self): + return self._lon_max + + @lon_max.setter + def lon_max(self, value): + if not (-180 <= value <= 180 and value > self._lon_min): + error = "Attempter to set lon_max to invalid value: %s" % (value) + logger.error(error) + raise ValueError(error) + + self._lon_max = value + + + def __str__(self): + lat_range = "({}, {})".format(self._lat_min, self._lat_max) + lon_range = "({}, {})".format(self._lon_min, self._lon_max) + temporal_boundaries = "({}, {})".format(self._start, self._end) + + formatted_repr = ( + "<Bounds - " + "lat-range: {}, " + "lon-range: {}, " + "temporal_boundaries: {}> " + ) + + return formatted_repr.format( + lat_range, + lon_range, + temporal_boundaries, + ) + + +def shapefile_boundary(boundary_type, region_names): + ''' + :param boundary_type: The type of spatial subset boundary + :type boundary_type: :mod:'string' - formatted_repr = ( - "<Bounds - " - "lat-range: {}, " - "lon-range: {}, " - "temporal_boundaries: {}> " - ) + :param region_names: An array of regions for spatial subset + :type region_names: :mod:'list' + ''' - return formatted_repr.format( - lat_range, - lon_range, - temporal_boundaries, - ) + map_read = Basemap + regions =[] + if boundary_type == 'us_states': + #map_read.readshapefile(ocw.__path__[0]+'/shape/usa_states','usa_states') + map_read.readshapefile('/home/huikyole/climate/ocw'+'/shape/usa_states','usa_states') + for region_name in region_names: + for iregion, region_info in enumerate(map_read.usa_states_info): + if region_info['st'] == region_name: + regions.append(np.array(map_read.usa_states[iregion])) + if boundary_type == 'countries': + #map_read.readshapefile(ocw.__path__[0]+'/shape/countries','countries') + map_read.readshapefile('/home/huikyole/climate/ocw'+'/shape/countries','countries') + for region_name in region_names: + for iregion, region_info in enumerate(map_read.countries_info): + if region_info['COUNTRY'] == region_name: + regions.append(np.array(map_read.countries[iregion])) + return regions + +def CORDEX_boundary(domain_name): + ''' + :param domain_name: CORDEX domain name (http://www.cordex.org/) + :type domain_name: :mod:'string' + ''' + if domain_name =='southamerica': + return -57.61, 18.50, 254.28-360., 343.02-360. + if domain_name =='centralamerica': + return -19.46, 34.83, 235.74-360., 337.78-360. + if domain_name =='northamerica': + return 12.55, 75.88, 189.26-360., 336.74-360. + if domain_name =='europe': + return 22.20, 71.84, 338.23-360., 64.4 + if domain_name =='africa': + return -45.76, 42.24, 335.36-360., 60.28 + if domain_name =='southasia': + return -15.23, 45.07, 19.88, 115.55 + if domain_name =='eastasia': + return -0.10, 61.90, 51.59, 179.99 + if domain_name =='centralasia': + return 18.34, 69.37, 11.05, 139.13 + if domain_name =='australasia': + return -52.36, 12.21, 89.25, 179.99 + if domain_name =='antartica': + return -89.48,-56.00, -179.00, 179.00 + if domain_name =='artic': + return 46.06, 89.50, -179.00, 179.00 + if domain_name =='mediterranean': + return 25.63, 56.66, 339.79-360.00, 50.85 + if domain_name =='middleeastnorthafrica': + return -7.00, 45.00, 333.00-360.00, 76.00 + if domain_name =='southeastasia': + return -15.14, 27.26, 89.26, 146.96
