Source code for polyfemos.util.coordinator
# -*- coding: utf-8 -*-
# -----------------------------------------------------------------------------
# This file is part of Polyfemos.
#
# Polyfemos 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 any later version.
#
# Polyfemos 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 and
# GNU General Public License along with Polyfemos. If not, see
# <https://www.gnu.org/licenses/>.'
#
# Author: Henrik Jänkävaara
# -----------------------------------------------------------------------------
"""
Coordinate transformation functions
:copyright:
2019, University of Oulu, Sodankyla Geophysical Observatory
:license:
GNU Lesser General Public License v3.0 or later
(https://spdx.org/licenses/LGPL-3.0-or-later.html)
"""
import pyproj
import numpy as np
from scipy.interpolate import SmoothBivariateSpline
from polyfemos.util.messenger import messenger
from polyfemos.util import fileutils
[docs]def ddm2dd(degs, mins, letter):
"""
:type degs: numlike
:param degs: coordinate degrees
:type mins: numlike
:param mins: coordinate decimal minutes
:type letter: str
:param letter: ``N``, ``S``, ``E``, or ``W``
:rtype: float
:return: coordinate as decimal degrees
"""
sign = [1, -1][letter in "SW"]
decimaldegrees = sign * float(degs) + float(mins) / 60.
return decimaldegrees
[docs]def transform_from_ozi_map(mapfile):
"""
Reads `OziExplorer <https://www.oziexplorer4.com/w/>`_
map files and returns a function to convert
WGS84 coordinates to pixels.
The map file can be created manually and it
should contain at least the following mandatory lines:
.. code-block:: text
Point01,xy, 110, 159,in, deg, 68, 47.222,N, 22, 46.498,E, grid, , , , N
Point02,xy, 219, 159,in, deg, 68, 50.001,N, 25, 32.324,E, grid, , , , N
Point03,xy, 329, 159,in, deg, 68, 50.063,N, 28, 20.055,E, grid, , , , N
Point04,xy, 110, 318,in, deg, 67, 19.826,N, 23, 2.020,E, grid, , , , N
Point05,xy, 219, 318,in, deg, 67, 22.408,N, 25, 37.707,E, grid, , , , N
Point06,xy, 329, 318,in, deg, 67, 22.466,N, 28, 15.139,E, grid, , , , N
Point07,xy, 110, 476,in, deg, 65, 52.937,N, 23, 15.536,E, grid, , , , N
Point08,xy, 219, 476,in, deg, 65, 55.346,N, 25, 42.393,E, grid, , , , N
Point09,xy, 329, 476,in, deg, 65, 55.401,N, 28, 10.860,E, grid, , , , N
.. code-block:: text
MMPLL,1, 19.499680, 70.147437
MMPLL,2, 31.374991, 70.251143
MMPLL,3, 30.424810, 64.431702
MMPLL,4, 21.120046, 64.353722
Each line is treated as a list of elements separated with commas,
whitespaces are removed.
The 'Point' entries are used to create the conversion and
the 'MMPLL' entries define the extrapolation area.
In 'Point' entries indices 2 and 3 are pixel coordinates and
indices from 6 to 11 are the corresponding WGS84 coordinates
(indices 2 and 3 in the 'MMPLL' entry).
Lines that do not start with either 'MMPLL' or 'Point' are skipped.
:type mapfile: str
:param mapfile: path to OziExplorer map file
:rtype: func
:return: A function taking WGS84 longitude and latitude as arguments
and returning pixel xy-coordinates as a tuple.
"""
points = []
corners = []
for row in fileutils.rowsof(mapfile, delims=", "):
if len(row) >= 4 and row[0] == "MMPLL":
corners.append([*map(float, row[2:4])])
elif len(row) >= 12 and row[0].startswith("Point"):
px, py = map(float, row[2:4])
lon = ddm2dd(*row[9:12])
lat = ddm2dd(*row[6:9])
points.append([lon, lat, px, py])
points = np.array(points)
corners = np.array(corners)
bx0, bx1 = np.min(corners[:, 0]), np.max(corners[:, 0])
by0, by1 = np.min(corners[:, 1]), np.max(corners[:, 1])
lons = points[:, 0]
lats = points[:, 1]
pxs = points[:, 2]
pys = points[:, 3]
sx = SmoothBivariateSpline(
lons, lats, pxs, bbox=[bx0, bx1, by0, by1], kx=1, ky=1)
sy = SmoothBivariateSpline(
lons, lats, pys, bbox=[bx0, bx1, by0, by1], kx=1, ky=1)
return lambda lon, lat: (sx(lon, lat), sy(lon, lat))
[docs]def get_transform(from_epsg, to_epsg):
"""
:type from_epsg: str
:param from_epsg: EPSG code of the original coordinate system
:type to_epsg: str
:param to_epsg: EPSG code of the resulting coordinate system
:rtype: func
:return: A coordinate transformation function taking xy-coordinates
in ``from_epsg`` coordinate system and returning xy-coordinates in
``to_epsg`` coordinate system
"""
try:
from_coords = pyproj.Proj("+init=EPSG:{}".format(from_epsg))
to_coords = pyproj.Proj("+init=EPSG:{}".format(to_epsg))
except RuntimeError:
msg = "No coordinate conversion available for ESPGs from " \
"'{}' to '{}'.".format(from_epsg, to_epsg)
messenger(msg, "W")
return lambda x, y: (x, y)
return lambda x, y: pyproj.transform(from_coords, to_coords, x, y)