diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..207da90 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +./geostructures/__pycache__ +./geostructures/utils/__pycache__ +./tests/__pycache__ +__pycache__/ +*.pyc \ No newline at end of file diff --git a/geostructures/__pycache__/__init__.cpython-313.pyc b/geostructures/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..15439a2 Binary files /dev/null and b/geostructures/__pycache__/__init__.cpython-313.pyc differ diff --git a/geostructures/__pycache__/_base.cpython-313.pyc b/geostructures/__pycache__/_base.cpython-313.pyc new file mode 100644 index 0000000..85d1865 Binary files /dev/null and b/geostructures/__pycache__/_base.cpython-313.pyc differ diff --git a/geostructures/__pycache__/_const.cpython-313.pyc b/geostructures/__pycache__/_const.cpython-313.pyc new file mode 100644 index 0000000..5fba843 Binary files /dev/null and b/geostructures/__pycache__/_const.cpython-313.pyc differ diff --git a/geostructures/__pycache__/_geometry.cpython-313.pyc b/geostructures/__pycache__/_geometry.cpython-313.pyc new file mode 100644 index 0000000..a24d205 Binary files /dev/null and b/geostructures/__pycache__/_geometry.cpython-313.pyc differ diff --git a/geostructures/__pycache__/_version.cpython-313.pyc b/geostructures/__pycache__/_version.cpython-313.pyc new file mode 100644 index 0000000..8676957 Binary files /dev/null and b/geostructures/__pycache__/_version.cpython-313.pyc differ diff --git a/geostructures/__pycache__/calc.cpython-313.pyc b/geostructures/__pycache__/calc.cpython-313.pyc new file mode 100644 index 0000000..c2dcc6f Binary files /dev/null and b/geostructures/__pycache__/calc.cpython-313.pyc differ diff --git a/geostructures/__pycache__/collections.cpython-313.pyc b/geostructures/__pycache__/collections.cpython-313.pyc new file mode 100644 index 0000000..765d981 Binary files /dev/null and b/geostructures/__pycache__/collections.cpython-313.pyc differ diff --git a/geostructures/__pycache__/coordinates.cpython-313.pyc b/geostructures/__pycache__/coordinates.cpython-313.pyc new file mode 100644 index 0000000..3a1a2c6 Binary files /dev/null and b/geostructures/__pycache__/coordinates.cpython-313.pyc differ diff --git a/geostructures/__pycache__/geohash.cpython-313.pyc b/geostructures/__pycache__/geohash.cpython-313.pyc new file mode 100644 index 0000000..200d494 Binary files /dev/null and b/geostructures/__pycache__/geohash.cpython-313.pyc differ diff --git a/geostructures/__pycache__/multistructures.cpython-313.pyc b/geostructures/__pycache__/multistructures.cpython-313.pyc new file mode 100644 index 0000000..8891a25 Binary files /dev/null and b/geostructures/__pycache__/multistructures.cpython-313.pyc differ diff --git a/geostructures/__pycache__/structures.cpython-313.pyc b/geostructures/__pycache__/structures.cpython-313.pyc new file mode 100644 index 0000000..0384387 Binary files /dev/null and b/geostructures/__pycache__/structures.cpython-313.pyc differ diff --git a/geostructures/__pycache__/time.cpython-313.pyc b/geostructures/__pycache__/time.cpython-313.pyc new file mode 100644 index 0000000..023f116 Binary files /dev/null and b/geostructures/__pycache__/time.cpython-313.pyc differ diff --git a/geostructures/__pycache__/typing.cpython-313.pyc b/geostructures/__pycache__/typing.cpython-313.pyc new file mode 100644 index 0000000..a126fb9 Binary files /dev/null and b/geostructures/__pycache__/typing.cpython-313.pyc differ diff --git a/geostructures/collections.py b/geostructures/collections.py index f3a4f99..d2352c8 100644 --- a/geostructures/collections.py +++ b/geostructures/collections.py @@ -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) diff --git a/geostructures/utils/__pycache__/__init__.cpython-313.pyc b/geostructures/utils/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..5029bee Binary files /dev/null and b/geostructures/utils/__pycache__/__init__.cpython-313.pyc differ diff --git a/geostructures/utils/__pycache__/agg_functions.cpython-313.pyc b/geostructures/utils/__pycache__/agg_functions.cpython-313.pyc new file mode 100644 index 0000000..64d2cbb Binary files /dev/null and b/geostructures/utils/__pycache__/agg_functions.cpython-313.pyc differ diff --git a/geostructures/utils/__pycache__/conditional_imports.cpython-313.pyc b/geostructures/utils/__pycache__/conditional_imports.cpython-313.pyc new file mode 100644 index 0000000..f0e00b5 Binary files /dev/null and b/geostructures/utils/__pycache__/conditional_imports.cpython-313.pyc differ diff --git a/geostructures/utils/__pycache__/functions.cpython-313.pyc b/geostructures/utils/__pycache__/functions.cpython-313.pyc new file mode 100644 index 0000000..426e02b Binary files /dev/null and b/geostructures/utils/__pycache__/functions.cpython-313.pyc differ diff --git a/geostructures/utils/__pycache__/logging.cpython-313.pyc b/geostructures/utils/__pycache__/logging.cpython-313.pyc new file mode 100644 index 0000000..1be2057 Binary files /dev/null and b/geostructures/utils/__pycache__/logging.cpython-313.pyc differ diff --git a/tests/__pycache__/__init__.cpython-313.pyc b/tests/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000..2f8028a Binary files /dev/null and b/tests/__pycache__/__init__.cpython-313.pyc differ diff --git a/tests/__pycache__/test_geohash.cpython-313-pytest-8.3.4.pyc b/tests/__pycache__/test_geohash.cpython-313-pytest-8.3.4.pyc new file mode 100644 index 0000000..0241ad2 Binary files /dev/null and b/tests/__pycache__/test_geohash.cpython-313-pytest-8.3.4.pyc differ diff --git a/tests/__pycache__/test_structures.cpython-313-pytest-8.3.4.pyc b/tests/__pycache__/test_structures.cpython-313-pytest-8.3.4.pyc new file mode 100644 index 0000000..7922555 Binary files /dev/null and b/tests/__pycache__/test_structures.cpython-313-pytest-8.3.4.pyc differ diff --git a/tests/__pycache__/test_structures.cpython-313.pyc b/tests/__pycache__/test_structures.cpython-313.pyc new file mode 100644 index 0000000..9656fb9 Binary files /dev/null and b/tests/__pycache__/test_structures.cpython-313.pyc differ diff --git a/tests/test_collections.py b/tests/test_collections.py index e6e7b00..7c1791f 100644 --- a/tests/test_collections.py +++ b/tests/test_collections.py @@ -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()