fastgpx 0.2.2__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. {fastgpx-0.2.2 → fastgpx-0.3.0}/PKG-INFO +1 -1
  2. {fastgpx-0.2.2 → fastgpx-0.3.0}/pyproject.toml +1 -1
  3. {fastgpx-0.2.2 → fastgpx-0.3.0}/src/cpp/fastgpx/fastgpx.cpp +5 -1
  4. {fastgpx-0.2.2 → fastgpx-0.3.0}/src/cpp/fastgpx/fastgpx.hpp +1 -0
  5. {fastgpx-0.2.2 → fastgpx-0.3.0}/src/cpp/python_fastgpx.cpp +7 -1
  6. {fastgpx-0.2.2 → fastgpx-0.3.0}/src/cpp/python_utc_chrono.hpp +13 -1
  7. {fastgpx-0.2.2 → fastgpx-0.3.0}/src/fastgpx/__init__.pyi +1 -1
  8. {fastgpx-0.2.2 → fastgpx-0.3.0}/src/fastgpx/fastgpx/__init__.pyi +8 -0
  9. {fastgpx-0.2.2 → fastgpx-0.3.0}/tests/test_fastgpx.py +0 -1
  10. fastgpx-0.3.0/tests/test_time_bounds.py +73 -0
  11. {fastgpx-0.2.2 → fastgpx-0.3.0}/uv.lock +1 -1
  12. {fastgpx-0.2.2 → fastgpx-0.3.0}/.clang-format +0 -0
  13. {fastgpx-0.2.2 → fastgpx-0.3.0}/.gitignore +0 -0
  14. {fastgpx-0.2.2 → fastgpx-0.3.0}/.python-version +0 -0
  15. {fastgpx-0.2.2 → fastgpx-0.3.0}/CMakeLists.txt +0 -0
  16. {fastgpx-0.2.2 → fastgpx-0.3.0}/CMakeSettings.json +0 -0
  17. {fastgpx-0.2.2 → fastgpx-0.3.0}/Development.md +0 -0
  18. {fastgpx-0.2.2 → fastgpx-0.3.0}/GPX.md +0 -0
  19. {fastgpx-0.2.2 → fastgpx-0.3.0}/LICENSE.md +0 -0
  20. {fastgpx-0.2.2 → fastgpx-0.3.0}/README.md +0 -0
  21. {fastgpx-0.2.2 → fastgpx-0.3.0}/benchmarks/benchmark_gpx.py +0 -0
  22. {fastgpx-0.2.2 → fastgpx-0.3.0}/benchmarks/benchmark_polyline.py +0 -0
  23. {fastgpx-0.2.2 → fastgpx-0.3.0}/benchmarks/gpx_parse.md +0 -0
  24. {fastgpx-0.2.2 → fastgpx-0.3.0}/catch2.py +0 -0
  25. {fastgpx-0.2.2 → fastgpx-0.3.0}/coverage.bat +0 -0
  26. {fastgpx-0.2.2 → fastgpx-0.3.0}/datetime.md +0 -0
  27. {fastgpx-0.2.2 → fastgpx-0.3.0}/docs/Makefile +0 -0
  28. {fastgpx-0.2.2 → fastgpx-0.3.0}/docs/make.bat +0 -0
  29. {fastgpx-0.2.2 → fastgpx-0.3.0}/docs/source/api.rst +0 -0
  30. {fastgpx-0.2.2 → fastgpx-0.3.0}/docs/source/conf.py +0 -0
  31. {fastgpx-0.2.2 → fastgpx-0.3.0}/docs/source/index.rst +0 -0
  32. {fastgpx-0.2.2 → fastgpx-0.3.0}/docs/source/overview.rst +0 -0
  33. {fastgpx-0.2.2 → fastgpx-0.3.0}/profiling/profile_polyline.py +0 -0
  34. {fastgpx-0.2.2 → fastgpx-0.3.0}/pytest.ini +0 -0
  35. {fastgpx-0.2.2 → fastgpx-0.3.0}/src/cpp/CMakeLists.txt +0 -0
  36. {fastgpx-0.2.2 → fastgpx-0.3.0}/src/cpp/app.cpp +0 -0
  37. {fastgpx-0.2.2 → fastgpx-0.3.0}/src/cpp/expected_gpx_data.json +0 -0
  38. {fastgpx-0.2.2 → fastgpx-0.3.0}/src/cpp/fastgpx/datetime.cpp +0 -0
  39. {fastgpx-0.2.2 → fastgpx-0.3.0}/src/cpp/fastgpx/datetime.hpp +0 -0
  40. {fastgpx-0.2.2 → fastgpx-0.3.0}/src/cpp/fastgpx/datetime_test.cpp +0 -0
  41. {fastgpx-0.2.2 → fastgpx-0.3.0}/src/cpp/fastgpx/errors.cpp +0 -0
  42. {fastgpx-0.2.2 → fastgpx-0.3.0}/src/cpp/fastgpx/errors.hpp +0 -0
  43. {fastgpx-0.2.2 → fastgpx-0.3.0}/src/cpp/fastgpx/errors_test.cpp +0 -0
  44. {fastgpx-0.2.2 → fastgpx-0.3.0}/src/cpp/fastgpx/fastgpx_test.cpp +0 -0
  45. {fastgpx-0.2.2 → fastgpx-0.3.0}/src/cpp/fastgpx/filesystem.cpp +0 -0
  46. {fastgpx-0.2.2 → fastgpx-0.3.0}/src/cpp/fastgpx/filesystem.hpp +0 -0
  47. {fastgpx-0.2.2 → fastgpx-0.3.0}/src/cpp/fastgpx/filesystem_test.cpp +0 -0
  48. {fastgpx-0.2.2 → fastgpx-0.3.0}/src/cpp/fastgpx/geom.cpp +0 -0
  49. {fastgpx-0.2.2 → fastgpx-0.3.0}/src/cpp/fastgpx/geom.hpp +0 -0
  50. {fastgpx-0.2.2 → fastgpx-0.3.0}/src/cpp/fastgpx/geom_test.cpp +0 -0
  51. {fastgpx-0.2.2 → fastgpx-0.3.0}/src/cpp/fastgpx/polyline.cpp +0 -0
  52. {fastgpx-0.2.2 → fastgpx-0.3.0}/src/cpp/fastgpx/polyline.hpp +0 -0
  53. {fastgpx-0.2.2 → fastgpx-0.3.0}/src/cpp/fastgpx/test_data.cpp +0 -0
  54. {fastgpx-0.2.2 → fastgpx-0.3.0}/src/cpp/fastgpx/test_data.hpp +0 -0
  55. {fastgpx-0.2.2 → fastgpx-0.3.0}/src/cpp/fastgpx/test_data.json +0 -0
  56. {fastgpx-0.2.2 → fastgpx-0.3.0}/src/cpp/fastgpx/test_data_test.cpp +0 -0
  57. {fastgpx-0.2.2 → fastgpx-0.3.0}/src/fastgpx/__init__.py +0 -0
  58. {fastgpx-0.2.2 → fastgpx-0.3.0}/src/fastgpx/fastgpx/geo.pyi +0 -0
  59. {fastgpx-0.2.2 → fastgpx-0.3.0}/src/fastgpx/fastgpx/polyline.pyi +0 -0
  60. {fastgpx-0.2.2 → fastgpx-0.3.0}/src/fastgpx/py.typed +0 -0
  61. {fastgpx-0.2.2 → fastgpx-0.3.0}/tests/test_bounds.py +0 -0
  62. {fastgpx-0.2.2 → fastgpx-0.3.0}/tests/test_polyline.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastgpx
3
- Version: 0.2.2
3
+ Version: 0.3.0
4
4
  Summary: An experimental Python library for parsing GPX files fast.
5
5
  Keywords: gpx,parser,fast
6
6
  Author: Thomas Thomassen
@@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"
4
4
 
5
5
  [project]
6
6
  name = "fastgpx"
7
- version = "0.2.2"
7
+ version = "0.3.0"
8
8
  requires-python = ">=3.11"
9
9
 
10
10
  description = "An experimental Python library for parsing GPX files fast."
@@ -43,10 +43,14 @@ std::chrono::system_clock::time_point TimePoint::value() const
43
43
 
44
44
  bool TimeBounds::IsEmpty() const
45
45
  {
46
- assert(start_time.has_value() == end_time.has_value());
47
46
  return !start_time.has_value() && !end_time.has_value();
48
47
  }
49
48
 
49
+ bool TimeBounds::IsRange() const
50
+ {
51
+ return start_time.has_value() && end_time.has_value();
52
+ }
53
+
50
54
  void TimeBounds::Add(const std::chrono::system_clock::time_point time_point)
51
55
  {
52
56
  if (start_time.has_value())
@@ -28,6 +28,7 @@ struct TimeBounds
28
28
  std::optional<std::chrono::system_clock::time_point> end_time = std::nullopt;
29
29
 
30
30
  bool IsEmpty() const;
31
+ bool IsRange() const;
31
32
 
32
33
  void Add(std::chrono::system_clock::time_point time_point);
33
34
  void Add(const TimeBounds& time_bounds);
@@ -53,7 +53,13 @@ PYBIND11_MODULE(fastgpx, m)
53
53
  py::arg("end_time"))
54
54
  .def_readwrite("start_time", &fastgpx::TimeBounds::start_time)
55
55
  .def_readwrite("end_time", &fastgpx::TimeBounds::end_time)
56
- .def("is_empty", &fastgpx::TimeBounds::IsEmpty);
56
+ .def("is_empty", &fastgpx::TimeBounds::IsEmpty)
57
+ .def("is_range", &fastgpx::TimeBounds::IsRange)
58
+ .def("add",
59
+ py::overload_cast<std::chrono::system_clock::time_point>(&fastgpx::TimeBounds::Add),
60
+ py::arg("datetime"))
61
+ .def("add", py::overload_cast<const fastgpx::TimeBounds&>(&fastgpx::TimeBounds::Add),
62
+ py::arg("timebounds"));
57
63
 
58
64
  py::class_<fastgpx::LatLong>(m, "LatLong")
59
65
  .def(py::init<>())
@@ -27,6 +27,15 @@
27
27
  namespace PYBIND11_NAMESPACE {
28
28
  namespace detail {
29
29
 
30
+ inline std::time_t to_utc_time_t(std::tm* tm)
31
+ {
32
+ #if defined(_WIN32)
33
+ return _mkgmtime(tm);
34
+ #else
35
+ return timegm(tm);
36
+ #endif
37
+ }
38
+
30
39
  template<typename type>
31
40
  class duration_caster
32
41
  {
@@ -195,7 +204,10 @@ public:
195
204
  return false;
196
205
  }
197
206
 
198
- value = time_point_cast<Duration>(system_clock::from_time_t(std::mktime(&cal)) + msecs);
207
+ // HACK: Assume datetime.datetime objects are in UTC, so we use timegm instead of mktime.
208
+ // value = time_point_cast<Duration>(system_clock::from_time_t(std::mktime(&cal)) + msecs);
209
+ std::time_t tt = to_utc_time_t(&cal);
210
+ value = time_point_cast<Duration>(system_clock::from_time_t(tt) + msecs);
199
211
  return true;
200
212
  }
201
213
 
@@ -9,4 +9,4 @@ from fastgpx.fastgpx import geo
9
9
  from fastgpx.fastgpx import parse
10
10
  from fastgpx.fastgpx import polyline
11
11
  from . import fastgpx
12
- __all__ = ['Bounds', 'Gpx', 'LatLong', 'Segment', 'TimeBounds', 'Track', 'fastgpx', 'geo', 'parse', 'polyline']
12
+ __all__: list = ['Bounds', 'Gpx', 'LatLong', 'Segment', 'TimeBounds', 'Track', 'geo', 'parse', 'polyline']
@@ -101,8 +101,16 @@ class TimeBounds:
101
101
  @typing.overload
102
102
  def __init__(self, start_time: datetime.datetime | None, end_time: datetime.datetime | None) -> None:
103
103
  ...
104
+ @typing.overload
105
+ def add(self, datetime: datetime.datetime) -> None:
106
+ ...
107
+ @typing.overload
108
+ def add(self, timebounds: TimeBounds) -> None:
109
+ ...
104
110
  def is_empty(self) -> bool:
105
111
  ...
112
+ def is_range(self) -> bool:
113
+ ...
106
114
  class Track:
107
115
  comment: str | None
108
116
  description: str | None
@@ -1,7 +1,6 @@
1
1
  import datetime
2
2
 
3
3
  import gpxpy
4
- import gpxpy.gpx
5
4
  import pytest
6
5
 
7
6
  import fastgpx
@@ -0,0 +1,73 @@
1
+ from datetime import datetime
2
+
3
+ import fastgpx
4
+
5
+
6
+ class TestTimeBounds:
7
+
8
+ def test_init_defaults(self):
9
+ bounds = fastgpx.TimeBounds()
10
+ assert bounds.is_empty()
11
+ assert not bounds.is_range()
12
+ assert bounds.start_time is None
13
+ assert bounds.end_time is None
14
+
15
+ def test_init_with_start_time(self):
16
+ start_time = datetime.fromisoformat("2025-06-20 08:07:28+00:00")
17
+ bounds = fastgpx.TimeBounds(start_time=start_time, end_time=None)
18
+ assert not bounds.is_empty()
19
+ assert not bounds.is_range()
20
+ assert bounds.start_time == start_time
21
+ assert bounds.end_time is None
22
+
23
+ def test_init_with_end_time(self):
24
+ end_time = datetime.fromisoformat("2025-06-27 13:37:00+00:00")
25
+ bounds = fastgpx.TimeBounds(start_time=None, end_time=end_time)
26
+ assert not bounds.is_empty()
27
+ assert not bounds.is_range()
28
+ assert bounds.start_time is None
29
+ assert bounds.end_time == end_time
30
+
31
+ def test_init_with_start_and_end_time(self):
32
+ start_time = datetime.fromisoformat("2025-06-20 08:07:28+00:00")
33
+ end_time = datetime.fromisoformat("2025-06-27 13:37:00+00:00")
34
+ bounds = fastgpx.TimeBounds(start_time=start_time, end_time=end_time)
35
+ assert not bounds.is_empty()
36
+ assert bounds.is_range()
37
+ assert bounds.start_time == start_time
38
+ assert bounds.end_time == end_time
39
+
40
+ def test_add_single_time_point(self):
41
+ bounds = fastgpx.TimeBounds()
42
+ time_point = datetime.fromisoformat("2025-06-20 08:07:28+00:00")
43
+ bounds.add(time_point)
44
+ assert not bounds.is_empty()
45
+ assert bounds.is_range()
46
+ assert bounds.start_time == time_point
47
+ assert bounds.end_time == time_point
48
+
49
+ def test_add_second_time_point(self):
50
+ bounds = fastgpx.TimeBounds()
51
+ time_point1 = datetime.fromisoformat("2025-06-20 08:07:28+00:00")
52
+ time_point2 = datetime.fromisoformat("2025-06-27 13:37:00+00:00")
53
+ bounds.add(time_point1)
54
+ bounds.add(time_point2)
55
+ assert not bounds.is_empty()
56
+ assert bounds.is_range()
57
+ assert bounds.start_time == time_point1
58
+ assert bounds.end_time == time_point2
59
+
60
+ def test_add_time_bounds(self):
61
+ bounds1 = fastgpx.TimeBounds(
62
+ start_time=datetime.fromisoformat("2025-06-20 08:07:28+00:00"),
63
+ end_time=datetime.fromisoformat("2025-06-27 13:37:00+00:00")
64
+ )
65
+ bounds2 = fastgpx.TimeBounds(
66
+ start_time=datetime.fromisoformat("2025-06-25 10:00:00+00:00"),
67
+ end_time=datetime.fromisoformat("2025-06-30 15:00:00+00:00")
68
+ )
69
+ bounds1.add(bounds2)
70
+ assert not bounds1.is_empty()
71
+ assert bounds1.is_range()
72
+ assert bounds1.start_time == bounds1.start_time
73
+ assert bounds1.end_time == bounds2.end_time
@@ -97,7 +97,7 @@ wheels = [
97
97
 
98
98
  [[package]]
99
99
  name = "fastgpx"
100
- version = "0.0.1.dev0"
100
+ version = "0.3.0"
101
101
  source = { editable = "." }
102
102
 
103
103
  [package.dev-dependencies]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes