Source code for polar2grid.grids.config_helper

#!/usr/bin/env python3
# encoding: utf-8
# Copyright (C) 2013-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/
"""Helper script for creating valid grid configuration entries.

The user provides a center longitude and latitude, along with other grid
parameters, and a configuration line is printed to stdout that can be added
to a user's own grid configuration file.

"""
from __future__ import annotations

import os
import sys
import warnings

from pyproj import CRS, Proj

from polar2grid.utils.convert_grids_conf_to_yaml import crs_to_proj_dict, ordered_dump

# grid_name,proj4, proj4_str,width,height,pixel_size_x,pixel_size_y,origin_x,origin_y
CONFIG_LINE_FORMAT = (
    "%(grid_name)s, proj4, %(proj4_str)s, %(width)d, %(height)d, %(pixel_size_x)0.5f, "
    "%(pixel_size_y)0.5f, %(origin_x)s, %(origin_y)s"
)


[docs]def determine_projection(center_lon: float, center_lat: float, proj4_str: str = None) -> CRS: """Get the 'best' projection to be used based on the center longitude and latitude provided.""" abs_lat = abs(center_lat) if proj4_str is None: if abs_lat < 15: proj4_str = ( "+proj=eqc +datum=WGS84 +ellps=WGS84 +lat_ts=%(center_lat)0.5f +lon_0=%(center_lon)0.5f " "+units=m +no_defs" ) elif abs_lat < 70: proj4_str = ( "+proj=lcc +datum=WGS84 +ellps=WGS84 +lat_0=%(center_lat)0.5f +lat_1=%(center_lat)0.5f " "+lon_0=%(center_lon)0.5f +units=m +no_defs" ) else: proj4_str = ( "+proj=stere +datum=WGS84 +ellps=WGS84 +lat_0=90 +lat_ts=%(center_lat)0.5f " "+lon_0=%(center_lon)0.5f +units=m" ) proj4_str = proj4_str % dict(center_lon=center_lon, center_lat=center_lat) return CRS.from_user_input(proj4_str)
[docs]def get_parser(): from argparse import SUPPRESS, ArgumentParser prog = os.getenv("PROG_NAME", sys.argv[0]) description = """This script is meant to help those unfamiliar with PROJ.4 and projections in general. By providing a few grid parameters this script will provide a grid configuration line that can be added to a user's custom grid configuration. Based on a center longitude and latitude, the script will choose an appropriate projection.""" parser = ArgumentParser(prog=prog, description=description) parser.add_argument("grid_name", type=str, help="Unique grid name") parser.add_argument("center_longitude", type=float, help="Decimal longitude value for center of grid (-180 to 180)") parser.add_argument("center_latitude", type=float, help="Decimal latitude value for center of grid (-90 to 90)") parser.add_argument( "pixel_size_x", type=float, help="""Size of each pixel in the X direction in grid units, meters for default projections.""", ) parser.add_argument( "pixel_size_y", type=float, help="""Size of each pixel in the Y direction in grid units, meters for default projections.""", ) parser.add_argument("grid_width", type=int, help="Grid width in number of pixels") parser.add_argument("grid_height", type=int, help="Grid height in number of pixels") parser.add_argument("-p", dest="proj_str", default=None, help="PROJ.4 projection string to override the default") # Don't force Y pixel size to be negative (for expert use only) parser.add_argument( "--dont-touch-ysize", dest="dont_touch_ysize", action="store_true", default=False, help=SUPPRESS ) parser.add_argument("--legacy-format", action="store_true", help="Produce a legacy '.conf' format grid definition.") return parser
[docs]def _create_legacy_line( grid_name: str, crs: CRS, clon: float, clat: float, grid_width: int, grid_height: int, pixel_size_x: float, pixel_size_y: float, ) -> str: p = Proj(crs) origin_x = p(clon, clat)[0] + (grid_width / 2.0 * pixel_size_x * -1) origin_y = p(clon, clat)[1] + (grid_height / 2.0 * pixel_size_y * -1) if p.crs.is_geographic: # Origin is in degrees so we need to add a unit string to it origin_x = "%0.5fdeg" % origin_x origin_y = "%0.5fdeg" % origin_y else: origin_lon, origin_lat = p(origin_x, origin_y, inverse=True) origin_x = "%0.5fdeg" % origin_lon origin_y = "%0.5fdeg" % origin_lat valid_config_line = CONFIG_LINE_FORMAT % { "grid_name": grid_name, "proj4_str": crs.to_proj4(), "origin_x": origin_x, "origin_y": origin_y, "pixel_size_x": pixel_size_x, "pixel_size_y": pixel_size_y, "width": grid_width, "height": grid_height, } return valid_config_line
[docs]def _create_yaml_entry( grid_name: str, crs: CRS, clon: float, clat: float, grid_width: int, grid_height: int, pixel_size_x: float, pixel_size_y: float, ): proj_dict = crs_to_proj_dict(crs) area_dict = { "projection": proj_dict, "shape": {"height": grid_height, "width": grid_width}, "center": {"x": clon, "y": clat, "units": "degrees"}, "resolution": {"dx": abs(pixel_size_x), "dy": abs(pixel_size_y)}, } return {grid_name: area_dict}
[docs]def main(argv=None): if argv is None: argv = sys.argv[1:] parser = get_parser() args = parser.parse_args(argv) grid_name = args.grid_name clon = args.center_longitude clat = args.center_latitude pixel_size_x = args.pixel_size_x pixel_size_y = args.pixel_size_y if pixel_size_y > 0 and not args.dont_touch_ysize: pixel_size_y *= -1 grid_width = args.grid_width grid_height = args.grid_height crs = determine_projection(clon, clat, args.proj_str) warnings.filterwarnings("ignore", module="pyproj", category=UserWarning) if clon < -180 or clon > 180: print("ERROR: Center longitude must be between -180 and 180 degrees") return -1 if clat < -90 or clat > 90: print("ERROR: Center latitude must be between -90 and 90 degrees") return -1 if args.legacy_format: valid_config_line = _create_legacy_line( grid_name, crs, clon, clat, grid_width, grid_height, pixel_size_x, pixel_size_y, ) print(valid_config_line) else: yaml_dict = _create_yaml_entry( grid_name, crs, clon, clat, grid_width, grid_height, pixel_size_x, pixel_size_y, ) yml_str = ordered_dump(yaml_dict, default_flow_style=None) print(yml_str)
if __name__ == "__main__": sys.exit(main())