Source code for pymepps.grid.builder

#!/bin/env python
# -*- coding: utf-8 -*-
#
# Created on 10.04.17
#
# Created for pymepps
#
# @author: Tobias Sebastian Finn, tobias.sebastian.finn@studium.uni-hamburg.de
#
#    Copyright (C) {2017}  {Tobias Sebastian Finn}
#
#    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/>.
#

# System modules
import logging
import re

# External modules

# Internal modules
from .lonlat import LonLatGrid
from .gaussian import GaussianGrid
from .projection import ProjectionGrid
from .curvilinear import CurvilinearGrid
from .unstructured import UnstructuredGrid


logger = logging.getLogger(__name__)


available_grids = {
    'lonlat': LonLatGrid,
    'gaussian': GaussianGrid,
    'projection': ProjectionGrid,
    'curvilinear': CurvilinearGrid,
    'unstructured': UnstructuredGrid
}


[docs]class GridBuilder(object): def __init__(self, griddes): """ Class representing horizontal grids of grid based files. This class is based on the grid description files of cdo [cdo]_. With this grid description is is possible to remap grid based arrays and to interpolate for a given latitude and longitude. Internally the grids are transformed to latitude and longitude points. .. [cdo] https://code.zmaw.de/projects/cdo Parameters ---------- griddes : str or dict(str, str/float) The grid description. This could be a path to a cdo conform grid description file. Another possibility is to use a cdo conform grid description within a str or a dict. The latitude and longitude values are calculated based on this parameter. Attributes ---------- griddes : dict(str, str/float) The grid description as dict. """ self._grid_handler = None self._latlon = None self._grid_dict = {} self.griddes = griddes @property def griddes(self): return self._grid_dict @griddes.setter def griddes(self, description): if isinstance(description, str): grid_str = self.open_string(description) grid_dict = self.decode_str(grid_str) elif isinstance(description, list): grid_dict = self.decode_str(description) elif isinstance(description, dict): grid_dict = description else: raise TypeError('The given grid description has to be a string, a ' 'dict or None!') self._set_grid_handler(grid_dict) self._grid_dict = grid_dict def _set_grid_handler(self, grid_dict): if 'proj4' in self._grid_dict: self._grid_handler = ProjectionGrid else: if 'gridtype' not in grid_dict: raise KeyError('There is no gridtype defined. Griddes is no ' 'valid cdo grid definition!') if grid_dict['gridtype'] not in available_grids: raise ValueError('The given gridtype "{0:s}" has no defined ' 'decoder yet, please use one of the available ' 'gridtypes!'.format(grid_dict['gridtype'])) self._grid_handler = available_grids[grid_dict['gridtype']]
[docs] @staticmethod def open_string(path_str): """ This method is used to check if the given str is a path or a grid string. Parameters ---------- path_str : str This string is checked and if it is a path it will be read. Returns ------- grid_str : str The given str or the read str. Raises ------ TypeError If path_str is not a str type. """ try: with open(path_str, 'r') as gf: grid_str = gf.read() except FileNotFoundError: grid_str = path_str return grid_str
[docs] @staticmethod def decode_str(grid_str): """ Method to clean the given grid str and to get a python dict. Key and value are separated with =. Every new key value pair needs a new line delimiter. Only alphanumeric characters are allowed as key and value. To delimit a value list use spaces and new lines. Lines with # are used as comment lines. Steps to decode the grid string: 1) String splitting by new line delimiter 2) Clean the lines from unallowed characters 3) Split the non-comment lines to key, value pairs 4) Append elements where no key, value pair is available to the previous value 5) Clean and split the key, value elements from spaces 6) Convert the values to float numbers Parameters ---------- grid_str : str or list(str) The given grid_str which should be decoded. If this is a string the string will be splitten by new line into a list. It is necessary that every list entry has only one key = value entry. Returns ------- grid_dict : dict(str, str or float) The decoded grid dict from the str. """ if isinstance(grid_str, str): grid_str_lines = list(grid_str.split('\n')) elif isinstance(grid_str, list): grid_str_lines = grid_str else: raise TypeError('The given grid_str has to be a str or a list of ' 'str!') preprocessed_lines = [re.sub('[^0-9a-zA-Z=#"\_\.\-\+ ]+', '', gs) for gs in grid_str_lines] splitted_lines = [line.split('=', 1) for line in preprocessed_lines if len(line) > 0 and '#' not in line] def clean_value(val): if '"' in val: val = [val.replace('"', '').strip(), ] else: val = list(filter(None, val.split(' '))) return val cleaned_lines = [] for line in splitted_lines: line[-1] = clean_value(line[-1]) if len(line) == 1 and cleaned_lines: cleaned_lines[-1][-1].extend(line[0]) elif len(line) == 2: line[0] = line[0].strip() cleaned_lines.append(line) grid_dict = {l[0]: l[1] for l in cleaned_lines} for k in grid_dict: try: grid_dict[k] = [float(val) if val[-1] != 'f' else float(val[:-1]) for val in grid_dict[k]] except ValueError: pass if len(grid_dict[k]) == 1: grid_dict[k] = grid_dict[k][0] return grid_dict
[docs] def build_grid(self): """ This method build up the grid with the griddes attribute. Returns ------- grid : child instance of Grid The built grid. The class of the grid is defined by the gridtype. The values of the grid are calculated with griddes. """ grid = self._grid_handler(self._grid_dict) return grid