|
| 1 | +from __future__ import absolute_import |
| 2 | +import logging |
| 3 | +import os |
| 4 | +import re |
| 5 | +import shutil |
| 6 | +from datetime import date |
| 7 | +from ftplib import FTP |
| 8 | + |
| 9 | +from dateutil.relativedelta import relativedelta |
| 10 | +from dataqs.processor_base import GeoDataMosaicProcessor, GS_DATA_DIR, \ |
| 11 | + GS_TMP_DIR |
| 12 | +from dataqs.helpers import get_band_count, gdal_translate, \ |
| 13 | + nc_convert, style_exists, cdo_fixlng |
| 14 | + |
| 15 | +logger = logging.getLogger("dataqs.processors") |
| 16 | +script_dir = os.path.dirname(os.path.realpath(__file__)) |
| 17 | + |
| 18 | + |
| 19 | +class CMAPProcessor(GeoDataMosaicProcessor): |
| 20 | + """ |
| 21 | + Processor for Land-Ocean Temperature Index, ERSSTv4, 1200km smoothing |
| 22 | + from the NASA Goddard Institute for Space Studies' Surface Temperature |
| 23 | + Analysis (GISTEMP). |
| 24 | + More info at http://data.giss.nasa.gov/gistemp/ |
| 25 | + """ |
| 26 | + prefix = "cmap" |
| 27 | + base_url = "ftp.cdc.noaa.gov" |
| 28 | + base_path = "/Datasets/cmap/enh/" |
| 29 | + base_name = "precip.mon.mean.nc" |
| 30 | + layer_name = 'cmap' |
| 31 | + bounds = '-178.75,178.75,-88.75,88.75' |
| 32 | + title = 'CPC Merged Analysis of Precipitation, 1979/01 - {}' |
| 33 | + abstract = """The CPC Merged Analysis of Precipitation ("CMAP") is a |
| 34 | +technique which produces pentad and monthly analyses of global precipitation |
| 35 | + in which observations from raingauges are merged with precipitation estimates |
| 36 | +from several satellite-based algorithms (infrared and microwave). The analyses |
| 37 | +are are on a 2.5 x 2.5 degree latitude/longitude grid and extend back to 1979. |
| 38 | +These data are comparable (but should not be confused with) similarly combined |
| 39 | +analyses by the Global Precipitation Climatology Project which are described in |
| 40 | +Huffman et al (1997). |
| 41 | +
|
| 42 | +It is important to note that the input data sources to make these analyses are |
| 43 | +not constant throughout the period of record. For example, SSM/I (passive |
| 44 | +microwave - scattering and emission) data became available in July of 1987; |
| 45 | +prior to that the only microwave-derived estimates available are from the MSU |
| 46 | +algorithm (Spencer 1993) which is emission-based thus precipitation estimates |
| 47 | +are avaialble only over oceanic areas. Furthermore, high temporal resolution IR |
| 48 | +data from geostationary satellites (every 3-hr) became available during 1986; |
| 49 | +prior to that, estimates from the OPI technique (Xie and Arkin 1997) are used |
| 50 | +based on OLR from polar orbiting satellites. |
| 51 | +
|
| 52 | +The merging technique is thoroughly described in Xie and Arkin (1997). Briefly, |
| 53 | +the methodology is a two-step process. First, the random error is reduced by |
| 54 | +linearly combining the satellite estimates using the maximum likelihood method, |
| 55 | +in which case the linear combination coefficients are inversely proportional to |
| 56 | +the square of the local random error of the individual data sources. Over global |
| 57 | + land areas the random error is defined for each time period and grid location |
| 58 | +by comparing the data source with the raingauge analysis over the surrounding |
| 59 | +area. Over oceans, the random error is defined by comparing the data sources |
| 60 | +with the raingauge observations over the Pacific atolls. Bias is reduced when |
| 61 | +the data sources are blended in the second step using the blending technique of |
| 62 | +Reynolds (1988). Here the data output from step 1 is used to define the "shape" |
| 63 | +of the precipitation field and the rain gauge data are used to constrain the |
| 64 | +amplitude. |
| 65 | +
|
| 66 | +Monthly and pentad CMAP estimates back to the 1979 are available from CPC ftp |
| 67 | +server. |
| 68 | +
|
| 69 | +References: |
| 70 | +
|
| 71 | +Huffman, G. J. and co-authors, 1997: The Global Precipitation Climatology |
| 72 | +Project (GPCP) combined data set. Bull. Amer. Meteor. Soc., 78, 5-20. |
| 73 | +
|
| 74 | +Reynolds, R. W., 1988: A real-time global sea surface temperature analysis. J. |
| 75 | +Climate, 1, 75-86. |
| 76 | +
|
| 77 | +Spencer, R. W., 1993: Global oceanic precipitation from the MSU during 1979-91 |
| 78 | +and comparisons to other climatologies. J. Climate, 6, 1301-1326. |
| 79 | +
|
| 80 | +Xie P., and P. A. Arkin, 1996: Global precipitation: a 17-year monthly analysis |
| 81 | +based on gauge observations, satellite estimates, and numerical model outputs. |
| 82 | +Bull. Amer. Meteor. Soc., 78, 2539-2558. |
| 83 | +""" |
| 84 | + |
| 85 | + def download(self, url, tmp_dir=GS_TMP_DIR, filename=None): |
| 86 | + if not filename: |
| 87 | + filename = url.rsplit('/')[-1] |
| 88 | + ftp = FTP(self.base_url) |
| 89 | + ftp.login('anonymous', 'anonymous') |
| 90 | + ftp.cwd(self.base_path) |
| 91 | + with open(os.path.join(self.tmp_dir, filename), |
| 92 | + 'wb') as outfile: |
| 93 | + ftp.retrbinary('RETR %s' % self.base_name, outfile.write) |
| 94 | + return filename |
| 95 | + |
| 96 | + def convert(self, nc_file): |
| 97 | + nc_transform = nc_convert(nc_file) |
| 98 | + cdo_transform = cdo_fixlng(nc_transform, bounds=self.bounds) |
| 99 | + return cdo_transform |
| 100 | + |
| 101 | + def extract_band(self, tif, band, outname): |
| 102 | + outfile = os.path.join(self.tmp_dir, outname) |
| 103 | + gdal_translate(tif, outfile, bands=[band], |
| 104 | + projection='EPSG:4326', |
| 105 | + options=['TILED=YES', 'COMPRESS=LZW']) |
| 106 | + return outfile |
| 107 | + |
| 108 | + def get_date(self, months): |
| 109 | + start_month = date(1979, 1, 1) |
| 110 | + return start_month + relativedelta(months=months - 1) |
| 111 | + |
| 112 | + def get_title(self, months): |
| 113 | + end_month = self.get_date(months) |
| 114 | + return self.title.format(end_month.strftime('%Y/%m')) |
| 115 | + |
| 116 | + def run(self): |
| 117 | + """ |
| 118 | + Retrieve and process the latest NetCDF file. |
| 119 | + """ |
| 120 | + ncfile = self.download( |
| 121 | + self.base_url, filename='{}.nc'.format(self.layer_name)) |
| 122 | + cdf_file = self.convert(os.path.join(self.tmp_dir, ncfile)) |
| 123 | + bands = get_band_count(cdf_file) |
| 124 | + img_list = self.get_mosaic_filenames(self.layer_name) |
| 125 | + for band in range(1, bands + 1): |
| 126 | + band_date = re.sub('[\-\.]+', '', self.get_date(band).isoformat()) |
| 127 | + img_name = '{}_{}T000000000Z.tif'.format(self.layer_name, band_date) |
| 128 | + if img_name not in img_list: |
| 129 | + band_tif = self.extract_band(cdf_file, band, img_name) |
| 130 | + dst_file = self.data_dir.format(gsd=GS_DATA_DIR, |
| 131 | + ws=self.workspace, |
| 132 | + layer=self.layer_name, |
| 133 | + file=img_name) |
| 134 | + dst_dir = os.path.dirname(dst_file) |
| 135 | + if not os.path.exists(dst_dir): |
| 136 | + os.makedirs(dst_dir) |
| 137 | + if dst_file.endswith('.tif'): |
| 138 | + shutil.move(os.path.join(self.tmp_dir, band_tif), dst_file) |
| 139 | + self.post_geoserver(dst_file, self.layer_name) |
| 140 | + |
| 141 | + if not style_exists(self.layer_name): |
| 142 | + with open(os.path.join(script_dir, |
| 143 | + 'resources/cmap.sld')) as sld: |
| 144 | + self.set_default_style(self.layer_name, self.layer_name, |
| 145 | + sld.read().format(latest_band=bands)) |
| 146 | + self.update_geonode(self.layer_name, title=self.get_title(bands), |
| 147 | + description=self.abstract, |
| 148 | + store=self.layer_name, |
| 149 | + bounds=('-178.75', '178.75', '-88.75', '88.75', |
| 150 | + 'EPSG:4326')) |
| 151 | + self.truncate_gs_cache(self.layer_name) |
| 152 | + self.cleanup() |
| 153 | + |
| 154 | + |
| 155 | +if __name__ == '__main__': |
| 156 | + processor = CMAPProcessor() |
| 157 | + processor.run() |
0 commit comments