Add Address.x and Address.y coordinates
- the Address.x and Address.y properties use the UTMCoordinate class behind the scenes - x and y are simple coordinates in an x-y plane - the (0, 0) origin is the southwest corner of Address.city.viewport
This commit is contained in:
parent
6f9935072e
commit
6cb4be80f6
5 changed files with 69 additions and 1 deletions
|
@ -134,8 +134,11 @@ per-file-ignores =
|
||||||
WPS115,
|
WPS115,
|
||||||
# Numbers are normal in config files.
|
# Numbers are normal in config files.
|
||||||
WPS432,
|
WPS432,
|
||||||
|
# No real string constant over-use.
|
||||||
src/urban_meal_delivery/db/addresses.py:
|
src/urban_meal_delivery/db/addresses.py:
|
||||||
WPS226,
|
WPS226,
|
||||||
|
src/urban_meal_delivery/db/cities.py:
|
||||||
|
WPS226,
|
||||||
src/urban_meal_delivery/db/orders.py:
|
src/urban_meal_delivery/db/orders.py:
|
||||||
WPS226,
|
WPS226,
|
||||||
tests/*.py:
|
tests/*.py:
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
"""Provide the ORM's `Address` model."""
|
"""Provide the ORM's `Address` model."""
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
from sqlalchemy import orm
|
from sqlalchemy import orm
|
||||||
from sqlalchemy.dialects import postgresql
|
from sqlalchemy.dialects import postgresql
|
||||||
from sqlalchemy.ext import hybrid
|
from sqlalchemy.ext import hybrid
|
||||||
|
|
||||||
from urban_meal_delivery.db import meta
|
from urban_meal_delivery.db import meta
|
||||||
|
from urban_meal_delivery.db import utils
|
||||||
|
|
||||||
|
|
||||||
class Address(meta.Base):
|
class Address(meta.Base):
|
||||||
|
@ -64,6 +67,15 @@ class Address(meta.Base):
|
||||||
foreign_keys='[Order._delivery_address_id]',
|
foreign_keys='[Order._delivery_address_id]',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
||||||
|
"""Create a new address."""
|
||||||
|
# Call SQLAlchemy's default `.__init__()` method.
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
self._utm_coordinates = utils.UTMCoordinate(
|
||||||
|
self.latitude, self.longitude, relative_to=self.city.as_origin,
|
||||||
|
)
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
"""Non-literal text representation."""
|
"""Non-literal text representation."""
|
||||||
return '<{cls}({street} in {city})>'.format(
|
return '<{cls}({street} in {city})>'.format(
|
||||||
|
@ -80,3 +92,13 @@ class Address(meta.Base):
|
||||||
`.is_primary` indicates the first in a group of `Address` objects.
|
`.is_primary` indicates the first in a group of `Address` objects.
|
||||||
"""
|
"""
|
||||||
return self.id == self._primary_id
|
return self.id == self._primary_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def x(self) -> int: # noqa=WPS111
|
||||||
|
"""The `.easting` of the address in meters, relative to the `.city`."""
|
||||||
|
return self._utm_coordinates.x
|
||||||
|
|
||||||
|
@property
|
||||||
|
def y(self) -> int: # noqa=WPS111
|
||||||
|
"""The `.northing` of the address in meters, relative to the `.city`."""
|
||||||
|
return self._utm_coordinates.y
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
"""Provide the ORM's `City` model."""
|
"""Provide the ORM's `City` model."""
|
||||||
|
|
||||||
from typing import Dict
|
from typing import Any, Dict
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
from sqlalchemy import orm
|
from sqlalchemy import orm
|
||||||
from sqlalchemy.dialects import postgresql
|
from sqlalchemy.dialects import postgresql
|
||||||
|
|
||||||
from urban_meal_delivery.db import meta
|
from urban_meal_delivery.db import meta
|
||||||
|
from urban_meal_delivery.db import utils
|
||||||
|
|
||||||
|
|
||||||
class City(meta.Base):
|
class City(meta.Base):
|
||||||
|
@ -45,6 +46,18 @@ class City(meta.Base):
|
||||||
# Relationships
|
# Relationships
|
||||||
addresses = orm.relationship('Address', back_populates='city')
|
addresses = orm.relationship('Address', back_populates='city')
|
||||||
|
|
||||||
|
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
||||||
|
"""Create a new city."""
|
||||||
|
# Call SQLAlchemy's default `.__init__()` method.
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
# Take the "lower left" of the viewport as the origin
|
||||||
|
# of a Cartesian coordinate system.
|
||||||
|
lower_left = self.viewport['southwest']
|
||||||
|
self._origin = utils.UTMCoordinate(
|
||||||
|
lower_left['latitude'], lower_left['longitude'],
|
||||||
|
)
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
"""Non-literal text representation."""
|
"""Non-literal text representation."""
|
||||||
return '<{cls}({name})>'.format(cls=self.__class__.__name__, name=self.name)
|
return '<{cls}({name})>'.format(cls=self.__class__.__name__, name=self.name)
|
||||||
|
@ -81,3 +94,12 @@ class City(meta.Base):
|
||||||
'longitude': self._southwest_longitude,
|
'longitude': self._southwest_longitude,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def as_origin(self) -> utils.UTMCoordinate:
|
||||||
|
"""The lower left corner of the `.viewport` in UTM coordinates.
|
||||||
|
|
||||||
|
This property serves as the `relative_to` argument to the
|
||||||
|
`UTMConstructor` when representing an `Address` in the x-y plane.
|
||||||
|
"""
|
||||||
|
return self._origin
|
||||||
|
|
|
@ -122,3 +122,15 @@ class TestProperties:
|
||||||
result = address.is_primary
|
result = address.is_primary
|
||||||
|
|
||||||
assert result is False
|
assert result is False
|
||||||
|
|
||||||
|
def test_x_is_positive(self, address):
|
||||||
|
"""Test `Address.x` property."""
|
||||||
|
result = address.x
|
||||||
|
|
||||||
|
assert result > 0
|
||||||
|
|
||||||
|
def test_y_is_positive(self, address):
|
||||||
|
"""Test `Address.y` property."""
|
||||||
|
result = address.y
|
||||||
|
|
||||||
|
assert result > 0
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from tests.db.utils import test_coordinates as consts
|
||||||
from urban_meal_delivery import db
|
from urban_meal_delivery import db
|
||||||
|
|
||||||
|
|
||||||
|
@ -63,3 +64,11 @@ class TestProperties:
|
||||||
assert len(result) == 2
|
assert len(result) == 2
|
||||||
assert result['latitude'] == pytest.approx(city_data[f'_{corner}_latitude'])
|
assert result['latitude'] == pytest.approx(city_data[f'_{corner}_latitude'])
|
||||||
assert result['longitude'] == pytest.approx(city_data[f'_{corner}_longitude'])
|
assert result['longitude'] == pytest.approx(city_data[f'_{corner}_longitude'])
|
||||||
|
|
||||||
|
def test_city_in_utm_coordinates(self, city):
|
||||||
|
"""Test `City.as_origin` property."""
|
||||||
|
result = city.as_origin
|
||||||
|
|
||||||
|
assert result.zone == consts.ZONE
|
||||||
|
assert consts.MIN_EASTING < result.easting < consts.MAX_EASTING
|
||||||
|
assert consts.MIN_NORTHING < result.northing < consts.MAX_NORTHING
|
||||||
|
|
Loading…
Reference in a new issue