Source code for polar2grid.filters._base

#!/usr/bin/env python
# encoding: utf-8
# Copyright (C) 2021 Space Science and Engineering Center (SSEC),
#  University of Wisconsin-Madison.
#
#     This program is free software: you can redistribute it and/or modify
#     it under the terms of the GNU 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 General Public License for more details.
#
#     You should have received a copy of the GNU General Public License
#     along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# This file is part of the polar2grid software package. Polar2grid takes
# satellite observation data, remaps it, and writes it to a file format for
# input into another program.
# Documentation: http://www.ssec.wisc.edu/software/polar2grid/
"""Base class for all filters."""

import logging

from satpy import Scene
from xarray import DataArray

logger = logging.getLogger(__name__)


[docs]class BaseFilter: """Base class for filtering products that don't match certain conditions. This class uses a series of metadata comparisons to check if a product should be checked for filtering. If any of the product metadata criteria match then filtering checks are continued. Otherwise, the product is ignored. If the criteria is not provided or is ``None`` or is empty then no products will be checked. If the criteria is passed as ``True`` then all products will be checked. By default, no extra filtering is performed and only the metadata criteria is used. This means that if no criteria is provided, this class will remove all provided DataArrays. """ FILTER_MSG = "Unloading '{}' due to filtering." def __init__(self, product_filter_criteria: dict = None): """Initialize thresholds and default search criteria.""" self._filter_criteria = product_filter_criteria
[docs] def _matches_criteria(self, data_arr: DataArray): attrs = data_arr.attrs if self._filter_criteria is True: # check all products return True if not self._filter_criteria: # if no criteria was provided then no products will be checked return False for filter_key, filter_list in self._filter_criteria.items(): if attrs.get(filter_key) in filter_list: return True return False
[docs] def _filter_data_array(self, data_arr: DataArray, _cache: dict): """Check if this DataArray should be removed. Returns: True if it meets the condition and does not have enough day data and should therefore be removed. False otherwise meaning it should be kept. """ if not self._matches_criteria(data_arr): return False # Subclasses should implement further logic here return True
[docs] def filter_scene(self, scene: Scene): """Create a new Scene with filtered DataArrays removed.""" _cache = {} remaining_ids = [] filtered_ids = [] for data_id in self._iter_scene_coarsest_to_finest_area(scene): logger.debug("Analyzing '{}' for filtering...".format(data_id)) if not self._filter_data_array(scene[data_id], _cache): remaining_ids.append(data_id) else: logger.debug(self.FILTER_MSG.format(data_id)) filtered_ids.append(data_id) if not remaining_ids: return None new_scn = scene.copy(remaining_ids) new_scn._wishlist = scene.wishlist.copy() - set(filtered_ids) return new_scn
[docs] @staticmethod def _iter_scene_coarsest_to_finest_area(scene: Scene): by_area_list = list(scene.iter_by_area()) by_area_list = sorted(by_area_list, key=lambda x: x[0].shape[0]) for _, data_arrays in by_area_list: yield from data_arrays