gri-trajectory 0.1.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 (47) hide show
  1. gri_trajectory-0.1.0/.docs_other_projects.md +85 -0
  2. gri_trajectory-0.1.0/.githooks/pre-push +18 -0
  3. gri_trajectory-0.1.0/.gitignore +36 -0
  4. gri_trajectory-0.1.0/.gitlab-ci-deps.yml +10 -0
  5. gri_trajectory-0.1.0/.gitlab-ci.yml +99 -0
  6. gri_trajectory-0.1.0/.hatch_build.py +46 -0
  7. gri_trajectory-0.1.0/.pre-commit-config.yaml +47 -0
  8. gri_trajectory-0.1.0/.pre-commit-unskipped.py +38 -0
  9. gri_trajectory-0.1.0/.python-version +1 -0
  10. gri_trajectory-0.1.0/.ruff.toml +60 -0
  11. gri_trajectory-0.1.0/.vscode/settings.json +15 -0
  12. gri_trajectory-0.1.0/CONTRIBUTING.md +124 -0
  13. gri_trajectory-0.1.0/LICENSE +21 -0
  14. gri_trajectory-0.1.0/PKG-INFO +115 -0
  15. gri_trajectory-0.1.0/README.md +88 -0
  16. gri_trajectory-0.1.0/gri_trajectory/__init__.py +64 -0
  17. gri_trajectory-0.1.0/gri_trajectory/_util.py +72 -0
  18. gri_trajectory-0.1.0/gri_trajectory/circular_orbit.py +241 -0
  19. gri_trajectory-0.1.0/gri_trajectory/coordinated_turn.py +230 -0
  20. gri_trajectory-0.1.0/gri_trajectory/great_circle.py +260 -0
  21. gri_trajectory-0.1.0/gri_trajectory/hermite.py +179 -0
  22. gri_trajectory-0.1.0/gri_trajectory/keplerian.py +266 -0
  23. gri_trajectory-0.1.0/gri_trajectory/legs.py +166 -0
  24. gri_trajectory-0.1.0/gri_trajectory/linear.py +136 -0
  25. gri_trajectory-0.1.0/gri_trajectory/placement.py +86 -0
  26. gri_trajectory-0.1.0/gri_trajectory/py.typed +0 -0
  27. gri_trajectory-0.1.0/gri_trajectory/segmented.py +201 -0
  28. gri_trajectory-0.1.0/gri_trajectory/sgp4.py +164 -0
  29. gri_trajectory-0.1.0/gri_trajectory/stationary.py +124 -0
  30. gri_trajectory-0.1.0/gri_trajectory/trajectory.py +79 -0
  31. gri_trajectory-0.1.0/gri_trajectory/waypoint.py +246 -0
  32. gri_trajectory-0.1.0/pyproject.toml +84 -0
  33. gri_trajectory-0.1.0/report.xml +1 -0
  34. gri_trajectory-0.1.0/test/__init__.py +1 -0
  35. gri_trajectory-0.1.0/test/test_circular_orbit.py +156 -0
  36. gri_trajectory-0.1.0/test/test_coordinated_turn.py +132 -0
  37. gri_trajectory-0.1.0/test/test_factories.py +52 -0
  38. gri_trajectory-0.1.0/test/test_great_circle.py +191 -0
  39. gri_trajectory-0.1.0/test/test_keplerian.py +334 -0
  40. gri_trajectory-0.1.0/test/test_legs.py +126 -0
  41. gri_trajectory-0.1.0/test/test_linear.py +106 -0
  42. gri_trajectory-0.1.0/test/test_placement.py +45 -0
  43. gri_trajectory-0.1.0/test/test_segmented.py +109 -0
  44. gri_trajectory-0.1.0/test/test_sgp4.py +181 -0
  45. gri_trajectory-0.1.0/test/test_stationary.py +129 -0
  46. gri_trajectory-0.1.0/test/test_waypoint.py +286 -0
  47. gri_trajectory-0.1.0/uv.lock +707 -0
@@ -0,0 +1,85 @@
1
+ [![GeoSol Research Logo](https://geosolresearch.com/logos/foss_logo.png "GeoSol Research")](https://geosolresearch.com)
2
+
3
+ # GeoSol FOSS Projects
4
+
5
+ [Back to main README](README.md)
6
+
7
+ GeoSol Research is working to create, maintain, and improve several FOSS projects in the geolocation space. Here is what we are currently offering!
8
+
9
+ ## Layer 1: Foundation
10
+
11
+ ### [GRI Fitter](https://gitlab.com/geosol-foss/python/gri-fitter)
12
+
13
+ Equation fitting wrapper around scipy.optimize.curve\_fit. Simplifies curve fitting workflows with a clean API.
14
+
15
+ ### [GRI Memoize](https://gitlab.com/geosol-foss/python/gri-memoize)
16
+
17
+ Class-based memoization with full type hints and IDE integration. Provides caching decorators for performance optimization.
18
+
19
+ ### [GRI Signal](https://gitlab.com/geosol-foss/python/gri-signal)
20
+
21
+ Functional signal processing library with pure functions operating on numpy arrays. Includes signal generation (tones, PRN, pulsed), channel simulation (delay, AWGN, Doppler, multipath), modulation (AM/FM/PM, BPSK/QPSK/QAM), sampling (ADC), filtering (lowpass, highpass, bandpass, notch, analytic), spectral analysis (PSD, SNR, THD, time-bandwidth product), cross-correlation, and beamforming.
22
+
23
+ ### [GRI Utils](https://gitlab.com/geosol-foss/python/gri-utils)
24
+
25
+ The math back end to many other projects. This includes constants, coordinate conversion and transformation, orbit propagation, observable calculations, and geolocation algorithms. Most are based around numpy arrays.
26
+
27
+ ## Layer 2: Mid-Level
28
+
29
+ ### [GRI Ell](https://gitlab.com/geosol-foss/python/gri-ell)
30
+
31
+ An object to handle statistical locations like ellipse and ellipsoid, centered around a GRI-Position object. Transformations between statistical spaces, conversions between sigma and percentages in 2D and 3D, statistical distances, and attributes.
32
+
33
+ ### [GRI Iono](https://gitlab.com/geosol-foss/python/gri-iono)
34
+
35
+ Ionospheric delay corrections using Bent, IRI, and NeQuick models.
36
+
37
+ ### [GRI Iono Data](https://gitlab.com/geosol-foss/python/gri-iono-data)
38
+
39
+ Solar flux and NeQuickG coefficient data pipeline for ionospheric modeling.
40
+
41
+ ### [GRI NSEpoch](https://gitlab.com/geosol-foss/python/gri-nsepoch)
42
+
43
+ A lot of the work we do is down to nanosecond or tens of nanosecond accuracy. We needed an object that could both handle that level of precision as well as do a lot of the string <=> time conversions for the many semi-standards formats we run into in this field.
44
+
45
+ ### [GRI Obs](https://gitlab.com/geosol-foss/python/gri-obs)
46
+
47
+ Typed observation objects (TDOA, FDOA, AOA, Range, PDOA) for geolocation measurements. Each observation carries measurement data, noise characterization, sensor geometry, and measurement math (predicted values, Jacobians, noise covariance) for EKF integration with Kalman filters.
48
+
49
+ ### [GRI Plot](https://gitlab.com/geosol-foss/python/gri-plot)
50
+
51
+ Plotting utilities for Plotly and Matplotlib. Provides consistent styling and common plot types for geospatial visualization.
52
+
53
+ ### [GRI Pos](https://gitlab.com/geosol-foss/python/gri-pos)
54
+
55
+ A position object that greatly simplifies handling coordinates, distances, and memory, mostly on the WGS-84 ellipsoid.
56
+
57
+ ### [GRI Terrain](https://gitlab.com/geosol-foss/python/gri-terrain)
58
+
59
+ Unified terrain elevation data access with fallback chain across multiple sources including DTED, Cloud-Optimized GeoTIFF, and Copernicus DEM.
60
+
61
+ ### [GRI Tropo](https://gitlab.com/geosol-foss/python/gri-tropo)
62
+
63
+ Tropospheric delay corrections using ITU-R 2019 models and ERA5 refractivity data.
64
+
65
+ ### [GRI Tropo Data](https://gitlab.com/geosol-foss/python/gri-tropo-data)
66
+
67
+ ERA5 data pipeline for downloading and processing tropospheric refractivity data used by gri-tropo.
68
+
69
+ ## Layer 3: Applications
70
+
71
+ ### [GRI Convolve](https://gitlab.com/geosol-foss/python/gri-convolve)
72
+
73
+ Ellipsoid convolution with outlier detection and clustering, plus Kalman tracking via Interacting Multiple Model (IMM) filter. Combines multiple statistical position estimates into refined solutions. The Kalman tracker supports both ellipsoid-based and raw observable (EKF) updates, bidirectional outlier detection, and online track segmentation.
74
+
75
+ ### [GRI GeoSim](https://gitlab.com/geosol-foss/python/gri-geosim)
76
+
77
+ Geometric/geolocation simulation library for platforms, sensors, emitters, and observables. Simulate satellite and ground-based geolocation scenarios with AOA/TDOA/FDOA measurements.
78
+
79
+ ### [GRI Sandbox](https://gitlab.com/geosol-foss/python/gri-sandbox)
80
+
81
+ Some neat (small) examples and projects we've put together over time using the above libraries!
82
+
83
+ ### [GRI SigSim](https://gitlab.com/geosol-foss/python/gri-sigsim)
84
+
85
+ Object-based RF signal simulation library. Wraps gri-signal functions with chainable object-oriented classes for waveforms (pulsed, PRN, tone), channel modeling (delay, Doppler, multipath), receiver simulation, and TOA estimation.
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env bash
2
+ # Tracked pre-push hook. Wired up automatically by the hatch build hook on
3
+ # `uv sync` (it sets core.hooksPath to this directory). Delegates to pre-commit
4
+ # in the project's .venv. Unlike a `pre-commit install` generated hook, the
5
+ # python path is resolved at run time, so this file is portable across clones.
6
+ ARGS=(hook-impl --config=.pre-commit-config.yaml --hook-type=pre-push)
7
+ HERE="$(cd "$(dirname "$0")" && pwd)"
8
+ ARGS+=(--hook-dir "$HERE" -- "$@")
9
+
10
+ INSTALL_PYTHON="$(git rev-parse --show-toplevel)/.venv/bin/python"
11
+ if [ -x "$INSTALL_PYTHON" ]; then
12
+ exec "$INSTALL_PYTHON" -mpre_commit "${ARGS[@]}"
13
+ elif command -v pre-commit > /dev/null; then
14
+ exec pre-commit "${ARGS[@]}"
15
+ else
16
+ echo '`pre-commit` not found. Run `uv sync` to set up the environment.' 1>&2
17
+ exit 1
18
+ fi
@@ -0,0 +1,36 @@
1
+ ### Python ###
2
+ *.py[cod]
3
+ __pycache__/
4
+ build/
5
+ dist/
6
+ wheels/
7
+ lib/
8
+ lib64/
9
+ *.egg-info/
10
+ *.egg
11
+ .venv
12
+
13
+ ### Temp files ###
14
+ *~
15
+ *.swp
16
+ .version
17
+ .pytest_cache/
18
+ .mypy_cache/
19
+ testreports/
20
+ .VSCodeCounter/
21
+ .coverage
22
+ coverage.xml
23
+ .cov_html/
24
+ *.pstats
25
+ *.stats
26
+ .claude/
27
+ CLAUDE.md
28
+ AGENTS.md
29
+
30
+ ### Credentials ###
31
+ .env*
32
+ *.key
33
+ *.pem
34
+ *.p12
35
+ .cdsapirc
36
+ .ecmwfdatastoresrc
@@ -0,0 +1,10 @@
1
+ # Project-specific dependencies to clone
2
+ # Customize this file per project to clone different repositories
3
+ # TODO: This is only a stopgap until the projects are on pypi. For projects with no
4
+ # dependencies: "before_script: []"
5
+ .clone_dependencies:
6
+ before_script:
7
+ - git clone https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.com/geosol-foss/python/gri-utils.git ../gri-utils
8
+ - git clone https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.com/geosol-foss/python/gri-memoize.git ../gri-memoize
9
+ - git clone https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.com/geosol-foss/python/gri-pos.git ../gri-pos
10
+ - git clone https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.com/geosol-foss/python/gri-nsepoch.git ../gri-nsepoch
@@ -0,0 +1,99 @@
1
+ include:
2
+ - local: .gitlab-ci-deps.yml
3
+
4
+ stages:
5
+ - test
6
+ - lint
7
+ - deploy
8
+
9
+ variables:
10
+ UV_CACHE_DIR: "$CI_PROJECT_DIR/.uv-cache"
11
+ COVERAGE_THRESHOLD: "70"
12
+
13
+ cache:
14
+ key: "${CI_COMMIT_REF_SLUG}"
15
+ paths:
16
+ - .uv-cache/
17
+ - .venv/
18
+
19
+ .python_base:
20
+ image: python:3.12
21
+ before_script:
22
+ # TODO: This reference and include is a stopgap until we have the projects on pypi
23
+ - !reference [.clone_dependencies, before_script]
24
+ - pip install uv
25
+ - uv sync
26
+ - source .venv/bin/activate
27
+
28
+ test:
29
+ extends: .python_base
30
+ stage: test
31
+ script:
32
+ - pytest --cov --cov-report=term --cov-report=html --cov-report=xml --junitxml=report.xml -v
33
+ coverage: '/(?i)total.*? (100(?:\.0+)?\%|[1-9]?\d(?:\.\d+)?\%)$/'
34
+ artifacts:
35
+ when: always
36
+ reports:
37
+ junit: report.xml
38
+ coverage_report:
39
+ coverage_format: cobertura
40
+ path: coverage.xml
41
+ paths:
42
+ - report.xml
43
+ - .cov_html/
44
+ - coverage.xml
45
+ expire_in: 30 days
46
+ only:
47
+ - branches
48
+ - merge_requests
49
+ - tags
50
+
51
+ lint:ruff:
52
+ extends: .python_base
53
+ stage: lint
54
+ script:
55
+ - ruff check . --config .ruff.toml
56
+ - ruff format --check . --config .ruff.toml
57
+ allow_failure: false
58
+ only:
59
+ - branches
60
+ - merge_requests
61
+ - tags
62
+
63
+ lint:ty:
64
+ extends: .python_base
65
+ stage: lint
66
+ script:
67
+ - ty check --output-format gitlab --exit-zero > ty-report.json
68
+ - ty check
69
+ allow_failure: false
70
+ artifacts:
71
+ when: always
72
+ reports:
73
+ codequality: ty-report.json
74
+ expire_in: 30 days
75
+ only:
76
+ - branches
77
+ - merge_requests
78
+ - tags
79
+
80
+ deploy:pypi:
81
+ stage: deploy
82
+ image: python:3.12
83
+ id_tokens:
84
+ PYPI_ID_TOKEN:
85
+ aud: pypi
86
+ variables:
87
+ UV_CACHE_DIR: /tmp/uv-cache
88
+ cache: []
89
+ before_script:
90
+ - pip install uv
91
+ script:
92
+ - uv build
93
+ - uv publish
94
+ artifacts:
95
+ paths:
96
+ - dist/
97
+ expire_in: 1 week
98
+ only:
99
+ - tags
@@ -0,0 +1,46 @@
1
+ """Custom hatchling build hook that wires up the project's git hooks.
2
+
3
+ `uv sync` installs this project in editable mode, which runs the hatchling
4
+ build backend and therefore this hook. The hook points git's ``core.hooksPath``
5
+ at the tracked ``.githooks`` directory, so a fresh clone gets working git hooks
6
+ on its first sync without a separate install step.
7
+
8
+ This only adjusts local git config and is a no-op outside a git work tree (for
9
+ example, in an isolated sdist/wheel build), so it is safe in CI release builds.
10
+ """
11
+
12
+ import shutil
13
+ import subprocess
14
+ from pathlib import Path
15
+ from typing import Any
16
+
17
+ from hatchling.builders.hooks.plugin.interface import BuildHookInterface
18
+
19
+
20
+ class GitHooksBuildHook(BuildHookInterface):
21
+ """Set ``core.hooksPath`` to the tracked ``.githooks`` directory."""
22
+
23
+ PLUGIN_NAME = "custom"
24
+
25
+ def initialize(self, version: str, build_data: dict[str, Any]) -> None: # noqa: ARG002
26
+ """Point git at ``.githooks`` if this build runs inside a work tree."""
27
+ root = Path(self.root)
28
+ git = shutil.which("git")
29
+ if git is None or not (root / ".githooks").is_dir():
30
+ return
31
+ # Only act inside a real git work tree; check=False keeps release
32
+ # builds (sdist/wheel in a non-git temp dir) from failing.
33
+ inside = subprocess.run( # noqa: S603
34
+ [git, "rev-parse", "--is-inside-work-tree"],
35
+ cwd=root,
36
+ capture_output=True,
37
+ text=True,
38
+ check=False,
39
+ )
40
+ if inside.stdout.strip() != "true":
41
+ return
42
+ subprocess.run( # noqa: S603
43
+ [git, "config", "core.hooksPath", ".githooks"],
44
+ cwd=root,
45
+ check=False,
46
+ )
@@ -0,0 +1,47 @@
1
+ # See https://pre-commit.com for more information
2
+ # See https://pre-commit.com/hooks.html for more hooks
3
+ # Checks run at pre-push (not pre-commit): commits stay fast, the suite runs
4
+ # once before code is shared. Hooks are wired up automatically on `uv sync`
5
+ # (see .hatch_build.py), so no separate `pre-commit install` step is needed.
6
+ default_stages: [pre-push]
7
+ repos:
8
+ - repo: https://github.com/gitleaks/gitleaks
9
+ rev: v8.24.3
10
+ hooks:
11
+ - id: gitleaks
12
+ - repo: https://github.com/pre-commit/pre-commit-hooks
13
+ # Base pre-commit-provided hooks
14
+ rev: v6.0.0
15
+ hooks:
16
+ - id: trailing-whitespace
17
+ args: [ --markdown-linebreak-ext=md ]
18
+ exclude: '\.dt[012]$'
19
+ - id: end-of-file-fixer
20
+ exclude: '\.dt[012]$'
21
+ - id: check-added-large-files
22
+ - repo: local
23
+ hooks:
24
+ - id: ruff-check
25
+ name: ruff check
26
+ entry: uv run --no-sync ruff check --fix --config .ruff.toml
27
+ language: system
28
+ types: [python]
29
+ - id: ruff-format
30
+ name: ruff format
31
+ entry: uv run --no-sync ruff format
32
+ language: system
33
+ types: [python]
34
+ - id: ty
35
+ name: ty
36
+ entry: uv run --no-sync ty check
37
+ language: system
38
+ types: [python]
39
+ # ty's [tool.ty.src] include scoping is overridden by explicit file
40
+ # args; run without filenames so it checks the configured source set
41
+ # (matching CI) instead of, say, test files passed in by pre-commit.
42
+ pass_filenames: false
43
+ - id: unskipped
44
+ name: Check for pytest skipped tests commented back in
45
+ entry: uv run --no-sync python .pre-commit-unskipped.py
46
+ language: system
47
+ files: '\.py$'
@@ -0,0 +1,38 @@
1
+ # ruff: noqa: T201
2
+ """Find all the unit tests and make sure none of the skips are commented in!
3
+
4
+ You can run this manually with:
5
+ > python .pre-commit-noskips.py
6
+ """
7
+
8
+ import sys
9
+ from pathlib import Path
10
+
11
+
12
+ def _main() -> None:
13
+ # Get the test directory, which should be adjascent to this script
14
+ script_dir = Path(__file__)
15
+ test_dir = script_dir.parent / "test"
16
+ if not test_dir.exists():
17
+ print("WARNING::: Didn't find a test directory!")
18
+ sys.exit(0)
19
+ tests = test_dir.rglob("test*.py")
20
+ found = False
21
+ for testfile in tests:
22
+ with testfile.open("r") as fp:
23
+ for idx, line in enumerate(fp, 1):
24
+ if line.strip().startswith("#") and (
25
+ "pytest.skip" in line or "pytest.mark.skip" in line
26
+ ):
27
+ print(
28
+ f"ERROR::: Line {idx} of {testfile.relative_to(test_dir)} "
29
+ "has a skip commented out:",
30
+ )
31
+ print(f" {idx}: {line}")
32
+ found = True
33
+ if found:
34
+ sys.exit(1) # Error4 aborts the commit
35
+
36
+
37
+ if __name__ == "__main__":
38
+ _main()
@@ -0,0 +1 @@
1
+ 3.12
@@ -0,0 +1,60 @@
1
+ extend = "pyproject.toml"
2
+
3
+ [lint]
4
+ select = ["ALL"]
5
+ #select = [
6
+ # # pycodestyle
7
+ # "E",
8
+ # # Pyflakes
9
+ # "F",
10
+ # # pyupgrade
11
+ # "UP",
12
+ # # flake8-bugbear
13
+ # "B",
14
+ # # flake8-simplify
15
+ # "SIM",
16
+ # # isort
17
+ # "I",
18
+ # # docstring
19
+ # "D",
20
+ # # TODO
21
+ # "TD",
22
+ #]
23
+
24
+ ignore = [
25
+ "D105", # Do not require magic method documentation
26
+ "ANN002", # Allow non-typed *args
27
+ "ANN003", # Allow non-typed **kwargs
28
+ "ANN204", # Do not require magic method documentation
29
+ "CPY", # Copyright notices
30
+ "EM101", # raw string in exception
31
+ "EM102", # fstrings in exceptions
32
+ "ERA001", # allow commented out code
33
+ "PERF203",# allow try-except in loops
34
+ "TRY003", # raise vanilla
35
+ "TD002", # TODO author
36
+ "TD003", # TODO link
37
+ "TD004", # TODO colon
38
+ "FIX001", # Fixme
39
+ "FIX002", # TODO
40
+ ]
41
+
42
+ [lint.extend-per-file-ignores]
43
+ "test/**/*.py" = [
44
+ "D100", # undocumented public module
45
+ "D103", # undocumented public function
46
+ "D104", # undocumented public package
47
+ "ANN001", # no type annotations for function arguments
48
+ "ANN201", # no return type for public function
49
+ "S101", # asserts allowed
50
+ "SLF001", # Allow private member access in unit tests
51
+ "PLR2004", # magic values allowed in comparisons
52
+ "PT011", # pytest.raises can be broad
53
+ "PT013", # allow sub-imports of pytest
54
+ "PLR0913", # allow many arguments in parameterized tests
55
+ ]
56
+
57
+ [lint.pydocstyle]
58
+ convention = "google"
59
+ ignore-var-parameters = true
60
+ ignore-decorators = ["typing.overload"]
@@ -0,0 +1,15 @@
1
+ {
2
+ "python.testing.pytestArgs": ["test"],
3
+ "python.testing.unittestEnabled": false,
4
+ "python.testing.pytestEnabled": true,
5
+ "editor.rulers": [88],
6
+ "[python]": {
7
+ "editor.formatOnSave": true,
8
+ "editor.defaultFormatter": "charliermarsh.ruff",
9
+ "editor.codeActionsOnSave": {
10
+ "source.fixAll": "explicit",
11
+ "source.organizeImports.ruff": "explicit"
12
+ }
13
+ },
14
+ "ruff.interpreter": ["${workspaceFolder}/.venv/bin/python"]
15
+ }
@@ -0,0 +1,124 @@
1
+ # Contributing
2
+
3
+ Thanks for your interest in contributing! This guide covers everything you need
4
+ to get started.
5
+
6
+ ## Setup
7
+
8
+ Requires **Python 3.12+** and **bash**.
9
+
10
+ ```bash
11
+ git clone <repo-url>
12
+ cd <repo>
13
+ uv sync
14
+ ```
15
+
16
+ `uv sync` creates a virtual environment with [uv](https://docs.astral.sh/uv/),
17
+ installs all dependencies (including any editable local siblings), and wires up
18
+ the git hooks automatically: a [hatchling](https://hatch.pypa.io/) build hook
19
+ (`.hatch_build.py`) runs during the editable install and points
20
+ `core.hooksPath` at the tracked `.githooks/` directory. No separate
21
+ `pre-commit install` step is needed. To run commands in the environment, prefix
22
+ them with `uv run` (for example `uv run pytest`), or activate it with
23
+ `source .venv/bin/activate`.
24
+
25
+ To start fresh, delete `.venv/` and run `uv sync` again.
26
+
27
+ > **Note (editable local siblings):** some `gri-*` projects depend on sibling
28
+ > packages as editable path sources. uv requires `[tool.uv]`
29
+ > `config-settings = { editable_mode = "compat" }` in `pyproject.toml` for those
30
+ > to import correctly. If a project declares editable path dependencies and
31
+ > imports fail, verify that setting is present.
32
+
33
+ ## Running Tests
34
+
35
+ [pytest](https://docs.pytest.org/) with
36
+ [pytest-cov](https://pypi.org/project/pytest-cov/) for coverage:
37
+
38
+ ```bash
39
+ pytest --cov # terminal summary
40
+ pytest --cov --cov-report html # detailed HTML report in .cov_html/
41
+ ```
42
+
43
+ CI requires **70% minimum** coverage. Open the HTML report to see exactly which
44
+ lines are covered:
45
+
46
+ ```bash
47
+ xdg-open .cov_html/index.html # or: google-chrome, firefox
48
+ ```
49
+
50
+ ## Linting and Type Checking
51
+
52
+ ### Ruff
53
+
54
+ [Ruff](https://docs.astral.sh/ruff/) handles both linting and formatting. The
55
+ configuration selects **all rules** (`select = ["ALL"]`) with a small ignore
56
+ list in `.ruff.toml`.
57
+
58
+ ```bash
59
+ ruff check # lint
60
+ ruff format # format
61
+ ```
62
+
63
+ ### ty
64
+
65
+ [ty](https://docs.astral.sh/ty/) for static type checking:
66
+
67
+ ```bash
68
+ ty check
69
+ ```
70
+
71
+ ### Pre-push hooks
72
+
73
+ All of the above run automatically on `git push` via
74
+ [pre-commit](https://pre-commit.com/) (configured for the `pre-push` stage, so
75
+ commits stay fast). If the push fails, fix the issues, commit the fix, and push
76
+ again.
77
+
78
+ ```bash
79
+ uv run pre-commit run --all-files --hook-stage pre-push # run manually
80
+ ```
81
+
82
+ > **Tip:** To force a push past the hooks (e.g. a work-in-progress branch), use
83
+ > `git push --no-verify`. CI will still enforce the same checks.
84
+
85
+ ## Code Style
86
+
87
+ - **Docstrings**: [Google convention](https://google.github.io/styleguide/pyguide.html#38-comments-and-docstrings)
88
+ - **Type hints**: Required on all public function signatures
89
+ - **Line length**: 88 characters max
90
+ - **Formatting**: Let `ruff format` handle it
91
+ - **Imports**: All at the top of the file, organized by ruff's isort rules
92
+
93
+ ## Submitting Changes
94
+
95
+ 1. Create a branch from `main`
96
+ 2. Make your changes with tests
97
+ 3. Ensure `pytest --cov` passes with >= 70% coverage
98
+ 4. Ensure `uv run pre-commit run --all-files --hook-stage pre-push` passes
99
+ 5. Open a merge request against `main`
100
+
101
+ ## IDE Setup (VS Code)
102
+
103
+ Install the [Ruff extension](https://marketplace.visualstudio.com/items?itemName=charliermarsh.ruff)
104
+ by Astral and add to your `settings.json`:
105
+
106
+ ```json
107
+ {
108
+ "[python]": {
109
+ "editor.formatOnSave": true,
110
+ "editor.defaultFormatter": "charliermarsh.ruff",
111
+ "editor.codeActionsOnSave": {
112
+ "source.fixAll": "explicit",
113
+ "source.organizeImports.ruff": "explicit"
114
+ }
115
+ },
116
+ "ruff.interpreter": ["${workspaceFolder}/.venv/bin/python"]
117
+ }
118
+ ```
119
+
120
+ For type checking in VS Code, install the
121
+ [ty extension](https://marketplace.visualstudio.com/items?itemName=astral-sh.ty)
122
+ by Astral. To avoid conflicts with Pylance, either let ty disable the Python
123
+ language server (the default) or keep Pylance and set
124
+ `"ty.disableLanguageServices": true` to use ty for diagnostics only.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 GeoSol Research Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.