compass-lib 0.0.4__tar.gz → 0.0.7__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 (38) hide show
  1. {compass_lib-0.0.4 → compass_lib-0.0.7}/PKG-INFO +15 -16
  2. {compass_lib-0.0.4 → compass_lib-0.0.7}/compass_lib/__init__.py +1 -1
  3. {compass_lib-0.0.4 → compass_lib-0.0.7}/compass_lib/geojson.py +161 -85
  4. {compass_lib-0.0.4 → compass_lib-0.0.7}/compass_lib/project/parser.py +3 -2
  5. {compass_lib-0.0.4 → compass_lib-0.0.7}/pyproject.toml +21 -29
  6. {compass_lib-0.0.4 → compass_lib-0.0.7}/LICENSE +0 -0
  7. {compass_lib-0.0.4 → compass_lib-0.0.7}/README.md +0 -0
  8. {compass_lib-0.0.4 → compass_lib-0.0.7}/compass_lib/commands/__init__.py +0 -0
  9. {compass_lib-0.0.4 → compass_lib-0.0.7}/compass_lib/commands/convert.py +0 -0
  10. {compass_lib-0.0.4 → compass_lib-0.0.7}/compass_lib/commands/encrypt.py +0 -0
  11. {compass_lib-0.0.4 → compass_lib-0.0.7}/compass_lib/commands/geojson.py +0 -0
  12. {compass_lib-0.0.4 → compass_lib-0.0.7}/compass_lib/commands/main.py +0 -0
  13. {compass_lib-0.0.4 → compass_lib-0.0.7}/compass_lib/constants.py +0 -0
  14. {compass_lib-0.0.4 → compass_lib-0.0.7}/compass_lib/enums.py +0 -0
  15. {compass_lib-0.0.4 → compass_lib-0.0.7}/compass_lib/errors.py +0 -0
  16. {compass_lib-0.0.4 → compass_lib-0.0.7}/compass_lib/geo_utils.py +0 -0
  17. {compass_lib-0.0.4 → compass_lib-0.0.7}/compass_lib/interface.py +0 -0
  18. {compass_lib-0.0.4 → compass_lib-0.0.7}/compass_lib/io.py +0 -0
  19. {compass_lib-0.0.4 → compass_lib-0.0.7}/compass_lib/models.py +0 -0
  20. {compass_lib-0.0.4 → compass_lib-0.0.7}/compass_lib/plot/__init__.py +0 -0
  21. {compass_lib-0.0.4 → compass_lib-0.0.7}/compass_lib/plot/models.py +0 -0
  22. {compass_lib-0.0.4 → compass_lib-0.0.7}/compass_lib/plot/parser.py +0 -0
  23. {compass_lib-0.0.4 → compass_lib-0.0.7}/compass_lib/project/__init__.py +0 -0
  24. {compass_lib-0.0.4 → compass_lib-0.0.7}/compass_lib/project/format.py +0 -0
  25. {compass_lib-0.0.4 → compass_lib-0.0.7}/compass_lib/project/models.py +0 -0
  26. {compass_lib-0.0.4 → compass_lib-0.0.7}/compass_lib/solver/__init__.py +0 -0
  27. {compass_lib-0.0.4 → compass_lib-0.0.7}/compass_lib/solver/ariane.py +0 -0
  28. {compass_lib-0.0.4 → compass_lib-0.0.7}/compass_lib/solver/base.py +0 -0
  29. {compass_lib-0.0.4 → compass_lib-0.0.7}/compass_lib/solver/lse.py +0 -0
  30. {compass_lib-0.0.4 → compass_lib-0.0.7}/compass_lib/solver/models.py +0 -0
  31. {compass_lib-0.0.4 → compass_lib-0.0.7}/compass_lib/solver/noop.py +0 -0
  32. {compass_lib-0.0.4 → compass_lib-0.0.7}/compass_lib/solver/proportional.py +0 -0
  33. {compass_lib-0.0.4 → compass_lib-0.0.7}/compass_lib/solver/sparse.py +0 -0
  34. {compass_lib-0.0.4 → compass_lib-0.0.7}/compass_lib/survey/__init__.py +0 -0
  35. {compass_lib-0.0.4 → compass_lib-0.0.7}/compass_lib/survey/format.py +0 -0
  36. {compass_lib-0.0.4 → compass_lib-0.0.7}/compass_lib/survey/models.py +0 -0
  37. {compass_lib-0.0.4 → compass_lib-0.0.7}/compass_lib/survey/parser.py +0 -0
  38. {compass_lib-0.0.4 → compass_lib-0.0.7}/compass_lib/validation.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: compass_lib
3
- Version: 0.0.4
3
+ Version: 0.0.7
4
4
  Summary: Compass Parser Library.
5
5
  Keywords: cave,survey,karst
6
6
  Author-email: Jonathan Dekhtiar <jonathan@dekhtiar.com>
@@ -21,23 +21,22 @@ Classifier: Programming Language :: Python :: 3
21
21
  Classifier: Programming Language :: Python :: 3 :: Only
22
22
  License-File: LICENSE
23
23
  Requires-Dist: geojson>=3.2,<4
24
- Requires-Dist: numpy>=1.26,<3
25
- Requires-Dist: scipy>=1.12,<2
26
- Requires-Dist: orjson>=3.10,<3.12
27
- Requires-Dist: pydantic>=2.12,<2.13
28
- Requires-Dist: pydantic-extra-types>=2.11,<3.0
24
+ Requires-Dist: numpy>=2.4.4,<3
25
+ Requires-Dist: scipy>=1.17.1,<2
26
+ Requires-Dist: orjson>=3.11.8,<3.12
27
+ Requires-Dist: pydantic>=2.13.1,<2.14
28
+ Requires-Dist: pydantic-extra-types>=2.11.1,<3.0
29
29
  Requires-Dist: pyIGRF14==1.0.4
30
- Requires-Dist: pyproj>=3.7.1,<3.8
31
- Requires-Dist: shapely>=2.0,<3
30
+ Requires-Dist: pyproj>=3.7.2,<3.8
31
+ Requires-Dist: shapely>=2.1.2,<3
32
32
  Requires-Dist: utm>=0.8.1,<0.9
33
- Requires-Dist: cryptography>=44.0.0,<47.0.0 ; extra == "test"
34
- Requires-Dist: python-dotenv>=1.0.0,<2.0.0 ; extra == "test"
35
- Requires-Dist: deepdiff>=7.0,<9.0 ; extra == "test"
36
- Requires-Dist: pytest>=8.0.0,<10.0.0 ; extra == "test"
37
- Requires-Dist: pytest-cov>=5.0.0,<8.0.0 ; extra == "test"
38
- Requires-Dist: pytest-dotenv>=0.5.0,<1.0.0 ; extra == "test"
39
- Requires-Dist: pytest-env>=1.1.3,<2.0.0 ; extra == "test"
40
- Requires-Dist: pytest-runner>=6.0.0,<7.0.0 ; extra == "test"
33
+ Requires-Dist: cryptography>=46.0.7,<47.0.0 ; extra == "test"
34
+ Requires-Dist: python-dotenv>=1.2.2,<2.0.0 ; extra == "test"
35
+ Requires-Dist: deepdiff>=7.0,<10.0 ; extra == "test"
36
+ Requires-Dist: pytest>=9.0.3,<10.0.0 ; extra == "test"
37
+ Requires-Dist: pytest-cov>=7.1.0,<8.0.0 ; extra == "test"
38
+ Requires-Dist: pytest-env>=1.6.0,<2.0.0 ; extra == "test"
39
+ Requires-Dist: pytest-runner>=6.0.1,<7.0.0 ; extra == "test"
41
40
  Requires-Dist: pytest-ordering>=0.6,<1.0.0 ; extra == "test"
42
41
  Requires-Dist: parameterized>=0.9.0,<0.10 ; extra == "test"
43
42
  Project-URL: Bug Reports, https://github.com/OpenSpeleo/pytool_compass_lib/issues
@@ -21,7 +21,7 @@ Usage:
21
21
  directives = read_mak_file(Path("project.MAK"))
22
22
  """
23
23
 
24
- __version__ = "0.0.4"
24
+ __version__ = "0.0.7"
25
25
 
26
26
  # Constants
27
27
  from compass_lib.constants import COMPASS_ENCODING
@@ -33,10 +33,10 @@ from __future__ import annotations
33
33
  import datetime
34
34
  import logging
35
35
  import math
36
+ import uuid
36
37
  from collections import deque
37
38
  from dataclasses import dataclass
38
39
  from dataclasses import field
39
- from dataclasses import replace
40
40
  from typing import TYPE_CHECKING
41
41
  from typing import Any
42
42
 
@@ -54,6 +54,7 @@ from shapely.ops import unary_union
54
54
  from compass_lib.constants import FEET_TO_METERS
55
55
  from compass_lib.constants import GEOJSON_COORDINATE_PRECISION
56
56
  from compass_lib.constants import JSON_ENCODING
57
+ from compass_lib.constants import METERS_TO_FEET
57
58
  from compass_lib.enums import Datum
58
59
  from compass_lib.geo_utils import GeoLocation
59
60
  from compass_lib.geo_utils import get_declination
@@ -153,7 +154,7 @@ class MisclosureLeg:
153
154
  the position computed by the arriving (typically longer) path; the
154
155
  ``existing`` is the position already set by the first path to
155
156
  reach the station. The longest path is considered the "true"
156
- segment; closure error is ghost − existing.
157
+ segment; closure error is ghost - existing.
157
158
  """
158
159
 
159
160
  existing: Station # Station already computed by another anchor's BFS
@@ -212,7 +213,6 @@ def length_to_meters(length_ft: float) -> float:
212
213
  _transformer_cache: dict[str, Transformer] = {}
213
214
 
214
215
 
215
-
216
216
  def _get_transformer(zone: int, northern: bool, datum: Datum) -> Transformer:
217
217
  """Get or create a cached transformer from UTM to WGS84.
218
218
 
@@ -488,9 +488,7 @@ def _compute_file_scopes(project: CompassMakFile) -> dict[str, int]:
488
488
  for fd in project.file_directives:
489
489
  # Explicit linking = at least one link station WITHOUT coordinates.
490
490
  # Link stations with coordinates are just anchors (fixed points).
491
- has_explicit_linking = any(
492
- ls.location is None for ls in fd.link_stations
493
- )
491
+ has_explicit_linking = any(ls.location is None for ls in fd.link_stations)
494
492
  if has_explicit_linking:
495
493
  current_scope += 1
496
494
  file_scopes[fd.file] = current_scope
@@ -559,12 +557,8 @@ def build_station_graph(
559
557
 
560
558
  all_shots.append((shot, file_dir.file, survey_name, survey))
561
559
 
562
- from_name = _scope_name(
563
- shot.from_station_name, scope_id, link_names
564
- )
565
- to_name = _scope_name(
566
- shot.to_station_name, scope_id, link_names
567
- )
560
+ from_name = _scope_name(shot.from_station_name, scope_id, link_names)
561
+ to_name = _scope_name(shot.to_station_name, scope_id, link_names)
568
562
 
569
563
  # Skip self-loops (from == to). These are data errors
570
564
  # that create impossible solver constraints.
@@ -587,12 +581,18 @@ def build_station_graph(
587
581
  # Pre-scoped names are stored in the tuple so BFS
588
582
  # does not need to re-scope per shot.
589
583
  adjacency[from_name].append(
590
- (shot, file_dir.file, survey_name, survey, False,
591
- from_name, to_name)
584
+ (
585
+ shot,
586
+ file_dir.file,
587
+ survey_name,
588
+ survey,
589
+ False,
590
+ from_name,
591
+ to_name,
592
+ )
592
593
  )
593
594
  adjacency[to_name].append(
594
- (shot, file_dir.file, survey_name, survey, True,
595
- from_name, to_name)
595
+ (shot, file_dir.file, survey_name, survey, True, from_name, to_name)
596
596
  )
597
597
 
598
598
  return adjacency, all_shots
@@ -803,9 +803,7 @@ def propagate_coordinates(
803
803
  primary_station.origin = primary_name
804
804
  result.stations = {primary_name: primary_station}
805
805
  result.anchors = {primary_name}
806
- _secondary_anchors = {
807
- k: v for k, v in anchors.items() if k != primary_name
808
- }
806
+ _secondary_anchors = {k: v for k, v in anchors.items() if k != primary_name}
809
807
  else:
810
808
  # Multi-origin BFS: seed from all anchors
811
809
  result.stations = dict(anchors)
@@ -854,8 +852,15 @@ def propagate_coordinates(
854
852
  if not current_station:
855
853
  continue
856
854
 
857
- for (shot, file_name, survey_name, survey, is_reverse,
858
- from_name, to_name) in adjacency.get(current_name, []):
855
+ for (
856
+ shot,
857
+ file_name,
858
+ survey_name,
859
+ survey,
860
+ is_reverse,
861
+ from_name,
862
+ to_name,
863
+ ) in adjacency.get(current_name, []):
859
864
  # Multi-origin: forward only. Single-origin: both.
860
865
  if is_reverse and not detect_same_origin_loops:
861
866
  continue
@@ -912,7 +917,10 @@ def propagate_coordinates(
912
917
  # the frontsight; for reverse edges it uses the backsight
913
918
  # (or frontsight + 180° when no backsight is available).
914
919
  shot_delta = compute_shot_delta(
915
- shot, is_reverse, declination, convergence,
920
+ shot,
921
+ is_reverse,
922
+ declination,
923
+ convergence,
916
924
  )
917
925
  de, dn, dz = shot_delta
918
926
 
@@ -933,8 +941,7 @@ def propagate_coordinates(
933
941
  new_station.survey = survey_name
934
942
  new_station.origin = current_station.origin
935
943
  new_station.path_distance = (
936
- current_station.path_distance
937
- + length_to_meters(shot.length or 0.0)
944
+ current_station.path_distance + length_to_meters(shot.length or 0.0)
938
945
  )
939
946
  result.stations[target_name] = new_station
940
947
 
@@ -1106,8 +1113,7 @@ def propagate_coordinates(
1106
1113
  ),
1107
1114
  )
1108
1115
  logger.info(
1109
- "Closure at anchor '%s': %.3f m "
1110
- "(path distance: %.1f m)",
1116
+ "Closure at anchor '%s': %.3f m (path distance: %.1f m)",
1111
1117
  sec_known.name,
1112
1118
  gap,
1113
1119
  bfs_station.path_distance,
@@ -1152,8 +1158,15 @@ def propagate_coordinates(
1152
1158
  if not current_station:
1153
1159
  continue
1154
1160
 
1155
- for (shot, file_name, survey_name, survey, is_reverse,
1156
- from_name, to_name) in adjacency.get(current_name, []):
1161
+ for (
1162
+ shot,
1163
+ file_name,
1164
+ survey_name,
1165
+ survey,
1166
+ is_reverse,
1167
+ from_name,
1168
+ to_name,
1169
+ ) in adjacency.get(current_name, []):
1157
1170
  # Same traversal rules as main BFS
1158
1171
  target_name = from_name if is_reverse else to_name
1159
1172
 
@@ -1175,18 +1188,24 @@ def propagate_coordinates(
1175
1188
  result.datum or Datum.WGS_1984,
1176
1189
  )
1177
1190
  declination = calculate_survey_declination(
1178
- station_wgs84, survey.header.date,
1191
+ station_wgs84,
1192
+ survey.header.date,
1179
1193
  )
1180
1194
  survey_declinations[survey_key] = declination
1181
1195
  logger.debug(
1182
1196
  "Survey %s declination: %.2f° (date: %s)",
1183
- survey_key, declination, survey.header.date,
1197
+ survey_key,
1198
+ declination,
1199
+ survey.header.date,
1184
1200
  )
1185
1201
  else:
1186
1202
  declination = survey_declinations[survey_key]
1187
1203
 
1188
1204
  shot_delta = compute_shot_delta(
1189
- shot, is_reverse, declination, convergence,
1205
+ shot,
1206
+ is_reverse,
1207
+ declination,
1208
+ convergence,
1190
1209
  )
1191
1210
  de, dn, dz = shot_delta
1192
1211
 
@@ -1287,8 +1306,15 @@ def propagate_coordinates(
1287
1306
  # reverse edges to orphaned stations.
1288
1307
  fallback_queue: deque[str] = deque()
1289
1308
  for name in list(visited_stations):
1290
- for (shot, file_name, survey_name, survey, is_reverse,
1291
- _from_scoped, _to_scoped) in adjacency.get(name, []):
1309
+ for (
1310
+ _,
1311
+ _,
1312
+ _,
1313
+ _,
1314
+ is_reverse,
1315
+ _from_scoped,
1316
+ _,
1317
+ ) in adjacency.get(name, []):
1292
1318
  if not is_reverse:
1293
1319
  continue
1294
1320
  if _from_scoped in orphaned:
@@ -1301,18 +1327,21 @@ def propagate_coordinates(
1301
1327
  if not current_station:
1302
1328
  continue
1303
1329
 
1304
- for (shot, file_name, survey_name, survey, is_reverse,
1305
- from_name, to_name) in adjacency.get(current_name, []):
1306
-
1330
+ for (
1331
+ shot,
1332
+ file_name,
1333
+ survey_name,
1334
+ survey,
1335
+ is_reverse,
1336
+ from_name,
1337
+ to_name,
1338
+ ) in adjacency.get(current_name, []):
1307
1339
  shot_key = (min(from_name, to_name), max(from_name, to_name))
1308
1340
  if shot_key in visited_shots:
1309
1341
  continue
1310
1342
 
1311
1343
  # Determine target and direction.
1312
- if is_reverse:
1313
- target_name = from_name
1314
- else:
1315
- target_name = to_name
1344
+ target_name = from_name if is_reverse else to_name
1316
1345
 
1317
1346
  if target_name in result.stations:
1318
1347
  continue # Already computed
@@ -1332,14 +1361,18 @@ def propagate_coordinates(
1332
1361
  result.datum or Datum.WGS_1984,
1333
1362
  )
1334
1363
  declination = calculate_survey_declination(
1335
- station_wgs84, survey.header.date,
1364
+ station_wgs84,
1365
+ survey.header.date,
1336
1366
  )
1337
1367
  survey_declinations[survey_key] = declination
1338
1368
  else:
1339
1369
  declination = survey_declinations[survey_key]
1340
1370
 
1341
1371
  forward_delta = compute_shot_delta(
1342
- shot, False, declination, convergence,
1372
+ shot,
1373
+ is_reverse=False,
1374
+ declination=declination,
1375
+ convergence=convergence,
1343
1376
  )
1344
1377
  de, dn, dz = forward_delta
1345
1378
 
@@ -1368,8 +1401,7 @@ def propagate_coordinates(
1368
1401
  new_station.survey = survey_name
1369
1402
  new_station.origin = current_station.origin
1370
1403
  new_station.path_distance = (
1371
- current_station.path_distance
1372
- + length_to_meters(shot.length or 0.0)
1404
+ current_station.path_distance + length_to_meters(shot.length or 0.0)
1373
1405
  )
1374
1406
  result.stations[target_name] = new_station
1375
1407
  visited_stations.add(target_name)
@@ -1434,7 +1466,9 @@ def compute_survey_coordinates(
1434
1466
  adjacency, _ = build_station_graph(project)
1435
1467
  anchors = find_anchor_stations(project)
1436
1468
  result = propagate_coordinates(
1437
- project, anchors, adjacency,
1469
+ project,
1470
+ anchors,
1471
+ adjacency,
1438
1472
  detect_same_origin_loops=(solver is not None),
1439
1473
  )
1440
1474
 
@@ -1526,7 +1560,12 @@ def station_to_feature(
1526
1560
  GeoJSON Feature with Point geometry
1527
1561
  """
1528
1562
  lon, lat = _cached_utm_to_wgs84(
1529
- station.easting, station.northing, zone, northern, datum, wgs84_cache,
1563
+ station.easting,
1564
+ station.northing,
1565
+ zone,
1566
+ northern,
1567
+ datum,
1568
+ wgs84_cache,
1530
1569
  )
1531
1570
 
1532
1571
  properties: dict[str, Any] = {
@@ -1568,16 +1607,22 @@ def anchor_to_feature(
1568
1607
  GeoJSON Feature with Point geometry and type "anchor"
1569
1608
  """
1570
1609
  lon, lat = _cached_utm_to_wgs84(
1571
- station.easting, station.northing, zone, northern, datum, wgs84_cache,
1610
+ station.easting,
1611
+ station.northing,
1612
+ zone,
1613
+ northern,
1614
+ datum,
1615
+ wgs84_cache,
1572
1616
  )
1573
1617
 
1618
+ depth_ft = round(station.elevation * METERS_TO_FEET, 2)
1619
+
1574
1620
  return Feature(
1575
1621
  geometry=Point((lon, lat, round(station.elevation, 2))),
1576
1622
  properties={
1577
- "type": "anchor",
1578
- "name": station.name,
1579
- "file": station.file,
1580
- "elevation_m": round(station.elevation, 2),
1623
+ "id": station.name,
1624
+ "depth": abs(depth_ft),
1625
+ "name": station.survey,
1581
1626
  },
1582
1627
  )
1583
1628
 
@@ -1602,29 +1647,24 @@ def leg_to_feature(
1602
1647
  GeoJSON Feature with LineString geometry
1603
1648
  """
1604
1649
  from_lon, from_lat = _cached_utm_to_wgs84(
1605
- leg.from_station.easting, leg.from_station.northing,
1606
- zone, northern, datum, wgs84_cache,
1650
+ leg.from_station.easting,
1651
+ leg.from_station.northing,
1652
+ zone,
1653
+ northern,
1654
+ datum,
1655
+ wgs84_cache,
1607
1656
  )
1608
1657
  to_lon, to_lat = _cached_utm_to_wgs84(
1609
- leg.to_station.easting, leg.to_station.northing,
1610
- zone, northern, datum, wgs84_cache,
1658
+ leg.to_station.easting,
1659
+ leg.to_station.northing,
1660
+ zone,
1661
+ northern,
1662
+ datum,
1663
+ wgs84_cache,
1611
1664
  )
1612
1665
 
1613
- properties: dict[str, Any] = {
1614
- "type": "leg",
1615
- "from": leg.from_station.name,
1616
- "to": leg.to_station.name,
1617
- "distance_ft": leg.distance,
1618
- "distance_m": round(length_to_meters(leg.distance), 2)
1619
- if leg.distance
1620
- else None,
1621
- "azimuth": leg.azimuth,
1622
- "inclination": leg.inclination,
1623
- "file": leg.file,
1624
- "survey": leg.survey,
1625
- }
1626
- if leg.from_station.origin:
1627
- properties["origin"] = leg.from_station.origin
1666
+ depth_m = leg.to_station.elevation
1667
+ depth_ft = round(depth_m * METERS_TO_FEET, 2)
1628
1668
 
1629
1669
  return Feature(
1630
1670
  geometry=LineString(
@@ -1633,7 +1673,11 @@ def leg_to_feature(
1633
1673
  (to_lon, to_lat, round(leg.to_station.elevation, 2)),
1634
1674
  ]
1635
1675
  ),
1636
- properties=properties,
1676
+ properties={
1677
+ "id": str(uuid.uuid5(uuid.NAMESPACE_OID, leg.to_station.name)),
1678
+ "depth": abs(depth_ft), # Compass depth is < 0
1679
+ "name": leg.survey,
1680
+ },
1637
1681
  )
1638
1682
 
1639
1683
 
@@ -1793,16 +1837,36 @@ def passage_to_feature(
1793
1837
 
1794
1838
  try:
1795
1839
  from_left = _cached_utm_to_wgs84(
1796
- from_left_e, from_left_n, zone, northern, datum, wgs84_cache,
1840
+ from_left_e,
1841
+ from_left_n,
1842
+ zone,
1843
+ northern,
1844
+ datum,
1845
+ wgs84_cache,
1797
1846
  )
1798
1847
  from_right = _cached_utm_to_wgs84(
1799
- from_right_e, from_right_n, zone, northern, datum, wgs84_cache,
1848
+ from_right_e,
1849
+ from_right_n,
1850
+ zone,
1851
+ northern,
1852
+ datum,
1853
+ wgs84_cache,
1800
1854
  )
1801
1855
  to_left = _cached_utm_to_wgs84(
1802
- to_left_e, to_left_n, zone, northern, datum, wgs84_cache,
1856
+ to_left_e,
1857
+ to_left_n,
1858
+ zone,
1859
+ northern,
1860
+ datum,
1861
+ wgs84_cache,
1803
1862
  )
1804
1863
  to_right = _cached_utm_to_wgs84(
1805
- to_right_e, to_right_n, zone, northern, datum, wgs84_cache,
1864
+ to_right_e,
1865
+ to_right_n,
1866
+ zone,
1867
+ northern,
1868
+ datum,
1869
+ wgs84_cache,
1806
1870
  )
1807
1871
  except InvalidCoordinateError:
1808
1872
  return None
@@ -1891,7 +1955,6 @@ def survey_to_geojson(
1891
1955
  # Add station points
1892
1956
  if include_stations:
1893
1957
  for name, station in survey.stations.items():
1894
-
1895
1958
  try:
1896
1959
  feat = station_to_feature(station, zone, northern, datum, wgs84_cache)
1897
1960
  if color_by_origin:
@@ -1949,12 +2012,18 @@ def survey_to_geojson(
1949
2012
  for misc in survey.misclosures:
1950
2013
  try:
1951
2014
  ex_lon, ex_lat = utm_to_wgs84(
1952
- misc.existing.easting, misc.existing.northing,
1953
- zone, northern, datum,
2015
+ misc.existing.easting,
2016
+ misc.existing.northing,
2017
+ zone,
2018
+ northern,
2019
+ datum,
1954
2020
  )
1955
2021
  gh_lon, gh_lat = utm_to_wgs84(
1956
- misc.ghost.easting, misc.ghost.northing,
1957
- zone, northern, datum,
2022
+ misc.ghost.easting,
2023
+ misc.ghost.northing,
2024
+ zone,
2025
+ northern,
2026
+ datum,
1958
2027
  )
1959
2028
 
1960
2029
  gap_dist = (
@@ -1991,10 +2060,12 @@ def survey_to_geojson(
1991
2060
  props["closing_segment_distance_m"] = round(closing_dist, 2)
1992
2061
 
1993
2062
  line_feat = Feature(
1994
- geometry=LineString([
1995
- (gh_lon, gh_lat, round(misc.ghost.elevation, 2)),
1996
- (ex_lon, ex_lat, round(misc.existing.elevation, 2)),
1997
- ]),
2063
+ geometry=LineString(
2064
+ [
2065
+ (gh_lon, gh_lat, round(misc.ghost.elevation, 2)),
2066
+ (ex_lon, ex_lat, round(misc.existing.elevation, 2)),
2067
+ ]
2068
+ ),
1998
2069
  properties=props,
1999
2070
  )
2000
2071
  features.append(line_feat)
@@ -2011,7 +2082,12 @@ def survey_to_geojson(
2011
2082
  if leg.excluded_from_plotting:
2012
2083
  continue
2013
2084
  passage = passage_to_feature(
2014
- leg, zone, northern, datum, station_lrud, wgs84_cache,
2085
+ leg,
2086
+ zone,
2087
+ northern,
2088
+ datum,
2089
+ station_lrud,
2090
+ wgs84_cache,
2015
2091
  )
2016
2092
  if not passage:
2017
2093
  continue
@@ -18,6 +18,7 @@ from compass_lib.enums import FormatIdentifier
18
18
  from compass_lib.errors import CompassParseException
19
19
  from compass_lib.errors import SourceLocation
20
20
  from compass_lib.project.models import CompassMakFile
21
+ from compass_lib.project.models import CompassProjectDirective
21
22
 
22
23
 
23
24
  class CompassProjectParser:
@@ -133,7 +134,7 @@ class CompassProjectParser:
133
134
  # Legacy model-returning methods (thin wrappers for backwards compat)
134
135
  # -------------------------------------------------------------------------
135
136
 
136
- def parse_file(self, path: Path) -> list["CompassProjectDirective"]: # noqa: F821
137
+ def parse_file(self, path: Path) -> list[CompassProjectDirective]:
137
138
  """Parse a project file.
138
139
 
139
140
  DEPRECATED: Use parse_file_to_dict() for new code.
@@ -156,7 +157,7 @@ class CompassProjectParser:
156
157
  self,
157
158
  data: str,
158
159
  source: str = "<string>",
159
- ) -> list["CompassProjectDirective"]: # noqa: F821
160
+ ) -> list[CompassProjectDirective]:
160
161
  """Parse project data from a string.
161
162
 
162
163
  DEPRECATED: Use parse_string_to_dict() for new code.
@@ -1,5 +1,5 @@
1
1
  [build-system]
2
- requires = ["flit_core >=3.11,<4"]
2
+ requires = ["flit_core >=3.12.0,<4"]
3
3
  build-backend = "flit_core.buildapi"
4
4
 
5
5
  [tool.uv]
@@ -7,14 +7,12 @@ package = true
7
7
 
8
8
  [dependency-groups]
9
9
  dev = [
10
- "check-manifest",
11
- "pre-commit>=4.1,<4.6",
12
- "ruff>=0.14,<0.16",
10
+ "prek>=0.3.9,<0.4",
11
+ "ruff>=0.15.10,<0.16",
13
12
  "flit>=3.12,<4",
14
- "wheel>=0.45",
15
13
  # File encryption/decryption
16
- "python-dotenv>=1.0.0,<2.0.0",
17
- "cryptography>=44.0.0,<47.0.0",
14
+ "python-dotenv>=1.2.2,<2.0.0",
15
+ "cryptography>=46.0.7,<47.0.0",
18
16
  ]
19
17
 
20
18
  [project]
@@ -55,14 +53,14 @@ classifiers = [
55
53
  ]
56
54
  dependencies = [
57
55
  "geojson>=3.2,<4",
58
- "numpy>=1.26,<3",
59
- "scipy>=1.12,<2",
60
- "orjson>=3.10,<3.12",
61
- "pydantic>=2.12,<2.13",
62
- "pydantic-extra-types>=2.11,<3.0",
56
+ "numpy>=2.4.4,<3",
57
+ "scipy>=1.17.1,<2",
58
+ "orjson>=3.11.8,<3.12",
59
+ "pydantic>=2.13.1,<2.14",
60
+ "pydantic-extra-types>=2.11.1,<3.0",
63
61
  "pyIGRF14==1.0.4",
64
- "pyproj>=3.7.1,<3.8",
65
- "shapely>=2.0,<3",
62
+ "pyproj>=3.7.2,<3.8",
63
+ "shapely>=2.1.2,<3",
66
64
  "utm>=0.8.1,<0.9",
67
65
  ]
68
66
  dynamic = ["description", "version"]
@@ -70,15 +68,14 @@ dynamic = ["description", "version"]
70
68
  [project.optional-dependencies]
71
69
  test = [
72
70
  # File encryption/decryption
73
- "cryptography>=44.0.0,<47.0.0",
74
- "python-dotenv>=1.0.0,<2.0.0",
71
+ "cryptography>=46.0.7,<47.0.0",
72
+ "python-dotenv>=1.2.2,<2.0.0",
75
73
  # Testing
76
- "deepdiff>=7.0,<9.0",
77
- "pytest>=8.0.0,<10.0.0",
78
- "pytest-cov>=5.0.0,<8.0.0",
79
- "pytest-dotenv>=0.5.0,<1.0.0",
80
- "pytest-env>=1.1.3,<2.0.0",
81
- "pytest-runner>=6.0.0,<7.0.0",
74
+ "deepdiff>=7.0,<10.0",
75
+ "pytest>=9.0.3,<10.0.0",
76
+ "pytest-cov>=7.1.0,<8.0.0",
77
+ "pytest-env>=1.6.0,<2.0.0",
78
+ "pytest-runner>=6.0.1,<7.0.0",
82
79
  "pytest-ordering>=0.6,<1.0.0",
83
80
  "parameterized>=0.9.0,<0.10",
84
81
  ]
@@ -97,17 +94,12 @@ convert = "compass_lib.commands.convert:convert"
97
94
  geojson = "compass_lib.commands.geojson:geojson"
98
95
 
99
96
  [tool.pytest.ini_options]
100
- env_files = [".env"]
101
97
  testpaths = ["tests/"]
102
98
  addopts = "-vvv --cov=compass_lib --cov-report=term-missing"
103
99
  # addopts = "-vvv --cov=compass_lib --cov-report=term-missing --capture=no"
104
100
 
105
- # # This is configuration specific to the `setuptools` build backend.
106
- # # If you are using a different build backend, you will need to change this.
107
- # [tool.setuptools]
108
- # # If there are data files included in your packages that need to be
109
- # # installed, specify them here.
110
- # package-data = {"sample" = ["*.dat"]}
101
+ [tool.pytest_env]
102
+ env_files = [".env"]
111
103
 
112
104
  [tool.isort]
113
105
  profile = "black"
File without changes
File without changes