Skip to content

Commit 01f5df2

Browse files
committed
Maintenance
1 parent c38c927 commit 01f5df2

20 files changed

Lines changed: 801 additions & 465 deletions

.github/workflows/main.yml

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,37 +12,44 @@ jobs:
1212
strategy:
1313
fail-fast: false
1414
matrix:
15-
python-version: [3.5, 3.6, 3.7, 3.8, 3.9]
15+
python-version: ["3.9", "3.10", "3.11", "3.12"]
1616

1717
runs-on: ubuntu-latest
1818

1919
steps:
20-
- uses: actions/checkout@v2
20+
- uses: actions/checkout@v5
2121

2222
- name: Set up Python ${{ matrix.python-version }}
23-
uses: actions/setup-python@v2
23+
uses: actions/setup-python@v5
2424
with:
2525
python-version: ${{ matrix.python-version }}
2626
architecture: x64
27+
cache: "pip"
2728

2829
- name: Display Python version
2930
run: python -c "import sys; print(sys.version)"
3031

3132
- name: Install dependencies
3233
run: |
3334
sudo apt-get update -y
34-
sudo apt-get install -y python-dev libgeos-dev
35-
pip install -U pip setuptools
36-
pip install -r dev-requirements.txt
35+
sudo apt-get install -y python-dev-is-python3 libgeos-dev
36+
pip install -e ".[dev]"
3737
3838
- name: Lint
39-
run: flake8
39+
run: ruff check ./quantized_mesh_tile ./tests ./scripts
4040

41-
- name: Isort
42-
run: isort --check-only --diff .
41+
- name: "Lint Code (pylint)"
42+
run: |
43+
pylint quantized_mesh_tile tests
44+
- name: Benchmark
45+
run: |
46+
python scripts/qmt_bench.py --vertices 50000 --triangles 100000 --iters 100
4347
44-
- name: Test
45-
run: coverage run --source=quantized_mesh_tile setup.py test
48+
- name: Test with pytest
49+
run: |
50+
echo "[run]
51+
relative_files = True" > .coveragerc
52+
pytest --cov-report term-missing --cov=. tests/
4653
4754
- name: Coveralls
4855
uses: AndreMiras/coveralls-python-action@develop

.github/workflows/release.yml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
name: Release to PyPI
2+
3+
on:
4+
## when a release is published (not drafts)
5+
release:
6+
types: [published]
7+
8+
# Permission can be added at job level or workflow level
9+
permissions:
10+
id-token: write # This is required for requesting the JWT
11+
contents: read # This is required for actions/checkout
12+
13+
jobs:
14+
release:
15+
runs-on: ubuntu-latest
16+
17+
steps:
18+
- name: Checkout
19+
uses: actions/checkout@v6
20+
- name: Set up Python 3.11
21+
uses: actions/setup-python@v6
22+
with:
23+
python-version: "3.11"
24+
- name: Install dependencies
25+
run: |
26+
python -m pip install --upgrade pip
27+
pip install -e ".[dev]"
28+
- name: Build package
29+
run: |
30+
pip install build twine
31+
python -m build
32+
33+
- name: Publish to PyPI
34+
env:
35+
TWINE_USERNAME: __token__
36+
TWINE_PASSWORD: ${{ secrets.PYPI_SECRET_KEY }}
37+
run: |
38+
twine upload dist/*

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,8 @@ doc/build/
1010
.coverage
1111
.vscode
1212
.eggs
13+
.egg-info
14+
.ruff_cache
15+
.pytest_cache
16+
.vscode
17+

CHANGELOG.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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+
## [0.7.0] - 2025-02-05
11+
12+
### Added
13+
14+
- Custom exception classes for better error handling: `TerrainTileError`, `InvalidGeometryError`, `MissingDimensionError`
15+
- Benchmark script (`scripts/qmt_bench.py`) for performance testing encode/decode operations
16+
- Type hints in `bbsphere.py`
17+
- PyPI long description from README.md
18+
- Pip caching in CI workflow
19+
20+
### Changed
21+
22+
- **BREAKING**: Minimum Python version is now 3.9 (dropped support for Python 3.5-3.8)
23+
- **BREAKING**: Minimum dependency versions: `numpy>=2.0.0`, `shapely>=2.0.0`
24+
- Migrated from `setup.py` to `pyproject.toml` (PEP 517/518)
25+
- Replaced flake8/isort with ruff for linting
26+
- Updated GitHub Actions from v2 to v5
27+
- Switched test runner from nose to pytest
28+
- Improved vertex lookup performance by using tuples instead of string keys in `TerrainTopology`
29+
- Code style updates: double quotes, trailing commas, improved multi-line formatting
30+
31+
### Fixed
32+
33+
- Edge indices metadata incorrectly used `indexData32` instead of `EdgeIndices32` for 32-bit tiles
34+
- `BYTESPLIT` constant typo (was 65636, now correctly 65536)
35+
- Mutable default arguments in `encode()` function
36+
- Shadowing of `type` builtin in `utils.py`
37+
38+
### Removed
39+
40+
- Shapely speedups import (deprecated in Shapely 2.0, now always enabled)
41+
- Legacy `setup.py`, `setup.cfg`, `dev-requirements.txt`, and `requirements.txt` files
42+
43+
## [0.6.1] - Previous release
44+
45+
See [GitHub releases](https://github.com/loicgasser/quantized-mesh-tile/releases) for earlier versions.
46+
47+
[Unreleased]: https://github.com/loicgasser/quantized-mesh-tile/compare/v0.7.0...HEAD
48+
[0.7.0]: https://github.com/loicgasser/quantized-mesh-tile/compare/v0.6.1...v0.7.0
49+
[0.6.1]: https://github.com/loicgasser/quantized-mesh-tile/releases/tag/v0.6.1

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,7 @@ quantized-mesh-tile
88
[Quantized-mesh-tile](https://github.com/AnalyticalGraphicsInc/quantized-mesh) is a Python encoder/decoder and topology builder for terrain tiles.
99

1010
Doc is hosted on Readthedocs: https://quantized-mesh-tile.readthedocs.io/en/latest/
11+
12+
## Changelog
13+
14+
See [CHANGELOG.md](CHANGELOG.md) for version history.

dev-requirements.txt

Lines changed: 0 additions & 8 deletions
This file was deleted.

pyproject.toml

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
[build-system]
2+
requires = ["setuptools>=40.6.0", "wheel"]
3+
build-backend = "setuptools.build_meta"
4+
5+
[project]
6+
name = "quantized-mesh-tile"
7+
version = "0.7.0"
8+
description = "Quantized-Mesh format reader and writer"
9+
readme = "README.md"
10+
authors = [{ name = "Loïc Gasser", email = "loicgasser4@gmail.com" }]
11+
license = { text = "MIT" }
12+
classifiers = [
13+
"Development Status :: Beta",
14+
"Environment :: Plugins",
15+
"Operating System :: OS Independent",
16+
"Programming Language :: Python",
17+
"Programming Language :: Python :: 3.9",
18+
"Programming Language :: Python :: 3.10",
19+
"Programming Language :: Python :: 3.11",
20+
"Programming Language :: Python :: 3.12",
21+
"Intended Audience :: Information Technology",
22+
"License :: OSI Approved :: MIT License",
23+
"Topic :: Scientific/Engineering :: GIS",
24+
]
25+
requires-python = ">=3.9,<4.0"
26+
dependencies = ["numpy>=2.0.0", "shapely>=2.0.0"]
27+
28+
[project.optional-dependencies]
29+
dev = [
30+
"coveralls",
31+
"mock",
32+
"pylint",
33+
"sphinx",
34+
"sphinx_rtd_theme",
35+
"sphinx-autobuild",
36+
"ruff",
37+
"pytest",
38+
"pytest-cov",
39+
]
40+
41+
[tool.ruff]
42+
src = ["quantized_mesh_tile", "tests", "scripts"]
43+
exclude = [
44+
".egg",
45+
".git",
46+
".tox",
47+
".venv",
48+
"venv",
49+
"_build",
50+
"buck-out",
51+
"build",
52+
"dist",
53+
"migrations",
54+
]
55+
line-length = 90
56+
57+
[tool.pylint.messages_control]
58+
# W0511: Used when a warning note as FIXME or XXX is detected.
59+
disable = ['C', 'R', 'W0511']

quantized_mesh_tile/__init__.py

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,17 @@
55
---------
66
"""
77

8-
# Enable Shapely "speedups" if available
9-
# http://toblerity.org/shapely/manual.html#performance
10-
from shapely import speedups
11-
128
from .terrain import TerrainTile
139
from .topology import TerrainTopology
1410

15-
if speedups.available:
16-
speedups.enable()
17-
1811

19-
def encode(geometries, bounds=[], autocorrectGeometries=False, hasLighting=False,
20-
watermask=[]):
12+
def encode(
13+
geometries,
14+
bounds=None,
15+
autocorrectGeometries=False,
16+
hasLighting=False,
17+
watermask=None,
18+
):
2119
"""
2220
Function to convert geometries into a
2321
:class:`quantized_mesh_tile.terrain.TerrainTile` instance.
@@ -65,14 +63,26 @@ def encode(geometries, bounds=[], autocorrectGeometries=False, hasLighting=False
6563
Default is `[]`.
6664
6765
"""
68-
topology = TerrainTopology(geometries=geometries,
69-
autocorrectGeometries=autocorrectGeometries,
70-
hasLighting=hasLighting)
66+
if bounds is None:
67+
bounds = []
68+
if watermask is None:
69+
watermask = []
70+
71+
topology = TerrainTopology(
72+
geometries=geometries,
73+
autocorrectGeometries=autocorrectGeometries,
74+
hasLighting=hasLighting,
75+
)
7176
if len(bounds) == 4:
7277
west, south, east, north = bounds
73-
tile = TerrainTile(topology=topology,
74-
watermask=watermask,
75-
west=west, south=south, east=east, north=north)
78+
tile = TerrainTile(
79+
topology=topology,
80+
watermask=watermask,
81+
west=west,
82+
south=south,
83+
east=east,
84+
north=north,
85+
)
7686
else:
7787
tile = TerrainTile(topology=topology, watermask=watermask)
7888
return tile
@@ -109,5 +119,6 @@ def decode(filePath, bounds, hasLighting=False, hasWatermask=False, gzipped=Fals
109119
west, south, east, north = bounds
110120
tile = TerrainTile(west=west, south=south, east=east, north=north)
111121
tile.fromFile(
112-
filePath, hasLighting=hasLighting, hasWatermask=hasWatermask, gzipped=gzipped)
122+
filePath, hasLighting=hasLighting, hasWatermask=hasWatermask, gzipped=gzipped
123+
)
113124
return tile

quantized_mesh_tile/bbsphere.py

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
# -*- coding: utf-8 -*-
22

33
import math
4+
from typing import List
5+
6+
from quantized_mesh_tile.exceptions import TerrainTileError
47

58
from . import cartesian3d as c3d
69

710

811
class BoundingSphere(object):
9-
def __init__(self, *args, **kwargs):
10-
MAX = float('infinity')
11-
MIN = float('-infinity')
12+
def __init__(self, *_, **kwargs):
13+
MAX = float("infinity")
14+
MIN = float("-infinity")
1215

13-
self.center = kwargs.get('center', [])
14-
self.radius = kwargs.get('radius', 0)
16+
self.center = kwargs.get("center", [])
17+
self.radius = kwargs.get("radius", 0)
1518
self.minPointX = [MAX, MAX, MAX]
1619
self.minPointY = [MAX, MAX, MAX]
1720
self.minPointZ = [MAX, MAX, MAX]
@@ -20,11 +23,10 @@ def __init__(self, *args, **kwargs):
2023
self.maxPointZ = [MIN, MIN, MIN]
2124

2225
# Based on Ritter's algorithm
23-
def fromPoints(self, points):
24-
26+
def fromPoints(self, points: List):
2527
nbPositions = len(points)
2628
if nbPositions < 2:
27-
raise Exception('Your list of points must contain at least 2 points')
29+
raise TerrainTileError("Your list of points must contain at least 2 points")
2830

2931
for i in range(0, nbPositions):
3032
point = points[i]
@@ -69,7 +71,7 @@ def fromPoints(self, points):
6971
ritterCenter = [
7072
(diameter1[0] + diameter2[0]) * 0.5,
7173
(diameter1[1] + diameter2[1]) * 0.5,
72-
(diameter1[2] + diameter2[2]) * 0.5
74+
(diameter1[2] + diameter2[2]) * 0.5,
7375
]
7476

7577
radiusSquared = c3d.magnitudeSquared(c3d.subtract(diameter2, ritterCenter))
@@ -99,12 +101,12 @@ def fromPoints(self, points):
99101
# Calculate center of new Ritter sphere
100102
oldToNew = oldCenterToPoint - ritterRadius
101103
ritterCenter = [
102-
(ritterRadius * ritterCenter[0] +
103-
oldToNew * currentP[0]) / oldCenterToPoint,
104-
(ritterRadius * ritterCenter[1] +
105-
oldToNew * currentP[1]) / oldCenterToPoint,
106-
(ritterRadius * ritterCenter[2] +
107-
oldToNew * currentP[2]) / oldCenterToPoint
104+
(ritterRadius * ritterCenter[0] + oldToNew * currentP[0])
105+
/ oldCenterToPoint,
106+
(ritterRadius * ritterCenter[1] + oldToNew * currentP[1])
107+
/ oldCenterToPoint,
108+
(ritterRadius * ritterCenter[2] + oldToNew * currentP[2])
109+
/ oldCenterToPoint,
108110
]
109111

110112
# Keep the naive sphere if smaller

quantized_mesh_tile/exceptions.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
class TerrainTileError(Exception):
2+
"""Base class for exceptions in this module."""
3+
4+
5+
class InvalidGeometryError(ValueError):
6+
"""Exception raised for invalid geometries."""
7+
8+
9+
class MissingDimensionError(ValueError):
10+
"""Exception raised for missing dimensions in geometries."""

0 commit comments

Comments
 (0)