mirror of
https://github.com/enzet/map-machine.git
synced 2025-05-21 13:06:25 +02:00
Issue #11: fix time mode.
Add gradient color detection from the color scale for the given value.
This commit is contained in:
parent
604fd0d14c
commit
aee36d0703
3 changed files with 57 additions and 35 deletions
|
@ -13,11 +13,14 @@ from typing import Any, Dict, List, Optional, Set
|
||||||
from roentgen import ui
|
from roentgen import ui
|
||||||
from roentgen.extract_icon import DEFAULT_SMALL_SHAPE_ID
|
from roentgen.extract_icon import DEFAULT_SMALL_SHAPE_ID
|
||||||
from roentgen.flinger import Flinger
|
from roentgen.flinger import Flinger
|
||||||
from roentgen.osm_reader import OSMMember, OSMRelation, OSMWay, OSMNode
|
from roentgen.osm_reader import Map, OSMMember, OSMRelation, OSMWay, OSMNode, Tagged
|
||||||
from roentgen.scheme import IconSet, Scheme
|
from roentgen.scheme import IconSet, Scheme
|
||||||
from roentgen.util import MinMax
|
from roentgen.util import MinMax, get_gradient_color
|
||||||
|
|
||||||
DEBUG: bool = False
|
DEBUG: bool = False
|
||||||
|
TIME_COLOR_SCALE: List[Color] = [
|
||||||
|
Color("#581845"), Color("#900C3F"), Color("#C70039"), Color("#FF5733"),
|
||||||
|
Color("#FFC300"), Color("#DAF7A6")]
|
||||||
|
|
||||||
|
|
||||||
def is_clockwise(polygon: List[OSMNode]) -> bool:
|
def is_clockwise(polygon: List[OSMNode]) -> bool:
|
||||||
|
@ -47,23 +50,23 @@ def make_counter_clockwise(polygon: List[OSMNode]) -> List[OSMNode]:
|
||||||
return list(reversed(polygon))
|
return list(reversed(polygon))
|
||||||
|
|
||||||
|
|
||||||
class Node:
|
class Node(Tagged):
|
||||||
"""
|
"""
|
||||||
Node in Röntgen terms.
|
Node in Röntgen terms.
|
||||||
"""
|
"""
|
||||||
def __init__(
|
def __init__(
|
||||||
self, icon_set: IconSet, tags: Dict[str, str],
|
self, icon_set: IconSet, tags: Dict[str, str],
|
||||||
point: np.array, coordinates: np.array,
|
point: np.array, coordinates: np.array,
|
||||||
priority: int = 0, is_for_node: bool = True):
|
priority: float = 0, is_for_node: bool = True):
|
||||||
assert point is not None
|
assert point is not None
|
||||||
|
|
||||||
self.icon_set: IconSet = icon_set
|
self.icon_set: IconSet = icon_set
|
||||||
self.tags = tags
|
self.tags: Dict[str, str] = tags
|
||||||
self.point: np.array = point
|
self.point: np.array = point
|
||||||
self.coordinates: np.array = coordinates
|
self.coordinates: np.array = coordinates
|
||||||
self.priority = priority
|
self.priority: float = priority
|
||||||
self.layer = 0
|
self.layer: float = 0
|
||||||
self.is_for_node = is_for_node
|
self.is_for_node: bool = is_for_node
|
||||||
|
|
||||||
def get_tag(self, key: str):
|
def get_tag(self, key: str):
|
||||||
if key in self.tags:
|
if key in self.tags:
|
||||||
|
@ -137,33 +140,14 @@ def get_user_color(text: str, seed: str) -> Color:
|
||||||
"""
|
"""
|
||||||
if text == "":
|
if text == "":
|
||||||
return Color("black")
|
return Color("black")
|
||||||
rgb = sha256((seed + text).encode("utf-8")).hexdigest()[-6:]
|
return Color("#" + sha256((seed + text).encode("utf-8")).hexdigest()[-6:])
|
||||||
r = int(rgb[0:2], 16)
|
|
||||||
g = int(rgb[2:4], 16)
|
|
||||||
b = int(rgb[4:6], 16)
|
|
||||||
c = (r + g + b) / 3.
|
|
||||||
cc = 0
|
|
||||||
r = r * (1 - cc) + c * cc
|
|
||||||
g = g * (1 - cc) + c * cc
|
|
||||||
b = b * (1 - cc) + c * cc
|
|
||||||
h = hex(int(r))[2:] + hex(int(g))[2:] + hex(int(b))[2:]
|
|
||||||
return Color("#" + "0" * (6 - len(h)) + h)
|
|
||||||
|
|
||||||
|
|
||||||
def get_time_color(time: Optional[datetime]) -> Color:
|
def get_time_color(time: Optional[datetime], boundaries: MinMax) -> Color:
|
||||||
"""
|
"""
|
||||||
Generate color based on time.
|
Generate color based on time.
|
||||||
"""
|
"""
|
||||||
if time is None:
|
return get_gradient_color(time, boundaries, TIME_COLOR_SCALE)
|
||||||
return Color("black")
|
|
||||||
delta = (datetime.now() - time).total_seconds()
|
|
||||||
time_color = hex(0xFF - min(0xFF, int(delta / 500000.)))[2:]
|
|
||||||
i_time_color = hex(min(0xFF, int(delta / 500000.)))[2:]
|
|
||||||
if len(time_color) == 1:
|
|
||||||
time_color = "0" + time_color
|
|
||||||
if len(i_time_color) == 1:
|
|
||||||
i_time_color = "0" + i_time_color
|
|
||||||
return Color("#" + time_color + "AA" + i_time_color)
|
|
||||||
|
|
||||||
|
|
||||||
def glue(ways: List[OSMWay]) -> List[List[OSMNode]]:
|
def glue(ways: List[OSMWay]) -> List[List[OSMNode]]:
|
||||||
|
@ -224,13 +208,13 @@ class Constructor:
|
||||||
Röntgen node and way constructor.
|
Röntgen node and way constructor.
|
||||||
"""
|
"""
|
||||||
def __init__(
|
def __init__(
|
||||||
self, check_level, mode: str, seed: str, map_, flinger: Flinger,
|
self, check_level, mode: str, seed: str, map_: Map,
|
||||||
scheme: Scheme):
|
flinger: Flinger, scheme: Scheme):
|
||||||
|
|
||||||
self.check_level = check_level
|
self.check_level = check_level
|
||||||
self.mode: str = mode
|
self.mode: str = mode
|
||||||
self.seed: str = seed
|
self.seed: str = seed
|
||||||
self.map_ = map_
|
self.map_: Map = map_
|
||||||
self.flinger: Flinger = flinger
|
self.flinger: Flinger = flinger
|
||||||
self.scheme: Scheme = scheme
|
self.scheme: Scheme = scheme
|
||||||
|
|
||||||
|
@ -300,7 +284,7 @@ class Constructor:
|
||||||
if self.mode == "time":
|
if self.mode == "time":
|
||||||
if not way:
|
if not way:
|
||||||
return
|
return
|
||||||
time_color = get_time_color(way.timestamp)
|
time_color = get_time_color(way.timestamp, self.map_.time)
|
||||||
self.ways.append(
|
self.ways.append(
|
||||||
Way(inners, outers,
|
Way(inners, outers,
|
||||||
{"fill": "none", "stroke": time_color.hex,
|
{"fill": "none", "stroke": time_color.hex,
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
"""
|
"""
|
||||||
Author: Sergey Vartanov (me@enzet.ru)
|
Author: Sergey Vartanov (me@enzet.ru)
|
||||||
|
|
||||||
|
Geo projection.
|
||||||
"""
|
"""
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
from roentgen.util import MinMax
|
from roentgen.util import MinMax
|
||||||
|
|
||||||
|
|
||||||
EQUATOR_LENGTH: float = 40_075_017
|
EQUATOR_LENGTH: float = 40_075_017 # (in meters)
|
||||||
|
|
||||||
|
|
||||||
def pseudo_mercator(coordinates: np.array) -> np.array:
|
def pseudo_mercator(coordinates: np.array) -> np.array:
|
||||||
|
@ -51,6 +53,8 @@ class Flinger:
|
||||||
|
|
||||||
def fling(self, coordinates: np.array) -> np.array:
|
def fling(self, coordinates: np.array) -> np.array:
|
||||||
"""
|
"""
|
||||||
|
Convert geo coordinates into SVG position points.
|
||||||
|
|
||||||
:param coordinates: vector to fling
|
:param coordinates: vector to fling
|
||||||
"""
|
"""
|
||||||
result: np.array = self.ratio * (
|
result: np.array = self.ratio * (
|
||||||
|
@ -63,5 +67,10 @@ class Flinger:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def get_scale(self, coordinates: np.array) -> float:
|
def get_scale(self, coordinates: np.array) -> float:
|
||||||
|
"""
|
||||||
|
Return pixels per meter ratio for the given geo coordinates.
|
||||||
|
|
||||||
|
:param coordinates: geo coordinates
|
||||||
|
"""
|
||||||
scale_factor = 1 / np.cos(coordinates[0] / 180 * np.pi)
|
scale_factor = 1 / np.cos(coordinates[0] / 180 * np.pi)
|
||||||
return self.pixels_per_meter * scale_factor
|
return self.pixels_per_meter * scale_factor
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from typing import Any, List
|
||||||
|
|
||||||
from colour import Color
|
from colour import Color
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,8 +25,12 @@ class MinMax:
|
||||||
return self.max_ - self.min_
|
return self.max_ - self.min_
|
||||||
|
|
||||||
def center(self):
|
def center(self):
|
||||||
|
"""
|
||||||
|
Get middle point between minimum and maximum.
|
||||||
|
"""
|
||||||
return (self.min_ + self.max_) / 2
|
return (self.min_ + self.max_) / 2
|
||||||
|
|
||||||
|
|
||||||
def is_bright(color: Color) -> bool:
|
def is_bright(color: Color) -> bool:
|
||||||
"""
|
"""
|
||||||
Is color bright enough to have black outline instead of white.
|
Is color bright enough to have black outline instead of white.
|
||||||
|
@ -33,3 +39,26 @@ def is_bright(color: Color) -> bool:
|
||||||
0.2126 * color.red * 256 +
|
0.2126 * color.red * 256 +
|
||||||
0.7152 * color.green * 256 +
|
0.7152 * color.green * 256 +
|
||||||
0.0722 * color.blue * 256 > 200)
|
0.0722 * color.blue * 256 > 200)
|
||||||
|
|
||||||
|
|
||||||
|
def get_gradient_color(value: Any, bounds: MinMax, colors: List[Color]):
|
||||||
|
"""
|
||||||
|
Get color from the color scale for the value.
|
||||||
|
|
||||||
|
:param value: given value (should be in bounds)
|
||||||
|
:param bounds: maximum and minimum values
|
||||||
|
:param colors: color scale
|
||||||
|
"""
|
||||||
|
color_length: int = len(colors) - 1
|
||||||
|
scale = colors + [Color("black")]
|
||||||
|
|
||||||
|
coefficient: float = (
|
||||||
|
0 if bounds.max_ == bounds.min_ else
|
||||||
|
(value - bounds.min_) / (bounds.max_ - bounds.min_))
|
||||||
|
coefficient = min(1.0, max(0.0, coefficient))
|
||||||
|
m: int = int(coefficient * color_length)
|
||||||
|
color_coefficient = (coefficient - m / color_length) * color_length
|
||||||
|
|
||||||
|
return Color(rgb=[
|
||||||
|
scale[m].rgb[i] + color_coefficient *
|
||||||
|
(scale[m + 1].rgb[i] - scale[m].rgb[i]) for i in range(3)])
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue