Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

13 14 create load map #22

Merged
merged 20 commits into from
Oct 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 24 additions & 5 deletions src/controllers/navigator/navigator.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from enum import Enum
from typing import Generic, List, Optional, Tuple, TypeVar
from typing import Dict, Generic, List, Optional, Tuple, TypeVar

from PyQt6.QtWidgets import QLabel, QStackedWidget, QVBoxLayout, QWidget
from reactivex import Observable
Expand All @@ -14,12 +14,30 @@


class Navigator(Generic[RouteName]):
__navigators: Dict[str, "Navigator"] = {}

__history_stack: BehaviorSubject[List[RouteName]]
__routes: List[Route]
__not_found_widget: Optional[QWidget]
__config: NavigatorConfig

def __init__(
def __init__(self) -> None:
self.__history_stack = BehaviorSubject([])
self.__routes = []
self.__not_found_widget = None
self.__config = NavigatorConfig()

@staticmethod
def get_navigator(name: str) -> "Navigator":
navigator = Navigator.__navigators.get(name)

if navigator is None:
navigator = Navigator()
Navigator.__navigators[name] = navigator

return navigator

def init(
self,
routes: List[Route],
default_name: RouteName,
Expand All @@ -36,11 +54,10 @@ def __init__(
Returns:
None
"""

self.__routes = routes
self.__history_stack = BehaviorSubject([default_name])
self.__not_found_widget = not_found_widget
self.__config = config
self.__history_stack.on_next([default_name])

@property
def history_stack(self) -> Observable[List[str]]:
Expand Down Expand Up @@ -146,7 +163,9 @@ def on_route_change(res: Tuple[int, Route]) -> None:
return widget

def __match_name(self, route_name: str, search_name: str) -> bool:
return route_name == search_name
return route_name == search_name or (
route_name.value == search_name if isinstance(route_name, Enum) else False
)

def __resolve_route(self, name: str) -> Tuple[int, Route]:
for i, route in enumerate(self.__routes):
Expand Down
3 changes: 2 additions & 1 deletion src/controllers/navigator/tests/test_navigator.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ class TestNavigator:

@pytest.fixture(autouse=True)
def init_navigator(self) -> Navigator[RoutesTest]:
self.navigator = Navigator[RoutesTest](
self.navigator = Navigator[RoutesTest]()
self.navigator.init(
routes=ROUTES,
default_name=DEFAULT_ROUTE_NAME,
not_found_widget=NotFoundWidget,
Expand Down
3 changes: 3 additions & 0 deletions src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
from PyQt6.QtWidgets import QApplication

from src.views.window import MainWindow
from views.modules.navigators import init_navigators

app = QApplication(sys.argv)

init_navigators()

window = MainWindow()
window.show()

Expand Down
7 changes: 7 additions & 0 deletions src/models/map/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from src.models.map.errors import *
from src.models.map.intersection import Intersection
from src.models.map.map import Map
from src.models.map.map_size import MapSize
from src.models.map.marker import Marker
from src.models.map.position import Position
from src.models.map.segment import Segment
2 changes: 2 additions & 0 deletions src/models/map/errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class MapLoadingError(Exception):
pass
29 changes: 29 additions & 0 deletions src/models/map/intersection.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from dataclasses import dataclass
from xml.etree.ElementTree import Element

from src.models.map.position import Position


@dataclass
class Intersection(Position):
"""Represent an intersection on the map."""

id: int
"""ID of the intersection.
"""

@staticmethod
def from_element(element: Element) -> "Intersection":
"""Creates an intersection instance from an XML element.

Args:
element (Element): XML element

Returns:
Intersection: Intersection instance
"""
return Intersection(
id=int(element.attrib["id"]),
latitude=float(element.attrib["latitude"]),
longitude=float(element.attrib["longitude"]),
)
14 changes: 14 additions & 0 deletions src/models/map/map.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from dataclasses import dataclass
from typing import Dict, List

from src.models.map.intersection import Intersection
from src.models.map.map_size import MapSize
from src.models.map.segment import Segment


@dataclass
class Map:
intersections: Dict[int, Intersection]
segments: List[Segment]
warehouse: Intersection
size: MapSize
58 changes: 58 additions & 0 deletions src/models/map/map_size.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import sys
from dataclasses import dataclass
from typing import Type, TypeVar

from src.models.map.position import Position

T = TypeVar("T", bound="MapSize")


@dataclass
class MapSize:
__min: Position
__max: Position
area: float

def __init__(self, min: Position, max: Position) -> None:
self.__min = min
self.__max = max
self.area = self.__calculate_area()

@classmethod
def inverse_max_size(cls: Type[T]) -> T:
"""Creates a MapSize instance with the inverted maximum possible size. (min = System MAX, max = System MIN)"""
return cls(
Position(sys.maxsize, sys.maxsize),
Position(sys.maxsize * -1, sys.maxsize * -1),
)

@property
def min(self) -> Position:
return self.__min

@min.setter
def min(self, value: Position) -> None:
self.__min = value
self.area = self.__calculate_area()

@property
def max(self) -> Position:
return self.__max

@max.setter
def max(self, value: Position) -> None:
self.__max = value
self.area = self.__calculate_area()

@property
def width(self) -> float:
return self.__max.longitude - self.__min.longitude

@property
def height(self) -> float:
return self.__max.latitude - self.__min.latitude

def __calculate_area(self) -> float:
return (self.__max.latitude - self.__min.latitude) * (
self.__max.longitude - self.__min.longitude
)
8 changes: 8 additions & 0 deletions src/models/map/marker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from dataclasses import dataclass

from src.models.map.position import Position


@dataclass
class Marker:
position: Position
60 changes: 60 additions & 0 deletions src/models/map/position.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from dataclasses import dataclass
from typing import List


@dataclass
class Position:
longitude: float
"""Longitude of the position.
"""
latitude: float
"""Latitude of the position.
"""

@property
def x(self) -> float:
"""Get the value of the X axis. Equivalent the longitude of the position."""
return self.longitude

@x.setter
def x(self, value: float) -> None:
"""Set the value of the X axis. Equivalent the longitude of the position."""
self.longitude = value

@property
def y(self) -> float:
"""Get the value of the Y axis. Equivalent the latitude of the position."""
return self.latitude

@y.setter
def y(self, value: float) -> None:
"""Set the value of the Y axis. Equivalent the latitude of the position."""
self.latitude = value

def max(self, p: "Position", *args: List["Position"]) -> "Position":
"""Get the maximum position between the current position and the given position.

Args:
p (Position): Other position to compare.

Returns:
Position: New position instance with the maximum values.
"""
return Position(
max(self.longitude, p.longitude, *map(lambda p: p.longitude, args)),
max(self.latitude, p.latitude, *map(lambda p: p.latitude, args)),
)

def min(self, p: "Position", *args: List["Position"]) -> "Position":
"""Get the minimum position between the current position and the given position.

Args:
p (Position): Other position to compare.

Returns:
Position: New position instance with the minimum values.
"""
return Position(
min(self.longitude, p.longitude, *map(lambda p: p.longitude, args)),
min(self.latitude, p.latitude, *map(lambda p: p.latitude, args)),
)
47 changes: 47 additions & 0 deletions src/models/map/segment.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from dataclasses import dataclass
from typing import Dict
from xml.etree.ElementTree import Element

from src.models.map.errors import MapLoadingError
from src.models.map.intersection import Intersection


@dataclass
class Segment:
name: str
origin: Intersection
destination: Intersection
length: float

@staticmethod
def from_element(
element: Element, intersections: Dict[int, Intersection]
) -> "Segment":
"""Creates a Segment instance from an XML element.

Args:
element (Element): XML element
intersections (Dict[int, Intersection]): Dictionary of intersections

Returns:
Segment: Segment instance
"""
name = element.attrib["name"]
origin = intersections[int(element.attrib["origin"])]
destination = intersections[int(element.attrib["destination"])]

if origin is None:
raise MapLoadingError(
f"No intersection with ID {element.attrib['origin']} for origin on {element.tag} {name}"
)
if destination is None:
raise MapLoadingError(
f"No intersection with ID {element.attrib['destination']} for destination on {element.tag} {name}"
)

return Segment(
name=name,
origin=origin,
destination=destination,
length=float(element.attrib["length"]),
)
43 changes: 43 additions & 0 deletions src/models/map/tests/test_map_size.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from src.models.map.map_size import MapSize
from src.models.map.position import Position


class TestMapSize:
def test_should_create(self):
assert MapSize(Position(0, 0), Position(0, 0)) is not None

def test_should_create_inverse_max_size(self):
assert MapSize.inverse_max_size() is not None

def test_should_calculate_area(self):
assert MapSize(Position(0, 0), Position(1, 1)).area == 1
assert MapSize(Position(0, 0), Position(2, 2)).area == 4
assert MapSize(Position(0, 0), Position(3, 3)).area == 9

def test_should_get_min(self):
assert MapSize(Position(0, 0), Position(1, 1)).min == Position(0, 0)

def test_should_set_min(self):
map_size = MapSize(Position(0, 0), Position(1, 1))
map_size.min = Position(1, 1)

assert map_size.min == Position(1, 1)

def test_should_get_max(self):
assert MapSize(Position(0, 0), Position(1, 1)).max == Position(1, 1)

def test_should_set_max(self):
map_size = MapSize(Position(0, 0), Position(1, 1))
map_size.max = Position(0, 0)

assert map_size.max == Position(0, 0)

def test_should_get_width(self):
assert MapSize(Position(0, 0), Position(1, 1)).width == 1
assert MapSize(Position(0, 0), Position(2, 2)).width == 2
assert MapSize(Position(0, 0), Position(3, 3)).width == 3

def test_should_get_height(self):
assert MapSize(Position(0, 0), Position(1, 1)).height == 1
assert MapSize(Position(0, 0), Position(2, 2)).height == 2
assert MapSize(Position(0, 0), Position(3, 3)).height == 3
26 changes: 26 additions & 0 deletions src/models/map/tests/test_position.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from src.models.map.position import Position


class TestPosition:
def test_should_create(self):
assert Position(0, 0) is not None

def test_should_get_longitude_equals_x(self):
LONGITUDE = 420

assert Position(LONGITUDE, 0).longitude == LONGITUDE
assert Position(LONGITUDE, 0).x == LONGITUDE

def test_should_get_latitude_equals_y(self):
LATITUDE = 69

assert Position(0, LATITUDE).latitude == LATITUDE
assert Position(0, LATITUDE).y == LATITUDE

def test_should_get_max(self):
assert Position(1, 1).max(Position(2, 2)) == Position(2, 2)
assert Position(1, 1).max(Position(2, 2), Position(3, 3)) == Position(3, 3)

def test_should_get_min(self):
assert Position(1, 1).min(Position(2, 2)) == Position(1, 1)
assert Position(1, 1).min(Position(2, 2), Position(3, 3)) == Position(1, 1)
Loading