#!/usr/bin/env python # -*- coding: utf-8 -*- # Copyright (c) 2019 # Author(s): # Martin Raspaud # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 of the License, or (at your option) any # later version. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License along # with this program. If not, see . """Base resampler class made for subclassing.""" import hashlib import json import os import numpy as np from pyresample.geometry import SwathDefinition def hash_dict(the_dict, the_hash=None): """Calculate a hash for a dictionary.""" if the_hash is None: the_hash = hashlib.sha1() the_hash.update(json.dumps(the_dict, sort_keys=True).encode('utf-8')) return the_hash class BaseResampler(object): """Base abstract resampler class.""" def __init__(self, source_geo_def, target_geo_def): """Initialize resampler with geolocation information. Args: source_geo_def (SwathDefinition, AreaDefinition): Geolocation definition for the data to be resampled target_geo_def (CoordinateDefinition, AreaDefinition): Geolocation definition for the area to resample data to. """ self.source_geo_def = source_geo_def self.target_geo_def = target_geo_def def get_hash(self, source_geo_def=None, target_geo_def=None, **kwargs): """Get hash for the current resample with the given *kwargs*.""" if source_geo_def is None: source_geo_def = self.source_geo_def if target_geo_def is None: target_geo_def = self.target_geo_def the_hash = source_geo_def.update_hash() target_geo_def.update_hash(the_hash) hash_dict(kwargs, the_hash) return the_hash.hexdigest() def precompute(self, **kwargs): """Do the precomputation. This is an optional step if the subclass wants to implement more complex features like caching or can share some calculations between multiple datasets to be processed. """ return None def compute(self, data, **kwargs): """Do the actual resampling. This must be implemented by subclasses. """ raise NotImplementedError def resample(self, data, cache_dir=None, mask_area=None, **kwargs): """Resample `data` by calling `precompute` and `compute` methods. Only certain resampling classes may use `cache_dir` and the `mask` provided when `mask_area` is True. The return value of calling the `precompute` method is passed as the `cache_id` keyword argument of the `compute` method, but may not be used directly for caching. It is up to the individual resampler subclasses to determine how this is used. Args: data (xarray.DataArray): Data to be resampled cache_dir (str): directory to cache precomputed results (default False, optional) mask_area (bool): Mask geolocation data where data values are invalid. This should be used when data values may affect what neighbors are considered valid. Returns (xarray.DataArray): Data resampled to the target area """ # default is to mask areas for SwathDefinitions if mask_area is None and isinstance( self.source_geo_def, SwathDefinition): mask_area = True if mask_area: if isinstance(self.source_geo_def, SwathDefinition): geo_dims = self.source_geo_def.lons.dims else: geo_dims = ('y', 'x') flat_dims = [dim for dim in data.dims if dim not in geo_dims] if np.issubdtype(data.dtype, np.integer): kwargs['mask'] = data == data.attrs.get('_FillValue', np.iinfo(data.dtype.type).max) else: kwargs['mask'] = data.isnull() kwargs['mask'] = kwargs['mask'].all(dim=flat_dims) cache_id = self.precompute(cache_dir=cache_dir, **kwargs) return self.compute(data, cache_id=cache_id, **kwargs) def _create_cache_filename(self, cache_dir=None, prefix='', fmt='.zarr', **kwargs): """Create filename for the cached resampling parameters.""" cache_dir = cache_dir or '.' hash_str = self.get_hash(**kwargs) return os.path.join(cache_dir, prefix + hash_str + fmt)