Skip to content
Open
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
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
./geostructures/__pycache__
./geostructures/utils/__pycache__
./tests/__pycache__
__pycache__/
*.pyc
Binary file not shown.
Binary file added geostructures/__pycache__/_base.cpython-313.pyc
Binary file not shown.
Binary file added geostructures/__pycache__/_const.cpython-313.pyc
Binary file not shown.
Binary file not shown.
Binary file added geostructures/__pycache__/_version.cpython-313.pyc
Binary file not shown.
Binary file added geostructures/__pycache__/calc.cpython-313.pyc
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added geostructures/__pycache__/geohash.cpython-313.pyc
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added geostructures/__pycache__/time.cpython-313.pyc
Binary file not shown.
Binary file added geostructures/__pycache__/typing.cpython-313.pyc
Binary file not shown.
37 changes: 37 additions & 0 deletions geostructures/collections.py
Original file line number Diff line number Diff line change
Expand Up @@ -829,3 +829,40 @@ def filter_impossible_journeys(self, max_speed: float) -> 'Track':

# Create a new Track with only valid geoshapes
return Track(valid_geoshapes)

def to_GeoLineString(self):
"""
Converts a Track of GeoPoint objects into a Track of GeoLineString objects.

This method takes a sequence of chronologically ordered GeoPoint objects
from the current Track instance and generates a sequence of GeoLineString
objects connecting consecutive GeoPoints. The resulting Track consists
of these GeoLineString segments.

Returns:
Track: A new Track instance where each GeoLineString represents
the connection between two consecutive GeoPoints.

Raises:
TypeError: If the Track contains shapes that are not instances of GeoPoint.

Notes:
- The datetime interval (dt) for each GeoLineString is derived from the
`end` datetime of the starting GeoPoint and the `start` datetime of
the next GeoPoint.
- The properties of the starting GeoPoint are copied to the GeoLineString.
"""
if not all(isinstance(shape, GeoPoint) for shape in self.geoshapes):
raise TypeError('Track must contain only Points.')

lines = []
shapes = self.geoshapes.copy()
while len(shapes)-1 != 0:
point = shapes.pop(0)
next_point = shapes[0]
line = GeoLineString([point.coordinate, next_point.coordinate])
line.dt = TimeInterval(point.dt.end, next_point.dt.start)
line._properties = point.properties.copy()
lines.append(line)

return Track(lines)
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added tests/__pycache__/__init__.cpython-313.pyc
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added tests/__pycache__/test_structures.cpython-313.pyc
Binary file not shown.
61 changes: 61 additions & 0 deletions tests/test_collections.py
Original file line number Diff line number Diff line change
Expand Up @@ -1065,3 +1065,64 @@ def test_track_intersection():

gbox = GeoBox(Coordinate(0., 2.), Coordinate(2., 0.), dt=datetime(2020, 1, 1, 3))
assert len(track1.filter_by_intersection(gbox)) == 1


@pytest.fixture
def geo_point_track():
"""Fixture to create a Track of GeoPoint objects."""
points = [
GeoPoint(
Coordinate(longitude, latitude),
dt=TimeInterval(
start=datetime(2025, 1, 1, hour),
end=datetime(2025, 1, 1, hour + 1)
),
properties={"id": f"point_{longitude}"}
)
for hour, (longitude, latitude) in enumerate([(0, 0), (1, 1), (2, 2)])
]
return Track(points)

@pytest.fixture
def mixed_track():
"""Fixture to create a Track with mixed GeoShapes."""
points = [
GeoPoint(
Coordinate(0, 0),
dt=TimeInterval(
start=datetime(2025, 1, 1, 0),
end=datetime(2025, 1, 1, 1)
),
properties={"id": "point_0"}
),
GeoLineString([Coordinate(0, 0), Coordinate(1, 1)],
dt=TimeInterval(
start=datetime(2025, 1, 1, 2),
end=datetime(2025, 1, 1, 3)
)) # Invalid for this test
]
return Track(points)

def test_to_geoline_string_conversion(geo_point_track):
"""Test successful conversion of GeoPoint Track to GeoLineString Track."""
track = geo_point_track
result = track.to_GeoLineString()

assert len(result.geoshapes) == 2 # Number of segments should be n-1 of GeoPoints
assert all(isinstance(shape, GeoLineString) for shape in result.geoshapes)

# Check segment properties
for i, line in enumerate(result.geoshapes):
assert line.dt.start == track.geoshapes[i].dt.end
assert line.dt.end == track.geoshapes[i + 1].dt.start
assert line._properties == track.geoshapes[i].properties
assert line.vertices == [
track.geoshapes[i].coordinate,
track.geoshapes[i + 1].coordinate,
]

def test_to_geoline_string_type_error(mixed_track):
"""Test that TypeError is raised when non-GeoPoint objects are present."""
track = mixed_track
with pytest.raises(TypeError, match="Track must contain only Points."):
track.to_GeoLineString()