Skip to content

Commit

Permalink
Issue #11: fix time mode.
Browse files Browse the repository at this point in the history
Add gradient color detection from the color scale for the given value.
  • Loading branch information
enzet committed Sep 20, 2020
1 parent 604fd0d commit aee36d0
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 37 deletions.
56 changes: 20 additions & 36 deletions roentgen/constructor.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,14 @@
from roentgen import ui
from roentgen.extract_icon import DEFAULT_SMALL_SHAPE_ID
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.util import MinMax
from roentgen.util import MinMax, get_gradient_color

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:
Expand Down Expand Up @@ -47,23 +50,23 @@ def make_counter_clockwise(polygon: List[OSMNode]) -> List[OSMNode]:
return list(reversed(polygon))


class Node:
class Node(Tagged):
"""
Node in Röntgen terms.
"""
def __init__(
self, icon_set: IconSet, tags: Dict[str, str],
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

self.icon_set: IconSet = icon_set
self.tags = tags
self.tags: Dict[str, str] = tags
self.point: np.array = point
self.coordinates: np.array = coordinates
self.priority = priority
self.layer = 0
self.is_for_node = is_for_node
self.priority: float = priority
self.layer: float = 0
self.is_for_node: bool = is_for_node

def get_tag(self, key: str):
if key in self.tags:
Expand Down Expand Up @@ -137,33 +140,14 @@ def get_user_color(text: str, seed: str) -> Color:
"""
if text == "":
return Color("black")
rgb = 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:
return Color("#" + sha256((seed + text).encode("utf-8")).hexdigest()[-6:])


def get_time_color(time: Optional[datetime], boundaries: MinMax) -> Color:
"""
Generate color based on time.
"""
if time is None:
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)
return get_gradient_color(time, boundaries, TIME_COLOR_SCALE)


def glue(ways: List[OSMWay]) -> List[List[OSMNode]]:
Expand Down Expand Up @@ -224,13 +208,13 @@ class Constructor:
Röntgen node and way constructor.
"""
def __init__(
self, check_level, mode: str, seed: str, map_, flinger: Flinger,
scheme: Scheme):
self, check_level, mode: str, seed: str, map_: Map,
flinger: Flinger, scheme: Scheme):

self.check_level = check_level
self.mode: str = mode
self.seed: str = seed
self.map_ = map_
self.map_: Map = map_
self.flinger: Flinger = flinger
self.scheme: Scheme = scheme

Expand Down Expand Up @@ -300,7 +284,7 @@ def construct_way(
if self.mode == "time":
if not way:
return
time_color = get_time_color(way.timestamp)
time_color = get_time_color(way.timestamp, self.map_.time)
self.ways.append(
Way(inners, outers,
{"fill": "none", "stroke": time_color.hex,
Expand Down
11 changes: 10 additions & 1 deletion roentgen/flinger.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
"""
Author: Sergey Vartanov (me@enzet.ru)
Geo projection.
"""
import numpy as np

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:
Expand Down Expand Up @@ -51,6 +53,8 @@ def __init__(self, geo_boundaries: MinMax, scale: float = 1000):

def fling(self, coordinates: np.array) -> np.array:
"""
Convert geo coordinates into SVG position points.
:param coordinates: vector to fling
"""
result: np.array = self.ratio * (
Expand All @@ -63,5 +67,10 @@ def fling(self, coordinates: np.array) -> np.array:
return result

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)
return self.pixels_per_meter * scale_factor
29 changes: 29 additions & 0 deletions roentgen/util.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from typing import Any, List

from colour import Color


Expand All @@ -23,8 +25,12 @@ def delta(self):
return self.max_ - self.min_

def center(self):
"""
Get middle point between minimum and maximum.
"""
return (self.min_ + self.max_) / 2


def is_bright(color: Color) -> bool:
"""
Is color bright enough to have black outline instead of white.
Expand All @@ -33,3 +39,26 @@ def is_bright(color: Color) -> bool:
0.2126 * color.red * 256 +
0.7152 * color.green * 256 +
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)])

0 comments on commit aee36d0

Please sign in to comment.