Skip to content

Commit

Permalink
Merge pull request #81 from traveltime-dev/add-time-map-wkt
Browse files Browse the repository at this point in the history
Add support for WKT time-map response
  • Loading branch information
arnasbr authored Nov 13, 2023
2 parents 63f3860 + 7ca1d1a commit 9b0f4f1
Show file tree
Hide file tree
Showing 19 changed files with 893 additions and 1 deletion.
62 changes: 62 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,68 @@ async def main():
print(results)


asyncio.run(main())
```

### WKT response

#### Returns:

* results: TimeMapWKTResponse - TimeMapWktResponse with isochrone shapes.

#### Example:

```python
import asyncio
from datetime import datetime

from traveltimepy import Driving, Coordinates, TravelTimeSdk


async def main():
sdk = TravelTimeSdk("YOUR_APP_ID", "YOUR_APP_KEY")

response = await sdk.time_map_wkt_async(
coordinates=[Coordinates(lat=51.507609, lng=-0.128315), Coordinates(lat=51.517609, lng=-0.138315)],
arrival_time=datetime.now(),
transportation=Driving()
)
response.pretty_print() # for a custom formatted response

print(response) # default Python print


asyncio.run(main())
```

### WKT_NO_HOLES response

#### Returns:

* results: TimeMapWKTResponse - TimeMapWktResponse with isochrone shapes (no holes).

#### Example:

```python
import asyncio
from datetime import datetime

from traveltimepy import Driving, Coordinates, TravelTimeSdk


async def main():
sdk = TravelTimeSdk("YOUR_APP_ID", "YOUR_APP_KEY")

response = await sdk.time_map_wkt_no_holes_async(
coordinates=[Coordinates(lat=51.507609, lng=-0.128315), Coordinates(lat=51.517609, lng=-0.138315)],
arrival_time=datetime.now(),
transportation=Driving()
)
response.pretty_print() # for a custom formatted response

print(response) # default Python print


asyncio.run(main())
```

Expand Down
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ install_requires =
pydantic>=1.7.4,<2.0
typing-extensions
geojson-pydantic
shapely
dacite
certifi >= 2021.5.30
aiohttp
Expand Down
64 changes: 64 additions & 0 deletions tests/time_map_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,38 @@ async def test_departures_geojson(sdk):
assert len(results) == 2


@pytest.mark.asyncio
async def test_departures_wkt(sdk):
response = await sdk.time_map_wkt_async(
coordinates=[
Coordinates(lat=51.507609, lng=-0.128315),
Coordinates(lat=51.517609, lng=-0.138315),
],
departure_time=datetime.now(),
travel_time=900,
transportation=Driving(),
search_range=Range(enabled=True, width=1800),
level_of_detail=LevelOfDetail(scale_type="simple", level="lowest"),
)
assert len(response.results) == 2


@pytest.mark.asyncio
async def test_departures_wkt_no_holes(sdk):
response = await sdk.time_map_wkt_no_holes_async(
coordinates=[
Coordinates(lat=51.507609, lng=-0.128315),
Coordinates(lat=51.517609, lng=-0.138315),
],
departure_time=datetime.now(),
travel_time=900,
transportation=Driving(),
search_range=Range(enabled=True, width=1800),
level_of_detail=LevelOfDetail(scale_type="simple", level="lowest"),
)
assert len(response.results) == 2


@pytest.mark.asyncio
async def test_arrivals(sdk):
results = await sdk.time_map_async(
Expand Down Expand Up @@ -68,6 +100,38 @@ async def test_arrivals_geojson(sdk):
assert len(results) == 2


@pytest.mark.asyncio
async def test_arrivals_wkt(sdk):
response = await sdk.time_map_wkt_async(
coordinates=[
Coordinates(lat=51.507609, lng=-0.128315),
Coordinates(lat=51.517609, lng=-0.138315),
],
arrival_time=datetime.now(),
travel_time=900,
transportation=Driving(),
search_range=Range(enabled=True, width=1800),
level_of_detail=LevelOfDetail(scale_type="simple", level="lowest"),
)
assert len(response.results) == 2


@pytest.mark.asyncio
async def test_arrivals_wkt_no_holes(sdk):
response = await sdk.time_map_wkt_no_holes_async(
coordinates=[
Coordinates(lat=51.507609, lng=-0.128315),
Coordinates(lat=51.517609, lng=-0.138315),
],
arrival_time=datetime.now(),
travel_time=900,
transportation=Driving(),
search_range=Range(enabled=True, width=1800),
level_of_detail=LevelOfDetail(scale_type="simple", level="lowest"),
)
assert len(response.results) == 2


@pytest.mark.asyncio
async def test_union_departures(sdk):
result = await sdk.union_async(
Expand Down
140 changes: 140 additions & 0 deletions tests/wkt_parsing_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import pytest # noqa

from traveltimepy import Coordinates
from traveltimepy.wkt import (
parse_wkt,
PointModel,
LineStringModel,
PolygonModel,
MultiPointModel,
MultiLineStringModel,
MultiPolygonModel,
)
from traveltimepy.wkt.error import (
InvalidWKTStringError,
NullGeometryError,
InvalidGeometryTypeError,
)

point_wkt = "POINT (0 0)"
line_wkt = "LINESTRING(0 0, 1 1, 2 2)"
poly_wkt = "POLYGON((0 0, 0 2, 2 2, 2 0, 0 0))"
mp_wkt = "MULTIPOINT(0 0, 1 1)"
mls_wkt = "MULTILINESTRING((0 0, 1 1), (2 2, 3 3))"
mpoly_wkt = "MULTIPOLYGON(((0 0, 0 2, 2 2, 2 0, 0 0)))"


def test_parse_point():
parsed = parse_wkt(point_wkt)
assert parsed == PointModel(coordinates=Coordinates(lat=0, lng=0))


def test_parse_line_string():
parsed = parse_wkt(line_wkt)
assert parsed == LineStringModel(
coordinates=[
PointModel(coordinates=Coordinates(lat=0.0, lng=0.0)),
PointModel(coordinates=Coordinates(lat=1.0, lng=1.0)),
PointModel(coordinates=Coordinates(lat=2.0, lng=2.0)),
],
)


def test_parse_polygon():
parsed = parse_wkt(poly_wkt)
assert parsed == PolygonModel(
exterior=LineStringModel(
coordinates=[
PointModel(coordinates=Coordinates(lat=0.0, lng=0.0)),
PointModel(coordinates=Coordinates(lat=0.0, lng=2.0)),
PointModel(coordinates=Coordinates(lat=2.0, lng=2.0)),
PointModel(coordinates=Coordinates(lat=2.0, lng=0.0)),
PointModel(coordinates=Coordinates(lat=0.0, lng=0.0)),
],
),
interiors=[],
)


def test_parse_multi_point():
parsed = parse_wkt(mp_wkt)
assert parsed == MultiPointModel(
coordinates=[
PointModel(coordinates=Coordinates(lat=0.0, lng=0.0)),
PointModel(coordinates=Coordinates(lat=1.0, lng=1.0)),
],
)


def test_parse_multi_line_string():
parsed = parse_wkt(mls_wkt)
assert parsed == MultiLineStringModel(
coordinates=[
LineStringModel(
coordinates=[
PointModel(
coordinates=Coordinates(lat=0.0, lng=0.0),
),
PointModel(
coordinates=Coordinates(lat=1.0, lng=1.0),
),
],
),
LineStringModel(
coordinates=[
PointModel(
coordinates=Coordinates(lat=2.0, lng=2.0),
),
PointModel(
coordinates=Coordinates(lat=3.0, lng=3.0),
),
],
),
],
)


def test_parse_multi_polygon():
parsed = parse_wkt(mpoly_wkt)
assert parsed == MultiPolygonModel(
coordinates=[
PolygonModel(
exterior=LineStringModel(
coordinates=[
PointModel(
coordinates=Coordinates(lat=0.0, lng=0.0),
),
PointModel(
coordinates=Coordinates(lat=0.0, lng=2.0),
),
PointModel(
coordinates=Coordinates(lat=2.0, lng=2.0),
),
PointModel(
coordinates=Coordinates(lat=2.0, lng=0.0),
),
PointModel(
coordinates=Coordinates(lat=0.0, lng=0.0),
),
],
),
interiors=[],
)
],
)


def test_invalid_wkt_string():
with pytest.raises(InvalidWKTStringError):
parse_wkt("INVALIDWKTSTRING")


def test_null_geometry():
with pytest.raises(NullGeometryError):
parse_wkt("POINT EMPTY")


def test_unsupported_geometry_type():
unsupported_wkt = "GEOMETRYCOLLECTION(POINT(2 3),LINESTRING(2 3, 3 4))"
with pytest.raises(InvalidGeometryTypeError):
parse_wkt(unsupported_wkt)
68 changes: 68 additions & 0 deletions tests/wkt_pretty_print_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import pytest # noqa

from traveltimepy import Coordinates
from traveltimepy.wkt import (
PointModel,
LineStringModel,
PolygonModel,
MultiPointModel,
MultiLineStringModel,
MultiPolygonModel,
)

# Updated mock data for LineStringModel with two distinct points
mock_point1 = PointModel(coordinates=Coordinates(lat=10, lng=20))
mock_point2 = PointModel(coordinates=Coordinates(lat=30, lng=40))
mock_linestring = LineStringModel(coordinates=[mock_point1, mock_point2])
mock_polygon = PolygonModel(exterior=mock_linestring, interiors=[mock_linestring])
mock_multipoint = MultiPointModel(coordinates=[mock_point1, mock_point2])
mock_multilinestring = MultiLineStringModel(
coordinates=[mock_linestring, mock_linestring]
)
mock_multipolygon = MultiPolygonModel(coordinates=[mock_polygon])


# Test functions
def test_pointmodel_pretty_print(capsys):
mock_point1.pretty_print()
captured = capsys.readouterr()
assert captured.out == "POINT: 10.0, 20.0\n"


def test_linestringmodel_pretty_print(capsys):
mock_linestring.pretty_print()
captured = capsys.readouterr()
assert captured.out == "LINE STRING:\n\tPOINT: 10.0, 20.0\n\tPOINT: 30.0, 40.0\n"


def test_polygonmodel_pretty_print(capsys):
mock_polygon.pretty_print()
captured = capsys.readouterr()
assert captured.out == (
"POLYGON:\n\tEXTERIOR:\n\t\tLINE STRING:\n\t\t\tPOINT: 10.0, 20.0\n\t\t\tPOINT: 30.0, 40.0\n"
"\tINTERIORS:\n\t\tLINE STRING:\n\t\t\tPOINT: 10.0, 20.0\n\t\t\tPOINT: 30.0, 40.0\n"
)


def test_multipointmodel_pretty_print(capsys):
mock_multipoint.pretty_print()
captured = capsys.readouterr()
assert captured.out == "MULTIPOINT:\n\tPOINT: 10.0, 20.0\n\tPOINT: 30.0, 40.0\n"


def test_multilinestringmodel_pretty_print(capsys):
mock_multilinestring.pretty_print()
captured = capsys.readouterr()
assert captured.out == (
"MULTILINESTRING:\n\tLINE STRING:\n\t\tPOINT: 10.0, 20.0\n\t\tPOINT: 30.0, 40.0\n"
"\tLINE STRING:\n\t\tPOINT: 10.0, 20.0\n\t\tPOINT: 30.0, 40.0\n"
)


def test_multipolygonmodel_pretty_print(capsys):
mock_multipolygon.pretty_print()
captured = capsys.readouterr()
assert captured.out == (
"MULTIPOLYGON:\n\tPOLYGON:\n\t\tEXTERIOR:\n\t\t\tLINE STRING:\n\t\t\t\tPOINT: 10.0, 20.0\n\t\t\t\tPOINT: 30.0, 40.0\n"
"\t\tINTERIORS:\n\t\t\tLINE STRING:\n\t\t\t\tPOINT: 10.0, 20.0\n\t\t\t\tPOINT: 30.0, 40.0\n"
)
20 changes: 20 additions & 0 deletions tests/wkt_validate_objects_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import pytest # noqa

from traveltimepy import Coordinates
from traveltimepy.wkt import LineStringModel, PointModel

mock_point1 = PointModel(coordinates=Coordinates(lat=10, lng=20))
mock_point2 = PointModel(coordinates=Coordinates(lat=30, lng=40))


def test_linestring_minimum_coordinates_valid():
# Create LineStringModel with two valid coordinates
linestring = LineStringModel(coordinates=[mock_point1, mock_point2])
assert len(linestring.coordinates) == 2


def test_linestring_minimum_coordinates_invalid():
# Test LineStringModel with less than two coordinates
with pytest.raises(ValueError) as excinfo:
LineStringModel(coordinates=[mock_point1])
assert "LineString must have at least 2 coordinates." in str(excinfo.value)
2 changes: 2 additions & 0 deletions traveltimepy/accept_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

class AcceptType(Enum):
JSON = "application/json"
WKT = "application/vnd.wkt+json"
WKT_NO_HOLES = "application/vnd.wkt-no-holes+json"
BOUNDING_BOXES_JSON = "application/vnd.bounding-boxes+json"
GEO_JSON = "application/geo+json"
OCTET_STREAM = "application/octet-stream"
Loading

0 comments on commit 9b0f4f1

Please sign in to comment.