griffine 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.
@@ -0,0 +1,40 @@
1
+ name: "Continuous integration"
2
+
3
+ concurrency:
4
+ group: ${{ github.ref }}
5
+ cancel-in-progress: false
6
+
7
+ on:
8
+ push:
9
+ branches:
10
+ - main
11
+ pull_request:
12
+
13
+ jobs:
14
+ ci:
15
+ name: Continuous integration
16
+ runs-on: ubuntu-latest
17
+ strategy:
18
+ matrix:
19
+ python-version:
20
+ - "3.11"
21
+ - "3.12"
22
+ - "3.13"
23
+ steps:
24
+ - uses: actions/checkout@v4
25
+ - uses: astral-sh/setup-uv@v5
26
+ with:
27
+ python-version: ${{ matrix.python-version }}
28
+ - name: Sync
29
+ run: uv sync
30
+ - name: Pre-Commit Hooks
31
+ run: uv run pre-commit run --all-files
32
+ - name: Test
33
+ run: uv run pytest
34
+ - name: "Upload coverage to Codecov"
35
+ uses: codecov/codecov-action@v4
36
+ env:
37
+ CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
38
+ with:
39
+ fail_ci_if_error: false
40
+ verbose: true
@@ -0,0 +1,32 @@
1
+ name: Build and release
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ pull_request:
8
+ branches:
9
+ - main
10
+ release:
11
+ types:
12
+ - published
13
+
14
+ jobs:
15
+ build-package:
16
+ runs-on: ubuntu-latest
17
+ environment:
18
+ name: pypi
19
+ url: https://pypi.org/p/griffine
20
+ permissions:
21
+ id-token: write
22
+ steps:
23
+ - uses: actions/checkout@v4
24
+ - uses: astral-sh/setup-uv@v5
25
+ - name: Create virtual environment
26
+ run: uv venv
27
+ - name: Install build
28
+ run: uv pip install build
29
+ - name: Build
30
+ run: .venv/bin/python -m build
31
+ - uses: pypa/gh-action-pypi-publish@release/v1
32
+ if: startsWith(github.ref, 'refs/tags')
@@ -0,0 +1,127 @@
1
+ __version__.py
2
+
3
+ # Byte-compiled / optimized / DLL files
4
+ __pycache__/
5
+ *.py[cod]
6
+ *$py.class
7
+
8
+ # C extensions
9
+ *.so
10
+
11
+ # Distribution / packaging
12
+ .Python
13
+ build/
14
+ develop-eggs/
15
+ dist/
16
+ downloads/
17
+ eggs/
18
+ .eggs/
19
+ lib/
20
+ lib64/
21
+ parts/
22
+ sdist/
23
+ var/
24
+ wheels/
25
+ share/python-wheels/
26
+ *.egg-info/
27
+ .installed.cfg
28
+ *.egg
29
+ MANIFEST
30
+
31
+ # PyInstaller
32
+ # Usually these files are written by a python script from a template
33
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
34
+ *.manifest
35
+ *.spec
36
+
37
+ # Installer logs
38
+ pip-log.txt
39
+ pip-delete-this-directory.txt
40
+
41
+ # Unit test / coverage reports
42
+ htmlcov/
43
+ .tox/
44
+ .nox/
45
+ .coverage
46
+ .coverage.*
47
+ .cache
48
+ nosetests.xml
49
+ coverage.xml
50
+ *.cover
51
+ *.py,cover
52
+ .hypothesis/
53
+ .pytest_cache/
54
+ cover/
55
+
56
+ # Translations
57
+ *.mo
58
+ *.pot
59
+
60
+ # Scrapy stuff:
61
+ .scrapy
62
+
63
+ # Sphinx documentation
64
+ docs/_build/
65
+
66
+ # PyBuilder
67
+ .pybuilder/
68
+ target/
69
+
70
+ # Jupyter Notebook
71
+ .ipynb_checkpoints
72
+
73
+ # IPython
74
+ profile_default/
75
+ ipython_config.py
76
+
77
+ # pyenv
78
+ # For a library or package, you might want to ignore these files since the code is
79
+ # intended to run in multiple environments; otherwise, check them in:
80
+ .python-version
81
+
82
+ # pdm
83
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
84
+ #pdm.lock
85
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
86
+ # in version control.
87
+ # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
88
+ .pdm.toml
89
+ .pdm-python
90
+ .pdm-build/
91
+
92
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
93
+ __pypackages__/
94
+
95
+ # Environments
96
+ .env
97
+ .env.*
98
+ .venv
99
+ env/
100
+ venv/
101
+ ENV/
102
+ env.bak/
103
+ venv.bak/
104
+
105
+ # Spyder project settings
106
+ .spyderproject
107
+ .spyproject
108
+
109
+ # Rope project settings
110
+ .ropeproject
111
+
112
+ # mkdocs documentation
113
+ /site
114
+
115
+ # mypy
116
+ .mypy_cache/
117
+ .dmypy.json
118
+ dmypy.json
119
+
120
+ # Pyre type checker
121
+ .pyre/
122
+
123
+ # pytype static type analyzer
124
+ .pytype/
125
+
126
+ # Cython debug symbols
127
+ cython_debug/
@@ -0,0 +1,50 @@
1
+ repos:
2
+ - repo: local
3
+ hooks:
4
+ - id: ruff_check
5
+ name: ruff check
6
+ entry: ruff check --force-exclude
7
+ language: python
8
+ 'types_or': [python, pyi]
9
+ args: [--fix, --exit-non-zero-on-fix]
10
+ require_serial: true
11
+ - id: ruff_format
12
+ name: ruff format
13
+ entry: ruff format --force-exclude
14
+ language: python
15
+ 'types_or': [python, pyi]
16
+ args: []
17
+ require_serial: true
18
+ - id: check-added-large-files
19
+ name: Check for added large files
20
+ entry: check-added-large-files
21
+ language: system
22
+ - id: check-toml
23
+ name: Check Toml
24
+ entry: check-toml
25
+ language: system
26
+ types: [toml]
27
+ - id: check-yaml
28
+ name: Check Yaml
29
+ entry: check-yaml
30
+ language: system
31
+ types: [yaml]
32
+ - id: end-of-file-fixer
33
+ name: Fix End of Files
34
+ entry: end-of-file-fixer
35
+ language: system
36
+ types: [text]
37
+ stages: [pre-commit, pre-push, manual]
38
+ - id: trailing-whitespace
39
+ name: Trim Trailing Whitespace
40
+ entry: trailing-whitespace-fixer
41
+ language: system
42
+ types: [text]
43
+ stages: [pre-commit, pre-push, manual]
44
+ - id: mypy
45
+ name: mypy
46
+ entry: mypy
47
+ language: python
48
+ 'types_or': [python, pyi]
49
+ args: []
50
+ require_serial: true
@@ -0,0 +1,27 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ### Added
11
+
12
+ ### Changed
13
+
14
+ ### Deprecated
15
+
16
+ ### Removed
17
+
18
+ ### Fixed
19
+
20
+ ### Security
21
+
22
+ ## [0.0.1] - 2025-04-24
23
+
24
+ Initial release 🎉
25
+
26
+ [unreleased]: https://github.com/jkeifer/griffine/compare/v0.1.0...HEAD
27
+ [0.0.1]: https://github.com/jkeifer/griffine/releases/tag/v0.1.0
griffine-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Jarrett Keifer
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.
@@ -0,0 +1,152 @@
1
+ Metadata-Version: 2.4
2
+ Name: griffine
3
+ Version: 0.1.0
4
+ Summary: Utilities for working with raster grids defined by affine transforms
5
+ Project-URL: Repository, https://github.com/jkeifer/griffine
6
+ Author-email: Jarrett Keifer <jkeifer0@gmail.com>
7
+ License: MIT
8
+ License-File: LICENSE
9
+ Requires-Python: >=3.11
10
+ Requires-Dist: affine>=2.4.0
11
+ Requires-Dist: pygeoif>=1.5.1
12
+ Description-Content-Type: text/markdown
13
+
14
+ # griffine
15
+
16
+ [![build-status-image]][build-status]
17
+ [![coverage-status-image]][codecov]
18
+ [![pypi-version]][pypi]
19
+
20
+ <img src="./images/logo.svg" width=300>
21
+
22
+ Utilities for working with *gri*ds that have a*ffine* transforms, typically for
23
+ working with rasters or other gridded data.
24
+
25
+ ## Installation
26
+
27
+ This package is distributed on pypi and can be `pip`-installed:
28
+
29
+ ```commandline
30
+ pip install griffine
31
+ ```
32
+
33
+ ## Usage
34
+
35
+ This library is composed of several major classes:
36
+
37
+ * `Grid`
38
+ * `TiledGrid`
39
+ * `AffineGrid`
40
+ * `TiledAffineGrid`
41
+ * `Point`
42
+ * `Cell` and `AffineCell`
43
+ * `Tile` and `AffineTile`
44
+
45
+ `Grid` represents a two-dimensional grid of `Cell`s, with a size defined by its
46
+ `cols` and `rows`. A `TiledGrid` is, effectively, a grid of grids. Each cell in
47
+ a `TiledGrid` is a `Tile`. `Tiles` are both `Cell`s and `Grid`s, where the tile
48
+ grid is a subset of the larger `Grid` that has been tiled.
49
+
50
+ `AffineGrid` is a `Grid` with the addition of an affine transformation to allow
51
+ transformations between grid/image space and model space (in the case of
52
+ geospatial data, model space would be the raster's coordinate reference
53
+ system). `AffineGrids` allow looking up the `Point` represented by a `Cell`
54
+ (using its origin, centroid, or antiorigin), or the `Cell` containing a
55
+ `Point`.
56
+
57
+ A `TiledAffineGrid` is to an `AffineGrid` as a `TiledGrid` is to a `Grid`: each
58
+ `AffileTile` in a `TiledAffineGrid` is an `AffineGrid` representing some subset
59
+ of the larger `AffineGrid` that was tiled. `TiledAffineGrids` allow finding the
60
+ `AffineTile` containing` a `Cell` or a `Point`.
61
+
62
+ `griffine` does not handle coordinate systems and thus does not do any
63
+ reprojection. It is expected that users ensure they are using a consistent CRS
64
+ between the affine transforms of their grid and any points.
65
+
66
+ The [Python `__geo_interface__`
67
+ protocol](https://gist.github.com/sgillies/2217756) is supported by all
68
+ operations accepting a `Point` and on the `Point` class itself, to easily allow
69
+ using or casting to point geometries from other Python libraries (`shapely`,
70
+ `odc-geo`, etc.).
71
+ affine_grid = grid.add_transform(transform)affine_cell.antiorigin
72
+ ### Examples
73
+
74
+ ```python
75
+ # Affine re-exported from the affine package
76
+ # Point is re-exported from the pygeoif package
77
+ from griffine import Affine, Grid, Point
78
+
79
+ # 10m pixel grid in UTM coordinates
80
+ transform = Affine(10, 0, 200000, 0, -10, 5100000)
81
+
82
+ # First we create a grid!
83
+ grid = Grid(10000, 5000)
84
+
85
+ # We can grab a cell from the grid using index notation.
86
+ cell = grid[424, 2343]
87
+ cell.row # 424
88
+ cell.col # 2343
89
+
90
+ # We can tile the grid using another grid.
91
+ # In this example we'd get a 10x5 tile grid
92
+ # where each tile is a grid of 1000x1000.
93
+ tile = grid.tile_into(Grid(10, 5))[0, 0]
94
+ tile.size # (1000, 1000)
95
+
96
+ # We can also add an affine transform to our grid.
97
+ # A transform allows converting between grid space and
98
+ # model space (i.e., cell coords and spatial coords).
99
+ affine_grid = grid.add_transform(transform)
100
+ affine_grid.origin # Point(200000.0, 5100000.0)
101
+ affine_grid.centroid # Point(225000.0, 5050000.0)
102
+ affine_grid.antiorigin # Point(250000.0, 5000000.0)
103
+
104
+ # Affine grids also support grabbing a cell via
105
+ # index notation. Affine grids will provide affine
106
+ # cells, which support transform-based operations too.
107
+ affine_cell = affine_grid[0, 0]
108
+ affine_cell.origin # Point(200000.0, 5100000.0)
109
+ affine_cell.centroid # Point(200005.0, 5099995.0)
110
+ affine_cell.antiorigin # Point(200010.0, 5099990.0)
111
+
112
+ # Transform operations can go the other way too.
113
+ # Let's make a point and find its enclosing cell!
114
+ point = Point(223433.2934, 5095752.8931)
115
+ affine_cell = affine_grid.point_to_cell(point)
116
+ affine_cell.row # 424
117
+ affine_cell.col # 2343
118
+
119
+ # Grids can also be tiled via a tile size expressed
120
+ # as a grid. Here we'll get a 10x5 tile grid, but the
121
+ # left and bottom edge tiles will not be full size.
122
+ tiled_affine_grid = affine_grid.tile_via(Grid(1024, 1024))
123
+
124
+ # Affine-enabled tiles and tile grids also support
125
+ # transform-based operations:
126
+ affine_tile = tiled_affine_grid.point_to_tile(point)
127
+ affine_tile.row # 0
128
+ affine_tile.col # 2
129
+ affine_tile_cell = affine_tile.point_to_cell(point)
130
+ affine_tile_cell.row # 424
131
+ affine_tile_cell.col # 2343
132
+ affine_tile_cell.tile_row # 0
133
+ affine_tile_cell.tile_col # 2
134
+
135
+ # We can work our way back up to the original grid
136
+ # from cells and tiles as needed:
137
+ #
138
+ # cell tile tile grid grid
139
+ affine_tile_cell.parent_grid.parent_grid.base_grid is affine_grid # True
140
+ ```
141
+
142
+ ## How to say "griffine"
143
+
144
+ The name of this library is pronounced "grif-fine", as in the words "grift",
145
+ and "fine". It's also okay to say it "grif-feen", as rhymes with "mean".
146
+
147
+ [build-status-image]: https://github.com/jkeifer/griffine/actions/workflows/ci.yml/badge.svg
148
+ [build-status]: https://github.com/jkeifer/griffine/actions/workflows/ci.yml
149
+ [coverage-status-image]: https://img.shields.io/codecov/c/github/jkeifer/griffine/main.svg
150
+ [codecov]: https://codecov.io/github/jkeifer/griffine?branch=main
151
+ [pypi-version]: https://img.shields.io/pypi/v/griffine.svg
152
+ [pypi]: https://pypi.org/project/griffine/
@@ -0,0 +1,139 @@
1
+ # griffine
2
+
3
+ [![build-status-image]][build-status]
4
+ [![coverage-status-image]][codecov]
5
+ [![pypi-version]][pypi]
6
+
7
+ <img src="./images/logo.svg" width=300>
8
+
9
+ Utilities for working with *gri*ds that have a*ffine* transforms, typically for
10
+ working with rasters or other gridded data.
11
+
12
+ ## Installation
13
+
14
+ This package is distributed on pypi and can be `pip`-installed:
15
+
16
+ ```commandline
17
+ pip install griffine
18
+ ```
19
+
20
+ ## Usage
21
+
22
+ This library is composed of several major classes:
23
+
24
+ * `Grid`
25
+ * `TiledGrid`
26
+ * `AffineGrid`
27
+ * `TiledAffineGrid`
28
+ * `Point`
29
+ * `Cell` and `AffineCell`
30
+ * `Tile` and `AffineTile`
31
+
32
+ `Grid` represents a two-dimensional grid of `Cell`s, with a size defined by its
33
+ `cols` and `rows`. A `TiledGrid` is, effectively, a grid of grids. Each cell in
34
+ a `TiledGrid` is a `Tile`. `Tiles` are both `Cell`s and `Grid`s, where the tile
35
+ grid is a subset of the larger `Grid` that has been tiled.
36
+
37
+ `AffineGrid` is a `Grid` with the addition of an affine transformation to allow
38
+ transformations between grid/image space and model space (in the case of
39
+ geospatial data, model space would be the raster's coordinate reference
40
+ system). `AffineGrids` allow looking up the `Point` represented by a `Cell`
41
+ (using its origin, centroid, or antiorigin), or the `Cell` containing a
42
+ `Point`.
43
+
44
+ A `TiledAffineGrid` is to an `AffineGrid` as a `TiledGrid` is to a `Grid`: each
45
+ `AffileTile` in a `TiledAffineGrid` is an `AffineGrid` representing some subset
46
+ of the larger `AffineGrid` that was tiled. `TiledAffineGrids` allow finding the
47
+ `AffineTile` containing` a `Cell` or a `Point`.
48
+
49
+ `griffine` does not handle coordinate systems and thus does not do any
50
+ reprojection. It is expected that users ensure they are using a consistent CRS
51
+ between the affine transforms of their grid and any points.
52
+
53
+ The [Python `__geo_interface__`
54
+ protocol](https://gist.github.com/sgillies/2217756) is supported by all
55
+ operations accepting a `Point` and on the `Point` class itself, to easily allow
56
+ using or casting to point geometries from other Python libraries (`shapely`,
57
+ `odc-geo`, etc.).
58
+ affine_grid = grid.add_transform(transform)affine_cell.antiorigin
59
+ ### Examples
60
+
61
+ ```python
62
+ # Affine re-exported from the affine package
63
+ # Point is re-exported from the pygeoif package
64
+ from griffine import Affine, Grid, Point
65
+
66
+ # 10m pixel grid in UTM coordinates
67
+ transform = Affine(10, 0, 200000, 0, -10, 5100000)
68
+
69
+ # First we create a grid!
70
+ grid = Grid(10000, 5000)
71
+
72
+ # We can grab a cell from the grid using index notation.
73
+ cell = grid[424, 2343]
74
+ cell.row # 424
75
+ cell.col # 2343
76
+
77
+ # We can tile the grid using another grid.
78
+ # In this example we'd get a 10x5 tile grid
79
+ # where each tile is a grid of 1000x1000.
80
+ tile = grid.tile_into(Grid(10, 5))[0, 0]
81
+ tile.size # (1000, 1000)
82
+
83
+ # We can also add an affine transform to our grid.
84
+ # A transform allows converting between grid space and
85
+ # model space (i.e., cell coords and spatial coords).
86
+ affine_grid = grid.add_transform(transform)
87
+ affine_grid.origin # Point(200000.0, 5100000.0)
88
+ affine_grid.centroid # Point(225000.0, 5050000.0)
89
+ affine_grid.antiorigin # Point(250000.0, 5000000.0)
90
+
91
+ # Affine grids also support grabbing a cell via
92
+ # index notation. Affine grids will provide affine
93
+ # cells, which support transform-based operations too.
94
+ affine_cell = affine_grid[0, 0]
95
+ affine_cell.origin # Point(200000.0, 5100000.0)
96
+ affine_cell.centroid # Point(200005.0, 5099995.0)
97
+ affine_cell.antiorigin # Point(200010.0, 5099990.0)
98
+
99
+ # Transform operations can go the other way too.
100
+ # Let's make a point and find its enclosing cell!
101
+ point = Point(223433.2934, 5095752.8931)
102
+ affine_cell = affine_grid.point_to_cell(point)
103
+ affine_cell.row # 424
104
+ affine_cell.col # 2343
105
+
106
+ # Grids can also be tiled via a tile size expressed
107
+ # as a grid. Here we'll get a 10x5 tile grid, but the
108
+ # left and bottom edge tiles will not be full size.
109
+ tiled_affine_grid = affine_grid.tile_via(Grid(1024, 1024))
110
+
111
+ # Affine-enabled tiles and tile grids also support
112
+ # transform-based operations:
113
+ affine_tile = tiled_affine_grid.point_to_tile(point)
114
+ affine_tile.row # 0
115
+ affine_tile.col # 2
116
+ affine_tile_cell = affine_tile.point_to_cell(point)
117
+ affine_tile_cell.row # 424
118
+ affine_tile_cell.col # 2343
119
+ affine_tile_cell.tile_row # 0
120
+ affine_tile_cell.tile_col # 2
121
+
122
+ # We can work our way back up to the original grid
123
+ # from cells and tiles as needed:
124
+ #
125
+ # cell tile tile grid grid
126
+ affine_tile_cell.parent_grid.parent_grid.base_grid is affine_grid # True
127
+ ```
128
+
129
+ ## How to say "griffine"
130
+
131
+ The name of this library is pronounced "grif-fine", as in the words "grift",
132
+ and "fine". It's also okay to say it "grif-feen", as rhymes with "mean".
133
+
134
+ [build-status-image]: https://github.com/jkeifer/griffine/actions/workflows/ci.yml/badge.svg
135
+ [build-status]: https://github.com/jkeifer/griffine/actions/workflows/ci.yml
136
+ [coverage-status-image]: https://img.shields.io/codecov/c/github/jkeifer/griffine/main.svg
137
+ [codecov]: https://codecov.io/github/jkeifer/griffine?branch=main
138
+ [pypi-version]: https://img.shields.io/pypi/v/griffine.svg
139
+ [pypi]: https://pypi.org/project/griffine/