From 8abcee5b68a26fe8012255f1467ebc6de59c5882 Mon Sep 17 00:00:00 2001 From: tang zhixiong Date: Sat, 31 Jan 2026 09:44:53 +0800 Subject: [PATCH 01/18] add coordinate transform functions for GeoJSON types Add unified coordinate transformation support for all GeoJSON geometry types: - to_enu/to_wgs84: WGS84 <-> ENU coordinate conversion - rotate: 3x3 rotation matrix - translate: 3D translation - scale: 3D scaling - affine: 4x4 affine transformation - transform: custom Python callable for arbitrary transformations All methods modify coordinates in-place and return self for chaining. Co-Authored-By: Claude (claude-opus-4.5) --- src/geobuf/geojson_transform.hpp | 269 +++++++++++++++++++++++++ src/pybind11_geojson.cpp | 116 +++++++++++ tests/test_transform.py | 327 +++++++++++++++++++++++++++++++ 3 files changed, 712 insertions(+) create mode 100644 src/geobuf/geojson_transform.hpp create mode 100644 tests/test_transform.py diff --git a/src/geobuf/geojson_transform.hpp b/src/geobuf/geojson_transform.hpp new file mode 100644 index 0000000..9f53bac --- /dev/null +++ b/src/geobuf/geojson_transform.hpp @@ -0,0 +1,269 @@ +#pragma once + +// https://github.com/microsoft/vscode-cpptools/issues/9692 +#if __INTELLISENSE__ +#undef __ARM_NEON +#undef __ARM_NEON__ +#endif + +#include "geojson_helpers.hpp" +#include +#include + +namespace cubao +{ + +// Matrix transform function type +using MatrixTransformFn = std::function)>; + +// Forward declarations +inline void transform_coords(std::vector &coords, + const MatrixTransformFn &fn); +inline void transform_coords(mapbox::geojson::point &pt, + const MatrixTransformFn &fn); +inline void transform_coords(mapbox::geojson::multi_point &geom, + const MatrixTransformFn &fn); +inline void transform_coords(mapbox::geojson::line_string &geom, + const MatrixTransformFn &fn); +inline void transform_coords(mapbox::geojson::linear_ring &geom, + const MatrixTransformFn &fn); +inline void transform_coords(mapbox::geojson::multi_line_string &geom, + const MatrixTransformFn &fn); +inline void transform_coords(mapbox::geojson::polygon &geom, + const MatrixTransformFn &fn); +inline void transform_coords(mapbox::geojson::multi_polygon &geom, + const MatrixTransformFn &fn); +inline void transform_coords(mapbox::geojson::geometry_collection &gc, + const MatrixTransformFn &fn); +inline void transform_coords(mapbox::geojson::geometry &g, + const MatrixTransformFn &fn); +inline void transform_coords(mapbox::geojson::feature &f, + const MatrixTransformFn &fn); +inline void transform_coords(mapbox::geojson::feature_collection &fc, + const MatrixTransformFn &fn); +inline void transform_coords(mapbox::geojson::geojson &geojson, + const MatrixTransformFn &fn); + +// Implementation for vector of points +inline void transform_coords(std::vector &coords, + const MatrixTransformFn &fn) +{ + if (coords.empty()) { + return; + } + auto matrix = as_row_vectors(coords); + fn(matrix); +} + +// Implementation for single point +inline void transform_coords(mapbox::geojson::point &pt, + const MatrixTransformFn &fn) +{ + auto matrix = as_row_vectors(pt); + fn(matrix); +} + +// Implementation for multi_point (inherits from vector) +inline void transform_coords(mapbox::geojson::multi_point &geom, + const MatrixTransformFn &fn) +{ + transform_coords(static_cast &>(geom), + fn); +} + +// Implementation for line_string (inherits from vector) +inline void transform_coords(mapbox::geojson::line_string &geom, + const MatrixTransformFn &fn) +{ + transform_coords(static_cast &>(geom), + fn); +} + +// Implementation for linear_ring (inherits from vector) +inline void transform_coords(mapbox::geojson::linear_ring &geom, + const MatrixTransformFn &fn) +{ + transform_coords(static_cast &>(geom), + fn); +} + +// Implementation for multi_line_string +inline void transform_coords(mapbox::geojson::multi_line_string &geom, + const MatrixTransformFn &fn) +{ + for (auto &ls : geom) { + transform_coords(ls, fn); + } +} + +// Implementation for polygon +inline void transform_coords(mapbox::geojson::polygon &geom, + const MatrixTransformFn &fn) +{ + for (auto &ring : geom) { + transform_coords(ring, fn); + } +} + +// Implementation for multi_polygon +inline void transform_coords(mapbox::geojson::multi_polygon &geom, + const MatrixTransformFn &fn) +{ + for (auto &poly : geom) { + for (auto &ring : poly) { + transform_coords(ring, fn); + } + } +} + +// Implementation for geometry_collection +inline void transform_coords(mapbox::geojson::geometry_collection &gc, + const MatrixTransformFn &fn) +{ + for (auto &g : gc) { + transform_coords(g, fn); + } +} + +// Implementation for geometry (recursive traversal using match) +inline void transform_coords(mapbox::geojson::geometry &g, + const MatrixTransformFn &fn) +{ + g.match( + [&](mapbox::geojson::point &pt) { transform_coords(pt, fn); }, + [&](mapbox::geojson::multi_point &mp) { transform_coords(mp, fn); }, + [&](mapbox::geojson::line_string &ls) { transform_coords(ls, fn); }, + [&](mapbox::geojson::linear_ring &lr) { transform_coords(lr, fn); }, + [&](mapbox::geojson::multi_line_string &mls) { + transform_coords(mls, fn); + }, + [&](mapbox::geojson::polygon &poly) { transform_coords(poly, fn); }, + [&](mapbox::geojson::multi_polygon &mpoly) { + transform_coords(mpoly, fn); + }, + [&](mapbox::geojson::geometry_collection &gc) { + transform_coords(gc, fn); + }, + [](auto &) {}); +} + +// Implementation for feature +inline void transform_coords(mapbox::geojson::feature &f, + const MatrixTransformFn &fn) +{ + transform_coords(f.geometry, fn); +} + +// Implementation for feature collection +inline void transform_coords(mapbox::geojson::feature_collection &fc, + const MatrixTransformFn &fn) +{ + for (auto &f : fc) { + transform_coords(f, fn); + } +} + +// Implementation for geojson variant +inline void transform_coords(mapbox::geojson::geojson &geojson, + const MatrixTransformFn &fn) +{ + geojson.match( + [&](mapbox::geojson::geometry &g) { transform_coords(g, fn); }, + [&](mapbox::geojson::feature &f) { transform_coords(f, fn); }, + [&](mapbox::geojson::feature_collection &fc) { + transform_coords(fc, fn); + }, + [](auto &) {}); +} + +// Preset transform function objects + +// WGS84 to ENU transform +struct Wgs84ToEnu +{ + Eigen::Vector3d anchor_lla; + bool cheap_ruler = true; + + void operator()(Eigen::Ref coords) const + { + if (coords.rows() == 0) { + return; + } + coords = lla2enu(coords, anchor_lla, cheap_ruler); + } +}; + +// ENU to WGS84 transform +struct EnuToWgs84 +{ + Eigen::Vector3d anchor_lla; + bool cheap_ruler = true; + + void operator()(Eigen::Ref coords) const + { + if (coords.rows() == 0) { + return; + } + coords = enu2lla(coords, anchor_lla, cheap_ruler); + } +}; + +// Affine transform (4x4 matrix) +struct AffineTransform +{ + Eigen::Matrix4d T; + + void operator()(Eigen::Ref coords) const + { + if (coords.rows() == 0) { + return; + } + apply_transform_inplace(T, coords); + } +}; + +// 3D rotation transform +struct Rotation3D +{ + Eigen::Matrix3d R; + + void operator()(Eigen::Ref coords) const + { + if (coords.rows() == 0) { + return; + } + coords = (R * coords.transpose()).transpose(); + } +}; + +// 3D translation transform +struct Translation3D +{ + Eigen::Vector3d offset; + + void operator()(Eigen::Ref coords) const + { + if (coords.rows() == 0) { + return; + } + coords.rowwise() += offset.transpose(); + } +}; + +// 3D scale transform +struct Scale3D +{ + Eigen::Vector3d scale; + + void operator()(Eigen::Ref coords) const + { + if (coords.rows() == 0) { + return; + } + coords.col(0) *= scale[0]; + coords.col(1) *= scale[1]; + coords.col(2) *= scale[2]; + } +}; + +} // namespace cubao diff --git a/src/pybind11_geojson.cpp b/src/pybind11_geojson.cpp index 8714278..6b8db32 100644 --- a/src/pybind11_geojson.cpp +++ b/src/pybind11_geojson.cpp @@ -8,6 +8,7 @@ #include "geobuf/geojson_cropping.hpp" #include "geobuf/geojson_helpers.hpp" +#include "geobuf/geojson_transform.hpp" #include "geobuf/pybind11_helpers.hpp" #include "geobuf/rapidjson_helpers.hpp" @@ -104,6 +105,112 @@ void bind_geojson(py::module &geojson) "clone", [](const Type &self) -> Type { return self; }, \ "Create a clone of the object") +// Transform methods macros +#define GEOMETRY_TRANSFORM(geom_type) \ + .def( \ + "transform", \ + [](mapbox::geojson::geom_type &self, \ + const py::object &fn) -> mapbox::geojson::geom_type & { \ + transform_coords(self, [&](Eigen::Ref coords) { \ + py::gil_scoped_acquire acquire; \ + auto arr = py::array_t( \ + {coords.rows(), (Eigen::Index)3}, \ + {sizeof(double) * 3, sizeof(double)}, coords.data(), \ + py::none()); \ + auto result = fn(arr); \ + if (!result.is_none()) { \ + auto mat = result.cast(); \ + coords = mat; \ + } \ + }); \ + return self; \ + }, \ + "fn"_a, rvp::reference_internal, \ + "Apply transform function to all coordinates (Nx3 numpy array)") + +#define GEOMETRY_TO_ENU(geom_type) \ + .def( \ + "to_enu", \ + [](mapbox::geojson::geom_type &self, const Eigen::Vector3d &anchor, \ + bool cheap_ruler) -> mapbox::geojson::geom_type & { \ + Wgs84ToEnu xform{anchor, cheap_ruler}; \ + transform_coords(self, xform); \ + return self; \ + }, \ + "anchor"_a, py::kw_only(), "cheap_ruler"_a = true, \ + rvp::reference_internal, \ + "Convert WGS84 (lon,lat,alt) to ENU coordinates") + +#define GEOMETRY_TO_WGS84(geom_type) \ + .def( \ + "to_wgs84", \ + [](mapbox::geojson::geom_type &self, const Eigen::Vector3d &anchor, \ + bool cheap_ruler) -> mapbox::geojson::geom_type & { \ + EnuToWgs84 xform{anchor, cheap_ruler}; \ + transform_coords(self, xform); \ + return self; \ + }, \ + "anchor"_a, py::kw_only(), "cheap_ruler"_a = true, \ + rvp::reference_internal, \ + "Convert ENU coordinates to WGS84 (lon,lat,alt)") + +#define GEOMETRY_ROTATE(geom_type) \ + .def( \ + "rotate", \ + [](mapbox::geojson::geom_type &self, const Eigen::Matrix3d &R) \ + -> mapbox::geojson::geom_type & { \ + Rotation3D xform{R}; \ + transform_coords(self, xform); \ + return self; \ + }, \ + "R"_a, rvp::reference_internal, \ + "Apply 3x3 rotation matrix to all coordinates") + +#define GEOMETRY_TRANSLATE(geom_type) \ + .def( \ + "translate", \ + [](mapbox::geojson::geom_type &self, const Eigen::Vector3d &offset) \ + -> mapbox::geojson::geom_type & { \ + Translation3D xform{offset}; \ + transform_coords(self, xform); \ + return self; \ + }, \ + "offset"_a, rvp::reference_internal, \ + "Translate all coordinates by offset vector") + +#define GEOMETRY_SCALE(geom_type) \ + .def( \ + "scale", \ + [](mapbox::geojson::geom_type &self, const Eigen::Vector3d &s) \ + -> mapbox::geojson::geom_type & { \ + Scale3D xform{s}; \ + transform_coords(self, xform); \ + return self; \ + }, \ + "scale"_a, rvp::reference_internal, \ + "Scale all coordinates by factors [sx, sy, sz]") + +#define GEOMETRY_AFFINE(geom_type) \ + .def( \ + "affine", \ + [](mapbox::geojson::geom_type &self, const Eigen::Matrix4d &T) \ + -> mapbox::geojson::geom_type & { \ + AffineTransform xform{T}; \ + transform_coords(self, xform); \ + return self; \ + }, \ + "T"_a, rvp::reference_internal, \ + "Apply 4x4 affine transformation matrix") + +#define GEOMETRY_TRANSFORM_METHODS(geom_type) \ + GEOMETRY_TRANSFORM(geom_type) \ + GEOMETRY_TO_ENU(geom_type) \ + GEOMETRY_TO_WGS84(geom_type) \ + GEOMETRY_ROTATE(geom_type) \ + GEOMETRY_TRANSLATE(geom_type) \ + GEOMETRY_SCALE(geom_type) \ + GEOMETRY_AFFINE(geom_type) + py::class_(geojson, "GeoJSON", py::module_local()) is_geojson_type(geometry) // is_geojson_type(feature) // @@ -147,6 +254,7 @@ void bind_geojson(py::module &geojson) rvp::reference_internal, "Round coordinates to specified decimal places") // GEOMETRY_DEDUPLICATE_XYZ(geojson) // + GEOMETRY_TRANSFORM_METHODS(geojson) // .def( "from_rapidjson", [](mapbox::geojson::geojson &self, @@ -581,6 +689,7 @@ void bind_geojson(py::module &geojson) "Get an iterator over the custom property keys") GEOMETRY_ROUND_COORDS(geometry) GEOMETRY_DEDUPLICATE_XYZ(geometry) + GEOMETRY_TRANSFORM_METHODS(geometry) .def_property_readonly( "__geo_interface__", [](const mapbox::geojson::geometry &self) -> py::object { @@ -762,6 +871,7 @@ void bind_geojson(py::module &geojson) "Enable pickling support for Point objects") // GEOMETRY_ROUND_COORDS(point) // GEOMETRY_DEDUPLICATE_XYZ(point) // + GEOMETRY_TRANSFORM_METHODS(point) // .def_property_readonly( "__geo_interface__", [](const mapbox::geojson::point &self) -> py::object { @@ -948,6 +1058,7 @@ void bind_geojson(py::module &geojson) "Pickle support for serialization") \ GEOMETRY_ROUND_COORDS(geom_type) \ GEOMETRY_DEDUPLICATE_XYZ(geom_type) \ + GEOMETRY_TRANSFORM_METHODS(geom_type) \ .def_property_readonly( \ "__geo_interface__", \ [](const mapbox::geojson::geom_type &self) -> py::object { \ @@ -1157,6 +1268,7 @@ void bind_geojson(py::module &geojson) py::kw_only(), "lon"_a = 8, "lat"_a = 8, "alt"_a = 3, \ rvp::reference_internal, "Round the coordinates of the geometry") \ GEOMETRY_DEDUPLICATE_XYZ(geom_type) \ + GEOMETRY_TRANSFORM_METHODS(geom_type) \ .def( \ "bbox", \ [](const mapbox::geojson::geom_type &self, bool with_z) \ @@ -1364,6 +1476,7 @@ void bind_geojson(py::module &geojson) py::kw_only(), "lon"_a = 8, "lat"_a = 8, "alt"_a = 3, rvp::reference_internal, "Round the coordinates of the MultiPolygon") + GEOMETRY_TRANSFORM_METHODS(multi_polygon) .def_property_readonly( "__geo_interface__", [](const mapbox::geojson::multi_polygon &self) -> py::object { @@ -1505,6 +1618,7 @@ void bind_geojson(py::module &geojson) rvp::reference_internal, "Round the coordinates of all geometries in the GeometryCollection") GEOMETRY_DEDUPLICATE_XYZ(geometry_collection) + GEOMETRY_TRANSFORM_METHODS(geometry_collection) .def_property_readonly( "__geo_interface__", [](const mapbox::geojson::geometry_collection &self) -> py::object { @@ -2187,6 +2301,7 @@ void bind_geojson(py::module &geojson) rvp::reference_internal, "Round the coordinates of the feature geometry") // GEOMETRY_DEDUPLICATE_XYZ(feature) + GEOMETRY_TRANSFORM_METHODS(feature) // ; @@ -2239,6 +2354,7 @@ void bind_geojson(py::module &geojson) rvp::reference_internal, "Round the coordinates of all features in the collection") GEOMETRY_DEDUPLICATE_XYZ(feature_collection) + GEOMETRY_TRANSFORM_METHODS(feature_collection) // round // .def( diff --git a/tests/test_transform.py b/tests/test_transform.py new file mode 100644 index 0000000..362bc3b --- /dev/null +++ b/tests/test_transform.py @@ -0,0 +1,327 @@ +from __future__ import annotations + +import numpy as np +import pytest + +from pybind11_geobuf import geojson + + +def sample_coords(): + """Sample WGS84 coordinates (lon, lat, alt)""" + return np.array( + [ + [120.40317479950272, 31.416966084052177, 1.0], + [120.28451900911591, 31.30578266928819, 2.0], + [120.35592249359615, 31.21781895672254, 3.0], + [120.67093786630113, 31.299502266522722, 4.0], + ] + ) + + +def sample_anchor(): + """Sample anchor point for ENU conversion""" + return np.array([120.4, 31.3, 0.0]) + + +class TestTransformPoint: + def test_transform_custom_function(self): + pt = geojson.Point(1.0, 2.0, 3.0) + + def double_coords(coords): + coords[:] *= 2 + + pt.transform(double_coords) + assert pt() == [2.0, 4.0, 6.0] + + def test_transform_return_new_array(self): + pt = geojson.Point(1.0, 2.0, 3.0) + + def add_offset(coords): + return coords + 10 + + pt.transform(add_offset) + assert pt() == [11.0, 12.0, 13.0] + + def test_to_enu_to_wgs84_roundtrip(self): + pt = geojson.Point(120.4, 31.3, 100.0) + original = pt.to_numpy().copy() + anchor = np.array([120.4, 31.3, 0.0]) + + pt.to_enu(anchor) + # After to_enu, coordinates should be near origin in ENU + enu_coords = pt.to_numpy() + assert np.abs(enu_coords[0]) < 1.0 # east ~0 + assert np.abs(enu_coords[1]) < 1.0 # north ~0 + assert np.abs(enu_coords[2] - 100.0) < 0.01 # up ~100 + + pt.to_wgs84(anchor) + np.testing.assert_allclose(pt.to_numpy(), original, rtol=1e-6) + + def test_translate(self): + pt = geojson.Point(1.0, 2.0, 3.0) + pt.translate(np.array([10.0, 20.0, 30.0])) + assert pt() == [11.0, 22.0, 33.0] + + def test_scale(self): + pt = geojson.Point(1.0, 2.0, 3.0) + pt.scale(np.array([2.0, 3.0, 4.0])) + assert pt() == [2.0, 6.0, 12.0] + + def test_rotate(self): + pt = geojson.Point(1.0, 0.0, 0.0) + # Rotate 90 degrees around Z axis + R = np.array([[0, -1, 0], [1, 0, 0], [0, 0, 1]], dtype=float) + pt.rotate(R) + np.testing.assert_allclose(pt.to_numpy(), [0.0, 1.0, 0.0], atol=1e-10) + + def test_affine(self): + pt = geojson.Point(1.0, 2.0, 3.0) + T = np.eye(4) + T[:3, 3] = [10, 20, 30] # translation + pt.affine(T) + assert pt() == [11.0, 22.0, 33.0] + + +class TestTransformLineString: + def test_transform_custom_function(self): + ls = geojson.LineString(sample_coords()) + original_shape = ls.to_numpy().shape + + def add_noise(coords): + coords[:, 2] += 10.0 + + ls.transform(add_noise) + result = ls.to_numpy() + assert result.shape == original_shape + np.testing.assert_allclose(result[:, 2], sample_coords()[:, 2] + 10.0) + + def test_to_enu_to_wgs84_roundtrip(self): + ls = geojson.LineString(sample_coords()) + original = ls.to_numpy().copy() + anchor = sample_anchor() + + ls.to_enu(anchor) + # After to_enu, coords should be in local ENU frame + enu_coords = ls.to_numpy() + assert not np.allclose(enu_coords, original) # should be different + + ls.to_wgs84(anchor) + np.testing.assert_allclose(ls.to_numpy(), original, rtol=1e-6) + + def test_translate(self): + ls = geojson.LineString([[0, 0, 0], [1, 1, 1]]) + ls.translate(np.array([10.0, 20.0, 30.0])) + expected = [[10, 20, 30], [11, 21, 31]] + np.testing.assert_allclose(ls.to_numpy(), expected) + + def test_chain_operations(self): + ls = geojson.LineString([[0, 0, 0], [1, 1, 1]]) + result = ls.translate(np.array([1, 1, 1])).scale(np.array([2, 2, 2])) + assert result == ls # chain should return self + expected = [[2, 2, 2], [4, 4, 4]] + np.testing.assert_allclose(ls.to_numpy(), expected) + + +class TestTransformMultiPoint: + def test_transform(self): + mp = geojson.MultiPoint(sample_coords()) + mp.translate(np.array([1.0, 2.0, 3.0])) + result = mp.to_numpy() + expected = sample_coords() + np.array([1.0, 2.0, 3.0]) + np.testing.assert_allclose(result, expected) + + +class TestTransformPolygon: + def test_transform(self): + coords = [[0, 0, 0], [1, 0, 0], [1, 1, 0], [0, 1, 0], [0, 0, 0]] + poly = geojson.Polygon(coords) + poly.translate(np.array([10.0, 20.0, 30.0])) + result = poly.to_numpy() + expected = np.array(coords) + np.array([10.0, 20.0, 30.0]) + np.testing.assert_allclose(result, expected) + + +class TestTransformMultiLineString: + def test_transform(self): + coords = sample_coords() + mls = geojson.MultiLineString(coords) + mls.push_back(coords * 2) + + mls.translate(np.array([1.0, 1.0, 1.0])) + + # Both line strings should be translated + for ls in mls: + assert ls.to_numpy()[0, 2] != 1.0 # z was 1 or 2, now +1 + + +class TestTransformMultiPolygon: + def test_transform(self): + coords = [[0, 0, 0], [1, 0, 0], [1, 1, 0], [0, 0, 0]] + mpoly = geojson.MultiPolygon(coords) + mpoly.translate(np.array([5.0, 5.0, 5.0])) + expected = np.array(coords) + np.array([5.0, 5.0, 5.0]) + # MultiPolygon.to_numpy returns the first polygon's first ring + np.testing.assert_allclose(mpoly.as_numpy(), expected) + + +class TestTransformGeometryCollection: + def test_transform(self): + gc = geojson.GeometryCollection() + gc.push_back(geojson.Point(1, 2, 3)) + gc.push_back(geojson.LineString([[0, 0, 0], [1, 1, 1]])) + + gc.translate(np.array([10.0, 10.0, 10.0])) + + pt = gc[0].as_point() + assert pt() == [11.0, 12.0, 13.0] + + ls = gc[1].as_line_string() + np.testing.assert_allclose(ls.to_numpy(), [[10, 10, 10], [11, 11, 11]]) + + +class TestTransformFeature: + def test_transform(self): + f = geojson.Feature() + f.geometry(geojson.LineString(sample_coords())) + original = f.to_numpy().copy() + + f.translate(np.array([0.0, 0.0, 100.0])) + result = f.to_numpy() + + np.testing.assert_allclose(result[:, :2], original[:, :2]) + np.testing.assert_allclose(result[:, 2], original[:, 2] + 100.0) + + def test_to_enu_to_wgs84_roundtrip(self): + f = geojson.Feature() + f.geometry(geojson.LineString(sample_coords())) + original = f.to_numpy().copy() + anchor = sample_anchor() + + f.to_enu(anchor).to_wgs84(anchor) + np.testing.assert_allclose(f.to_numpy(), original, rtol=1e-6) + + +class TestTransformFeatureCollection: + def test_transform(self): + fc = geojson.FeatureCollection() + f1 = geojson.Feature() + f1.geometry(geojson.Point(1, 2, 3)) + f2 = geojson.Feature() + f2.geometry(geojson.LineString([[0, 0, 0], [1, 1, 1]])) + fc.append(f1) + fc.append(f2) + + fc.translate(np.array([10.0, 10.0, 10.0])) + + assert fc[0].geometry().as_point()() == [11.0, 12.0, 13.0] + np.testing.assert_allclose( + fc[1].geometry().as_line_string().to_numpy(), [[10, 10, 10], [11, 11, 11]] + ) + + def test_to_enu_to_wgs84_roundtrip(self): + fc = geojson.FeatureCollection() + f = geojson.Feature() + f.geometry(geojson.LineString(sample_coords())) + fc.append(f) + + original = fc[0].to_numpy().copy() + anchor = sample_anchor() + + fc.to_enu(anchor).to_wgs84(anchor) + np.testing.assert_allclose(fc[0].to_numpy(), original, rtol=1e-6) + + +class TestTransformGeometry: + def test_transform_point(self): + g = geojson.Geometry(geojson.Point(1, 2, 3)) + g.translate(np.array([10.0, 10.0, 10.0])) + assert g.as_point()() == [11.0, 12.0, 13.0] + + def test_transform_line_string(self): + g = geojson.Geometry(geojson.LineString([[0, 0, 0], [1, 1, 1]])) + g.scale(np.array([2.0, 2.0, 2.0])) + np.testing.assert_allclose( + g.as_line_string().to_numpy(), [[0, 0, 0], [2, 2, 2]] + ) + + +class TestTransformGeoJSON: + def test_transform_geometry(self): + gj = geojson.GeoJSON(geojson.Geometry(geojson.Point(1, 2, 3))) + gj.translate(np.array([10.0, 10.0, 10.0])) + assert gj.as_geometry().as_point()() == [11.0, 12.0, 13.0] + + def test_transform_feature(self): + f = geojson.Feature() + f.geometry(geojson.Point(1, 2, 3)) + gj = geojson.GeoJSON(f) + gj.translate(np.array([10.0, 10.0, 10.0])) + assert gj.as_feature().geometry().as_point()() == [11.0, 12.0, 13.0] + + +class TestCheapRulerOption: + def test_cheap_ruler_vs_full(self): + ls = geojson.LineString(sample_coords()) + anchor = sample_anchor() + + # With cheap_ruler=True (default) + ls_cheap = ls.clone() + ls_cheap.to_enu(anchor, cheap_ruler=True) + + # With cheap_ruler=False + ls_full = ls.clone() + ls_full.to_enu(anchor, cheap_ruler=False) + + # Results should be similar but not identical + cheap_coords = ls_cheap.to_numpy() + full_coords = ls_full.to_numpy() + + # X and Y should be close (within a few hundred meters for typical scenarios) + # Z differs significantly because cheap_ruler preserves original Z while + # full transform converts through ECEF which changes altitude reference + # Use absolute tolerance of 100m for comparison since cheap_ruler is an approximation + # and the test data spans ~30km, so 100m error is reasonable + np.testing.assert_allclose(cheap_coords[:, :2], full_coords[:, :2], atol=100) + + +def test_chain_multiple_transforms(): + """Test chaining multiple transform operations""" + fc = geojson.FeatureCollection() + f = geojson.Feature() + f.geometry(geojson.LineString(sample_coords())) + fc.append(f) + + anchor = sample_anchor() + + # Chain: to_enu -> translate -> rotate -> to_wgs84 + R = np.eye(3) # identity rotation + + # This should work without errors + result = fc.to_enu(anchor).translate(np.array([100.0, 100.0, 0.0])).rotate(R) + + # Verify result is the same object (fluent interface) + assert result is fc + + +def test_transform_preserves_properties(): + """Test that transform operations preserve feature properties""" + f = geojson.Feature() + f.geometry(geojson.LineString([[0, 0, 0], [1, 1, 1]])) + f.properties({"name": "test", "value": 42}) + + f.translate(np.array([10.0, 10.0, 10.0])) + + # Properties should be unchanged + assert f.properties()["name"]() == "test" + assert f.properties()["value"]() == 42 + + # Geometry should be translated + np.testing.assert_allclose(f.to_numpy(), [[10, 10, 10], [11, 11, 11]]) + + +if __name__ == "__main__": + import os + import sys + + os.chdir(os.path.dirname(__file__)) + sys.exit(pytest.main([__file__, "-v", "-x"])) From ae38d7aee3b56dab1d324080cb1282e585e89893 Mon Sep 17 00:00:00 2001 From: tang zhixiong Date: Sat, 31 Jan 2026 10:39:16 +0800 Subject: [PATCH 02/18] lint code --- .pre-commit-config.yaml | 2 +- src/geobuf/geojson_transform.hpp | 31 ++++++++++++++-------------- src/pybind11_geojson.cpp | 35 ++++++++++++++++---------------- 3 files changed, 33 insertions(+), 35 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 88b5602..95aea89 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -44,7 +44,7 @@ repos: # Changes tabs to spaces - repo: https://github.com/Lucas-C/pre-commit-hooks - rev: v1.1.13 + rev: v1.5.6 hooks: - id: remove-tabs exclude: ^(docs|Makefile|benchmarks/Makefile) diff --git a/src/geobuf/geojson_transform.hpp b/src/geobuf/geojson_transform.hpp index 9f53bac..80e7a9f 100644 --- a/src/geobuf/geojson_transform.hpp +++ b/src/geobuf/geojson_transform.hpp @@ -129,22 +129,21 @@ inline void transform_coords(mapbox::geojson::geometry_collection &gc, inline void transform_coords(mapbox::geojson::geometry &g, const MatrixTransformFn &fn) { - g.match( - [&](mapbox::geojson::point &pt) { transform_coords(pt, fn); }, - [&](mapbox::geojson::multi_point &mp) { transform_coords(mp, fn); }, - [&](mapbox::geojson::line_string &ls) { transform_coords(ls, fn); }, - [&](mapbox::geojson::linear_ring &lr) { transform_coords(lr, fn); }, - [&](mapbox::geojson::multi_line_string &mls) { - transform_coords(mls, fn); - }, - [&](mapbox::geojson::polygon &poly) { transform_coords(poly, fn); }, - [&](mapbox::geojson::multi_polygon &mpoly) { - transform_coords(mpoly, fn); - }, - [&](mapbox::geojson::geometry_collection &gc) { - transform_coords(gc, fn); - }, - [](auto &) {}); + g.match([&](mapbox::geojson::point &pt) { transform_coords(pt, fn); }, + [&](mapbox::geojson::multi_point &mp) { transform_coords(mp, fn); }, + [&](mapbox::geojson::line_string &ls) { transform_coords(ls, fn); }, + [&](mapbox::geojson::linear_ring &lr) { transform_coords(lr, fn); }, + [&](mapbox::geojson::multi_line_string &mls) { + transform_coords(mls, fn); + }, + [&](mapbox::geojson::polygon &poly) { transform_coords(poly, fn); }, + [&](mapbox::geojson::multi_polygon &mpoly) { + transform_coords(mpoly, fn); + }, + [&](mapbox::geojson::geometry_collection &gc) { + transform_coords(gc, fn); + }, + [](auto &) {}); } // Implementation for feature diff --git a/src/pybind11_geojson.cpp b/src/pybind11_geojson.cpp index 6b8db32..7c95a98 100644 --- a/src/pybind11_geojson.cpp +++ b/src/pybind11_geojson.cpp @@ -113,10 +113,10 @@ void bind_geojson(py::module &geojson) const py::object &fn) -> mapbox::geojson::geom_type & { \ transform_coords(self, [&](Eigen::Ref coords) { \ py::gil_scoped_acquire acquire; \ - auto arr = py::array_t( \ - {coords.rows(), (Eigen::Index)3}, \ - {sizeof(double) * 3, sizeof(double)}, coords.data(), \ - py::none()); \ + auto arr = \ + py::array_t({coords.rows(), (Eigen::Index)3}, \ + {sizeof(double) * 3, sizeof(double)}, \ + coords.data(), py::none()); \ auto result = fn(arr); \ if (!result.is_none()) { \ auto mat = result.cast(); \ @@ -157,8 +157,8 @@ void bind_geojson(py::module &geojson) #define GEOMETRY_ROTATE(geom_type) \ .def( \ "rotate", \ - [](mapbox::geojson::geom_type &self, const Eigen::Matrix3d &R) \ - -> mapbox::geojson::geom_type & { \ + [](mapbox::geojson::geom_type &self, \ + const Eigen::Matrix3d &R) -> mapbox::geojson::geom_type & { \ Rotation3D xform{R}; \ transform_coords(self, xform); \ return self; \ @@ -169,8 +169,8 @@ void bind_geojson(py::module &geojson) #define GEOMETRY_TRANSLATE(geom_type) \ .def( \ "translate", \ - [](mapbox::geojson::geom_type &self, const Eigen::Vector3d &offset) \ - -> mapbox::geojson::geom_type & { \ + [](mapbox::geojson::geom_type &self, \ + const Eigen::Vector3d &offset) -> mapbox::geojson::geom_type & { \ Translation3D xform{offset}; \ transform_coords(self, xform); \ return self; \ @@ -181,8 +181,8 @@ void bind_geojson(py::module &geojson) #define GEOMETRY_SCALE(geom_type) \ .def( \ "scale", \ - [](mapbox::geojson::geom_type &self, const Eigen::Vector3d &s) \ - -> mapbox::geojson::geom_type & { \ + [](mapbox::geojson::geom_type &self, \ + const Eigen::Vector3d &s) -> mapbox::geojson::geom_type & { \ Scale3D xform{s}; \ transform_coords(self, xform); \ return self; \ @@ -193,8 +193,8 @@ void bind_geojson(py::module &geojson) #define GEOMETRY_AFFINE(geom_type) \ .def( \ "affine", \ - [](mapbox::geojson::geom_type &self, const Eigen::Matrix4d &T) \ - -> mapbox::geojson::geom_type & { \ + [](mapbox::geojson::geom_type &self, \ + const Eigen::Matrix4d &T) -> mapbox::geojson::geom_type & { \ AffineTransform xform{T}; \ transform_coords(self, xform); \ return self; \ @@ -1268,7 +1268,7 @@ void bind_geojson(py::module &geojson) py::kw_only(), "lon"_a = 8, "lat"_a = 8, "alt"_a = 3, \ rvp::reference_internal, "Round the coordinates of the geometry") \ GEOMETRY_DEDUPLICATE_XYZ(geom_type) \ - GEOMETRY_TRANSFORM_METHODS(geom_type) \ + GEOMETRY_TRANSFORM_METHODS(geom_type) \ .def( \ "bbox", \ [](const mapbox::geojson::geom_type &self, bool with_z) \ @@ -1476,7 +1476,7 @@ void bind_geojson(py::module &geojson) py::kw_only(), "lon"_a = 8, "lat"_a = 8, "alt"_a = 3, rvp::reference_internal, "Round the coordinates of the MultiPolygon") - GEOMETRY_TRANSFORM_METHODS(multi_polygon) + GEOMETRY_TRANSFORM_METHODS(multi_polygon) .def_property_readonly( "__geo_interface__", [](const mapbox::geojson::multi_polygon &self) -> py::object { @@ -1618,7 +1618,7 @@ void bind_geojson(py::module &geojson) rvp::reference_internal, "Round the coordinates of all geometries in the GeometryCollection") GEOMETRY_DEDUPLICATE_XYZ(geometry_collection) - GEOMETRY_TRANSFORM_METHODS(geometry_collection) + GEOMETRY_TRANSFORM_METHODS(geometry_collection) .def_property_readonly( "__geo_interface__", [](const mapbox::geojson::geometry_collection &self) -> py::object { @@ -2300,8 +2300,7 @@ void bind_geojson(py::module &geojson) py::kw_only(), "lon"_a = 8, "lat"_a = 8, "alt"_a = 3, rvp::reference_internal, "Round the coordinates of the feature geometry") // - GEOMETRY_DEDUPLICATE_XYZ(feature) - GEOMETRY_TRANSFORM_METHODS(feature) + GEOMETRY_DEDUPLICATE_XYZ(feature) GEOMETRY_TRANSFORM_METHODS(feature) // ; @@ -2354,7 +2353,7 @@ void bind_geojson(py::module &geojson) rvp::reference_internal, "Round the coordinates of all features in the collection") GEOMETRY_DEDUPLICATE_XYZ(feature_collection) - GEOMETRY_TRANSFORM_METHODS(feature_collection) + GEOMETRY_TRANSFORM_METHODS(feature_collection) // round // .def( From e44a61cb057f1aa721519f26d9168a97feb23a23 Mon Sep 17 00:00:00 2001 From: tang zhixiong Date: Sat, 31 Jan 2026 10:43:54 +0800 Subject: [PATCH 03/18] update stubs --- Makefile | 1 + src/pybind11_geobuf/_core/__init__.pyi | 113 ++- src/pybind11_geobuf/_core/geojson.pyi | 1120 ++++++++++++++---------- src/pybind11_geobuf/_core/tf.pyi | 151 +++- 4 files changed, 838 insertions(+), 547 deletions(-) diff --git a/Makefile b/Makefile index 92705cf..aa6ddfd 100644 --- a/Makefile +++ b/Makefile @@ -44,6 +44,7 @@ cli_test: cli_test1 cli_test2 cli_test3 cli_test4 restub: pybind11-stubgen pybind11_geobuf._core -o stubs cp -rf stubs/pybind11_geobuf/_core src/pybind11_geobuf + pre-commit run --files src/pybind11_geobuf/_core/*.pyi test_all: @cd build && for t in $(wildcard $(BUILD_DIR)/test_*); do echo $$t && eval $$t >/dev/null 2>&1 && echo 'ok' || echo $(RED)Not Ok$(NC); done diff --git a/src/pybind11_geobuf/_core/__init__.pyi b/src/pybind11_geobuf/_core/__init__.pyi index 45e2943..fab0b75 100644 --- a/src/pybind11_geobuf/_core/__init__.pyi +++ b/src/pybind11_geobuf/_core/__init__.pyi @@ -1,11 +1,12 @@ from __future__ import annotations +import collections.abc import numpy -import pybind11_stubgen.typing_ext +import numpy.typing import typing from . import geojson from . import tf -__all__ = [ +__all__: list[str] = [ "Decoder", "Encoder", "GeobufIndex", @@ -65,7 +66,7 @@ class Decoder: """ def decode_feature( self, bytes: str, only_geometry: bool = False, only_properties: bool = False - ) -> geojson.Feature | None: + ) -> pybind11_geobuf._core.geojson.Feature | None: """ Decode Protocol Buffer (PBF) feature. @@ -151,9 +152,9 @@ class Encoder: def __init__( self, *, - max_precision: int = 1000000, + max_precision: typing.SupportsInt = 1000000, only_xy: bool = False, - round_z: int | None = None, + round_z: typing.SupportsInt | None = None, ) -> None: """ Initialize an Encoder object. @@ -293,8 +294,12 @@ class GeobufIndex: """ @typing.overload def decode_feature( - self, index: int, *, only_geometry: bool = False, only_properties: bool = False - ) -> geojson.Feature | None: + self, + index: typing.SupportsInt, + *, + only_geometry: bool = False, + only_properties: bool = False, + ) -> pybind11_geobuf._core.geojson.Feature | None: """ Decode a feature from the Geobuf file. @@ -309,7 +314,7 @@ class GeobufIndex: @typing.overload def decode_feature( self, bytes: str, *, only_geometry: bool = False, only_properties: bool = False - ) -> geojson.Feature | None: + ) -> pybind11_geobuf._core.geojson.Feature | None: """ Decode a feature from bytes. @@ -323,7 +328,7 @@ class GeobufIndex: """ def decode_feature_of_id( self, id: str, *, only_geometry: bool = False, only_properties: bool = False - ) -> geojson.Feature | None: + ) -> pybind11_geobuf._core.geojson.Feature | None: """ Decode a feature by its ID. @@ -337,7 +342,7 @@ class GeobufIndex: """ def decode_features( self, - index: list[int], + index: collections.abc.Sequence[typing.SupportsInt], *, only_geometry: bool = False, only_properties: bool = False, @@ -382,7 +387,9 @@ class GeobufIndex: Returns: None """ - def mmap_bytes(self, offset: int, length: int) -> bytes | None: + def mmap_bytes( + self, offset: typing.SupportsInt, length: typing.SupportsInt + ) -> bytes | None: """ Read bytes from the memory-mapped file. @@ -418,8 +425,8 @@ class GeobufIndex: """ def query( self, - arg0: numpy.ndarray[numpy.float64[2, 1]], - arg1: numpy.ndarray[numpy.float64[2, 1]], + arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[2, 1]"], + arg1: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[2, 1]"], ) -> set[int]: """ Query features within a bounding box. @@ -490,7 +497,9 @@ class NodeItem: """ Check if this node's bounding box intersects with another node's bounding box """ - def to_numpy(self) -> numpy.ndarray[numpy.float64[4, 1]]: + def to_numpy( + self, + ) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[4, 1]"]: """ Convert the node's bounding box to a numpy array [minX, minY, maxX, maxY] """ @@ -532,7 +541,11 @@ class NodeItem: class PackedRTree: def search( - self, min_x: float, min_y: float, max_x: float, max_y: float + self, + min_x: typing.SupportsFloat, + min_y: typing.SupportsFloat, + max_x: typing.SupportsFloat, + max_y: typing.SupportsFloat, ) -> list[int]: """ Search for items within the given bounding box. @@ -547,7 +560,9 @@ class PackedRTree: list: List of offsets of items within the bounding box. """ @property - def extent(self) -> numpy.ndarray[numpy.float64[4, 1]]: ... + def extent( + self, + ) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[4, 1]"]: ... @property def node_size(self) -> int: ... @property @@ -582,7 +597,9 @@ class Planet: Returns: None """ - def copy(self, arg0: numpy.ndarray[numpy.int32[m, 1]]) -> geojson.FeatureCollection: + def copy( + self, arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.int32, "[m, 1]"] + ) -> geojson.FeatureCollection: """ Create a deep copy of the Planet object. @@ -591,7 +608,9 @@ class Planet: """ def crop( self, - polygon: numpy.ndarray[numpy.float64[m, 2], numpy.ndarray.flags.c_contiguous], + polygon: typing.Annotated[ + numpy.typing.NDArray[numpy.float64], "[m, 2]", "flags.c_contiguous" + ], *, clipping_mode: str = "longest", strip_properties: bool = False, @@ -634,9 +653,9 @@ class Planet: """ def query( self, - min: numpy.ndarray[numpy.float64[2, 1]], - max: numpy.ndarray[numpy.float64[2, 1]], - ) -> numpy.ndarray[numpy.int32[m, 1]]: + min: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[2, 1]"], + max: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[2, 1]"], + ) -> typing.Annotated[numpy.typing.NDArray[numpy.int32], "[m, 1]"]: """ Query features within the given bounding box. @@ -682,11 +701,11 @@ class rapidjson: def __getstate__(self) -> int: ... def __hash__(self) -> int: ... def __index__(self) -> int: ... - def __init__(self, value: int) -> None: ... + def __init__(self, value: typing.SupportsInt) -> None: ... def __int__(self) -> int: ... def __ne__(self, other: typing.Any) -> bool: ... def __repr__(self) -> str: ... - def __setstate__(self, state: int) -> None: ... + def __setstate__(self, state: typing.SupportsInt) -> None: ... def __str__(self) -> str: ... @property def name(self) -> str: ... @@ -825,19 +844,19 @@ class rapidjson: """ Set the value to an empty array """ - def SetDouble(self, arg0: float) -> rapidjson: + def SetDouble(self, arg0: typing.SupportsFloat) -> rapidjson: """ Set the value to a double """ - def SetFloat(self, arg0: float) -> rapidjson: + def SetFloat(self, arg0: typing.SupportsFloat) -> rapidjson: """ Set the value to a float """ - def SetInt(self, arg0: int) -> rapidjson: + def SetInt(self, arg0: typing.SupportsInt) -> rapidjson: """ Set the value to an integer """ - def SetInt64(self, arg0: int) -> rapidjson: + def SetInt64(self, arg0: typing.SupportsInt) -> rapidjson: """ Set the value to a 64-bit integer """ @@ -849,11 +868,11 @@ class rapidjson: """ Set the value to an empty object """ - def SetUint(self, arg0: int) -> rapidjson: + def SetUint(self, arg0: typing.SupportsInt) -> rapidjson: """ Set the value to an unsigned integer """ - def SetUint64(self, arg0: int) -> rapidjson: + def SetUint64(self, arg0: typing.SupportsInt) -> rapidjson: """ Set the value to a 64-bit unsigned integer """ @@ -887,7 +906,7 @@ class rapidjson: Delete a member by key """ @typing.overload - def __delitem__(self, arg0: int) -> None: + def __delitem__(self, arg0: typing.SupportsInt) -> None: """ Delete an array element by index """ @@ -901,7 +920,7 @@ class rapidjson: Get a member value by key """ @typing.overload - def __getitem__(self, arg0: int) -> rapidjson: + def __getitem__(self, arg0: typing.SupportsInt) -> rapidjson: """ Get an array element by index """ @@ -925,7 +944,7 @@ class rapidjson: Compare two RapidJSON values for inequality """ @typing.overload - def __setitem__(self, index: int, value: typing.Any) -> typing.Any: + def __setitem__(self, index: typing.SupportsInt, value: typing.Any) -> typing.Any: """ Set array element by index """ @@ -991,9 +1010,9 @@ class rapidjson: *, sort_keys: bool = True, strip_geometry_z_0: bool = True, - round_geojson_non_geometry: int | None = 3, + round_geojson_non_geometry: typing.SupportsInt | None = 3, round_geojson_geometry: typing.Annotated[ - list[int], pybind11_stubgen.typing_ext.FixedSize(3) + collections.abc.Sequence[typing.SupportsInt], "FixedSize(3)" ] | None = [8, 8, 3], denoise_double_0: bool = True, @@ -1010,7 +1029,11 @@ class rapidjson: Append value to array """ def round( - self, *, precision: float = 3, depth: int = 32, skip_keys: list[str] = [] + self, + *, + precision: typing.SupportsFloat = 3, + depth: typing.SupportsInt = 32, + skip_keys: collections.abc.Sequence[str] = [], ) -> rapidjson: """ Round numeric values in the JSON @@ -1019,13 +1042,15 @@ class rapidjson: self, *, precision: typing.Annotated[ - list[int], pybind11_stubgen.typing_ext.FixedSize(3) + collections.abc.Sequence[typing.SupportsInt], "FixedSize(3)" ] = [8, 8, 3], ) -> rapidjson: """ Round geometry coordinates in GeoJSON """ - def round_geojson_non_geometry(self, *, precision: int = 3) -> rapidjson: + def round_geojson_non_geometry( + self, *, precision: typing.SupportsInt = 3 + ) -> rapidjson: """ Round non-geometry numeric values in GeoJSON """ @@ -1073,10 +1098,10 @@ def normalize_json( sort_keys: bool = True, denoise_double_0: bool = True, strip_geometry_z_0: bool = True, - round_non_geojson: int | None = 3, - round_geojson_non_geometry: int | None = 3, + round_non_geojson: typing.SupportsInt | None = 3, + round_geojson_non_geometry: typing.SupportsInt | None = 3, round_geojson_geometry: typing.Annotated[ - list[int], pybind11_stubgen.typing_ext.FixedSize(3) + collections.abc.Sequence[typing.SupportsInt], "FixedSize(3)" ] | None = [8, 8, 3], ) -> bool: @@ -1105,10 +1130,10 @@ def normalize_json( sort_keys: bool = True, denoise_double_0: bool = True, strip_geometry_z_0: bool = True, - round_non_geojson: int | None = 3, - round_geojson_non_geometry: int | None = 3, + round_non_geojson: typing.SupportsInt | None = 3, + round_geojson_non_geometry: typing.SupportsInt | None = 3, round_geojson_geometry: typing.Annotated[ - list[int], pybind11_stubgen.typing_ext.FixedSize(3) + collections.abc.Sequence[typing.SupportsInt], "FixedSize(3)" ] | None = [8, 8, 3], ) -> rapidjson: @@ -1170,4 +1195,4 @@ def str2json2str( Optional[str]: Converted JSON string, or None if input is invalid. """ -__version__: str = "0.2.2" +__version__: str = "0.2.3" diff --git a/src/pybind11_geobuf/_core/geojson.pyi b/src/pybind11_geobuf/_core/geojson.pyi index 8c8e58e..3f7a63a 100644 --- a/src/pybind11_geobuf/_core/geojson.pyi +++ b/src/pybind11_geobuf/_core/geojson.pyi @@ -1,31 +1,10 @@ from __future__ import annotations +import collections.abc import numpy +import numpy.typing import pybind11_geobuf._core import typing - -__all__ = [ - "Feature", - "FeatureCollection", - "FeatureList", - "GeoJSON", - "Geometry", - "GeometryBase", - "GeometryCollection", - "GeometryList", - "LineString", - "LineStringList", - "LinearRing", - "LinearRingList", - "MultiLineString", - "MultiPoint", - "MultiPolygon", - "Point", - "Polygon", - "PolygonList", - "coordinates", - "value", -] - +__all__: list[str] = ['Feature', 'FeatureCollection', 'FeatureList', 'GeoJSON', 'Geometry', 'GeometryBase', 'GeometryCollection', 'GeometryList', 'LineString', 'LineStringList', 'LinearRing', 'LinearRingList', 'MultiLineString', 'MultiPoint', 'MultiPolygon', 'Point', 'Polygon', 'PolygonList', 'coordinates', 'value'] class Feature: __hash__: typing.ClassVar[None] = None def __call__(self) -> typing.Any: @@ -80,17 +59,15 @@ class Feature: """ Set a custom property value by key """ - def as_numpy( - self, - ) -> numpy.ndarray[ - numpy.float64[m, 3], - numpy.ndarray.flags.writeable, - numpy.ndarray.flags.c_contiguous, - ]: + def affine(self, T: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[4, 4]"]) -> Feature: + """ + Apply 4x4 affine transformation matrix + """ + def as_numpy(self) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]", "flags.writeable", "flags.c_contiguous"]: """ Get a numpy view of the feature geometry """ - def bbox(self, *, with_z: bool = False) -> numpy.ndarray[numpy.float64[m, 1]]: + def bbox(self, *, with_z: bool = False) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 1]"]: """ Compute the bounding box of the feature """ @@ -116,15 +93,7 @@ class Feature: """ Remove duplicate consecutive points based on their XYZ coordinates """ - def dump( - self, - path: str, - *, - indent: bool = False, - sort_keys: bool = False, - precision: int = 8, - only_xy: bool = False, - ) -> bool: + def dump(self, path: str, *, indent: bool = False, sort_keys: bool = False, precision: typing.SupportsInt = 8, only_xy: bool = False) -> bool: """ Dump the feature to a file (GeoJSON or Geobuf) """ @@ -197,11 +166,11 @@ class Feature: """ Set the feature ID """ - def items(self) -> typing.Iterator[tuple[str, value]]: + def items(self) -> collections.abc.Iterator[tuple[str, value]]: """ Get an iterator over custom property items """ - def keys(self) -> typing.Iterator[str]: + def keys(self) -> collections.abc.Iterator[str]: """ Get an iterator over custom property keys """ @@ -234,17 +203,27 @@ class Feature: """ Set a property value by key """ - def round(self, *, lon: int = 8, lat: int = 8, alt: int = 3) -> Feature: + def rotate(self, R: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 3]"]) -> Feature: + """ + Apply 3x3 rotation matrix to all coordinates + """ + def round(self, *, lon: typing.SupportsInt = 8, lat: typing.SupportsInt = 8, alt: typing.SupportsInt = 3) -> Feature: """ Round the coordinates of the feature geometry """ - def to_geobuf( - self, *, precision: int = 8, only_xy: bool = False, round_z: int | None = None - ) -> bytes: + def scale(self, scale: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> Feature: + """ + Scale all coordinates by factors [sx, sy, sz] + """ + def to_enu(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> Feature: + """ + Convert WGS84 (lon,lat,alt) to ENU coordinates + """ + def to_geobuf(self, *, precision: typing.SupportsInt = 8, only_xy: bool = False, round_z: typing.SupportsInt | None = None) -> bytes: """ Convert the feature to Geobuf bytes """ - def to_numpy(self) -> numpy.ndarray[numpy.float64[m, 3]]: + def to_numpy(self) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]"]: """ Convert the feature geometry to a numpy array """ @@ -252,7 +231,18 @@ class Feature: """ Convert the feature to a RapidJSON value """ - + def to_wgs84(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> Feature: + """ + Convert ENU coordinates to WGS84 (lon,lat,alt) + """ + def transform(self, fn: typing.Any) -> Feature: + """ + Apply transform function to all coordinates (Nx3 numpy array) + """ + def translate(self, offset: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> Feature: + """ + Translate all coordinates by offset vector + """ class FeatureCollection(FeatureList): def __call__(self) -> typing.Any: """ @@ -272,7 +262,7 @@ class FeatureCollection(FeatureList): Delete a custom property """ @typing.overload - def __delitem__(self, arg0: int) -> None: + def __delitem__(self, arg0: typing.SupportsInt) -> None: """ Delete the list elements at index ``i`` """ @@ -287,7 +277,7 @@ class FeatureCollection(FeatureList): Get a custom property by key """ @typing.overload - def __getitem__(self, arg0: int) -> Feature: + def __getitem__(self, arg0: typing.SupportsInt) -> Feature: """ Get a feature from the collection by index """ @@ -307,7 +297,7 @@ class FeatureCollection(FeatureList): Initialize a FeatureCollection from another FeatureCollection """ @typing.overload - def __init__(self, N: int) -> None: + def __init__(self, N: typing.SupportsInt) -> None: """ Initialize a FeatureCollection with N empty features """ @@ -317,7 +307,7 @@ class FeatureCollection(FeatureList): Set a custom property """ @typing.overload - def __setitem__(self, arg0: int, arg1: Feature) -> None: + def __setitem__(self, arg0: typing.SupportsInt, arg1: Feature) -> None: """ Set a feature in the collection at the specified index """ @@ -326,6 +316,10 @@ class FeatureCollection(FeatureList): """ Assign list elements using a slice object """ + def affine(self, T: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[4, 4]"]) -> FeatureCollection: + """ + Apply 4x4 affine transformation matrix + """ def clone(self) -> FeatureCollection: """ Create a clone of the object @@ -344,15 +338,7 @@ class FeatureCollection(FeatureList): """ Remove duplicate consecutive points based on their XYZ coordinates """ - def dump( - self, - path: str, - *, - indent: bool = False, - sort_keys: bool = False, - precision: int = 8, - only_xy: bool = False, - ) -> bool: + def dump(self, path: str, *, indent: bool = False, sort_keys: bool = False, precision: typing.SupportsInt = 8, only_xy: bool = False) -> bool: """ Dump the FeatureCollection to a file (GeoJSON or Geobuf) """ @@ -360,17 +346,15 @@ class FeatureCollection(FeatureList): """ Load the FeatureCollection from Geobuf bytes """ - def from_rapidjson( - self, arg0: pybind11_geobuf._core.rapidjson - ) -> FeatureCollection: + def from_rapidjson(self, arg0: pybind11_geobuf._core.rapidjson) -> FeatureCollection: """ Load the FeatureCollection from a RapidJSON value """ - def items(self) -> typing.Iterator[tuple[str, value]]: + def items(self) -> collections.abc.Iterator[tuple[str, value]]: """ Return an iterator over the items of custom properties """ - def keys(self) -> typing.Iterator[str]: + def keys(self) -> collections.abc.Iterator[str]: """ Return an iterator over the keys of custom properties """ @@ -378,17 +362,27 @@ class FeatureCollection(FeatureList): """ Load the FeatureCollection from a file (GeoJSON or Geobuf) """ - def resize(self, arg0: int) -> FeatureCollection: + def resize(self, arg0: typing.SupportsInt) -> FeatureCollection: """ Resize the FeatureCollection to contain N features """ - def round(self, *, lon: int = 8, lat: int = 8, alt: int = 3) -> FeatureCollection: + def rotate(self, R: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 3]"]) -> FeatureCollection: + """ + Apply 3x3 rotation matrix to all coordinates + """ + def round(self, *, lon: typing.SupportsInt = 8, lat: typing.SupportsInt = 8, alt: typing.SupportsInt = 3) -> FeatureCollection: """ Round the coordinates of all features in the collection """ - def to_geobuf( - self, *, precision: int = 8, only_xy: bool = False, round_z: int | None = None - ) -> bytes: + def scale(self, scale: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> FeatureCollection: + """ + Scale all coordinates by factors [sx, sy, sz] + """ + def to_enu(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> FeatureCollection: + """ + Convert WGS84 (lon,lat,alt) to ENU coordinates + """ + def to_geobuf(self, *, precision: typing.SupportsInt = 8, only_xy: bool = False, round_z: typing.SupportsInt | None = None) -> bytes: """ Convert the FeatureCollection to Geobuf bytes """ @@ -396,24 +390,36 @@ class FeatureCollection(FeatureList): """ Convert the FeatureCollection to a RapidJSON value """ - def values(self) -> typing.Iterator[value]: + def to_wgs84(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> FeatureCollection: + """ + Convert ENU coordinates to WGS84 (lon,lat,alt) + """ + def transform(self, fn: typing.Any) -> FeatureCollection: + """ + Apply transform function to all coordinates (Nx3 numpy array) + """ + def translate(self, offset: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> FeatureCollection: + """ + Translate all coordinates by offset vector + """ + def values(self) -> collections.abc.Iterator[value]: """ Return an iterator over the values of custom properties """ - class FeatureList: __hash__: typing.ClassVar[None] = None def __bool__(self) -> bool: """ Check whether the list is nonempty """ - def __call__(self) -> typing.Any: ... + def __call__(self) -> typing.Any: + ... def __contains__(self, x: Feature) -> bool: """ Return true the container contains ``x`` """ @typing.overload - def __delitem__(self, arg0: int) -> None: + def __delitem__(self, arg0: typing.SupportsInt) -> None: """ Delete the list elements at index ``i`` """ @@ -422,28 +428,36 @@ class FeatureList: """ Delete list elements using a slice object """ - def __eq__(self, arg0: FeatureList) -> bool: ... + def __eq__(self, arg0: FeatureList) -> bool: + ... @typing.overload def __getitem__(self, s: slice) -> FeatureList: """ Retrieve list elements using a slice object """ @typing.overload - def __getitem__(self, arg0: int) -> Feature: ... + def __getitem__(self, arg0: typing.SupportsInt) -> Feature: + ... @typing.overload - def __init__(self) -> None: ... + def __init__(self) -> None: + ... @typing.overload def __init__(self, arg0: FeatureList) -> None: """ Copy constructor """ @typing.overload - def __init__(self, arg0: typing.Iterable) -> None: ... - def __iter__(self) -> typing.Iterator[Feature]: ... - def __len__(self) -> int: ... - def __ne__(self, arg0: FeatureList) -> bool: ... + def __init__(self, arg0: collections.abc.Iterable) -> None: + ... + def __iter__(self) -> collections.abc.Iterator[Feature]: + ... + def __len__(self) -> int: + ... + def __ne__(self, arg0: FeatureList) -> bool: + ... @typing.overload - def __setitem__(self, arg0: int, arg1: Feature) -> None: ... + def __setitem__(self, arg0: typing.SupportsInt, arg1: Feature) -> None: + ... @typing.overload def __setitem__(self, arg0: slice, arg1: FeatureList) -> None: """ @@ -467,11 +481,11 @@ class FeatureList: Extend the list by appending all the items in the given list """ @typing.overload - def extend(self, L: typing.Iterable) -> None: + def extend(self, L: collections.abc.Iterable) -> None: """ Extend the list by appending all the items in the given list """ - def insert(self, i: int, x: Feature) -> None: + def insert(self, i: typing.SupportsInt, x: Feature) -> None: """ Insert an item at a given position. """ @@ -481,7 +495,7 @@ class FeatureList: Remove and return the last item """ @typing.overload - def pop(self, i: int) -> Feature: + def pop(self, i: typing.SupportsInt) -> Feature: """ Remove and return the item at index ``i`` """ @@ -489,7 +503,6 @@ class FeatureList: """ Remove the first item from the list whose value is x. It is an error if there is no such item. """ - class GeoJSON: __hash__: typing.ClassVar[None] = None def __call__(self) -> typing.Any: @@ -532,6 +545,10 @@ class GeoJSON: """ Check if two GeoJSON objects are not equal """ + def affine(self, T: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[4, 4]"]) -> GeoJSON: + """ + Apply 4x4 affine transformation matrix + """ def as_feature(self) -> ...: """ Get this GeoJSON object as a feature (if it is one) @@ -548,13 +565,7 @@ class GeoJSON: """ Create a clone of the object """ - def crop( - self, - polygon: numpy.ndarray[numpy.float64[m, 3]], - *, - clipping_mode: str = "longest", - max_z_offset: float | None = None, - ) -> ...: + def crop(self, polygon: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[m, 3]"], *, clipping_mode: str = 'longest', max_z_offset: typing.SupportsFloat | None = None) -> ...: """ Crop the GeoJSON object using a polygon """ @@ -562,15 +573,7 @@ class GeoJSON: """ Remove duplicate consecutive points based on their XYZ coordinates """ - def dump( - self, - path: str, - *, - indent: bool = False, - sort_keys: bool = False, - precision: int = 8, - only_xy: bool = False, - ) -> bool: + def dump(self, path: str, *, indent: bool = False, sort_keys: bool = False, precision: typing.SupportsInt = 8, only_xy: bool = False) -> bool: """ Dump the GeoJSON object to a file """ @@ -598,13 +601,23 @@ class GeoJSON: """ Load a GeoJSON object from a file """ - def round(self, *, lon: int = 8, lat: int = 8, alt: int = 3) -> GeoJSON: + def rotate(self, R: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 3]"]) -> GeoJSON: + """ + Apply 3x3 rotation matrix to all coordinates + """ + def round(self, *, lon: typing.SupportsInt = 8, lat: typing.SupportsInt = 8, alt: typing.SupportsInt = 3) -> GeoJSON: """ Round coordinates to specified decimal places """ - def to_geobuf( - self, *, precision: int = 8, only_xy: bool = False, round_z: int | None = None - ) -> bytes: + def scale(self, scale: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> GeoJSON: + """ + Scale all coordinates by factors [sx, sy, sz] + """ + def to_enu(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> GeoJSON: + """ + Convert WGS84 (lon,lat,alt) to ENU coordinates + """ + def to_geobuf(self, *, precision: typing.SupportsInt = 8, only_xy: bool = False, round_z: typing.SupportsInt | None = None) -> bytes: """ Encode the GeoJSON object to a Geobuf byte string """ @@ -612,7 +625,18 @@ class GeoJSON: """ Convert the GeoJSON object to a RapidJSON value """ - + def to_wgs84(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> GeoJSON: + """ + Convert ENU coordinates to WGS84 (lon,lat,alt) + """ + def transform(self, fn: typing.Any) -> GeoJSON: + """ + Apply transform function to all coordinates (Nx3 numpy array) + """ + def translate(self, offset: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> GeoJSON: + """ + Translate all coordinates by offset vector + """ class Geometry(GeometryBase): __hash__: typing.ClassVar[None] = None def __call__(self) -> typing.Any: @@ -639,7 +663,8 @@ class Geometry(GeometryBase): """ Get a custom property value """ - def __getstate__(self) -> typing.Any: ... + def __getstate__(self) -> typing.Any: + ... @typing.overload def __init__(self) -> None: """ @@ -700,7 +725,7 @@ class Geometry(GeometryBase): """ Initialize from a Python dictionary """ - def __iter__(self) -> typing.Iterator[str]: + def __iter__(self) -> collections.abc.Iterator[str]: """ Get an iterator over the custom property keys """ @@ -720,6 +745,10 @@ class Geometry(GeometryBase): """ Pickle support for Geometry objects """ + def affine(self, T: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[4, 4]"]) -> Geometry: + """ + Apply 4x4 affine transformation matrix + """ def as_geometry_collection(self) -> ...: """ Get this geometry as a geometry_collection (if it is one) @@ -740,13 +769,7 @@ class Geometry(GeometryBase): """ Get this geometry as a multi_polygon (if it is one) """ - def as_numpy( - self, - ) -> numpy.ndarray[ - numpy.float64[m, 3], - numpy.ndarray.flags.writeable, - numpy.ndarray.flags.c_contiguous, - ]: + def as_numpy(self) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]", "flags.writeable", "flags.c_contiguous"]: """ Get a numpy view of the geometry coordinates """ @@ -758,7 +781,7 @@ class Geometry(GeometryBase): """ Get this geometry as a polygon (if it is one) """ - def bbox(self, *, with_z: bool = False) -> numpy.ndarray[numpy.float64[m, 1]]: + def bbox(self, *, with_z: bool = False) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 1]"]: """ Get the bounding box of the geometry """ @@ -776,7 +799,7 @@ class Geometry(GeometryBase): Get the 'custom_properties' attribute """ @typing.overload - def custom_properties(self, new_value: ...) -> Geometry: + def custom_properties(self, new_value: ..., std: ..., std: ..., mapbox: ..., std: ..., std: ..., std: ..., std: ..., std: ..., std: ..., std: ..., std: ..., std: ..., mapbox: ...) -> Geometry: """ Set the 'custom_properties' attribute """ @@ -784,15 +807,7 @@ class Geometry(GeometryBase): """ Remove duplicate consecutive points based on their XYZ coordinates """ - def dump( - self, - path: str, - *, - indent: bool = False, - sort_keys: bool = False, - precision: int = 8, - only_xy: bool = False, - ) -> bool: + def dump(self, path: str, *, indent: bool = False, sort_keys: bool = False, precision: typing.SupportsInt = 8, only_xy: bool = False) -> bool: """ Dump the geometry to a file """ @@ -800,9 +815,7 @@ class Geometry(GeometryBase): """ Decode a Geobuf byte string into a geometry """ - def from_numpy( - self, arg0: numpy.ndarray[numpy.float64[m, n], numpy.ndarray.flags.c_contiguous] - ) -> Geometry: + def from_numpy(self, arg0: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]) -> Geometry: """ Set geometry coordinates from a numpy array """ @@ -846,11 +859,11 @@ class Geometry(GeometryBase): """ Check if this geometry is of type polygon """ - def items(self) -> typing.Iterator[tuple[str, ...]]: + def items(self) -> collections.abc.Iterator[tuple[str, ...]]: """ Get an iterator over the custom property items """ - def keys(self) -> typing.Iterator[str]: + def keys(self) -> collections.abc.Iterator[str]: """ Get an iterator over the custom property keys """ @@ -868,14 +881,12 @@ class Geometry(GeometryBase): Add a point to the geometry """ @typing.overload - def push_back(self, arg0: numpy.ndarray[numpy.float64[m, 1]]) -> Geometry: + def push_back(self, arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[m, 1]"]) -> Geometry: """ Add a point to the geometry """ @typing.overload - def push_back( - self, arg0: numpy.ndarray[numpy.float64[m, n], numpy.ndarray.flags.c_contiguous] - ) -> Geometry: + def push_back(self, arg0: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]) -> Geometry: """ Add multiple points to the geometry """ @@ -894,21 +905,31 @@ class Geometry(GeometryBase): """ Add a line string to a multi-line string geometry """ - def resize(self, arg0: int) -> Geometry: + def resize(self, arg0: typing.SupportsInt) -> Geometry: """ Resize the geometry """ - def round(self, *, lon: int = 8, lat: int = 8, alt: int = 3) -> Geometry: + def rotate(self, R: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 3]"]) -> Geometry: + """ + Apply 3x3 rotation matrix to all coordinates + """ + def round(self, *, lon: typing.SupportsInt = 8, lat: typing.SupportsInt = 8, alt: typing.SupportsInt = 3) -> Geometry: """ Round coordinates to specified decimal places """ - def to_geobuf( - self, *, precision: int = 8, only_xy: bool = False, round_z: int | None = None - ) -> bytes: + def scale(self, scale: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> Geometry: + """ + Scale all coordinates by factors [sx, sy, sz] + """ + def to_enu(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> Geometry: + """ + Convert WGS84 (lon,lat,alt) to ENU coordinates + """ + def to_geobuf(self, *, precision: typing.SupportsInt = 8, only_xy: bool = False, round_z: typing.SupportsInt | None = None) -> bytes: """ Encode the geometry to a Geobuf byte string """ - def to_numpy(self) -> numpy.ndarray[numpy.float64[m, 3]]: + def to_numpy(self) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]"]: """ Convert geometry coordinates to a numpy array """ @@ -916,20 +937,31 @@ class Geometry(GeometryBase): """ Convert the geometry to a RapidJSON value """ + def to_wgs84(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> Geometry: + """ + Convert ENU coordinates to WGS84 (lon,lat,alt) + """ + def transform(self, fn: typing.Any) -> Geometry: + """ + Apply transform function to all coordinates (Nx3 numpy array) + """ + def translate(self, offset: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> Geometry: + """ + Translate all coordinates by offset vector + """ def type(self) -> str: """ Get the type of the geometry """ - def values(self) -> typing.Iterator[...]: + def values(self) -> collections.abc.Iterator[...]: """ Get an iterator over the custom property values """ @property - def __geo_interface__(self) -> typing.Any: ... - + def __geo_interface__(self) -> typing.Any: + ... class GeometryBase: pass - class GeometryCollection(GeometryList): __hash__: typing.ClassVar[None] = None def __call__(self) -> typing.Any: @@ -940,7 +972,8 @@ class GeometryCollection(GeometryList): """ Check if two GeometryCollections are equal """ - def __getstate__(self) -> typing.Any: ... + def __getstate__(self) -> typing.Any: + ... @typing.overload def __init__(self) -> None: """ @@ -952,7 +985,7 @@ class GeometryCollection(GeometryList): Copy constructor for GeometryCollection """ @typing.overload - def __init__(self, N: int) -> None: + def __init__(self, N: typing.SupportsInt) -> None: """ Construct a GeometryCollection with N empty geometries """ @@ -961,42 +994,42 @@ class GeometryCollection(GeometryList): Check if two GeometryCollections are not equal """ @typing.overload - def __setitem__(self, arg0: int, arg1: Geometry) -> GeometryCollection: + def __setitem__(self, arg0: typing.SupportsInt, arg1: Geometry) -> GeometryCollection: """ Set a geometry in the GeometryCollection by index """ @typing.overload - def __setitem__(self, arg0: int, arg1: Point) -> GeometryCollection: + def __setitem__(self, arg0: typing.SupportsInt, arg1: Point) -> GeometryCollection: """ Set a geometry in the GeometryCollection by index """ @typing.overload - def __setitem__(self, arg0: int, arg1: MultiPoint) -> GeometryCollection: + def __setitem__(self, arg0: typing.SupportsInt, arg1: MultiPoint) -> GeometryCollection: """ Set a geometry in the GeometryCollection by index """ @typing.overload - def __setitem__(self, arg0: int, arg1: LineString) -> GeometryCollection: + def __setitem__(self, arg0: typing.SupportsInt, arg1: LineString) -> GeometryCollection: """ Set a geometry in the GeometryCollection by index """ @typing.overload - def __setitem__(self, arg0: int, arg1: MultiLineString) -> GeometryCollection: + def __setitem__(self, arg0: typing.SupportsInt, arg1: MultiLineString) -> GeometryCollection: """ Set a geometry in the GeometryCollection by index """ @typing.overload - def __setitem__(self, arg0: int, arg1: Polygon) -> GeometryCollection: + def __setitem__(self, arg0: typing.SupportsInt, arg1: Polygon) -> GeometryCollection: """ Set a geometry in the GeometryCollection by index """ @typing.overload - def __setitem__(self, arg0: int, arg1: MultiPolygon) -> GeometryCollection: + def __setitem__(self, arg0: typing.SupportsInt, arg1: MultiPolygon) -> GeometryCollection: """ Set a geometry in the GeometryCollection by index """ @typing.overload - def __setitem__(self, arg0: int, arg1: GeometryCollection) -> GeometryCollection: + def __setitem__(self, arg0: typing.SupportsInt, arg1: GeometryCollection) -> GeometryCollection: """ Set a geometry in the GeometryCollection by index """ @@ -1004,6 +1037,10 @@ class GeometryCollection(GeometryList): """ Pickle support for GeometryCollection """ + def affine(self, T: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[4, 4]"]) -> GeometryCollection: + """ + Apply 4x4 affine transformation matrix + """ def clear(self) -> GeometryCollection: """ Clear all geometries from the GeometryCollection @@ -1012,9 +1049,7 @@ class GeometryCollection(GeometryList): """ Remove duplicate consecutive points based on their XYZ coordinates """ - def from_rapidjson( - self, arg0: pybind11_geobuf._core.rapidjson - ) -> GeometryCollection: + def from_rapidjson(self, arg0: pybind11_geobuf._core.rapidjson) -> GeometryCollection: """ Set the GeometryCollection from a RapidJSON value """ @@ -1062,24 +1097,47 @@ class GeometryCollection(GeometryList): """ Add a new geometry to the GeometryCollection """ - def resize(self, arg0: int) -> GeometryCollection: + def resize(self, arg0: typing.SupportsInt) -> GeometryCollection: """ Resize the GeometryCollection to contain N geometries """ - def round(self, *, lon: int = 8, lat: int = 8, alt: int = 3) -> GeometryCollection: + def rotate(self, R: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 3]"]) -> GeometryCollection: + """ + Apply 3x3 rotation matrix to all coordinates + """ + def round(self, *, lon: typing.SupportsInt = 8, lat: typing.SupportsInt = 8, alt: typing.SupportsInt = 3) -> GeometryCollection: """ Round the coordinates of all geometries in the GeometryCollection """ + def scale(self, scale: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> GeometryCollection: + """ + Scale all coordinates by factors [sx, sy, sz] + """ + def to_enu(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> GeometryCollection: + """ + Convert WGS84 (lon,lat,alt) to ENU coordinates + """ def to_rapidjson(self) -> pybind11_geobuf._core.rapidjson: """ Convert the GeometryCollection to a RapidJSON value """ + def to_wgs84(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> GeometryCollection: + """ + Convert ENU coordinates to WGS84 (lon,lat,alt) + """ + def transform(self, fn: typing.Any) -> GeometryCollection: + """ + Apply transform function to all coordinates (Nx3 numpy array) + """ + def translate(self, offset: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> GeometryCollection: + """ + Translate all coordinates by offset vector + """ @property def __geo_interface__(self) -> typing.Any: """ Return the __geo_interface__ representation of the GeometryCollection """ - class GeometryList: __hash__: typing.ClassVar[None] = None def __bool__(self) -> bool: @@ -1091,7 +1149,7 @@ class GeometryList: Return true the container contains ``x`` """ @typing.overload - def __delitem__(self, arg0: int) -> None: + def __delitem__(self, arg0: typing.SupportsInt) -> None: """ Delete the list elements at index ``i`` """ @@ -1100,28 +1158,36 @@ class GeometryList: """ Delete list elements using a slice object """ - def __eq__(self, arg0: GeometryList) -> bool: ... + def __eq__(self, arg0: GeometryList) -> bool: + ... @typing.overload def __getitem__(self, s: slice) -> GeometryList: """ Retrieve list elements using a slice object """ @typing.overload - def __getitem__(self, arg0: int) -> Geometry: ... + def __getitem__(self, arg0: typing.SupportsInt) -> Geometry: + ... @typing.overload - def __init__(self) -> None: ... + def __init__(self) -> None: + ... @typing.overload def __init__(self, arg0: GeometryList) -> None: """ Copy constructor """ @typing.overload - def __init__(self, arg0: typing.Iterable) -> None: ... - def __iter__(self) -> typing.Iterator[Geometry]: ... - def __len__(self) -> int: ... - def __ne__(self, arg0: GeometryList) -> bool: ... + def __init__(self, arg0: collections.abc.Iterable) -> None: + ... + def __iter__(self) -> collections.abc.Iterator[Geometry]: + ... + def __len__(self) -> int: + ... + def __ne__(self, arg0: GeometryList) -> bool: + ... @typing.overload - def __setitem__(self, arg0: int, arg1: Geometry) -> None: ... + def __setitem__(self, arg0: typing.SupportsInt, arg1: Geometry) -> None: + ... @typing.overload def __setitem__(self, arg0: slice, arg1: GeometryList) -> None: """ @@ -1145,11 +1211,11 @@ class GeometryList: Extend the list by appending all the items in the given list """ @typing.overload - def extend(self, L: typing.Iterable) -> None: + def extend(self, L: collections.abc.Iterable) -> None: """ Extend the list by appending all the items in the given list """ - def insert(self, i: int, x: Geometry) -> None: + def insert(self, i: typing.SupportsInt, x: Geometry) -> None: """ Insert an item at a given position. """ @@ -1159,7 +1225,7 @@ class GeometryList: Remove and return the last item """ @typing.overload - def pop(self, i: int) -> Geometry: + def pop(self, i: typing.SupportsInt) -> Geometry: """ Remove and return the item at index ``i`` """ @@ -1167,7 +1233,6 @@ class GeometryList: """ Remove the first item from the list whose value is x. It is an error if there is no such item. """ - class LineString(coordinates): __hash__: typing.ClassVar[None] = None def __call__(self) -> typing.Any: @@ -1186,24 +1251,23 @@ class LineString(coordinates): """ Check if two LineStrings are equal """ - def __getitem__(self, arg0: int) -> Point: + def __getitem__(self, arg0: typing.SupportsInt) -> Point: """ Get a point from the geometry by index """ - def __getstate__(self) -> typing.Any: ... + def __getstate__(self) -> typing.Any: + ... @typing.overload def __init__(self) -> None: """ Default constructor for LineString """ @typing.overload - def __init__( - self, arg0: numpy.ndarray[numpy.float64[m, n], numpy.ndarray.flags.c_contiguous] - ) -> None: + def __init__(self, arg0: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]) -> None: """ Initialize from a numpy array of points """ - def __iter__(self) -> typing.Iterator[Point]: + def __iter__(self) -> collections.abc.Iterator[Point]: """ Iterate over the points in the geometry """ @@ -1216,14 +1280,12 @@ class LineString(coordinates): Check if two LineStrings are not equal """ @typing.overload - def __setitem__(self, arg0: int, arg1: Point) -> Point: + def __setitem__(self, arg0: typing.SupportsInt, arg1: Point) -> Point: """ Set a point in the geometry by index """ @typing.overload - def __setitem__( - self, arg0: int, arg1: numpy.ndarray[numpy.float64[m, 1]] - ) -> numpy.ndarray[numpy.float64[m, 1]]: + def __setitem__(self, arg0: typing.SupportsInt, arg1: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[m, 1]"]) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 1]"]: """ Set a point in the geometry by index using a vector """ @@ -1231,17 +1293,15 @@ class LineString(coordinates): """ Pickle support for serialization """ - def as_numpy( - self, - ) -> numpy.ndarray[ - numpy.float64[m, 3], - numpy.ndarray.flags.writeable, - numpy.ndarray.flags.c_contiguous, - ]: + def affine(self, T: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[4, 4]"]) -> LineString: + """ + Apply 4x4 affine transformation matrix + """ + def as_numpy(self) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]", "flags.writeable", "flags.c_contiguous"]: """ Get a numpy view of the geometry points """ - def bbox(self, *, with_z: bool = False) -> numpy.ndarray[numpy.float64[m, 1]]: + def bbox(self, *, with_z: bool = False) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 1]"]: """ Compute the bounding box of the geometry """ @@ -1263,9 +1323,7 @@ class LineString(coordinates): """ Remove duplicate consecutive points based on their XYZ coordinates """ - def from_numpy( - self, arg0: numpy.ndarray[numpy.float64[m, n], numpy.ndarray.flags.c_contiguous] - ) -> LineString: + def from_numpy(self, arg0: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]) -> LineString: """ Set the geometry points from a numpy array """ @@ -1283,19 +1341,31 @@ class LineString(coordinates): Add a point to the end of the geometry """ @typing.overload - def push_back(self, arg0: numpy.ndarray[numpy.float64[m, 1]]) -> LineString: + def push_back(self, arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[m, 1]"]) -> LineString: """ Add a point to the end of the geometry using a vector """ - def resize(self, arg0: int) -> LineString: + def resize(self, arg0: typing.SupportsInt) -> LineString: """ Resize the geometry to the specified size """ - def round(self, *, lon: int = 8, lat: int = 8, alt: int = 3) -> LineString: + def rotate(self, R: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 3]"]) -> LineString: + """ + Apply 3x3 rotation matrix to all coordinates + """ + def round(self, *, lon: typing.SupportsInt = 8, lat: typing.SupportsInt = 8, alt: typing.SupportsInt = 3) -> LineString: """ Round coordinates to specified decimal places """ - def to_numpy(self) -> numpy.ndarray[numpy.float64[m, 3]]: + def scale(self, scale: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> LineString: + """ + Scale all coordinates by factors [sx, sy, sz] + """ + def to_enu(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> LineString: + """ + Convert WGS84 (lon,lat,alt) to ENU coordinates + """ + def to_numpy(self) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]"]: """ Convert the geometry points to a numpy array """ @@ -1303,17 +1373,27 @@ class LineString(coordinates): """ Convert to a RapidJSON value """ + def to_wgs84(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> LineString: + """ + Convert ENU coordinates to WGS84 (lon,lat,alt) + """ + def transform(self, fn: typing.Any) -> LineString: + """ + Apply transform function to all coordinates (Nx3 numpy array) + """ + def translate(self, offset: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> LineString: + """ + Translate all coordinates by offset vector + """ @property def __geo_interface__(self) -> typing.Any: """ Return the __geo_interface__ representation """ - class LineStringList: """ A list of LineStrings """ - __hash__: typing.ClassVar[None] = None def __bool__(self) -> bool: """ @@ -1324,7 +1404,7 @@ class LineStringList: Return true the container contains ``x`` """ @typing.overload - def __delitem__(self, arg0: int) -> None: + def __delitem__(self, arg0: typing.SupportsInt) -> None: """ Delete the list elements at index ``i`` """ @@ -1333,28 +1413,36 @@ class LineStringList: """ Delete list elements using a slice object """ - def __eq__(self, arg0: LineStringList) -> bool: ... + def __eq__(self, arg0: LineStringList) -> bool: + ... @typing.overload def __getitem__(self, s: slice) -> LineStringList: """ Retrieve list elements using a slice object """ @typing.overload - def __getitem__(self, arg0: int) -> LineString: ... + def __getitem__(self, arg0: typing.SupportsInt) -> LineString: + ... @typing.overload - def __init__(self) -> None: ... + def __init__(self) -> None: + ... @typing.overload def __init__(self, arg0: LineStringList) -> None: """ Copy constructor """ @typing.overload - def __init__(self, arg0: typing.Iterable) -> None: ... - def __iter__(self) -> typing.Iterator[LineString]: ... - def __len__(self) -> int: ... - def __ne__(self, arg0: LineStringList) -> bool: ... + def __init__(self, arg0: collections.abc.Iterable) -> None: + ... + def __iter__(self) -> collections.abc.Iterator[LineString]: + ... + def __len__(self) -> int: + ... + def __ne__(self, arg0: LineStringList) -> bool: + ... @typing.overload - def __setitem__(self, arg0: int, arg1: LineString) -> None: ... + def __setitem__(self, arg0: typing.SupportsInt, arg1: LineString) -> None: + ... @typing.overload def __setitem__(self, arg0: slice, arg1: LineStringList) -> None: """ @@ -1378,11 +1466,11 @@ class LineStringList: Extend the list by appending all the items in the given list """ @typing.overload - def extend(self, L: typing.Iterable) -> None: + def extend(self, L: collections.abc.Iterable) -> None: """ Extend the list by appending all the items in the given list """ - def insert(self, i: int, x: LineString) -> None: + def insert(self, i: typing.SupportsInt, x: LineString) -> None: """ Insert an item at a given position. """ @@ -1392,7 +1480,7 @@ class LineStringList: Remove and return the last item """ @typing.overload - def pop(self, i: int) -> LineString: + def pop(self, i: typing.SupportsInt) -> LineString: """ Remove and return the item at index ``i`` """ @@ -1400,7 +1488,6 @@ class LineStringList: """ Remove the first item from the list whose value is x. It is an error if there is no such item. """ - class LinearRing(coordinates): __hash__: typing.ClassVar[None] = None def __call__(self) -> typing.Any: @@ -1419,7 +1506,7 @@ class LinearRing(coordinates): """ Check if two LinearRings are equal """ - def __getitem__(self, arg0: int) -> Point: + def __getitem__(self, arg0: typing.SupportsInt) -> Point: """ Get a point from the geometry by index """ @@ -1427,7 +1514,7 @@ class LinearRing(coordinates): """ Default constructor for LinearRing """ - def __iter__(self) -> typing.Iterator[Point]: + def __iter__(self) -> collections.abc.Iterator[Point]: """ Iterate over the points in the geometry """ @@ -1440,24 +1527,16 @@ class LinearRing(coordinates): Check if two LinearRings are not equal """ @typing.overload - def __setitem__(self, arg0: int, arg1: Point) -> Point: + def __setitem__(self, arg0: typing.SupportsInt, arg1: Point) -> Point: """ Set a point in the geometry by index """ @typing.overload - def __setitem__( - self, arg0: int, arg1: numpy.ndarray[numpy.float64[m, 1]] - ) -> numpy.ndarray[numpy.float64[m, 1]]: + def __setitem__(self, arg0: typing.SupportsInt, arg1: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[m, 1]"]) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 1]"]: """ Set a point in the geometry by index using a vector """ - def as_numpy( - self, - ) -> numpy.ndarray[ - numpy.float64[m, 3], - numpy.ndarray.flags.writeable, - numpy.ndarray.flags.c_contiguous, - ]: + def as_numpy(self) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]", "flags.writeable", "flags.c_contiguous"]: """ Get a numpy view of the geometry points """ @@ -1469,9 +1548,7 @@ class LinearRing(coordinates): """ Create a clone of the object """ - def from_numpy( - self, arg0: numpy.ndarray[numpy.float64[m, n], numpy.ndarray.flags.c_contiguous] - ) -> LinearRing: + def from_numpy(self, arg0: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]) -> LinearRing: """ Set the geometry points from a numpy array """ @@ -1485,20 +1562,18 @@ class LinearRing(coordinates): Add a point to the end of the geometry """ @typing.overload - def push_back(self, arg0: numpy.ndarray[numpy.float64[m, 1]]) -> LinearRing: + def push_back(self, arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[m, 1]"]) -> LinearRing: """ Add a point to the end of the geometry using a vector """ - def to_numpy(self) -> numpy.ndarray[numpy.float64[m, 3]]: + def to_numpy(self) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]"]: """ Convert the geometry points to a numpy array """ - class LinearRingList: """ A list of LinearRings """ - __hash__: typing.ClassVar[None] = None def __bool__(self) -> bool: """ @@ -1509,7 +1584,7 @@ class LinearRingList: Return true the container contains ``x`` """ @typing.overload - def __delitem__(self, arg0: int) -> None: + def __delitem__(self, arg0: typing.SupportsInt) -> None: """ Delete the list elements at index ``i`` """ @@ -1518,28 +1593,36 @@ class LinearRingList: """ Delete list elements using a slice object """ - def __eq__(self, arg0: LinearRingList) -> bool: ... + def __eq__(self, arg0: LinearRingList) -> bool: + ... @typing.overload def __getitem__(self, s: slice) -> LinearRingList: """ Retrieve list elements using a slice object """ @typing.overload - def __getitem__(self, arg0: int) -> LinearRing: ... + def __getitem__(self, arg0: typing.SupportsInt) -> LinearRing: + ... @typing.overload - def __init__(self) -> None: ... + def __init__(self) -> None: + ... @typing.overload def __init__(self, arg0: LinearRingList) -> None: """ Copy constructor """ @typing.overload - def __init__(self, arg0: typing.Iterable) -> None: ... - def __iter__(self) -> typing.Iterator[LinearRing]: ... - def __len__(self) -> int: ... - def __ne__(self, arg0: LinearRingList) -> bool: ... + def __init__(self, arg0: collections.abc.Iterable) -> None: + ... + def __iter__(self) -> collections.abc.Iterator[LinearRing]: + ... + def __len__(self) -> int: + ... + def __ne__(self, arg0: LinearRingList) -> bool: + ... @typing.overload - def __setitem__(self, arg0: int, arg1: LinearRing) -> None: ... + def __setitem__(self, arg0: typing.SupportsInt, arg1: LinearRing) -> None: + ... @typing.overload def __setitem__(self, arg0: slice, arg1: LinearRingList) -> None: """ @@ -1563,11 +1646,11 @@ class LinearRingList: Extend the list by appending all the items in the given list """ @typing.overload - def extend(self, L: typing.Iterable) -> None: + def extend(self, L: collections.abc.Iterable) -> None: """ Extend the list by appending all the items in the given list """ - def insert(self, i: int, x: LinearRing) -> None: + def insert(self, i: typing.SupportsInt, x: LinearRing) -> None: """ Insert an item at a given position. """ @@ -1577,7 +1660,7 @@ class LinearRingList: Remove and return the last item """ @typing.overload - def pop(self, i: int) -> LinearRing: + def pop(self, i: typing.SupportsInt) -> LinearRing: """ Remove and return the item at index ``i`` """ @@ -1585,7 +1668,6 @@ class LinearRingList: """ Remove the first item from the list whose value is x. It is an error if there is no such item. """ - class MultiLineString(LineStringList): __hash__: typing.ClassVar[None] = None def __call__(self) -> typing.Any: @@ -1604,11 +1686,12 @@ class MultiLineString(LineStringList): """ Check if two MultiLineStrings are equal """ - def __getitem__(self, arg0: int) -> LineString: + def __getitem__(self, arg0: typing.SupportsInt) -> LineString: """ Get a linear ring by index """ - def __getstate__(self) -> typing.Any: ... + def __getstate__(self) -> typing.Any: + ... @typing.overload def __init__(self) -> None: """ @@ -1625,13 +1708,11 @@ class MultiLineString(LineStringList): Construct MultiLineString from a single LineString """ @typing.overload - def __init__( - self, arg0: numpy.ndarray[numpy.float64[m, n], numpy.ndarray.flags.c_contiguous] - ) -> None: + def __init__(self, arg0: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]) -> None: """ Initialize from a numpy array of points """ - def __iter__(self) -> typing.Iterator[LineString]: + def __iter__(self) -> collections.abc.Iterator[LineString]: """ Return an iterator over the linear rings in the geometry """ @@ -1643,11 +1724,7 @@ class MultiLineString(LineStringList): """ Check if two MultiLineStrings are not equal """ - def __setitem__( - self, - arg0: int, - arg1: numpy.ndarray[numpy.float64[m, n], numpy.ndarray.flags.c_contiguous], - ) -> numpy.ndarray[numpy.float64[m, n], numpy.ndarray.flags.c_contiguous]: + def __setitem__(self, arg0: typing.SupportsInt, arg1: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]: """ Set a linear ring by index using a numpy array of points """ @@ -1655,17 +1732,15 @@ class MultiLineString(LineStringList): """ Pickle support for the geometry """ - def as_numpy( - self, - ) -> numpy.ndarray[ - numpy.float64[m, 3], - numpy.ndarray.flags.writeable, - numpy.ndarray.flags.c_contiguous, - ]: + def affine(self, T: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[4, 4]"]) -> MultiLineString: + """ + Apply 4x4 affine transformation matrix + """ + def as_numpy(self) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]", "flags.writeable", "flags.c_contiguous"]: """ Return a numpy view of the geometry's points """ - def bbox(self, *, with_z: bool = False) -> numpy.ndarray[numpy.float64[m, 1]]: + def bbox(self, *, with_z: bool = False) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 1]"]: """ Compute the bounding box of the geometry """ @@ -1681,9 +1756,7 @@ class MultiLineString(LineStringList): """ Remove duplicate consecutive points based on their XYZ coordinates """ - def from_numpy( - self, arg0: numpy.ndarray[numpy.float64[m, n], numpy.ndarray.flags.c_contiguous] - ) -> MultiLineString: + def from_numpy(self, arg0: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]) -> MultiLineString: """ Set the geometry from a numpy array of points """ @@ -1696,9 +1769,7 @@ class MultiLineString(LineStringList): Remove the last point from the last linear ring """ @typing.overload - def push_back( - self, arg0: numpy.ndarray[numpy.float64[m, n], numpy.ndarray.flags.c_contiguous] - ) -> MultiLineString: + def push_back(self, arg0: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]) -> MultiLineString: """ Add a new linear ring from a numpy array of points """ @@ -1707,11 +1778,23 @@ class MultiLineString(LineStringList): """ Add a new linear ring """ - def round(self, *, lon: int = 8, lat: int = 8, alt: int = 3) -> MultiLineString: + def rotate(self, R: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 3]"]) -> MultiLineString: + """ + Apply 3x3 rotation matrix to all coordinates + """ + def round(self, *, lon: typing.SupportsInt = 8, lat: typing.SupportsInt = 8, alt: typing.SupportsInt = 3) -> MultiLineString: """ Round the coordinates of the geometry """ - def to_numpy(self) -> numpy.ndarray[numpy.float64[m, 3]]: + def scale(self, scale: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> MultiLineString: + """ + Scale all coordinates by factors [sx, sy, sz] + """ + def to_enu(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> MultiLineString: + """ + Convert WGS84 (lon,lat,alt) to ENU coordinates + """ + def to_numpy(self) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]"]: """ Convert the geometry to a numpy array """ @@ -1719,12 +1802,23 @@ class MultiLineString(LineStringList): """ Convert the geometry to a RapidJSON value """ + def to_wgs84(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> MultiLineString: + """ + Convert ENU coordinates to WGS84 (lon,lat,alt) + """ + def transform(self, fn: typing.Any) -> MultiLineString: + """ + Apply transform function to all coordinates (Nx3 numpy array) + """ + def translate(self, offset: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> MultiLineString: + """ + Translate all coordinates by offset vector + """ @property def __geo_interface__(self) -> typing.Any: """ Return the __geo_interface__ representation of the geometry """ - class MultiPoint(coordinates): __hash__: typing.ClassVar[None] = None def __call__(self) -> typing.Any: @@ -1743,24 +1837,23 @@ class MultiPoint(coordinates): """ Check if two MultiPoints are equal """ - def __getitem__(self, arg0: int) -> Point: + def __getitem__(self, arg0: typing.SupportsInt) -> Point: """ Get a point from the geometry by index """ - def __getstate__(self) -> typing.Any: ... + def __getstate__(self) -> typing.Any: + ... @typing.overload def __init__(self) -> None: """ Default constructor for MultiPoint """ @typing.overload - def __init__( - self, arg0: numpy.ndarray[numpy.float64[m, n], numpy.ndarray.flags.c_contiguous] - ) -> None: + def __init__(self, arg0: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]) -> None: """ Initialize from a numpy array of points """ - def __iter__(self) -> typing.Iterator[Point]: + def __iter__(self) -> collections.abc.Iterator[Point]: """ Iterate over the points in the geometry """ @@ -1773,14 +1866,12 @@ class MultiPoint(coordinates): Check if two MultiPoints are not equal """ @typing.overload - def __setitem__(self, arg0: int, arg1: Point) -> Point: + def __setitem__(self, arg0: typing.SupportsInt, arg1: Point) -> Point: """ Set a point in the geometry by index """ @typing.overload - def __setitem__( - self, arg0: int, arg1: numpy.ndarray[numpy.float64[m, 1]] - ) -> numpy.ndarray[numpy.float64[m, 1]]: + def __setitem__(self, arg0: typing.SupportsInt, arg1: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[m, 1]"]) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 1]"]: """ Set a point in the geometry by index using a vector """ @@ -1788,17 +1879,15 @@ class MultiPoint(coordinates): """ Pickle support for serialization """ - def as_numpy( - self, - ) -> numpy.ndarray[ - numpy.float64[m, 3], - numpy.ndarray.flags.writeable, - numpy.ndarray.flags.c_contiguous, - ]: + def affine(self, T: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[4, 4]"]) -> MultiPoint: + """ + Apply 4x4 affine transformation matrix + """ + def as_numpy(self) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]", "flags.writeable", "flags.c_contiguous"]: """ Get a numpy view of the geometry points """ - def bbox(self, *, with_z: bool = False) -> numpy.ndarray[numpy.float64[m, 1]]: + def bbox(self, *, with_z: bool = False) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 1]"]: """ Compute the bounding box of the geometry """ @@ -1814,9 +1903,7 @@ class MultiPoint(coordinates): """ Remove duplicate consecutive points based on their XYZ coordinates """ - def from_numpy( - self, arg0: numpy.ndarray[numpy.float64[m, n], numpy.ndarray.flags.c_contiguous] - ) -> MultiPoint: + def from_numpy(self, arg0: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]) -> MultiPoint: """ Set the geometry points from a numpy array """ @@ -1834,19 +1921,31 @@ class MultiPoint(coordinates): Add a point to the end of the geometry """ @typing.overload - def push_back(self, arg0: numpy.ndarray[numpy.float64[m, 1]]) -> MultiPoint: + def push_back(self, arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[m, 1]"]) -> MultiPoint: """ Add a point to the end of the geometry using a vector """ - def resize(self, arg0: int) -> MultiPoint: + def resize(self, arg0: typing.SupportsInt) -> MultiPoint: """ Resize the geometry to the specified size """ - def round(self, *, lon: int = 8, lat: int = 8, alt: int = 3) -> MultiPoint: + def rotate(self, R: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 3]"]) -> MultiPoint: + """ + Apply 3x3 rotation matrix to all coordinates + """ + def round(self, *, lon: typing.SupportsInt = 8, lat: typing.SupportsInt = 8, alt: typing.SupportsInt = 3) -> MultiPoint: """ Round coordinates to specified decimal places """ - def to_numpy(self) -> numpy.ndarray[numpy.float64[m, 3]]: + def scale(self, scale: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> MultiPoint: + """ + Scale all coordinates by factors [sx, sy, sz] + """ + def to_enu(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> MultiPoint: + """ + Convert WGS84 (lon,lat,alt) to ENU coordinates + """ + def to_numpy(self) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]"]: """ Convert the geometry points to a numpy array """ @@ -1854,12 +1953,23 @@ class MultiPoint(coordinates): """ Convert to a RapidJSON value """ + def to_wgs84(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> MultiPoint: + """ + Convert ENU coordinates to WGS84 (lon,lat,alt) + """ + def transform(self, fn: typing.Any) -> MultiPoint: + """ + Apply transform function to all coordinates (Nx3 numpy array) + """ + def translate(self, offset: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> MultiPoint: + """ + Translate all coordinates by offset vector + """ @property def __geo_interface__(self) -> typing.Any: """ Return the __geo_interface__ representation """ - class MultiPolygon(PolygonList): __hash__: typing.ClassVar[None] = None def __call__(self) -> typing.Any: @@ -1878,11 +1988,12 @@ class MultiPolygon(PolygonList): """ Check if two MultiPolygons are equal """ - def __getitem__(self, arg0: int) -> Polygon: + def __getitem__(self, arg0: typing.SupportsInt) -> Polygon: """ Get a Polygon from the MultiPolygon by index """ - def __getstate__(self) -> typing.Any: ... + def __getstate__(self) -> typing.Any: + ... @typing.overload def __init__(self) -> None: """ @@ -1899,13 +2010,11 @@ class MultiPolygon(PolygonList): Construct MultiPolygon from a container of Polygons """ @typing.overload - def __init__( - self, arg0: numpy.ndarray[numpy.float64[m, n], numpy.ndarray.flags.c_contiguous] - ) -> None: + def __init__(self, arg0: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]) -> None: """ Construct MultiPolygon from a numpy array of points """ - def __iter__(self) -> typing.Iterator[Polygon]: + def __iter__(self) -> collections.abc.Iterator[Polygon]: """ Return an iterator over the Polygons in the MultiPolygon """ @@ -1918,16 +2027,12 @@ class MultiPolygon(PolygonList): Check if two MultiPolygons are not equal """ @typing.overload - def __setitem__(self, arg0: int, arg1: Polygon) -> Polygon: + def __setitem__(self, arg0: typing.SupportsInt, arg1: Polygon) -> Polygon: """ Set a Polygon in the MultiPolygon by index """ @typing.overload - def __setitem__( - self, - arg0: int, - arg1: numpy.ndarray[numpy.float64[m, n], numpy.ndarray.flags.c_contiguous], - ) -> Polygon: + def __setitem__(self, arg0: typing.SupportsInt, arg1: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]) -> Polygon: """ Set a Polygon in the MultiPolygon by index using a numpy array """ @@ -1935,17 +2040,15 @@ class MultiPolygon(PolygonList): """ Pickle support for MultiPolygon """ - def as_numpy( - self, - ) -> numpy.ndarray[ - numpy.float64[m, 3], - numpy.ndarray.flags.writeable, - numpy.ndarray.flags.c_contiguous, - ]: + def affine(self, T: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[4, 4]"]) -> MultiPolygon: + """ + Apply 4x4 affine transformation matrix + """ + def as_numpy(self) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]", "flags.writeable", "flags.c_contiguous"]: """ Return a numpy view of the MultiPolygon coordinates """ - def bbox(self, *, with_z: bool = False) -> numpy.ndarray[numpy.float64[m, 1]]: + def bbox(self, *, with_z: bool = False) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 1]"]: """ Compute the bounding box of the MultiPolygon """ @@ -1957,9 +2060,7 @@ class MultiPolygon(PolygonList): """ Create a clone of the object """ - def from_numpy( - self, arg0: numpy.ndarray[numpy.float64[m, n], numpy.ndarray.flags.c_contiguous] - ) -> MultiPolygon: + def from_numpy(self, arg0: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]) -> MultiPolygon: """ Set MultiPolygon coordinates from a numpy array """ @@ -1972,9 +2073,7 @@ class MultiPolygon(PolygonList): Remove the last Polygon from the MultiPolygon """ @typing.overload - def push_back( - self, arg0: numpy.ndarray[numpy.float64[m, n], numpy.ndarray.flags.c_contiguous] - ) -> MultiPolygon: + def push_back(self, arg0: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]) -> MultiPolygon: """ Add a new Polygon to the MultiPolygon from a numpy array """ @@ -1983,11 +2082,23 @@ class MultiPolygon(PolygonList): """ Add a new Polygon to the MultiPolygon """ - def round(self, *, lon: int = 8, lat: int = 8, alt: int = 3) -> MultiPolygon: + def rotate(self, R: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 3]"]) -> MultiPolygon: + """ + Apply 3x3 rotation matrix to all coordinates + """ + def round(self, *, lon: typing.SupportsInt = 8, lat: typing.SupportsInt = 8, alt: typing.SupportsInt = 3) -> MultiPolygon: """ Round the coordinates of the MultiPolygon """ - def to_numpy(self: Polygon) -> numpy.ndarray[numpy.float64[m, 3]]: + def scale(self, scale: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> MultiPolygon: + """ + Scale all coordinates by factors [sx, sy, sz] + """ + def to_enu(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> MultiPolygon: + """ + Convert WGS84 (lon,lat,alt) to ENU coordinates + """ + def to_numpy(self: Polygon) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]"]: """ Convert MultiPolygon to a numpy array """ @@ -1995,12 +2106,23 @@ class MultiPolygon(PolygonList): """ Convert the MultiPolygon to a RapidJSON value """ + def to_wgs84(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> MultiPolygon: + """ + Convert ENU coordinates to WGS84 (lon,lat,alt) + """ + def transform(self, fn: typing.Any) -> MultiPolygon: + """ + Apply transform function to all coordinates (Nx3 numpy array) + """ + def translate(self, offset: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> MultiPolygon: + """ + Translate all coordinates by offset vector + """ @property def __geo_interface__(self) -> typing.Any: """ Return the __geo_interface__ representation of the MultiPolygon """ - class Point: __hash__: typing.ClassVar[None] = None def __call__(self) -> typing.Any: @@ -2019,27 +2141,28 @@ class Point: """ Check if two Points are equal """ - def __getitem__(self, index: int) -> float: + def __getitem__(self, index: typing.SupportsInt) -> float: """ Get the coordinate value at the specified index (0: x, 1: y, 2: z) """ - def __getstate__(self) -> typing.Any: ... + def __getstate__(self) -> typing.Any: + ... @typing.overload def __init__(self) -> None: """ Initialize an empty Point """ @typing.overload - def __init__(self, x: float, y: float, z: float = 0.0) -> None: + def __init__(self, x: typing.SupportsFloat, y: typing.SupportsFloat, z: typing.SupportsFloat = 0.0) -> None: """ Initialize a Point with coordinates (x, y, z) """ @typing.overload - def __init__(self, arg0: numpy.ndarray[numpy.float64[m, 1]]) -> None: + def __init__(self, arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[m, 1]"]) -> None: """ Initialize a Point from a numpy array or vector """ - def __iter__(self) -> typing.Iterator[float]: + def __iter__(self) -> collections.abc.Iterator[float]: """ Return an iterator over the point's coordinates """ @@ -2051,7 +2174,7 @@ class Point: """ Check if two Points are not equal """ - def __setitem__(self, index: int, value: float) -> float: + def __setitem__(self, index: typing.SupportsInt, value: typing.SupportsFloat) -> float: """ Set the coordinate value at the specified index (0: x, 1: y, 2: z) """ @@ -2059,13 +2182,15 @@ class Point: """ Enable pickling support for Point objects """ - def as_numpy( - self, - ) -> numpy.ndarray[numpy.float64[3, 1], numpy.ndarray.flags.writeable]: + def affine(self, T: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[4, 4]"]) -> Point: + """ + Apply 4x4 affine transformation matrix + """ + def as_numpy(self) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[3, 1]", "flags.writeable"]: """ Get a numpy view of the point coordinates """ - def bbox(self, *, with_z: bool = False) -> numpy.ndarray[numpy.float64[m, 1]]: + def bbox(self, *, with_z: bool = False) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 1]"]: """ Get the bounding box of the point """ @@ -2081,7 +2206,7 @@ class Point: """ Remove duplicate consecutive points based on their XYZ coordinates """ - def from_numpy(self, arg0: numpy.ndarray[numpy.float64[m, 1]]) -> Point: + def from_numpy(self, arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[m, 1]"]) -> Point: """ Set point coordinates from a numpy array """ @@ -2089,11 +2214,23 @@ class Point: """ Create a Point from a RapidJSON value """ - def round(self, *, lon: int = 8, lat: int = 8, alt: int = 3) -> Point: + def rotate(self, R: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 3]"]) -> Point: + """ + Apply 3x3 rotation matrix to all coordinates + """ + def round(self, *, lon: typing.SupportsInt = 8, lat: typing.SupportsInt = 8, alt: typing.SupportsInt = 3) -> Point: """ Round coordinates to specified decimal places """ - def to_numpy(self) -> numpy.ndarray[numpy.float64[3, 1]]: + def scale(self, scale: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> Point: + """ + Scale all coordinates by factors [sx, sy, sz] + """ + def to_enu(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> Point: + """ + Convert WGS84 (lon,lat,alt) to ENU coordinates + """ + def to_numpy(self) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[3, 1]"]: """ Convert point coordinates to a numpy array """ @@ -2101,6 +2238,18 @@ class Point: """ Convert the Point to a RapidJSON value """ + def to_wgs84(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> Point: + """ + Convert ENU coordinates to WGS84 (lon,lat,alt) + """ + def transform(self, fn: typing.Any) -> Point: + """ + Apply transform function to all coordinates (Nx3 numpy array) + """ + def translate(self, offset: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> Point: + """ + Translate all coordinates by offset vector + """ @property def __geo_interface__(self) -> typing.Any: """ @@ -2112,22 +2261,24 @@ class Point: Get or set the x-coordinate of the point """ @x.setter - def x(self, arg1: float) -> None: ... + def x(self, arg1: typing.SupportsFloat) -> None: + ... @property def y(self) -> float: """ Get or set the y-coordinate of the point """ @y.setter - def y(self, arg1: float) -> None: ... + def y(self, arg1: typing.SupportsFloat) -> None: + ... @property def z(self) -> float: """ Get or set the z-coordinate of the point """ @z.setter - def z(self, arg1: float) -> None: ... - + def z(self, arg1: typing.SupportsFloat) -> None: + ... class Polygon(LinearRingList): __hash__: typing.ClassVar[None] = None def __call__(self) -> typing.Any: @@ -2146,11 +2297,12 @@ class Polygon(LinearRingList): """ Check if two Polygons are equal """ - def __getitem__(self, arg0: int) -> LinearRing: + def __getitem__(self, arg0: typing.SupportsInt) -> LinearRing: """ Get a linear ring by index """ - def __getstate__(self) -> typing.Any: ... + def __getstate__(self) -> typing.Any: + ... @typing.overload def __init__(self) -> None: """ @@ -2167,13 +2319,11 @@ class Polygon(LinearRingList): Construct Polygon from a single LinearRing (shell) """ @typing.overload - def __init__( - self, arg0: numpy.ndarray[numpy.float64[m, n], numpy.ndarray.flags.c_contiguous] - ) -> None: + def __init__(self, arg0: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]) -> None: """ Initialize from a numpy array of points """ - def __iter__(self) -> typing.Iterator[LinearRing]: + def __iter__(self) -> collections.abc.Iterator[LinearRing]: """ Return an iterator over the linear rings in the geometry """ @@ -2185,11 +2335,7 @@ class Polygon(LinearRingList): """ Check if two Polygons are not equal """ - def __setitem__( - self, - arg0: int, - arg1: numpy.ndarray[numpy.float64[m, n], numpy.ndarray.flags.c_contiguous], - ) -> numpy.ndarray[numpy.float64[m, n], numpy.ndarray.flags.c_contiguous]: + def __setitem__(self, arg0: typing.SupportsInt, arg1: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]: """ Set a linear ring by index using a numpy array of points """ @@ -2197,17 +2343,15 @@ class Polygon(LinearRingList): """ Pickle support for the geometry """ - def as_numpy( - self, - ) -> numpy.ndarray[ - numpy.float64[m, 3], - numpy.ndarray.flags.writeable, - numpy.ndarray.flags.c_contiguous, - ]: + def affine(self, T: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[4, 4]"]) -> Polygon: + """ + Apply 4x4 affine transformation matrix + """ + def as_numpy(self) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]", "flags.writeable", "flags.c_contiguous"]: """ Return a numpy view of the geometry's points """ - def bbox(self, *, with_z: bool = False) -> numpy.ndarray[numpy.float64[m, 1]]: + def bbox(self, *, with_z: bool = False) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 1]"]: """ Compute the bounding box of the geometry """ @@ -2223,9 +2367,7 @@ class Polygon(LinearRingList): """ Remove duplicate consecutive points based on their XYZ coordinates """ - def from_numpy( - self, arg0: numpy.ndarray[numpy.float64[m, n], numpy.ndarray.flags.c_contiguous] - ) -> Polygon: + def from_numpy(self, arg0: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]) -> Polygon: """ Set the geometry from a numpy array of points """ @@ -2238,9 +2380,7 @@ class Polygon(LinearRingList): Remove the last point from the last linear ring """ @typing.overload - def push_back( - self, arg0: numpy.ndarray[numpy.float64[m, n], numpy.ndarray.flags.c_contiguous] - ) -> Polygon: + def push_back(self, arg0: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]) -> Polygon: """ Add a new linear ring from a numpy array of points """ @@ -2249,11 +2389,23 @@ class Polygon(LinearRingList): """ Add a new linear ring """ - def round(self, *, lon: int = 8, lat: int = 8, alt: int = 3) -> Polygon: + def rotate(self, R: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 3]"]) -> Polygon: + """ + Apply 3x3 rotation matrix to all coordinates + """ + def round(self, *, lon: typing.SupportsInt = 8, lat: typing.SupportsInt = 8, alt: typing.SupportsInt = 3) -> Polygon: """ Round the coordinates of the geometry """ - def to_numpy(self) -> numpy.ndarray[numpy.float64[m, 3]]: + def scale(self, scale: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> Polygon: + """ + Scale all coordinates by factors [sx, sy, sz] + """ + def to_enu(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> Polygon: + """ + Convert WGS84 (lon,lat,alt) to ENU coordinates + """ + def to_numpy(self) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]"]: """ Convert the geometry to a numpy array """ @@ -2261,12 +2413,23 @@ class Polygon(LinearRingList): """ Convert the geometry to a RapidJSON value """ + def to_wgs84(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> Polygon: + """ + Convert ENU coordinates to WGS84 (lon,lat,alt) + """ + def transform(self, fn: typing.Any) -> Polygon: + """ + Apply transform function to all coordinates (Nx3 numpy array) + """ + def translate(self, offset: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> Polygon: + """ + Translate all coordinates by offset vector + """ @property def __geo_interface__(self) -> typing.Any: """ Return the __geo_interface__ representation of the geometry """ - class PolygonList: __hash__: typing.ClassVar[None] = None def __bool__(self) -> bool: @@ -2278,7 +2441,7 @@ class PolygonList: Return true the container contains ``x`` """ @typing.overload - def __delitem__(self, arg0: int) -> None: + def __delitem__(self, arg0: typing.SupportsInt) -> None: """ Delete the list elements at index ``i`` """ @@ -2287,28 +2450,36 @@ class PolygonList: """ Delete list elements using a slice object """ - def __eq__(self, arg0: PolygonList) -> bool: ... + def __eq__(self, arg0: PolygonList) -> bool: + ... @typing.overload def __getitem__(self, s: slice) -> PolygonList: """ Retrieve list elements using a slice object """ @typing.overload - def __getitem__(self, arg0: int) -> Polygon: ... + def __getitem__(self, arg0: typing.SupportsInt) -> Polygon: + ... @typing.overload - def __init__(self) -> None: ... + def __init__(self) -> None: + ... @typing.overload def __init__(self, arg0: PolygonList) -> None: """ Copy constructor """ @typing.overload - def __init__(self, arg0: typing.Iterable) -> None: ... - def __iter__(self) -> typing.Iterator[Polygon]: ... - def __len__(self) -> int: ... - def __ne__(self, arg0: PolygonList) -> bool: ... + def __init__(self, arg0: collections.abc.Iterable) -> None: + ... + def __iter__(self) -> collections.abc.Iterator[Polygon]: + ... + def __len__(self) -> int: + ... + def __ne__(self, arg0: PolygonList) -> bool: + ... @typing.overload - def __setitem__(self, arg0: int, arg1: Polygon) -> None: ... + def __setitem__(self, arg0: typing.SupportsInt, arg1: Polygon) -> None: + ... @typing.overload def __setitem__(self, arg0: slice, arg1: PolygonList) -> None: """ @@ -2332,11 +2503,11 @@ class PolygonList: Extend the list by appending all the items in the given list """ @typing.overload - def extend(self, L: typing.Iterable) -> None: + def extend(self, L: collections.abc.Iterable) -> None: """ Extend the list by appending all the items in the given list """ - def insert(self, i: int, x: Polygon) -> None: + def insert(self, i: typing.SupportsInt, x: Polygon) -> None: """ Insert an item at a given position. """ @@ -2346,7 +2517,7 @@ class PolygonList: Remove and return the last item """ @typing.overload - def pop(self, i: int) -> Polygon: + def pop(self, i: typing.SupportsInt) -> Polygon: """ Remove and return the item at index ``i`` """ @@ -2354,7 +2525,6 @@ class PolygonList: """ Remove the first item from the list whose value is x. It is an error if there is no such item. """ - class coordinates: __hash__: typing.ClassVar[None] = None def __bool__(self) -> bool: @@ -2366,7 +2536,7 @@ class coordinates: Return true the container contains ``x`` """ @typing.overload - def __delitem__(self, arg0: int) -> None: + def __delitem__(self, arg0: typing.SupportsInt) -> None: """ Delete the list elements at index ``i`` """ @@ -2375,28 +2545,36 @@ class coordinates: """ Delete list elements using a slice object """ - def __eq__(self, arg0: coordinates) -> bool: ... + def __eq__(self, arg0: coordinates) -> bool: + ... @typing.overload def __getitem__(self, s: slice) -> coordinates: """ Retrieve list elements using a slice object """ @typing.overload - def __getitem__(self, arg0: int) -> ...: ... + def __getitem__(self, arg0: typing.SupportsInt) -> ...: + ... @typing.overload - def __init__(self) -> None: ... + def __init__(self) -> None: + ... @typing.overload def __init__(self, arg0: coordinates) -> None: """ Copy constructor """ @typing.overload - def __init__(self, arg0: typing.Iterable) -> None: ... - def __iter__(self) -> typing.Iterator[...]: ... - def __len__(self) -> int: ... - def __ne__(self, arg0: coordinates) -> bool: ... + def __init__(self, arg0: collections.abc.Iterable) -> None: + ... + def __iter__(self) -> collections.abc.Iterator[...]: + ... + def __len__(self) -> int: + ... + def __ne__(self, arg0: coordinates) -> bool: + ... @typing.overload - def __setitem__(self, arg0: int, arg1: ...) -> None: ... + def __setitem__(self, arg0: typing.SupportsInt, arg1: ...) -> None: + ... @typing.overload def __setitem__(self, arg0: slice, arg1: coordinates) -> None: """ @@ -2406,13 +2584,7 @@ class coordinates: """ Add an item to the end of the list """ - def as_numpy( - self, - ) -> numpy.ndarray[ - numpy.float64[m, 3], - numpy.ndarray.flags.writeable, - numpy.ndarray.flags.c_contiguous, - ]: + def as_numpy(self) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]", "flags.writeable", "flags.c_contiguous"]: """ Get a numpy view of the coordinates """ @@ -2430,17 +2602,15 @@ class coordinates: Extend the list by appending all the items in the given list """ @typing.overload - def extend(self, L: typing.Iterable) -> None: + def extend(self, L: collections.abc.Iterable) -> None: """ Extend the list by appending all the items in the given list """ - def from_numpy( - self, arg0: numpy.ndarray[numpy.float64[m, n], numpy.ndarray.flags.c_contiguous] - ) -> coordinates: + def from_numpy(self, arg0: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]) -> coordinates: """ Set coordinates from a numpy array """ - def insert(self, i: int, x: ...) -> None: + def insert(self, i: typing.SupportsInt, x: ...) -> None: """ Insert an item at a given position. """ @@ -2450,7 +2620,7 @@ class coordinates: Remove and return the last item """ @typing.overload - def pop(self, i: int) -> ...: + def pop(self, i: typing.SupportsInt) -> ...: """ Remove and return the item at index ``i`` """ @@ -2458,25 +2628,28 @@ class coordinates: """ Remove the first item from the list whose value is x. It is an error if there is no such item. """ - def to_numpy(self) -> numpy.ndarray[numpy.float64[m, 3]]: + def to_numpy(self) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]"]: """ Convert coordinates to a numpy array """ - class value: class ItemsView: - def __iter__(self) -> typing.Iterator: ... - def __len__(self) -> int: ... - + def __iter__(self) -> collections.abc.Iterator: + ... + def __len__(self) -> int: + ... class KeysView: - def __contains__(self, arg0: typing.Any) -> bool: ... - def __iter__(self) -> typing.Iterator: ... - def __len__(self) -> int: ... - + def __contains__(self, arg0: typing.Any) -> bool: + ... + def __iter__(self) -> collections.abc.Iterator: + ... + def __len__(self) -> int: + ... class ValuesView: - def __iter__(self) -> typing.Iterator: ... - def __len__(self) -> int: ... - + def __iter__(self) -> collections.abc.Iterator: + ... + def __len__(self) -> int: + ... class array_type: __hash__: typing.ClassVar[None] = None def __bool__(self) -> bool: @@ -2492,7 +2665,7 @@ class value: Return true the container contains ``x`` """ @typing.overload - def __delitem__(self, arg0: int) -> None: + def __delitem__(self, arg0: typing.SupportsInt) -> None: """ Delete the list elements at index ``i`` """ @@ -2501,28 +2674,32 @@ class value: """ Delete list elements using a slice object """ - def __eq__(self, arg0: value.array_type) -> bool: ... + def __eq__(self, arg0: value.array_type) -> bool: + ... @typing.overload def __getitem__(self, s: slice) -> value.array_type: """ Retrieve list elements using a slice object """ @typing.overload - def __getitem__(self, arg0: int) -> value: ... + def __getitem__(self, arg0: typing.SupportsInt) -> value: + ... @typing.overload - def __getitem__(self, arg0: int) -> value: + def __getitem__(self, arg0: typing.SupportsInt) -> value: """ Get an item from the GeoJSON array by index """ @typing.overload - def __init__(self) -> None: ... + def __init__(self) -> None: + ... @typing.overload def __init__(self, arg0: value.array_type) -> None: """ Copy constructor """ @typing.overload - def __init__(self, arg0: typing.Iterable) -> None: ... + def __init__(self, arg0: collections.abc.Iterable) -> None: + ... @typing.overload def __init__(self) -> None: """ @@ -2533,18 +2710,22 @@ class value: """ Construct a GeoJSON array from a Python iterable """ - def __iter__(self) -> typing.Iterator[value]: ... - def __len__(self) -> int: ... - def __ne__(self, arg0: value.array_type) -> bool: ... + def __iter__(self) -> collections.abc.Iterator[value]: + ... + def __len__(self) -> int: + ... + def __ne__(self, arg0: value.array_type) -> bool: + ... @typing.overload - def __setitem__(self, arg0: int, arg1: value) -> None: ... + def __setitem__(self, arg0: typing.SupportsInt, arg1: value) -> None: + ... @typing.overload def __setitem__(self, arg0: slice, arg1: value.array_type) -> None: """ Assign list elements using a slice object """ @typing.overload - def __setitem__(self, arg0: int, arg1: typing.Any) -> value: + def __setitem__(self, arg0: typing.SupportsInt, arg1: typing.Any) -> value: """ Set an item in the GeoJSON array by index """ @@ -2572,17 +2753,15 @@ class value: Extend the list by appending all the items in the given list """ @typing.overload - def extend(self, L: typing.Iterable) -> None: + def extend(self, L: collections.abc.Iterable) -> None: """ Extend the list by appending all the items in the given list """ - def from_rapidjson( - self, arg0: pybind11_geobuf._core.rapidjson - ) -> value.array_type: + def from_rapidjson(self, arg0: pybind11_geobuf._core.rapidjson) -> value.array_type: """ Set the GeoJSON array from a RapidJSON value """ - def insert(self, i: int, x: value) -> None: + def insert(self, i: typing.SupportsInt, x: value) -> None: """ Insert an item at a given position. """ @@ -2592,7 +2771,7 @@ class value: Remove and return the last item """ @typing.overload - def pop(self, i: int) -> value: + def pop(self, i: typing.SupportsInt) -> value: """ Remove and return the item at index ``i`` """ @@ -2604,7 +2783,6 @@ class value: """ Convert the GeoJSON array to a RapidJSON value """ - class object_type: def __bool__(self) -> bool: """ @@ -2615,13 +2793,18 @@ class value: Convert the GeoJSON object to a Python dict """ @typing.overload - def __contains__(self, arg0: str) -> bool: ... + def __contains__(self, arg0: str) -> bool: + ... @typing.overload - def __contains__(self, arg0: typing.Any) -> bool: ... - def __delitem__(self, arg0: str) -> None: ... - def __getitem__(self, arg0: str) -> value: ... + def __contains__(self, arg0: typing.Any) -> bool: + ... + def __delitem__(self, arg0: str) -> None: + ... + def __getitem__(self, arg0: str) -> value: + ... @typing.overload - def __init__(self) -> None: ... + def __init__(self) -> None: + ... @typing.overload def __init__(self) -> None: """ @@ -2632,10 +2815,13 @@ class value: """ Construct a GeoJSON object from a Python dict """ - def __iter__(self) -> typing.Iterator[str]: ... - def __len__(self) -> int: ... + def __iter__(self) -> collections.abc.Iterator[str]: + ... + def __len__(self) -> int: + ... @typing.overload - def __setitem__(self, arg0: str, arg1: value) -> None: ... + def __setitem__(self, arg0: str, arg1: value) -> None: + ... @typing.overload def __setitem__(self, arg0: str, arg1: typing.Any) -> value: """ @@ -2645,23 +2831,23 @@ class value: """ Clear the GeoJSON object """ - def from_rapidjson( - self, arg0: pybind11_geobuf._core.rapidjson - ) -> value.object_type: + def from_rapidjson(self, arg0: pybind11_geobuf._core.rapidjson) -> value.object_type: """ Convert a RapidJSON value to a GeoJSON object """ @typing.overload - def items(self) -> value.ItemsView: ... + def items(self) -> value.ItemsView: + ... @typing.overload - def items(self) -> typing.Iterator[tuple[str, value]]: + def items(self) -> collections.abc.Iterator[tuple[str, value]]: """ Get an iterator over the items (key-value pairs) of the GeoJSON object """ @typing.overload - def keys(self) -> value.KeysView: ... + def keys(self) -> value.KeysView: + ... @typing.overload - def keys(self) -> typing.Iterator[str]: + def keys(self) -> collections.abc.Iterator[str]: """ Get an iterator over the keys of the GeoJSON object """ @@ -2670,13 +2856,13 @@ class value: Convert the GeoJSON object to a RapidJSON value """ @typing.overload - def values(self) -> value.ValuesView: ... + def values(self) -> value.ValuesView: + ... @typing.overload - def values(self) -> typing.Iterator[value]: + def values(self) -> collections.abc.Iterator[value]: """ Get an iterator over the values of the GeoJSON object """ - def Get(self) -> typing.Any: """ Get the GeoJSON value as a Python object @@ -2719,12 +2905,12 @@ class value: Delete an item from the GeoJSON object by key """ @typing.overload - def __delitem__(self, arg0: int) -> None: + def __delitem__(self, arg0: typing.SupportsInt) -> None: """ Delete an item from the GeoJSON array by index """ @typing.overload - def __getitem__(self, arg0: int) -> value: + def __getitem__(self, arg0: typing.SupportsInt) -> value: """ Get an item from the GeoJSON array by index """ @@ -2753,7 +2939,7 @@ class value: Set an item in the GeoJSON object by key """ @typing.overload - def __setitem__(self, arg0: int, arg1: typing.Any) -> typing.Any: + def __setitem__(self, arg0: typing.SupportsInt, arg1: typing.Any) -> typing.Any: """ Set an item in the GeoJSON array by index """ @@ -2785,11 +2971,11 @@ class value: """ Check if the GeoJSON value is an object """ - def items(self) -> typing.Iterator[tuple[str, value]]: + def items(self) -> collections.abc.Iterator[tuple[str, value]]: """ Get an iterator over the items of the GeoJSON object """ - def keys(self) -> typing.Iterator[str]: + def keys(self) -> collections.abc.Iterator[str]: """ Get an iterator over the keys of the GeoJSON object """ @@ -2809,7 +2995,7 @@ class value: """ Convert the GeoJSON value to a RapidJSON value """ - def values(self) -> typing.Iterator[value]: + def values(self) -> collections.abc.Iterator[value]: """ Get an iterator over the values of the GeoJSON object """ diff --git a/src/pybind11_geobuf/_core/tf.pyi b/src/pybind11_geobuf/_core/tf.pyi index bd4e0a3..4170630 100644 --- a/src/pybind11_geobuf/_core/tf.pyi +++ b/src/pybind11_geobuf/_core/tf.pyi @@ -1,8 +1,9 @@ from __future__ import annotations import numpy +import numpy.typing import typing -__all__ = [ +__all__: list[str] = [ "R_ecef_enu", "T_ecef_enu", "apply_transform", @@ -16,65 +17,143 @@ __all__ = [ "lla2enu", ] -def R_ecef_enu(lon: float, lat: float) -> numpy.ndarray[numpy.float64[3, 3]]: ... +def R_ecef_enu( + lon: typing.SupportsFloat, lat: typing.SupportsFloat +) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[3, 3]"]: + """ + Get rotation matrix from ECEF to ENU coordinate system. + """ + @typing.overload def T_ecef_enu( - lon: float, lat: float, alt: float -) -> numpy.ndarray[numpy.float64[4, 4]]: ... + lon: typing.SupportsFloat, lat: typing.SupportsFloat, alt: typing.SupportsFloat +) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[4, 4]"]: + """ + Get transformation matrix from ECEF to ENU coordinate system. + """ + @typing.overload def T_ecef_enu( - lla: numpy.ndarray[numpy.float64[3, 1]], -) -> numpy.ndarray[numpy.float64[4, 4]]: ... + lla: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], +) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[4, 4]"]: + """ + Get transformation matrix from ECEF to ENU coordinate system using LLA vector. + """ + def apply_transform( - T: numpy.ndarray[numpy.float64[4, 4]], - coords: numpy.ndarray[numpy.float64[m, 3], numpy.ndarray.flags.c_contiguous], -) -> numpy.ndarray[numpy.float64[m, 3]]: ... + T: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[4, 4]"], + coords: typing.Annotated[ + numpy.typing.NDArray[numpy.float64], "[m, 3]", "flags.c_contiguous" + ], +) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]"]: + """ + Apply transformation matrix to coordinates. + """ + def apply_transform_inplace( - T: numpy.ndarray[numpy.float64[4, 4]], - coords: numpy.ndarray[ - numpy.float64[m, 3], - numpy.ndarray.flags.writeable, - numpy.ndarray.flags.c_contiguous, + T: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[4, 4]"], + coords: typing.Annotated[ + numpy.typing.NDArray[numpy.float64], + "[m, 3]", + "flags.writeable", + "flags.c_contiguous", ], *, - batch_size: int = 1000, -) -> None: ... -def cheap_ruler_k(latitude: float) -> numpy.ndarray[numpy.float64[3, 1]]: ... + batch_size: typing.SupportsInt = 1000, +) -> None: + """ + Apply transformation matrix to coordinates in-place. + """ + +def cheap_ruler_k( + latitude: typing.SupportsFloat, +) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[3, 1]"]: + """ + Get the cheap ruler's unit conversion factor for a given latitude. + """ + def ecef2enu( - ecefs: numpy.ndarray[numpy.float64[m, 3], numpy.ndarray.flags.c_contiguous], + ecefs: typing.Annotated[ + numpy.typing.NDArray[numpy.float64], "[m, 3]", "flags.c_contiguous" + ], *, - anchor_lla: numpy.ndarray[numpy.float64[3, 1]] | None = None, + anchor_lla: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"] + | None = None, cheap_ruler: bool = False, -) -> numpy.ndarray[numpy.float64[m, 3]]: ... +) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]"]: + """ + Convert ECEF to ENU (East, North, Up) coordinates. + """ + @typing.overload -def ecef2lla(x: float, y: float, z: float) -> numpy.ndarray[numpy.float64[3, 1]]: ... +def ecef2lla( + x: typing.SupportsFloat, y: typing.SupportsFloat, z: typing.SupportsFloat +) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[3, 1]"]: + """ + Convert ECEF coordinates to LLA (Longitude, Latitude, Altitude). + """ + @typing.overload def ecef2lla( - ecefs: numpy.ndarray[numpy.float64[m, 3], numpy.ndarray.flags.c_contiguous], -) -> numpy.ndarray[numpy.float64[m, 3]]: ... + ecefs: typing.Annotated[ + numpy.typing.NDArray[numpy.float64], "[m, 3]", "flags.c_contiguous" + ], +) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]"]: + """ + Convert multiple ECEF coordinates to LLA (Longitude, Latitude, Altitude). + """ + def enu2ecef( - enus: numpy.ndarray[numpy.float64[m, 3], numpy.ndarray.flags.c_contiguous], + enus: typing.Annotated[ + numpy.typing.NDArray[numpy.float64], "[m, 3]", "flags.c_contiguous" + ], *, - anchor_lla: numpy.ndarray[numpy.float64[3, 1]], + anchor_lla: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], cheap_ruler: bool = False, -) -> numpy.ndarray[numpy.float64[m, 3]]: ... +) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]"]: + """ + Convert ENU (East, North, Up) to ECEF coordinates. + """ + def enu2lla( - enus: numpy.ndarray[numpy.float64[m, 3], numpy.ndarray.flags.c_contiguous], + enus: typing.Annotated[ + numpy.typing.NDArray[numpy.float64], "[m, 3]", "flags.c_contiguous" + ], *, - anchor_lla: numpy.ndarray[numpy.float64[3, 1]], + anchor_lla: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], cheap_ruler: bool = True, -) -> numpy.ndarray[numpy.float64[m, 3]]: ... +) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]"]: + """ + Convert ENU (East, North, Up) to LLA (Longitude, Latitude, Altitude) coordinates. + """ + @typing.overload def lla2ecef( - lon: float, lat: float, alt: float -) -> numpy.ndarray[numpy.float64[3, 1]]: ... + lon: typing.SupportsFloat, lat: typing.SupportsFloat, alt: typing.SupportsFloat +) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[3, 1]"]: + """ + Convert LLA (Longitude, Latitude, Altitude) to ECEF coordinates. + """ + @typing.overload def lla2ecef( - llas: numpy.ndarray[numpy.float64[m, 3], numpy.ndarray.flags.c_contiguous], -) -> numpy.ndarray[numpy.float64[m, 3]]: ... + llas: typing.Annotated[ + numpy.typing.NDArray[numpy.float64], "[m, 3]", "flags.c_contiguous" + ], +) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]"]: + """ + Convert multiple LLA (Longitude, Latitude, Altitude) to ECEF coordinates. + """ + def lla2enu( - llas: numpy.ndarray[numpy.float64[m, 3], numpy.ndarray.flags.c_contiguous], + llas: typing.Annotated[ + numpy.typing.NDArray[numpy.float64], "[m, 3]", "flags.c_contiguous" + ], *, - anchor_lla: numpy.ndarray[numpy.float64[3, 1]] | None = None, + anchor_lla: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"] + | None = None, cheap_ruler: bool = True, -) -> numpy.ndarray[numpy.float64[m, 3]]: ... +) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]"]: + """ + Convert LLA (Longitude, Latitude, Altitude) to ENU (East, North, Up) coordinates. + """ From c37bfdbeca67b1b508bb7e2b8fc0bf55d3daab35 Mon Sep 17 00:00:00 2001 From: tang zhixiong Date: Sat, 31 Jan 2026 10:55:00 +0800 Subject: [PATCH 04/18] fix tests --- pyproject.toml | 2 +- tests/test_transform.py | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 83554da..45b5649 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "scikit_build_core.build" [project] name = "pybind11_geobuf" -version = "0.2.3" +version = "0.2.4" description="c++ geobuf with python binding" readme = "README.md" authors = [ diff --git a/tests/test_transform.py b/tests/test_transform.py index 362bc3b..b422bac 100644 --- a/tests/test_transform.py +++ b/tests/test_transform.py @@ -80,6 +80,11 @@ def test_affine(self): T[:3, 3] = [10, 20, 30] # translation pt.affine(T) assert pt() == [11.0, 22.0, 33.0] + pt = geojson.Point(1.0, 2.0, 3.0) + T[:, 0] *= 5 + T[:, 1] *= 2 + pt.affine(T) + assert pt() == [15.0, 24.0, 33.0] class TestTransformLineString: From 7025bff7e711fb3db17ee5219330d16facb0e08a Mon Sep 17 00:00:00 2001 From: tang zhixiong Date: Sat, 31 Jan 2026 11:12:08 +0800 Subject: [PATCH 05/18] fix lint --- Makefile | 4 +- src/pybind11_geobuf/_core/__init__.pyi | 2 +- src/pybind11_geobuf/_core/geojson.pyi | 1215 +++++++++++++++++------- 3 files changed, 887 insertions(+), 334 deletions(-) diff --git a/Makefile b/Makefile index aa6ddfd..2a31df1 100644 --- a/Makefile +++ b/Makefile @@ -44,7 +44,9 @@ cli_test: cli_test1 cli_test2 cli_test3 cli_test4 restub: pybind11-stubgen pybind11_geobuf._core -o stubs cp -rf stubs/pybind11_geobuf/_core src/pybind11_geobuf - pre-commit run --files src/pybind11_geobuf/_core/*.pyi + # Fix duplicate parameter names generated by pybind11-stubgen + sed -i '' 's/new_value: \.\.\., std: \.\.\., std: \.\.\., mapbox: \.\.\., std: \.\.\., std: \.\.\., std: \.\.\., std: \.\.\., std: \.\.\., std: \.\.\., std: \.\.\., std: \.\.\., std: \.\.\., mapbox: \.\.\./new_value: .../g' src/pybind11_geobuf/_core/geojson.pyi + pre-commit run --files src/pybind11_geobuf/_core/*.pyi || pre-commit run --files src/pybind11_geobuf/_core/*.pyi test_all: @cd build && for t in $(wildcard $(BUILD_DIR)/test_*); do echo $$t && eval $$t >/dev/null 2>&1 && echo 'ok' || echo $(RED)Not Ok$(NC); done diff --git a/src/pybind11_geobuf/_core/__init__.pyi b/src/pybind11_geobuf/_core/__init__.pyi index fab0b75..20bd49d 100644 --- a/src/pybind11_geobuf/_core/__init__.pyi +++ b/src/pybind11_geobuf/_core/__init__.pyi @@ -1195,4 +1195,4 @@ def str2json2str( Optional[str]: Converted JSON string, or None if input is invalid. """ -__version__: str = "0.2.3" +__version__: str = "0.2.4" diff --git a/src/pybind11_geobuf/_core/geojson.pyi b/src/pybind11_geobuf/_core/geojson.pyi index 3f7a63a..a813db5 100644 --- a/src/pybind11_geobuf/_core/geojson.pyi +++ b/src/pybind11_geobuf/_core/geojson.pyi @@ -4,7 +4,30 @@ import numpy import numpy.typing import pybind11_geobuf._core import typing -__all__: list[str] = ['Feature', 'FeatureCollection', 'FeatureList', 'GeoJSON', 'Geometry', 'GeometryBase', 'GeometryCollection', 'GeometryList', 'LineString', 'LineStringList', 'LinearRing', 'LinearRingList', 'MultiLineString', 'MultiPoint', 'MultiPolygon', 'Point', 'Polygon', 'PolygonList', 'coordinates', 'value'] + +__all__: list[str] = [ + "Feature", + "FeatureCollection", + "FeatureList", + "GeoJSON", + "Geometry", + "GeometryBase", + "GeometryCollection", + "GeometryList", + "LineString", + "LineStringList", + "LinearRing", + "LinearRingList", + "MultiLineString", + "MultiPoint", + "MultiPolygon", + "Point", + "Polygon", + "PolygonList", + "coordinates", + "value", +] + class Feature: __hash__: typing.ClassVar[None] = None def __call__(self) -> typing.Any: @@ -59,15 +82,26 @@ class Feature: """ Set a custom property value by key """ - def affine(self, T: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[4, 4]"]) -> Feature: + def affine( + self, T: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[4, 4]"] + ) -> Feature: """ Apply 4x4 affine transformation matrix """ - def as_numpy(self) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]", "flags.writeable", "flags.c_contiguous"]: + def as_numpy( + self, + ) -> typing.Annotated[ + numpy.typing.NDArray[numpy.float64], + "[m, 3]", + "flags.writeable", + "flags.c_contiguous", + ]: """ Get a numpy view of the feature geometry """ - def bbox(self, *, with_z: bool = False) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 1]"]: + def bbox( + self, *, with_z: bool = False + ) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 1]"]: """ Compute the bounding box of the feature """ @@ -93,7 +127,15 @@ class Feature: """ Remove duplicate consecutive points based on their XYZ coordinates """ - def dump(self, path: str, *, indent: bool = False, sort_keys: bool = False, precision: typing.SupportsInt = 8, only_xy: bool = False) -> bool: + def dump( + self, + path: str, + *, + indent: bool = False, + sort_keys: bool = False, + precision: typing.SupportsInt = 8, + only_xy: bool = False, + ) -> bool: """ Dump the feature to a file (GeoJSON or Geobuf) """ @@ -203,27 +245,50 @@ class Feature: """ Set a property value by key """ - def rotate(self, R: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 3]"]) -> Feature: + def rotate( + self, R: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 3]"] + ) -> Feature: """ Apply 3x3 rotation matrix to all coordinates """ - def round(self, *, lon: typing.SupportsInt = 8, lat: typing.SupportsInt = 8, alt: typing.SupportsInt = 3) -> Feature: + def round( + self, + *, + lon: typing.SupportsInt = 8, + lat: typing.SupportsInt = 8, + alt: typing.SupportsInt = 3, + ) -> Feature: """ Round the coordinates of the feature geometry """ - def scale(self, scale: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> Feature: + def scale( + self, scale: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"] + ) -> Feature: """ Scale all coordinates by factors [sx, sy, sz] """ - def to_enu(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> Feature: + def to_enu( + self, + anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], + *, + cheap_ruler: bool = True, + ) -> Feature: """ Convert WGS84 (lon,lat,alt) to ENU coordinates """ - def to_geobuf(self, *, precision: typing.SupportsInt = 8, only_xy: bool = False, round_z: typing.SupportsInt | None = None) -> bytes: + def to_geobuf( + self, + *, + precision: typing.SupportsInt = 8, + only_xy: bool = False, + round_z: typing.SupportsInt | None = None, + ) -> bytes: """ Convert the feature to Geobuf bytes """ - def to_numpy(self) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]"]: + def to_numpy( + self, + ) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]"]: """ Convert the feature geometry to a numpy array """ @@ -231,7 +296,12 @@ class Feature: """ Convert the feature to a RapidJSON value """ - def to_wgs84(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> Feature: + def to_wgs84( + self, + anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], + *, + cheap_ruler: bool = True, + ) -> Feature: """ Convert ENU coordinates to WGS84 (lon,lat,alt) """ @@ -239,10 +309,13 @@ class Feature: """ Apply transform function to all coordinates (Nx3 numpy array) """ - def translate(self, offset: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> Feature: + def translate( + self, offset: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"] + ) -> Feature: """ Translate all coordinates by offset vector """ + class FeatureCollection(FeatureList): def __call__(self) -> typing.Any: """ @@ -316,7 +389,9 @@ class FeatureCollection(FeatureList): """ Assign list elements using a slice object """ - def affine(self, T: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[4, 4]"]) -> FeatureCollection: + def affine( + self, T: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[4, 4]"] + ) -> FeatureCollection: """ Apply 4x4 affine transformation matrix """ @@ -338,7 +413,15 @@ class FeatureCollection(FeatureList): """ Remove duplicate consecutive points based on their XYZ coordinates """ - def dump(self, path: str, *, indent: bool = False, sort_keys: bool = False, precision: typing.SupportsInt = 8, only_xy: bool = False) -> bool: + def dump( + self, + path: str, + *, + indent: bool = False, + sort_keys: bool = False, + precision: typing.SupportsInt = 8, + only_xy: bool = False, + ) -> bool: """ Dump the FeatureCollection to a file (GeoJSON or Geobuf) """ @@ -346,7 +429,9 @@ class FeatureCollection(FeatureList): """ Load the FeatureCollection from Geobuf bytes """ - def from_rapidjson(self, arg0: pybind11_geobuf._core.rapidjson) -> FeatureCollection: + def from_rapidjson( + self, arg0: pybind11_geobuf._core.rapidjson + ) -> FeatureCollection: """ Load the FeatureCollection from a RapidJSON value """ @@ -366,23 +451,44 @@ class FeatureCollection(FeatureList): """ Resize the FeatureCollection to contain N features """ - def rotate(self, R: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 3]"]) -> FeatureCollection: + def rotate( + self, R: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 3]"] + ) -> FeatureCollection: """ Apply 3x3 rotation matrix to all coordinates """ - def round(self, *, lon: typing.SupportsInt = 8, lat: typing.SupportsInt = 8, alt: typing.SupportsInt = 3) -> FeatureCollection: + def round( + self, + *, + lon: typing.SupportsInt = 8, + lat: typing.SupportsInt = 8, + alt: typing.SupportsInt = 3, + ) -> FeatureCollection: """ Round the coordinates of all features in the collection """ - def scale(self, scale: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> FeatureCollection: + def scale( + self, scale: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"] + ) -> FeatureCollection: """ Scale all coordinates by factors [sx, sy, sz] """ - def to_enu(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> FeatureCollection: + def to_enu( + self, + anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], + *, + cheap_ruler: bool = True, + ) -> FeatureCollection: """ Convert WGS84 (lon,lat,alt) to ENU coordinates """ - def to_geobuf(self, *, precision: typing.SupportsInt = 8, only_xy: bool = False, round_z: typing.SupportsInt | None = None) -> bytes: + def to_geobuf( + self, + *, + precision: typing.SupportsInt = 8, + only_xy: bool = False, + round_z: typing.SupportsInt | None = None, + ) -> bytes: """ Convert the FeatureCollection to Geobuf bytes """ @@ -390,7 +496,12 @@ class FeatureCollection(FeatureList): """ Convert the FeatureCollection to a RapidJSON value """ - def to_wgs84(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> FeatureCollection: + def to_wgs84( + self, + anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], + *, + cheap_ruler: bool = True, + ) -> FeatureCollection: """ Convert ENU coordinates to WGS84 (lon,lat,alt) """ @@ -398,7 +509,9 @@ class FeatureCollection(FeatureList): """ Apply transform function to all coordinates (Nx3 numpy array) """ - def translate(self, offset: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> FeatureCollection: + def translate( + self, offset: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"] + ) -> FeatureCollection: """ Translate all coordinates by offset vector """ @@ -406,14 +519,14 @@ class FeatureCollection(FeatureList): """ Return an iterator over the values of custom properties """ + class FeatureList: __hash__: typing.ClassVar[None] = None def __bool__(self) -> bool: """ Check whether the list is nonempty """ - def __call__(self) -> typing.Any: - ... + def __call__(self) -> typing.Any: ... def __contains__(self, x: Feature) -> bool: """ Return true the container contains ``x`` @@ -428,36 +541,28 @@ class FeatureList: """ Delete list elements using a slice object """ - def __eq__(self, arg0: FeatureList) -> bool: - ... + def __eq__(self, arg0: FeatureList) -> bool: ... @typing.overload def __getitem__(self, s: slice) -> FeatureList: """ Retrieve list elements using a slice object """ @typing.overload - def __getitem__(self, arg0: typing.SupportsInt) -> Feature: - ... + def __getitem__(self, arg0: typing.SupportsInt) -> Feature: ... @typing.overload - def __init__(self) -> None: - ... + def __init__(self) -> None: ... @typing.overload def __init__(self, arg0: FeatureList) -> None: """ Copy constructor """ @typing.overload - def __init__(self, arg0: collections.abc.Iterable) -> None: - ... - def __iter__(self) -> collections.abc.Iterator[Feature]: - ... - def __len__(self) -> int: - ... - def __ne__(self, arg0: FeatureList) -> bool: - ... + def __init__(self, arg0: collections.abc.Iterable) -> None: ... + def __iter__(self) -> collections.abc.Iterator[Feature]: ... + def __len__(self) -> int: ... + def __ne__(self, arg0: FeatureList) -> bool: ... @typing.overload - def __setitem__(self, arg0: typing.SupportsInt, arg1: Feature) -> None: - ... + def __setitem__(self, arg0: typing.SupportsInt, arg1: Feature) -> None: ... @typing.overload def __setitem__(self, arg0: slice, arg1: FeatureList) -> None: """ @@ -503,6 +608,7 @@ class FeatureList: """ Remove the first item from the list whose value is x. It is an error if there is no such item. """ + class GeoJSON: __hash__: typing.ClassVar[None] = None def __call__(self) -> typing.Any: @@ -545,7 +651,9 @@ class GeoJSON: """ Check if two GeoJSON objects are not equal """ - def affine(self, T: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[4, 4]"]) -> GeoJSON: + def affine( + self, T: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[4, 4]"] + ) -> GeoJSON: """ Apply 4x4 affine transformation matrix """ @@ -565,7 +673,13 @@ class GeoJSON: """ Create a clone of the object """ - def crop(self, polygon: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[m, 3]"], *, clipping_mode: str = 'longest', max_z_offset: typing.SupportsFloat | None = None) -> ...: + def crop( + self, + polygon: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[m, 3]"], + *, + clipping_mode: str = "longest", + max_z_offset: typing.SupportsFloat | None = None, + ) -> ...: """ Crop the GeoJSON object using a polygon """ @@ -573,7 +687,15 @@ class GeoJSON: """ Remove duplicate consecutive points based on their XYZ coordinates """ - def dump(self, path: str, *, indent: bool = False, sort_keys: bool = False, precision: typing.SupportsInt = 8, only_xy: bool = False) -> bool: + def dump( + self, + path: str, + *, + indent: bool = False, + sort_keys: bool = False, + precision: typing.SupportsInt = 8, + only_xy: bool = False, + ) -> bool: """ Dump the GeoJSON object to a file """ @@ -601,23 +723,44 @@ class GeoJSON: """ Load a GeoJSON object from a file """ - def rotate(self, R: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 3]"]) -> GeoJSON: + def rotate( + self, R: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 3]"] + ) -> GeoJSON: """ Apply 3x3 rotation matrix to all coordinates """ - def round(self, *, lon: typing.SupportsInt = 8, lat: typing.SupportsInt = 8, alt: typing.SupportsInt = 3) -> GeoJSON: + def round( + self, + *, + lon: typing.SupportsInt = 8, + lat: typing.SupportsInt = 8, + alt: typing.SupportsInt = 3, + ) -> GeoJSON: """ Round coordinates to specified decimal places """ - def scale(self, scale: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> GeoJSON: + def scale( + self, scale: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"] + ) -> GeoJSON: """ Scale all coordinates by factors [sx, sy, sz] """ - def to_enu(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> GeoJSON: + def to_enu( + self, + anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], + *, + cheap_ruler: bool = True, + ) -> GeoJSON: """ Convert WGS84 (lon,lat,alt) to ENU coordinates """ - def to_geobuf(self, *, precision: typing.SupportsInt = 8, only_xy: bool = False, round_z: typing.SupportsInt | None = None) -> bytes: + def to_geobuf( + self, + *, + precision: typing.SupportsInt = 8, + only_xy: bool = False, + round_z: typing.SupportsInt | None = None, + ) -> bytes: """ Encode the GeoJSON object to a Geobuf byte string """ @@ -625,7 +768,12 @@ class GeoJSON: """ Convert the GeoJSON object to a RapidJSON value """ - def to_wgs84(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> GeoJSON: + def to_wgs84( + self, + anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], + *, + cheap_ruler: bool = True, + ) -> GeoJSON: """ Convert ENU coordinates to WGS84 (lon,lat,alt) """ @@ -633,10 +781,13 @@ class GeoJSON: """ Apply transform function to all coordinates (Nx3 numpy array) """ - def translate(self, offset: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> GeoJSON: + def translate( + self, offset: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"] + ) -> GeoJSON: """ Translate all coordinates by offset vector """ + class Geometry(GeometryBase): __hash__: typing.ClassVar[None] = None def __call__(self) -> typing.Any: @@ -663,8 +814,7 @@ class Geometry(GeometryBase): """ Get a custom property value """ - def __getstate__(self) -> typing.Any: - ... + def __getstate__(self) -> typing.Any: ... @typing.overload def __init__(self) -> None: """ @@ -745,7 +895,9 @@ class Geometry(GeometryBase): """ Pickle support for Geometry objects """ - def affine(self, T: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[4, 4]"]) -> Geometry: + def affine( + self, T: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[4, 4]"] + ) -> Geometry: """ Apply 4x4 affine transformation matrix """ @@ -769,7 +921,14 @@ class Geometry(GeometryBase): """ Get this geometry as a multi_polygon (if it is one) """ - def as_numpy(self) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]", "flags.writeable", "flags.c_contiguous"]: + def as_numpy( + self, + ) -> typing.Annotated[ + numpy.typing.NDArray[numpy.float64], + "[m, 3]", + "flags.writeable", + "flags.c_contiguous", + ]: """ Get a numpy view of the geometry coordinates """ @@ -781,7 +940,9 @@ class Geometry(GeometryBase): """ Get this geometry as a polygon (if it is one) """ - def bbox(self, *, with_z: bool = False) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 1]"]: + def bbox( + self, *, with_z: bool = False + ) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 1]"]: """ Get the bounding box of the geometry """ @@ -799,7 +960,7 @@ class Geometry(GeometryBase): Get the 'custom_properties' attribute """ @typing.overload - def custom_properties(self, new_value: ..., std: ..., std: ..., mapbox: ..., std: ..., std: ..., std: ..., std: ..., std: ..., std: ..., std: ..., std: ..., std: ..., mapbox: ...) -> Geometry: + def custom_properties(self, new_value: ...) -> Geometry: """ Set the 'custom_properties' attribute """ @@ -807,7 +968,15 @@ class Geometry(GeometryBase): """ Remove duplicate consecutive points based on their XYZ coordinates """ - def dump(self, path: str, *, indent: bool = False, sort_keys: bool = False, precision: typing.SupportsInt = 8, only_xy: bool = False) -> bool: + def dump( + self, + path: str, + *, + indent: bool = False, + sort_keys: bool = False, + precision: typing.SupportsInt = 8, + only_xy: bool = False, + ) -> bool: """ Dump the geometry to a file """ @@ -815,7 +984,12 @@ class Geometry(GeometryBase): """ Decode a Geobuf byte string into a geometry """ - def from_numpy(self, arg0: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]) -> Geometry: + def from_numpy( + self, + arg0: typing.Annotated[ + numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous" + ], + ) -> Geometry: """ Set geometry coordinates from a numpy array """ @@ -881,12 +1055,19 @@ class Geometry(GeometryBase): Add a point to the geometry """ @typing.overload - def push_back(self, arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[m, 1]"]) -> Geometry: + def push_back( + self, arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[m, 1]"] + ) -> Geometry: """ Add a point to the geometry """ @typing.overload - def push_back(self, arg0: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]) -> Geometry: + def push_back( + self, + arg0: typing.Annotated[ + numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous" + ], + ) -> Geometry: """ Add multiple points to the geometry """ @@ -909,27 +1090,50 @@ class Geometry(GeometryBase): """ Resize the geometry """ - def rotate(self, R: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 3]"]) -> Geometry: + def rotate( + self, R: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 3]"] + ) -> Geometry: """ Apply 3x3 rotation matrix to all coordinates """ - def round(self, *, lon: typing.SupportsInt = 8, lat: typing.SupportsInt = 8, alt: typing.SupportsInt = 3) -> Geometry: + def round( + self, + *, + lon: typing.SupportsInt = 8, + lat: typing.SupportsInt = 8, + alt: typing.SupportsInt = 3, + ) -> Geometry: """ Round coordinates to specified decimal places """ - def scale(self, scale: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> Geometry: + def scale( + self, scale: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"] + ) -> Geometry: """ Scale all coordinates by factors [sx, sy, sz] """ - def to_enu(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> Geometry: + def to_enu( + self, + anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], + *, + cheap_ruler: bool = True, + ) -> Geometry: """ Convert WGS84 (lon,lat,alt) to ENU coordinates """ - def to_geobuf(self, *, precision: typing.SupportsInt = 8, only_xy: bool = False, round_z: typing.SupportsInt | None = None) -> bytes: + def to_geobuf( + self, + *, + precision: typing.SupportsInt = 8, + only_xy: bool = False, + round_z: typing.SupportsInt | None = None, + ) -> bytes: """ Encode the geometry to a Geobuf byte string """ - def to_numpy(self) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]"]: + def to_numpy( + self, + ) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]"]: """ Convert geometry coordinates to a numpy array """ @@ -937,7 +1141,12 @@ class Geometry(GeometryBase): """ Convert the geometry to a RapidJSON value """ - def to_wgs84(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> Geometry: + def to_wgs84( + self, + anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], + *, + cheap_ruler: bool = True, + ) -> Geometry: """ Convert ENU coordinates to WGS84 (lon,lat,alt) """ @@ -945,7 +1154,9 @@ class Geometry(GeometryBase): """ Apply transform function to all coordinates (Nx3 numpy array) """ - def translate(self, offset: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> Geometry: + def translate( + self, offset: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"] + ) -> Geometry: """ Translate all coordinates by offset vector """ @@ -958,10 +1169,11 @@ class Geometry(GeometryBase): Get an iterator over the custom property values """ @property - def __geo_interface__(self) -> typing.Any: - ... + def __geo_interface__(self) -> typing.Any: ... + class GeometryBase: pass + class GeometryCollection(GeometryList): __hash__: typing.ClassVar[None] = None def __call__(self) -> typing.Any: @@ -972,8 +1184,7 @@ class GeometryCollection(GeometryList): """ Check if two GeometryCollections are equal """ - def __getstate__(self) -> typing.Any: - ... + def __getstate__(self) -> typing.Any: ... @typing.overload def __init__(self) -> None: """ @@ -994,7 +1205,9 @@ class GeometryCollection(GeometryList): Check if two GeometryCollections are not equal """ @typing.overload - def __setitem__(self, arg0: typing.SupportsInt, arg1: Geometry) -> GeometryCollection: + def __setitem__( + self, arg0: typing.SupportsInt, arg1: Geometry + ) -> GeometryCollection: """ Set a geometry in the GeometryCollection by index """ @@ -1004,32 +1217,44 @@ class GeometryCollection(GeometryList): Set a geometry in the GeometryCollection by index """ @typing.overload - def __setitem__(self, arg0: typing.SupportsInt, arg1: MultiPoint) -> GeometryCollection: + def __setitem__( + self, arg0: typing.SupportsInt, arg1: MultiPoint + ) -> GeometryCollection: """ Set a geometry in the GeometryCollection by index """ @typing.overload - def __setitem__(self, arg0: typing.SupportsInt, arg1: LineString) -> GeometryCollection: + def __setitem__( + self, arg0: typing.SupportsInt, arg1: LineString + ) -> GeometryCollection: """ Set a geometry in the GeometryCollection by index """ @typing.overload - def __setitem__(self, arg0: typing.SupportsInt, arg1: MultiLineString) -> GeometryCollection: + def __setitem__( + self, arg0: typing.SupportsInt, arg1: MultiLineString + ) -> GeometryCollection: """ Set a geometry in the GeometryCollection by index """ @typing.overload - def __setitem__(self, arg0: typing.SupportsInt, arg1: Polygon) -> GeometryCollection: + def __setitem__( + self, arg0: typing.SupportsInt, arg1: Polygon + ) -> GeometryCollection: """ Set a geometry in the GeometryCollection by index """ @typing.overload - def __setitem__(self, arg0: typing.SupportsInt, arg1: MultiPolygon) -> GeometryCollection: + def __setitem__( + self, arg0: typing.SupportsInt, arg1: MultiPolygon + ) -> GeometryCollection: """ Set a geometry in the GeometryCollection by index """ @typing.overload - def __setitem__(self, arg0: typing.SupportsInt, arg1: GeometryCollection) -> GeometryCollection: + def __setitem__( + self, arg0: typing.SupportsInt, arg1: GeometryCollection + ) -> GeometryCollection: """ Set a geometry in the GeometryCollection by index """ @@ -1037,7 +1262,9 @@ class GeometryCollection(GeometryList): """ Pickle support for GeometryCollection """ - def affine(self, T: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[4, 4]"]) -> GeometryCollection: + def affine( + self, T: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[4, 4]"] + ) -> GeometryCollection: """ Apply 4x4 affine transformation matrix """ @@ -1049,7 +1276,9 @@ class GeometryCollection(GeometryList): """ Remove duplicate consecutive points based on their XYZ coordinates """ - def from_rapidjson(self, arg0: pybind11_geobuf._core.rapidjson) -> GeometryCollection: + def from_rapidjson( + self, arg0: pybind11_geobuf._core.rapidjson + ) -> GeometryCollection: """ Set the GeometryCollection from a RapidJSON value """ @@ -1101,19 +1330,34 @@ class GeometryCollection(GeometryList): """ Resize the GeometryCollection to contain N geometries """ - def rotate(self, R: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 3]"]) -> GeometryCollection: + def rotate( + self, R: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 3]"] + ) -> GeometryCollection: """ Apply 3x3 rotation matrix to all coordinates """ - def round(self, *, lon: typing.SupportsInt = 8, lat: typing.SupportsInt = 8, alt: typing.SupportsInt = 3) -> GeometryCollection: + def round( + self, + *, + lon: typing.SupportsInt = 8, + lat: typing.SupportsInt = 8, + alt: typing.SupportsInt = 3, + ) -> GeometryCollection: """ Round the coordinates of all geometries in the GeometryCollection """ - def scale(self, scale: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> GeometryCollection: + def scale( + self, scale: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"] + ) -> GeometryCollection: """ Scale all coordinates by factors [sx, sy, sz] """ - def to_enu(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> GeometryCollection: + def to_enu( + self, + anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], + *, + cheap_ruler: bool = True, + ) -> GeometryCollection: """ Convert WGS84 (lon,lat,alt) to ENU coordinates """ @@ -1121,7 +1365,12 @@ class GeometryCollection(GeometryList): """ Convert the GeometryCollection to a RapidJSON value """ - def to_wgs84(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> GeometryCollection: + def to_wgs84( + self, + anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], + *, + cheap_ruler: bool = True, + ) -> GeometryCollection: """ Convert ENU coordinates to WGS84 (lon,lat,alt) """ @@ -1129,7 +1378,9 @@ class GeometryCollection(GeometryList): """ Apply transform function to all coordinates (Nx3 numpy array) """ - def translate(self, offset: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> GeometryCollection: + def translate( + self, offset: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"] + ) -> GeometryCollection: """ Translate all coordinates by offset vector """ @@ -1138,6 +1389,7 @@ class GeometryCollection(GeometryList): """ Return the __geo_interface__ representation of the GeometryCollection """ + class GeometryList: __hash__: typing.ClassVar[None] = None def __bool__(self) -> bool: @@ -1158,36 +1410,28 @@ class GeometryList: """ Delete list elements using a slice object """ - def __eq__(self, arg0: GeometryList) -> bool: - ... + def __eq__(self, arg0: GeometryList) -> bool: ... @typing.overload def __getitem__(self, s: slice) -> GeometryList: """ Retrieve list elements using a slice object """ @typing.overload - def __getitem__(self, arg0: typing.SupportsInt) -> Geometry: - ... + def __getitem__(self, arg0: typing.SupportsInt) -> Geometry: ... @typing.overload - def __init__(self) -> None: - ... + def __init__(self) -> None: ... @typing.overload def __init__(self, arg0: GeometryList) -> None: """ Copy constructor """ @typing.overload - def __init__(self, arg0: collections.abc.Iterable) -> None: - ... - def __iter__(self) -> collections.abc.Iterator[Geometry]: - ... - def __len__(self) -> int: - ... - def __ne__(self, arg0: GeometryList) -> bool: - ... + def __init__(self, arg0: collections.abc.Iterable) -> None: ... + def __iter__(self) -> collections.abc.Iterator[Geometry]: ... + def __len__(self) -> int: ... + def __ne__(self, arg0: GeometryList) -> bool: ... @typing.overload - def __setitem__(self, arg0: typing.SupportsInt, arg1: Geometry) -> None: - ... + def __setitem__(self, arg0: typing.SupportsInt, arg1: Geometry) -> None: ... @typing.overload def __setitem__(self, arg0: slice, arg1: GeometryList) -> None: """ @@ -1233,6 +1477,7 @@ class GeometryList: """ Remove the first item from the list whose value is x. It is an error if there is no such item. """ + class LineString(coordinates): __hash__: typing.ClassVar[None] = None def __call__(self) -> typing.Any: @@ -1255,15 +1500,19 @@ class LineString(coordinates): """ Get a point from the geometry by index """ - def __getstate__(self) -> typing.Any: - ... + def __getstate__(self) -> typing.Any: ... @typing.overload def __init__(self) -> None: """ Default constructor for LineString """ @typing.overload - def __init__(self, arg0: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]) -> None: + def __init__( + self, + arg0: typing.Annotated[ + numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous" + ], + ) -> None: """ Initialize from a numpy array of points """ @@ -1285,7 +1534,11 @@ class LineString(coordinates): Set a point in the geometry by index """ @typing.overload - def __setitem__(self, arg0: typing.SupportsInt, arg1: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[m, 1]"]) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 1]"]: + def __setitem__( + self, + arg0: typing.SupportsInt, + arg1: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[m, 1]"], + ) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 1]"]: """ Set a point in the geometry by index using a vector """ @@ -1293,15 +1546,26 @@ class LineString(coordinates): """ Pickle support for serialization """ - def affine(self, T: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[4, 4]"]) -> LineString: + def affine( + self, T: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[4, 4]"] + ) -> LineString: """ Apply 4x4 affine transformation matrix """ - def as_numpy(self) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]", "flags.writeable", "flags.c_contiguous"]: + def as_numpy( + self, + ) -> typing.Annotated[ + numpy.typing.NDArray[numpy.float64], + "[m, 3]", + "flags.writeable", + "flags.c_contiguous", + ]: """ Get a numpy view of the geometry points """ - def bbox(self, *, with_z: bool = False) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 1]"]: + def bbox( + self, *, with_z: bool = False + ) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 1]"]: """ Compute the bounding box of the geometry """ @@ -1323,7 +1587,12 @@ class LineString(coordinates): """ Remove duplicate consecutive points based on their XYZ coordinates """ - def from_numpy(self, arg0: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]) -> LineString: + def from_numpy( + self, + arg0: typing.Annotated[ + numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous" + ], + ) -> LineString: """ Set the geometry points from a numpy array """ @@ -1341,7 +1610,9 @@ class LineString(coordinates): Add a point to the end of the geometry """ @typing.overload - def push_back(self, arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[m, 1]"]) -> LineString: + def push_back( + self, arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[m, 1]"] + ) -> LineString: """ Add a point to the end of the geometry using a vector """ @@ -1349,23 +1620,40 @@ class LineString(coordinates): """ Resize the geometry to the specified size """ - def rotate(self, R: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 3]"]) -> LineString: + def rotate( + self, R: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 3]"] + ) -> LineString: """ Apply 3x3 rotation matrix to all coordinates """ - def round(self, *, lon: typing.SupportsInt = 8, lat: typing.SupportsInt = 8, alt: typing.SupportsInt = 3) -> LineString: + def round( + self, + *, + lon: typing.SupportsInt = 8, + lat: typing.SupportsInt = 8, + alt: typing.SupportsInt = 3, + ) -> LineString: """ Round coordinates to specified decimal places """ - def scale(self, scale: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> LineString: + def scale( + self, scale: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"] + ) -> LineString: """ Scale all coordinates by factors [sx, sy, sz] """ - def to_enu(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> LineString: + def to_enu( + self, + anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], + *, + cheap_ruler: bool = True, + ) -> LineString: """ Convert WGS84 (lon,lat,alt) to ENU coordinates """ - def to_numpy(self) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]"]: + def to_numpy( + self, + ) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]"]: """ Convert the geometry points to a numpy array """ @@ -1373,7 +1661,12 @@ class LineString(coordinates): """ Convert to a RapidJSON value """ - def to_wgs84(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> LineString: + def to_wgs84( + self, + anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], + *, + cheap_ruler: bool = True, + ) -> LineString: """ Convert ENU coordinates to WGS84 (lon,lat,alt) """ @@ -1381,7 +1674,9 @@ class LineString(coordinates): """ Apply transform function to all coordinates (Nx3 numpy array) """ - def translate(self, offset: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> LineString: + def translate( + self, offset: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"] + ) -> LineString: """ Translate all coordinates by offset vector """ @@ -1390,10 +1685,12 @@ class LineString(coordinates): """ Return the __geo_interface__ representation """ + class LineStringList: """ A list of LineStrings """ + __hash__: typing.ClassVar[None] = None def __bool__(self) -> bool: """ @@ -1413,36 +1710,28 @@ class LineStringList: """ Delete list elements using a slice object """ - def __eq__(self, arg0: LineStringList) -> bool: - ... + def __eq__(self, arg0: LineStringList) -> bool: ... @typing.overload def __getitem__(self, s: slice) -> LineStringList: """ Retrieve list elements using a slice object """ @typing.overload - def __getitem__(self, arg0: typing.SupportsInt) -> LineString: - ... + def __getitem__(self, arg0: typing.SupportsInt) -> LineString: ... @typing.overload - def __init__(self) -> None: - ... + def __init__(self) -> None: ... @typing.overload def __init__(self, arg0: LineStringList) -> None: """ Copy constructor """ @typing.overload - def __init__(self, arg0: collections.abc.Iterable) -> None: - ... - def __iter__(self) -> collections.abc.Iterator[LineString]: - ... - def __len__(self) -> int: - ... - def __ne__(self, arg0: LineStringList) -> bool: - ... + def __init__(self, arg0: collections.abc.Iterable) -> None: ... + def __iter__(self) -> collections.abc.Iterator[LineString]: ... + def __len__(self) -> int: ... + def __ne__(self, arg0: LineStringList) -> bool: ... @typing.overload - def __setitem__(self, arg0: typing.SupportsInt, arg1: LineString) -> None: - ... + def __setitem__(self, arg0: typing.SupportsInt, arg1: LineString) -> None: ... @typing.overload def __setitem__(self, arg0: slice, arg1: LineStringList) -> None: """ @@ -1488,6 +1777,7 @@ class LineStringList: """ Remove the first item from the list whose value is x. It is an error if there is no such item. """ + class LinearRing(coordinates): __hash__: typing.ClassVar[None] = None def __call__(self) -> typing.Any: @@ -1532,11 +1822,22 @@ class LinearRing(coordinates): Set a point in the geometry by index """ @typing.overload - def __setitem__(self, arg0: typing.SupportsInt, arg1: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[m, 1]"]) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 1]"]: + def __setitem__( + self, + arg0: typing.SupportsInt, + arg1: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[m, 1]"], + ) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 1]"]: """ Set a point in the geometry by index using a vector """ - def as_numpy(self) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]", "flags.writeable", "flags.c_contiguous"]: + def as_numpy( + self, + ) -> typing.Annotated[ + numpy.typing.NDArray[numpy.float64], + "[m, 3]", + "flags.writeable", + "flags.c_contiguous", + ]: """ Get a numpy view of the geometry points """ @@ -1548,7 +1849,12 @@ class LinearRing(coordinates): """ Create a clone of the object """ - def from_numpy(self, arg0: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]) -> LinearRing: + def from_numpy( + self, + arg0: typing.Annotated[ + numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous" + ], + ) -> LinearRing: """ Set the geometry points from a numpy array """ @@ -1562,18 +1868,24 @@ class LinearRing(coordinates): Add a point to the end of the geometry """ @typing.overload - def push_back(self, arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[m, 1]"]) -> LinearRing: + def push_back( + self, arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[m, 1]"] + ) -> LinearRing: """ Add a point to the end of the geometry using a vector """ - def to_numpy(self) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]"]: + def to_numpy( + self, + ) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]"]: """ Convert the geometry points to a numpy array """ + class LinearRingList: """ A list of LinearRings """ + __hash__: typing.ClassVar[None] = None def __bool__(self) -> bool: """ @@ -1593,36 +1905,28 @@ class LinearRingList: """ Delete list elements using a slice object """ - def __eq__(self, arg0: LinearRingList) -> bool: - ... + def __eq__(self, arg0: LinearRingList) -> bool: ... @typing.overload def __getitem__(self, s: slice) -> LinearRingList: """ Retrieve list elements using a slice object """ @typing.overload - def __getitem__(self, arg0: typing.SupportsInt) -> LinearRing: - ... + def __getitem__(self, arg0: typing.SupportsInt) -> LinearRing: ... @typing.overload - def __init__(self) -> None: - ... + def __init__(self) -> None: ... @typing.overload def __init__(self, arg0: LinearRingList) -> None: """ Copy constructor """ @typing.overload - def __init__(self, arg0: collections.abc.Iterable) -> None: - ... - def __iter__(self) -> collections.abc.Iterator[LinearRing]: - ... - def __len__(self) -> int: - ... - def __ne__(self, arg0: LinearRingList) -> bool: - ... + def __init__(self, arg0: collections.abc.Iterable) -> None: ... + def __iter__(self) -> collections.abc.Iterator[LinearRing]: ... + def __len__(self) -> int: ... + def __ne__(self, arg0: LinearRingList) -> bool: ... @typing.overload - def __setitem__(self, arg0: typing.SupportsInt, arg1: LinearRing) -> None: - ... + def __setitem__(self, arg0: typing.SupportsInt, arg1: LinearRing) -> None: ... @typing.overload def __setitem__(self, arg0: slice, arg1: LinearRingList) -> None: """ @@ -1668,6 +1972,7 @@ class LinearRingList: """ Remove the first item from the list whose value is x. It is an error if there is no such item. """ + class MultiLineString(LineStringList): __hash__: typing.ClassVar[None] = None def __call__(self) -> typing.Any: @@ -1690,8 +1995,7 @@ class MultiLineString(LineStringList): """ Get a linear ring by index """ - def __getstate__(self) -> typing.Any: - ... + def __getstate__(self) -> typing.Any: ... @typing.overload def __init__(self) -> None: """ @@ -1708,7 +2012,12 @@ class MultiLineString(LineStringList): Construct MultiLineString from a single LineString """ @typing.overload - def __init__(self, arg0: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]) -> None: + def __init__( + self, + arg0: typing.Annotated[ + numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous" + ], + ) -> None: """ Initialize from a numpy array of points """ @@ -1724,7 +2033,15 @@ class MultiLineString(LineStringList): """ Check if two MultiLineStrings are not equal """ - def __setitem__(self, arg0: typing.SupportsInt, arg1: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]: + def __setitem__( + self, + arg0: typing.SupportsInt, + arg1: typing.Annotated[ + numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous" + ], + ) -> typing.Annotated[ + numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous" + ]: """ Set a linear ring by index using a numpy array of points """ @@ -1732,15 +2049,26 @@ class MultiLineString(LineStringList): """ Pickle support for the geometry """ - def affine(self, T: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[4, 4]"]) -> MultiLineString: + def affine( + self, T: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[4, 4]"] + ) -> MultiLineString: """ Apply 4x4 affine transformation matrix """ - def as_numpy(self) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]", "flags.writeable", "flags.c_contiguous"]: + def as_numpy( + self, + ) -> typing.Annotated[ + numpy.typing.NDArray[numpy.float64], + "[m, 3]", + "flags.writeable", + "flags.c_contiguous", + ]: """ Return a numpy view of the geometry's points """ - def bbox(self, *, with_z: bool = False) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 1]"]: + def bbox( + self, *, with_z: bool = False + ) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 1]"]: """ Compute the bounding box of the geometry """ @@ -1756,7 +2084,12 @@ class MultiLineString(LineStringList): """ Remove duplicate consecutive points based on their XYZ coordinates """ - def from_numpy(self, arg0: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]) -> MultiLineString: + def from_numpy( + self, + arg0: typing.Annotated[ + numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous" + ], + ) -> MultiLineString: """ Set the geometry from a numpy array of points """ @@ -1769,7 +2102,12 @@ class MultiLineString(LineStringList): Remove the last point from the last linear ring """ @typing.overload - def push_back(self, arg0: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]) -> MultiLineString: + def push_back( + self, + arg0: typing.Annotated[ + numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous" + ], + ) -> MultiLineString: """ Add a new linear ring from a numpy array of points """ @@ -1778,23 +2116,40 @@ class MultiLineString(LineStringList): """ Add a new linear ring """ - def rotate(self, R: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 3]"]) -> MultiLineString: + def rotate( + self, R: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 3]"] + ) -> MultiLineString: """ Apply 3x3 rotation matrix to all coordinates """ - def round(self, *, lon: typing.SupportsInt = 8, lat: typing.SupportsInt = 8, alt: typing.SupportsInt = 3) -> MultiLineString: + def round( + self, + *, + lon: typing.SupportsInt = 8, + lat: typing.SupportsInt = 8, + alt: typing.SupportsInt = 3, + ) -> MultiLineString: """ Round the coordinates of the geometry """ - def scale(self, scale: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> MultiLineString: + def scale( + self, scale: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"] + ) -> MultiLineString: """ Scale all coordinates by factors [sx, sy, sz] """ - def to_enu(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> MultiLineString: + def to_enu( + self, + anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], + *, + cheap_ruler: bool = True, + ) -> MultiLineString: """ Convert WGS84 (lon,lat,alt) to ENU coordinates """ - def to_numpy(self) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]"]: + def to_numpy( + self, + ) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]"]: """ Convert the geometry to a numpy array """ @@ -1802,7 +2157,12 @@ class MultiLineString(LineStringList): """ Convert the geometry to a RapidJSON value """ - def to_wgs84(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> MultiLineString: + def to_wgs84( + self, + anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], + *, + cheap_ruler: bool = True, + ) -> MultiLineString: """ Convert ENU coordinates to WGS84 (lon,lat,alt) """ @@ -1810,7 +2170,9 @@ class MultiLineString(LineStringList): """ Apply transform function to all coordinates (Nx3 numpy array) """ - def translate(self, offset: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> MultiLineString: + def translate( + self, offset: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"] + ) -> MultiLineString: """ Translate all coordinates by offset vector """ @@ -1819,6 +2181,7 @@ class MultiLineString(LineStringList): """ Return the __geo_interface__ representation of the geometry """ + class MultiPoint(coordinates): __hash__: typing.ClassVar[None] = None def __call__(self) -> typing.Any: @@ -1841,15 +2204,19 @@ class MultiPoint(coordinates): """ Get a point from the geometry by index """ - def __getstate__(self) -> typing.Any: - ... + def __getstate__(self) -> typing.Any: ... @typing.overload def __init__(self) -> None: """ Default constructor for MultiPoint """ @typing.overload - def __init__(self, arg0: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]) -> None: + def __init__( + self, + arg0: typing.Annotated[ + numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous" + ], + ) -> None: """ Initialize from a numpy array of points """ @@ -1871,7 +2238,11 @@ class MultiPoint(coordinates): Set a point in the geometry by index """ @typing.overload - def __setitem__(self, arg0: typing.SupportsInt, arg1: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[m, 1]"]) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 1]"]: + def __setitem__( + self, + arg0: typing.SupportsInt, + arg1: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[m, 1]"], + ) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 1]"]: """ Set a point in the geometry by index using a vector """ @@ -1879,15 +2250,26 @@ class MultiPoint(coordinates): """ Pickle support for serialization """ - def affine(self, T: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[4, 4]"]) -> MultiPoint: + def affine( + self, T: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[4, 4]"] + ) -> MultiPoint: """ Apply 4x4 affine transformation matrix """ - def as_numpy(self) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]", "flags.writeable", "flags.c_contiguous"]: + def as_numpy( + self, + ) -> typing.Annotated[ + numpy.typing.NDArray[numpy.float64], + "[m, 3]", + "flags.writeable", + "flags.c_contiguous", + ]: """ Get a numpy view of the geometry points """ - def bbox(self, *, with_z: bool = False) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 1]"]: + def bbox( + self, *, with_z: bool = False + ) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 1]"]: """ Compute the bounding box of the geometry """ @@ -1903,7 +2285,12 @@ class MultiPoint(coordinates): """ Remove duplicate consecutive points based on their XYZ coordinates """ - def from_numpy(self, arg0: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]) -> MultiPoint: + def from_numpy( + self, + arg0: typing.Annotated[ + numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous" + ], + ) -> MultiPoint: """ Set the geometry points from a numpy array """ @@ -1921,7 +2308,9 @@ class MultiPoint(coordinates): Add a point to the end of the geometry """ @typing.overload - def push_back(self, arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[m, 1]"]) -> MultiPoint: + def push_back( + self, arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[m, 1]"] + ) -> MultiPoint: """ Add a point to the end of the geometry using a vector """ @@ -1929,23 +2318,40 @@ class MultiPoint(coordinates): """ Resize the geometry to the specified size """ - def rotate(self, R: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 3]"]) -> MultiPoint: + def rotate( + self, R: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 3]"] + ) -> MultiPoint: """ Apply 3x3 rotation matrix to all coordinates """ - def round(self, *, lon: typing.SupportsInt = 8, lat: typing.SupportsInt = 8, alt: typing.SupportsInt = 3) -> MultiPoint: + def round( + self, + *, + lon: typing.SupportsInt = 8, + lat: typing.SupportsInt = 8, + alt: typing.SupportsInt = 3, + ) -> MultiPoint: """ Round coordinates to specified decimal places """ - def scale(self, scale: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> MultiPoint: + def scale( + self, scale: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"] + ) -> MultiPoint: """ Scale all coordinates by factors [sx, sy, sz] """ - def to_enu(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> MultiPoint: + def to_enu( + self, + anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], + *, + cheap_ruler: bool = True, + ) -> MultiPoint: """ Convert WGS84 (lon,lat,alt) to ENU coordinates """ - def to_numpy(self) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]"]: + def to_numpy( + self, + ) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]"]: """ Convert the geometry points to a numpy array """ @@ -1953,7 +2359,12 @@ class MultiPoint(coordinates): """ Convert to a RapidJSON value """ - def to_wgs84(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> MultiPoint: + def to_wgs84( + self, + anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], + *, + cheap_ruler: bool = True, + ) -> MultiPoint: """ Convert ENU coordinates to WGS84 (lon,lat,alt) """ @@ -1961,7 +2372,9 @@ class MultiPoint(coordinates): """ Apply transform function to all coordinates (Nx3 numpy array) """ - def translate(self, offset: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> MultiPoint: + def translate( + self, offset: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"] + ) -> MultiPoint: """ Translate all coordinates by offset vector """ @@ -1970,6 +2383,7 @@ class MultiPoint(coordinates): """ Return the __geo_interface__ representation """ + class MultiPolygon(PolygonList): __hash__: typing.ClassVar[None] = None def __call__(self) -> typing.Any: @@ -1992,8 +2406,7 @@ class MultiPolygon(PolygonList): """ Get a Polygon from the MultiPolygon by index """ - def __getstate__(self) -> typing.Any: - ... + def __getstate__(self) -> typing.Any: ... @typing.overload def __init__(self) -> None: """ @@ -2010,7 +2423,12 @@ class MultiPolygon(PolygonList): Construct MultiPolygon from a container of Polygons """ @typing.overload - def __init__(self, arg0: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]) -> None: + def __init__( + self, + arg0: typing.Annotated[ + numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous" + ], + ) -> None: """ Construct MultiPolygon from a numpy array of points """ @@ -2032,7 +2450,13 @@ class MultiPolygon(PolygonList): Set a Polygon in the MultiPolygon by index """ @typing.overload - def __setitem__(self, arg0: typing.SupportsInt, arg1: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]) -> Polygon: + def __setitem__( + self, + arg0: typing.SupportsInt, + arg1: typing.Annotated[ + numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous" + ], + ) -> Polygon: """ Set a Polygon in the MultiPolygon by index using a numpy array """ @@ -2040,15 +2464,26 @@ class MultiPolygon(PolygonList): """ Pickle support for MultiPolygon """ - def affine(self, T: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[4, 4]"]) -> MultiPolygon: + def affine( + self, T: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[4, 4]"] + ) -> MultiPolygon: """ Apply 4x4 affine transformation matrix """ - def as_numpy(self) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]", "flags.writeable", "flags.c_contiguous"]: + def as_numpy( + self, + ) -> typing.Annotated[ + numpy.typing.NDArray[numpy.float64], + "[m, 3]", + "flags.writeable", + "flags.c_contiguous", + ]: """ Return a numpy view of the MultiPolygon coordinates """ - def bbox(self, *, with_z: bool = False) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 1]"]: + def bbox( + self, *, with_z: bool = False + ) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 1]"]: """ Compute the bounding box of the MultiPolygon """ @@ -2060,7 +2495,12 @@ class MultiPolygon(PolygonList): """ Create a clone of the object """ - def from_numpy(self, arg0: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]) -> MultiPolygon: + def from_numpy( + self, + arg0: typing.Annotated[ + numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous" + ], + ) -> MultiPolygon: """ Set MultiPolygon coordinates from a numpy array """ @@ -2073,7 +2513,12 @@ class MultiPolygon(PolygonList): Remove the last Polygon from the MultiPolygon """ @typing.overload - def push_back(self, arg0: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]) -> MultiPolygon: + def push_back( + self, + arg0: typing.Annotated[ + numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous" + ], + ) -> MultiPolygon: """ Add a new Polygon to the MultiPolygon from a numpy array """ @@ -2082,23 +2527,40 @@ class MultiPolygon(PolygonList): """ Add a new Polygon to the MultiPolygon """ - def rotate(self, R: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 3]"]) -> MultiPolygon: + def rotate( + self, R: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 3]"] + ) -> MultiPolygon: """ Apply 3x3 rotation matrix to all coordinates """ - def round(self, *, lon: typing.SupportsInt = 8, lat: typing.SupportsInt = 8, alt: typing.SupportsInt = 3) -> MultiPolygon: + def round( + self, + *, + lon: typing.SupportsInt = 8, + lat: typing.SupportsInt = 8, + alt: typing.SupportsInt = 3, + ) -> MultiPolygon: """ Round the coordinates of the MultiPolygon """ - def scale(self, scale: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> MultiPolygon: + def scale( + self, scale: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"] + ) -> MultiPolygon: """ Scale all coordinates by factors [sx, sy, sz] """ - def to_enu(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> MultiPolygon: + def to_enu( + self, + anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], + *, + cheap_ruler: bool = True, + ) -> MultiPolygon: """ Convert WGS84 (lon,lat,alt) to ENU coordinates """ - def to_numpy(self: Polygon) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]"]: + def to_numpy( + self: Polygon, + ) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]"]: """ Convert MultiPolygon to a numpy array """ @@ -2106,7 +2568,12 @@ class MultiPolygon(PolygonList): """ Convert the MultiPolygon to a RapidJSON value """ - def to_wgs84(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> MultiPolygon: + def to_wgs84( + self, + anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], + *, + cheap_ruler: bool = True, + ) -> MultiPolygon: """ Convert ENU coordinates to WGS84 (lon,lat,alt) """ @@ -2114,7 +2581,9 @@ class MultiPolygon(PolygonList): """ Apply transform function to all coordinates (Nx3 numpy array) """ - def translate(self, offset: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> MultiPolygon: + def translate( + self, offset: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"] + ) -> MultiPolygon: """ Translate all coordinates by offset vector """ @@ -2123,6 +2592,7 @@ class MultiPolygon(PolygonList): """ Return the __geo_interface__ representation of the MultiPolygon """ + class Point: __hash__: typing.ClassVar[None] = None def __call__(self) -> typing.Any: @@ -2145,20 +2615,26 @@ class Point: """ Get the coordinate value at the specified index (0: x, 1: y, 2: z) """ - def __getstate__(self) -> typing.Any: - ... + def __getstate__(self) -> typing.Any: ... @typing.overload def __init__(self) -> None: """ Initialize an empty Point """ @typing.overload - def __init__(self, x: typing.SupportsFloat, y: typing.SupportsFloat, z: typing.SupportsFloat = 0.0) -> None: + def __init__( + self, + x: typing.SupportsFloat, + y: typing.SupportsFloat, + z: typing.SupportsFloat = 0.0, + ) -> None: """ Initialize a Point with coordinates (x, y, z) """ @typing.overload - def __init__(self, arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[m, 1]"]) -> None: + def __init__( + self, arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[m, 1]"] + ) -> None: """ Initialize a Point from a numpy array or vector """ @@ -2174,7 +2650,9 @@ class Point: """ Check if two Points are not equal """ - def __setitem__(self, index: typing.SupportsInt, value: typing.SupportsFloat) -> float: + def __setitem__( + self, index: typing.SupportsInt, value: typing.SupportsFloat + ) -> float: """ Set the coordinate value at the specified index (0: x, 1: y, 2: z) """ @@ -2182,15 +2660,23 @@ class Point: """ Enable pickling support for Point objects """ - def affine(self, T: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[4, 4]"]) -> Point: + def affine( + self, T: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[4, 4]"] + ) -> Point: """ Apply 4x4 affine transformation matrix """ - def as_numpy(self) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[3, 1]", "flags.writeable"]: + def as_numpy( + self, + ) -> typing.Annotated[ + numpy.typing.NDArray[numpy.float64], "[3, 1]", "flags.writeable" + ]: """ Get a numpy view of the point coordinates """ - def bbox(self, *, with_z: bool = False) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 1]"]: + def bbox( + self, *, with_z: bool = False + ) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 1]"]: """ Get the bounding box of the point """ @@ -2206,7 +2692,9 @@ class Point: """ Remove duplicate consecutive points based on their XYZ coordinates """ - def from_numpy(self, arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[m, 1]"]) -> Point: + def from_numpy( + self, arg0: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[m, 1]"] + ) -> Point: """ Set point coordinates from a numpy array """ @@ -2214,23 +2702,40 @@ class Point: """ Create a Point from a RapidJSON value """ - def rotate(self, R: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 3]"]) -> Point: + def rotate( + self, R: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 3]"] + ) -> Point: """ Apply 3x3 rotation matrix to all coordinates """ - def round(self, *, lon: typing.SupportsInt = 8, lat: typing.SupportsInt = 8, alt: typing.SupportsInt = 3) -> Point: + def round( + self, + *, + lon: typing.SupportsInt = 8, + lat: typing.SupportsInt = 8, + alt: typing.SupportsInt = 3, + ) -> Point: """ Round coordinates to specified decimal places """ - def scale(self, scale: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> Point: + def scale( + self, scale: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"] + ) -> Point: """ Scale all coordinates by factors [sx, sy, sz] """ - def to_enu(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> Point: + def to_enu( + self, + anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], + *, + cheap_ruler: bool = True, + ) -> Point: """ Convert WGS84 (lon,lat,alt) to ENU coordinates """ - def to_numpy(self) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[3, 1]"]: + def to_numpy( + self, + ) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[3, 1]"]: """ Convert point coordinates to a numpy array """ @@ -2238,7 +2743,12 @@ class Point: """ Convert the Point to a RapidJSON value """ - def to_wgs84(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> Point: + def to_wgs84( + self, + anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], + *, + cheap_ruler: bool = True, + ) -> Point: """ Convert ENU coordinates to WGS84 (lon,lat,alt) """ @@ -2246,7 +2756,9 @@ class Point: """ Apply transform function to all coordinates (Nx3 numpy array) """ - def translate(self, offset: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> Point: + def translate( + self, offset: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"] + ) -> Point: """ Translate all coordinates by offset vector """ @@ -2261,24 +2773,22 @@ class Point: Get or set the x-coordinate of the point """ @x.setter - def x(self, arg1: typing.SupportsFloat) -> None: - ... + def x(self, arg1: typing.SupportsFloat) -> None: ... @property def y(self) -> float: """ Get or set the y-coordinate of the point """ @y.setter - def y(self, arg1: typing.SupportsFloat) -> None: - ... + def y(self, arg1: typing.SupportsFloat) -> None: ... @property def z(self) -> float: """ Get or set the z-coordinate of the point """ @z.setter - def z(self, arg1: typing.SupportsFloat) -> None: - ... + def z(self, arg1: typing.SupportsFloat) -> None: ... + class Polygon(LinearRingList): __hash__: typing.ClassVar[None] = None def __call__(self) -> typing.Any: @@ -2301,8 +2811,7 @@ class Polygon(LinearRingList): """ Get a linear ring by index """ - def __getstate__(self) -> typing.Any: - ... + def __getstate__(self) -> typing.Any: ... @typing.overload def __init__(self) -> None: """ @@ -2319,7 +2828,12 @@ class Polygon(LinearRingList): Construct Polygon from a single LinearRing (shell) """ @typing.overload - def __init__(self, arg0: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]) -> None: + def __init__( + self, + arg0: typing.Annotated[ + numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous" + ], + ) -> None: """ Initialize from a numpy array of points """ @@ -2335,7 +2849,15 @@ class Polygon(LinearRingList): """ Check if two Polygons are not equal """ - def __setitem__(self, arg0: typing.SupportsInt, arg1: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]: + def __setitem__( + self, + arg0: typing.SupportsInt, + arg1: typing.Annotated[ + numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous" + ], + ) -> typing.Annotated[ + numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous" + ]: """ Set a linear ring by index using a numpy array of points """ @@ -2343,15 +2865,26 @@ class Polygon(LinearRingList): """ Pickle support for the geometry """ - def affine(self, T: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[4, 4]"]) -> Polygon: + def affine( + self, T: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[4, 4]"] + ) -> Polygon: """ Apply 4x4 affine transformation matrix """ - def as_numpy(self) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]", "flags.writeable", "flags.c_contiguous"]: + def as_numpy( + self, + ) -> typing.Annotated[ + numpy.typing.NDArray[numpy.float64], + "[m, 3]", + "flags.writeable", + "flags.c_contiguous", + ]: """ Return a numpy view of the geometry's points """ - def bbox(self, *, with_z: bool = False) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 1]"]: + def bbox( + self, *, with_z: bool = False + ) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 1]"]: """ Compute the bounding box of the geometry """ @@ -2367,7 +2900,12 @@ class Polygon(LinearRingList): """ Remove duplicate consecutive points based on their XYZ coordinates """ - def from_numpy(self, arg0: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]) -> Polygon: + def from_numpy( + self, + arg0: typing.Annotated[ + numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous" + ], + ) -> Polygon: """ Set the geometry from a numpy array of points """ @@ -2380,7 +2918,12 @@ class Polygon(LinearRingList): Remove the last point from the last linear ring """ @typing.overload - def push_back(self, arg0: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]) -> Polygon: + def push_back( + self, + arg0: typing.Annotated[ + numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous" + ], + ) -> Polygon: """ Add a new linear ring from a numpy array of points """ @@ -2389,23 +2932,40 @@ class Polygon(LinearRingList): """ Add a new linear ring """ - def rotate(self, R: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 3]"]) -> Polygon: + def rotate( + self, R: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 3]"] + ) -> Polygon: """ Apply 3x3 rotation matrix to all coordinates """ - def round(self, *, lon: typing.SupportsInt = 8, lat: typing.SupportsInt = 8, alt: typing.SupportsInt = 3) -> Polygon: + def round( + self, + *, + lon: typing.SupportsInt = 8, + lat: typing.SupportsInt = 8, + alt: typing.SupportsInt = 3, + ) -> Polygon: """ Round the coordinates of the geometry """ - def scale(self, scale: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> Polygon: + def scale( + self, scale: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"] + ) -> Polygon: """ Scale all coordinates by factors [sx, sy, sz] """ - def to_enu(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> Polygon: + def to_enu( + self, + anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], + *, + cheap_ruler: bool = True, + ) -> Polygon: """ Convert WGS84 (lon,lat,alt) to ENU coordinates """ - def to_numpy(self) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]"]: + def to_numpy( + self, + ) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]"]: """ Convert the geometry to a numpy array """ @@ -2413,7 +2973,12 @@ class Polygon(LinearRingList): """ Convert the geometry to a RapidJSON value """ - def to_wgs84(self, anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], *, cheap_ruler: bool = True) -> Polygon: + def to_wgs84( + self, + anchor: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"], + *, + cheap_ruler: bool = True, + ) -> Polygon: """ Convert ENU coordinates to WGS84 (lon,lat,alt) """ @@ -2421,7 +2986,9 @@ class Polygon(LinearRingList): """ Apply transform function to all coordinates (Nx3 numpy array) """ - def translate(self, offset: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"]) -> Polygon: + def translate( + self, offset: typing.Annotated[numpy.typing.ArrayLike, numpy.float64, "[3, 1]"] + ) -> Polygon: """ Translate all coordinates by offset vector """ @@ -2430,6 +2997,7 @@ class Polygon(LinearRingList): """ Return the __geo_interface__ representation of the geometry """ + class PolygonList: __hash__: typing.ClassVar[None] = None def __bool__(self) -> bool: @@ -2450,36 +3018,28 @@ class PolygonList: """ Delete list elements using a slice object """ - def __eq__(self, arg0: PolygonList) -> bool: - ... + def __eq__(self, arg0: PolygonList) -> bool: ... @typing.overload def __getitem__(self, s: slice) -> PolygonList: """ Retrieve list elements using a slice object """ @typing.overload - def __getitem__(self, arg0: typing.SupportsInt) -> Polygon: - ... + def __getitem__(self, arg0: typing.SupportsInt) -> Polygon: ... @typing.overload - def __init__(self) -> None: - ... + def __init__(self) -> None: ... @typing.overload def __init__(self, arg0: PolygonList) -> None: """ Copy constructor """ @typing.overload - def __init__(self, arg0: collections.abc.Iterable) -> None: - ... - def __iter__(self) -> collections.abc.Iterator[Polygon]: - ... - def __len__(self) -> int: - ... - def __ne__(self, arg0: PolygonList) -> bool: - ... + def __init__(self, arg0: collections.abc.Iterable) -> None: ... + def __iter__(self) -> collections.abc.Iterator[Polygon]: ... + def __len__(self) -> int: ... + def __ne__(self, arg0: PolygonList) -> bool: ... @typing.overload - def __setitem__(self, arg0: typing.SupportsInt, arg1: Polygon) -> None: - ... + def __setitem__(self, arg0: typing.SupportsInt, arg1: Polygon) -> None: ... @typing.overload def __setitem__(self, arg0: slice, arg1: PolygonList) -> None: """ @@ -2525,6 +3085,7 @@ class PolygonList: """ Remove the first item from the list whose value is x. It is an error if there is no such item. """ + class coordinates: __hash__: typing.ClassVar[None] = None def __bool__(self) -> bool: @@ -2545,36 +3106,28 @@ class coordinates: """ Delete list elements using a slice object """ - def __eq__(self, arg0: coordinates) -> bool: - ... + def __eq__(self, arg0: coordinates) -> bool: ... @typing.overload def __getitem__(self, s: slice) -> coordinates: """ Retrieve list elements using a slice object """ @typing.overload - def __getitem__(self, arg0: typing.SupportsInt) -> ...: - ... + def __getitem__(self, arg0: typing.SupportsInt) -> ...: ... @typing.overload - def __init__(self) -> None: - ... + def __init__(self) -> None: ... @typing.overload def __init__(self, arg0: coordinates) -> None: """ Copy constructor """ @typing.overload - def __init__(self, arg0: collections.abc.Iterable) -> None: - ... - def __iter__(self) -> collections.abc.Iterator[...]: - ... - def __len__(self) -> int: - ... - def __ne__(self, arg0: coordinates) -> bool: - ... + def __init__(self, arg0: collections.abc.Iterable) -> None: ... + def __iter__(self) -> collections.abc.Iterator[...]: ... + def __len__(self) -> int: ... + def __ne__(self, arg0: coordinates) -> bool: ... @typing.overload - def __setitem__(self, arg0: typing.SupportsInt, arg1: ...) -> None: - ... + def __setitem__(self, arg0: typing.SupportsInt, arg1: ...) -> None: ... @typing.overload def __setitem__(self, arg0: slice, arg1: coordinates) -> None: """ @@ -2584,7 +3137,14 @@ class coordinates: """ Add an item to the end of the list """ - def as_numpy(self) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]", "flags.writeable", "flags.c_contiguous"]: + def as_numpy( + self, + ) -> typing.Annotated[ + numpy.typing.NDArray[numpy.float64], + "[m, 3]", + "flags.writeable", + "flags.c_contiguous", + ]: """ Get a numpy view of the coordinates """ @@ -2606,7 +3166,12 @@ class coordinates: """ Extend the list by appending all the items in the given list """ - def from_numpy(self, arg0: typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous"]) -> coordinates: + def from_numpy( + self, + arg0: typing.Annotated[ + numpy.typing.NDArray[numpy.float64], "[m, n]", "flags.c_contiguous" + ], + ) -> coordinates: """ Set coordinates from a numpy array """ @@ -2628,28 +3193,27 @@ class coordinates: """ Remove the first item from the list whose value is x. It is an error if there is no such item. """ - def to_numpy(self) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]"]: + def to_numpy( + self, + ) -> typing.Annotated[numpy.typing.NDArray[numpy.float64], "[m, 3]"]: """ Convert coordinates to a numpy array """ + class value: class ItemsView: - def __iter__(self) -> collections.abc.Iterator: - ... - def __len__(self) -> int: - ... + def __iter__(self) -> collections.abc.Iterator: ... + def __len__(self) -> int: ... + class KeysView: - def __contains__(self, arg0: typing.Any) -> bool: - ... - def __iter__(self) -> collections.abc.Iterator: - ... - def __len__(self) -> int: - ... + def __contains__(self, arg0: typing.Any) -> bool: ... + def __iter__(self) -> collections.abc.Iterator: ... + def __len__(self) -> int: ... + class ValuesView: - def __iter__(self) -> collections.abc.Iterator: - ... - def __len__(self) -> int: - ... + def __iter__(self) -> collections.abc.Iterator: ... + def __len__(self) -> int: ... + class array_type: __hash__: typing.ClassVar[None] = None def __bool__(self) -> bool: @@ -2674,32 +3238,28 @@ class value: """ Delete list elements using a slice object """ - def __eq__(self, arg0: value.array_type) -> bool: - ... + def __eq__(self, arg0: value.array_type) -> bool: ... @typing.overload def __getitem__(self, s: slice) -> value.array_type: """ Retrieve list elements using a slice object """ @typing.overload - def __getitem__(self, arg0: typing.SupportsInt) -> value: - ... + def __getitem__(self, arg0: typing.SupportsInt) -> value: ... @typing.overload def __getitem__(self, arg0: typing.SupportsInt) -> value: """ Get an item from the GeoJSON array by index """ @typing.overload - def __init__(self) -> None: - ... + def __init__(self) -> None: ... @typing.overload def __init__(self, arg0: value.array_type) -> None: """ Copy constructor """ @typing.overload - def __init__(self, arg0: collections.abc.Iterable) -> None: - ... + def __init__(self, arg0: collections.abc.Iterable) -> None: ... @typing.overload def __init__(self) -> None: """ @@ -2710,15 +3270,11 @@ class value: """ Construct a GeoJSON array from a Python iterable """ - def __iter__(self) -> collections.abc.Iterator[value]: - ... - def __len__(self) -> int: - ... - def __ne__(self, arg0: value.array_type) -> bool: - ... + def __iter__(self) -> collections.abc.Iterator[value]: ... + def __len__(self) -> int: ... + def __ne__(self, arg0: value.array_type) -> bool: ... @typing.overload - def __setitem__(self, arg0: typing.SupportsInt, arg1: value) -> None: - ... + def __setitem__(self, arg0: typing.SupportsInt, arg1: value) -> None: ... @typing.overload def __setitem__(self, arg0: slice, arg1: value.array_type) -> None: """ @@ -2757,7 +3313,9 @@ class value: """ Extend the list by appending all the items in the given list """ - def from_rapidjson(self, arg0: pybind11_geobuf._core.rapidjson) -> value.array_type: + def from_rapidjson( + self, arg0: pybind11_geobuf._core.rapidjson + ) -> value.array_type: """ Set the GeoJSON array from a RapidJSON value """ @@ -2783,6 +3341,7 @@ class value: """ Convert the GeoJSON array to a RapidJSON value """ + class object_type: def __bool__(self) -> bool: """ @@ -2793,18 +3352,13 @@ class value: Convert the GeoJSON object to a Python dict """ @typing.overload - def __contains__(self, arg0: str) -> bool: - ... + def __contains__(self, arg0: str) -> bool: ... @typing.overload - def __contains__(self, arg0: typing.Any) -> bool: - ... - def __delitem__(self, arg0: str) -> None: - ... - def __getitem__(self, arg0: str) -> value: - ... + def __contains__(self, arg0: typing.Any) -> bool: ... + def __delitem__(self, arg0: str) -> None: ... + def __getitem__(self, arg0: str) -> value: ... @typing.overload - def __init__(self) -> None: - ... + def __init__(self) -> None: ... @typing.overload def __init__(self) -> None: """ @@ -2815,13 +3369,10 @@ class value: """ Construct a GeoJSON object from a Python dict """ - def __iter__(self) -> collections.abc.Iterator[str]: - ... - def __len__(self) -> int: - ... + def __iter__(self) -> collections.abc.Iterator[str]: ... + def __len__(self) -> int: ... @typing.overload - def __setitem__(self, arg0: str, arg1: value) -> None: - ... + def __setitem__(self, arg0: str, arg1: value) -> None: ... @typing.overload def __setitem__(self, arg0: str, arg1: typing.Any) -> value: """ @@ -2831,21 +3382,21 @@ class value: """ Clear the GeoJSON object """ - def from_rapidjson(self, arg0: pybind11_geobuf._core.rapidjson) -> value.object_type: + def from_rapidjson( + self, arg0: pybind11_geobuf._core.rapidjson + ) -> value.object_type: """ Convert a RapidJSON value to a GeoJSON object """ @typing.overload - def items(self) -> value.ItemsView: - ... + def items(self) -> value.ItemsView: ... @typing.overload def items(self) -> collections.abc.Iterator[tuple[str, value]]: """ Get an iterator over the items (key-value pairs) of the GeoJSON object """ @typing.overload - def keys(self) -> value.KeysView: - ... + def keys(self) -> value.KeysView: ... @typing.overload def keys(self) -> collections.abc.Iterator[str]: """ @@ -2856,13 +3407,13 @@ class value: Convert the GeoJSON object to a RapidJSON value """ @typing.overload - def values(self) -> value.ValuesView: - ... + def values(self) -> value.ValuesView: ... @typing.overload def values(self) -> collections.abc.Iterator[value]: """ Get an iterator over the values of the GeoJSON object """ + def Get(self) -> typing.Any: """ Get the GeoJSON value as a Python object From 3e11e391cf874bdf048660ec8a7566633682b79b Mon Sep 17 00:00:00 2001 From: tang zhixiong Date: Sat, 31 Jan 2026 11:13:18 +0800 Subject: [PATCH 06/18] Remove headers submodule --- .gitmodules | 3 --- headers | 1 - 2 files changed, 4 deletions(-) delete mode 160000 headers diff --git a/.gitmodules b/.gitmodules index 21f5cc7..ae16b31 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,6 +4,3 @@ [submodule "pygeobuf"] path = pygeobuf url = https://github.com/pygeobuf/pygeobuf.git -[submodule "headers"] - path = headers - url = https://github.com/cubao/headers.git diff --git a/headers b/headers deleted file mode 160000 index 0a21abb..0000000 --- a/headers +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 0a21abb45cd8269b08363d638903dd4ff1de048b From d982024db9eff03d0c44a834f3ac39c76994c156 Mon Sep 17 00:00:00 2001 From: tang zhixiong Date: Sat, 31 Jan 2026 11:15:34 +0800 Subject: [PATCH 07/18] update makefile --- CMakeLists.txt | 7 ++++++- pyproject.toml | 4 +++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9de33a7..06a83e4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,7 +34,12 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_CXX_STANDARD 17) -include_directories(SYSTEM ${PROJECT_SOURCE_DIR}/headers/include) +execute_process( + COMMAND "${Python_EXECUTABLE}" -c + "import cubao_headers; print(cubao_headers.get_include())" + OUTPUT_VARIABLE CUBAO_HEADERS_INCLUDE_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE) +include_directories(SYSTEM ${CUBAO_HEADERS_INCLUDE_DIR}) include_directories(${PROJECT_BINARY_DIR} ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/src/geobuf) option(BUILD_SHARED_LIBS "Build shared library." OFF) diff --git a/pyproject.toml b/pyproject.toml index 45b5649..2b73f6c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["scikit-build-core>=0.3.3", "pybind11"] +requires = ["scikit-build-core>=0.3.3", "pybind11", "cubao-headers>=0.0.8"] build-backend = "scikit_build_core.build" @@ -22,6 +22,8 @@ classifiers = [ "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", ] From db7b70cff436d4bdefa1ab89c7c86b5fd95d6e49 Mon Sep 17 00:00:00 2001 From: tang zhixiong Date: Sat, 31 Jan 2026 11:17:25 +0800 Subject: [PATCH 08/18] fix --- MANIFEST.in | 3 --- 1 file changed, 3 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 093cce6..8348bb3 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,8 +1,5 @@ include README.md LICENSE pybind11/LICENSE include CMakeLists.txt -graft headers/include -graft pybind11/include -graft pybind11/tools graft src graft examples graft tests From 55f628275fa705dd491ff17caa791b786dc7af3a Mon Sep 17 00:00:00 2001 From: tang zhixiong Date: Sat, 31 Jan 2026 11:19:08 +0800 Subject: [PATCH 09/18] fix lint --- CMakeLists.txt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 06a83e4..bfd568c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,10 +35,9 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_CXX_STANDARD 17) execute_process( - COMMAND "${Python_EXECUTABLE}" -c - "import cubao_headers; print(cubao_headers.get_include())" - OUTPUT_VARIABLE CUBAO_HEADERS_INCLUDE_DIR - OUTPUT_STRIP_TRAILING_WHITESPACE) + COMMAND "${Python_EXECUTABLE}" -c "import cubao_headers; print(cubao_headers.get_include())" + OUTPUT_VARIABLE CUBAO_HEADERS_INCLUDE_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE) include_directories(SYSTEM ${CUBAO_HEADERS_INCLUDE_DIR}) include_directories(${PROJECT_BINARY_DIR} ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/src/geobuf) From a9308598619be8f50fac41afc4b1128e6d4eeeed Mon Sep 17 00:00:00 2001 From: tang zhixiong Date: Sat, 31 Jan 2026 11:57:36 +0800 Subject: [PATCH 10/18] fix compiling --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 2b73f6c..7f48d2c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["scikit-build-core>=0.3.3", "pybind11", "cubao-headers>=0.0.8"] +requires = ["scikit-build-core>=0.3.3", "pybind11", "cubao-headers>=0.1.0"] build-backend = "scikit_build_core.build" From 89d964b6284e0e1a2ee96ecb5b0e845f8d3bde90 Mon Sep 17 00:00:00 2001 From: tang zhixiong Date: Sat, 31 Jan 2026 12:14:28 +0800 Subject: [PATCH 11/18] fix --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 7f48d2c..ee97099 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,7 +32,7 @@ Homepage = "https://geobuf-cpp.readthedocs.io" [project.optional-dependencies] -test = ["pytest", "scipy"] +test = ["pytest"] [tool.scikit-build] From 96dc8cfb9dd646a072904a4b9c4220f6ac6d5172 Mon Sep 17 00:00:00 2001 From: tang zhixiong Date: Sat, 31 Jan 2026 12:31:40 +0800 Subject: [PATCH 12/18] fix CI --- pyproject.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index ee97099..5f53b98 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,6 +11,11 @@ readme = "README.md" authors = [ { name = "district10", email = "dvorak4tzx@gmail.com" }, ] +dependencies = [ + "fire", + "loguru", + "numpy", +] requires-python = ">=3.7" classifiers = [ "Development Status :: 4 - Beta", From e389574806206ad43bd3b88cda0c84c4c9b244b0 Mon Sep 17 00:00:00 2001 From: tang zhixiong Date: Sat, 31 Jan 2026 14:16:00 +0800 Subject: [PATCH 13/18] drop arm64 --- .github/workflows/wheels.yml | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index 3db26ec..e856a63 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -44,40 +44,31 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest, windows-latest, macos-latest] + os: [ubuntu-latest, macos-latest, windows-latest] steps: - uses: actions/checkout@v4 with: submodules: true - - name: Set up QEMU - if: runner.os == 'Linux' - uses: docker/setup-qemu-action@v2 - with: - platforms: all - - uses: pypa/cibuildwheel@v2.22 env: - # CIBW_ARCHS: auto64 - CIBW_ARCHS_LINUX: x86_64 aarch64 - CIBW_ARCHS_WINDOWS: AMD64 # ARM64 - CIBW_ARCHS_MACOS: x86_64 arm64 - CIBW_BEFORE_BUILD: pip install numpy fire --prefer-binary - # https://cibuildwheel.readthedocs.io/en/stable/options/#build-skip - CIBW_SKIP: pp* *i686 *musllinux* + CIBW_ARCHS_MACOS: universal2 + CIBW_ARCHS_WINDOWS: AMD64 ARM64 + CIBW_SKIP: "pp* *musllinux* *_i686" CIBW_TEST_SKIP: "*macosx* *win* *aarch64" + CIBW_BEFORE_BUILD: "pip install --prefer-binary numpy fire pytest" - name: Verify clean directory run: git diff --exit-code shell: bash - - name: Upload wheels - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v4 with: name: cibw-wheels-${{ matrix.os }} path: wheelhouse/*.whl + upload_all: name: Upload if release needs: [build_wheels, build_sdist] From 13a75be5d2c594a4e1d0576cbd30b4d14e54da20 Mon Sep 17 00:00:00 2001 From: tang zhixiong Date: Sat, 31 Jan 2026 14:28:40 +0800 Subject: [PATCH 14/18] Use native ARM runner instead of QEMU for Linux aarch64 builds - Replace QEMU emulation with native ubuntu-24.04-arm runner - Use macos-14 (Apple Silicon) with universal2 for macOS - Add job-index to artifact names to avoid conflicts Co-Authored-By: Claude Opus 4.5 --- .github/workflows/wheels.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index e856a63..befbfb6 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -44,7 +44,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest, macos-latest, windows-latest] + os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, macos-14] steps: - uses: actions/checkout@v4 @@ -65,7 +65,7 @@ jobs: - uses: actions/upload-artifact@v4 with: - name: cibw-wheels-${{ matrix.os }} + name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }} path: wheelhouse/*.whl From b471a31783171120ec1729bbc1a19cb82e31a76b Mon Sep 17 00:00:00 2001 From: tang zhixiong Date: Sat, 31 Jan 2026 14:37:54 +0800 Subject: [PATCH 15/18] fix --- CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index bfd568c..32fb0b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,10 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-pragma-once-outside-header") endif() +if(MSVC) + add_compile_options(/bigobj) +endif() + add_definitions(-D_CRT_SECURE_NO_WARNINGS) add_definitions(-DPROJECT_SOURCE_DIR="${PROJECT_SOURCE_DIR}") add_definitions(-DPROJECT_BINARY_DIR="${PROJECT_BINARY_DIR}") From 20628ba5d094f32920c49646b152c172aef2b735 Mon Sep 17 00:00:00 2001 From: tang zhixiong Date: Sat, 31 Jan 2026 15:05:26 +0800 Subject: [PATCH 16/18] Fix macOS wheel build by excluding static library from install delocate-wheel fails on .a files since they use ar archive format, not Mach-O. The static library is already linked into _core.so. Co-Authored-By: Claude Opus 4.5 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 32fb0b3..25b1bdd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,7 +52,7 @@ file(GLOB_RECURSE SOURCES src/**/*.cpp) add_library(${PROJECT_NAME} ${SOURCES} ${HEADERS}) target_link_libraries(${PROJECT_NAME} ${CONAN_LIBS}) set_target_properties(${PROJECT_NAME} PROPERTIES CXX_VISIBILITY_PRESET "hidden") -install(TARGETS ${PROJECT_NAME} DESTINATION ${PROJECT_NAME}) +# Don't install static library to wheel - delocate-wheel can't handle .a files add_executable(pbf_decoder src/geobuf/pbf_decoder.cpp) target_compile_definitions(pbf_decoder PUBLIC -DPBF_DECODER_ENABLE_MAIN) From d8295fa582e69c195645440eded05cb1892bf234 Mon Sep 17 00:00:00 2001 From: tang zhixiong Date: Sat, 31 Jan 2026 15:11:27 +0800 Subject: [PATCH 17/18] update docs --- CLAUDE.md | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++ version.h.in | 19 --------------- 2 files changed, 68 insertions(+), 19 deletions(-) create mode 100644 CLAUDE.md delete mode 100644 version.h.in diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..f01f0d4 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,68 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +C++ port of [mapbox/geobuf](https://github.com/mapbox/geobuf) with Python bindings via pybind11. Geobuf is a compact binary encoding for GeoJSON using Protocol Buffers. + +## Build Commands + +```bash +# Initialize submodules (required first time) +git submodule update --init --recursive + +# Build Python extension (editable install) +make build + +# Run all C++ tests +make test_all + +# Run Python tests +make pytest +# Or directly: pytest tests/test_basic.py + +# Lint code +make lint + +# Roundtrip tests (compare C++ vs JS implementations) +make roundtrip_test_cpp +make roundtrip_test_js + +# CLI tests +make cli_test +``` + +## Architecture + +### Core C++ Library (`src/geobuf/`) + +- **geobuf.hpp/cpp**: Main `Encoder` and `Decoder` classes for GeoJSON ↔ Geobuf (protobuf) conversion +- **geobuf_index.hpp**: `GeobufIndex` class for spatial indexing and random access to features in large Geobuf files using memory-mapped I/O and packed R-tree +- **planet.hpp**: `Planet` class wrapping feature collections with spatial query support via `PackedRTree` +- **geojson_helpers.hpp**: JSON normalization utilities (rounding, sorting keys, denoising) +- **rapidjson_helpers.hpp**: RapidJSON wrapper utilities + +### Python Bindings (`src/`) + +- **main.cpp**: pybind11 module definition exposing `Encoder`, `Decoder`, `GeobufIndex`, `Planet`, `PackedRTree` +- **pybind11_geojson.cpp**: Bindings for mapbox::geojson types (Point, LineString, Polygon, Feature, FeatureCollection) +- **pybind11_rapidjson.cpp**: Bindings for RapidJSON value types + +### Key Dependencies (all header-only, in submodules) + +- `rapidjson`: JSON parsing/serialization +- `geojson-cpp` (forked): GeoJSON representation with Z-coordinate and custom_properties support +- `protozero`: Protocol Buffer encoding/decoding +- `geometry.hpp` (forked): Geometry types + +### Python Package (`src/pybind11_geobuf/`) + +- CLI via `python -m pybind11_geobuf` with commands: `json2geobuf`, `geobuf2json`, `pbf_decode`, `normalize_json`, `round_trip`, `is_subset_of` + +## Key Classes + +- `mapbox::geobuf::Encoder`: Encodes GeoJSON to compact Geobuf format with configurable precision +- `mapbox::geobuf::Decoder`: Decodes Geobuf back to GeoJSON, supports partial decoding (header, individual features) +- `cubao::GeobufIndex`: Enables random access to features in large Geobuf files without loading entire file +- `cubao::Planet`: Feature collection with built-in spatial index for bounding box queries diff --git a/version.h.in b/version.h.in deleted file mode 100644 index a534895..0000000 --- a/version.h.in +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef @PROJECT_NAME_UPPERCASE@_VERSION_H -#define @PROJECT_NAME_UPPERCASE@_VERSION_H - -#define @PROJECT_NAME_UPPERCASE@_MAJOR_VERSION (@MAJOR_VERSION@) -#define @PROJECT_NAME_UPPERCASE@_MINOR_VERSION (@MINOR_VERSION@) -#define @PROJECT_NAME_UPPERCASE@_PATCH_VERSION (@PATCH_VERSION@) -#define @PROJECT_NAME_UPPERCASE@_VERSION "@PROJECT_VERSION@" - -#define DATA_DIR "@PROJECT_SOURCE_DIR@/data" -#define PROJECT_SOURCE_DIR "@PROJECT_SOURCE_DIR@" -#define PROJECT_BINARY_DIR "@PROJECT_BINARY_DIR@" - -#define USERNAME_HOSTNAME "@USERNAME_HOSTNAME@" -#define GIT_BRANCH "@GIT_BRANCH@" -#define GIT_COMMIT_HASH "@GIT_COMMIT_HASH@" -#define GIT_COMMIT_COUNT GIT_COMMIT_COUNT -#define GIT_DIFF_NAME_ONLY "@GIT_DIFF_NAME_ONLY@" - -#endif // @PROJECT_NAME_UPPERCASE@_VERSION_H From 6851dc34dd8b4de68a4090824c5368fb28432c53 Mon Sep 17 00:00:00 2001 From: tang zhixiong Date: Sat, 31 Jan 2026 15:19:32 +0800 Subject: [PATCH 18/18] add caching (works) --- .github/workflows/format.yml | 11 +++++++++-- .github/workflows/pip.yml | 14 ++++++++++++++ .github/workflows/wheels.yml | 11 +++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index c792242..c0d58c5 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -15,8 +15,15 @@ jobs: name: Format runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: python-version: "3.x" + - name: Cache pre-commit + uses: actions/cache@v4 + with: + path: ~/.cache/pre-commit + key: pre-commit-${{ hashFiles('.pre-commit-config.yaml') }} + restore-keys: | + pre-commit- - uses: pre-commit/action@v3.0.0 diff --git a/.github/workflows/pip.yml b/.github/workflows/pip.yml index 57f6308..88a9802 100644 --- a/.github/workflows/pip.yml +++ b/.github/workflows/pip.yml @@ -28,6 +28,20 @@ jobs: python-version: ${{ matrix.python-version }} allow-prereleases: true + - name: Get pip cache dir + id: pip-cache + shell: bash + run: echo "dir=$(pip cache dir)" >> $GITHUB_OUTPUT + + - name: Cache pip dependencies + uses: actions/cache@v4 + with: + path: ${{ steps.pip-cache.outputs.dir }} + key: ${{ runner.os }}-pip-${{ matrix.python-version }}-${{ hashFiles('pyproject.toml') }} + restore-keys: | + ${{ runner.os }}-pip-${{ matrix.python-version }}- + ${{ runner.os }}-pip- + - name: Add requirements run: python -m pip install --upgrade wheel setuptools diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index befbfb6..0e0fcf2 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -51,6 +51,17 @@ jobs: with: submodules: true + - name: Cache cibuildwheel + uses: actions/cache@v4 + with: + path: | + ~/Library/Caches/pip + ~/.cache/pip + ~/AppData/Local/pip/Cache + key: cibw-pip-${{ matrix.os }}-${{ hashFiles('pyproject.toml') }} + restore-keys: | + cibw-pip-${{ matrix.os }}- + - uses: pypa/cibuildwheel@v2.22 env: CIBW_ARCHS_MACOS: universal2