mirror of
https://github.com/enzet/map-machine.git
synced 2025-05-16 18:46:24 +02:00
114 lines
3.6 KiB
Python
114 lines
3.6 KiB
Python
"""
|
|
Author: Sergey Vartanov (me@enzet.ru)
|
|
"""
|
|
import math
|
|
import numpy as np
|
|
from typing import Optional
|
|
|
|
|
|
def get_ratio(maximum, minimum, ratio: float = 1):
|
|
return (maximum[0] - minimum[0]) * ratio / (maximum[1] - minimum[1])
|
|
|
|
|
|
def map_(
|
|
value: float, current_min: float, current_max: float, target_min: float,
|
|
target_max: float):
|
|
"""
|
|
Map current value in bounds of current_min and current_max to bounds of
|
|
target_min and target_max.
|
|
"""
|
|
return \
|
|
target_min + (value - current_min) / (current_max - current_min) * \
|
|
(target_max - target_min)
|
|
|
|
|
|
class Geo:
|
|
def __init__(self, lat: float, lon: float):
|
|
self.lat: float = lat
|
|
self.lon: float = lon
|
|
|
|
def __getitem__(self, item) -> Optional[float]:
|
|
if item == 0:
|
|
return self.lon
|
|
if item == 1:
|
|
return self.lat
|
|
return None
|
|
|
|
def __add__(self, other: "Geo") -> "Geo":
|
|
return Geo(self.lat + other.lat, self.lon + other.lon)
|
|
|
|
def __sub__(self, other: "Geo") -> "Geo":
|
|
return Geo(self.lat - other.lat, self.lon - other.lon)
|
|
|
|
def __repr__(self) -> str:
|
|
return f"{self.lat}, {self.lon}"
|
|
|
|
|
|
class GeoFlinger:
|
|
def __init__(
|
|
self, minimum, maximum, target_minimum, target_maximum):
|
|
"""
|
|
:param minimum: minimum latitude and longitude
|
|
:param maximum: maximum latitude and longitude
|
|
:param target_minimum: minimum of the resulting image
|
|
:param target_maximum: maximum of the resulting image
|
|
"""
|
|
self.minimum = minimum
|
|
self.maximum = maximum
|
|
|
|
# Ratio is depended of latitude. It is always <= 1. In one latitude
|
|
# degree is always 40 000 / 360 km. In one current longitude degree is
|
|
# about 40 000 / 360 * ratio km.
|
|
|
|
ratio = math.sin(
|
|
(90.0 - ((self.maximum.lat + self.minimum.lat) / 2.0))
|
|
/ 180.0 * math.pi)
|
|
|
|
# Longitude displayed as x.
|
|
# Latitude displayed as y.
|
|
|
|
# Ratio is x / y.
|
|
|
|
space: np.array = [0, 0]
|
|
current_ratio = get_ratio(self.maximum, self.minimum, ratio)
|
|
target_ratio = get_ratio(target_maximum, target_minimum)
|
|
|
|
if current_ratio >= target_ratio:
|
|
n = (target_maximum[0] - target_minimum[0]) / \
|
|
(maximum.lon - minimum.lon) / ratio
|
|
space[1] = \
|
|
((target_maximum[1] - target_minimum[1]) -
|
|
(maximum.lat - minimum.lat) * n) / 2.0
|
|
space[0] = 0
|
|
else:
|
|
n = (target_maximum[1] - target_minimum[1]) / \
|
|
(maximum.lat - minimum.lat) * ratio
|
|
space[0] = \
|
|
((target_maximum[0] - target_minimum[0]) -
|
|
(maximum.lon - minimum.lon) * n) / 2.0
|
|
space[1] = 0
|
|
|
|
self.target_minimum = np.add(target_minimum, space)
|
|
self.target_maximum = np.subtract(target_maximum, space)
|
|
|
|
meters_per_pixel = \
|
|
(self.maximum.lat - self.minimum.lat) / \
|
|
(self.target_maximum[1] - self.target_minimum[1]) * \
|
|
40000 / 360 * 1000
|
|
|
|
self.scale = 1 / meters_per_pixel
|
|
|
|
self.space = space
|
|
|
|
def fling(self, current) -> np.array:
|
|
"""
|
|
:param current: vector to fling
|
|
"""
|
|
x = map_(
|
|
current.lon, self.minimum.lon, self.maximum.lon,
|
|
self.target_minimum[0], self.target_maximum[0])
|
|
y = map_(
|
|
self.maximum.lat + self.minimum.lat - current.lat,
|
|
self.minimum.lat, self.maximum.lat,
|
|
self.target_minimum[1], self.target_maximum[1])
|
|
return np.array([x, y])
|