diff --git a/python/PyQt6/core/__init__.py.in b/python/PyQt6/core/__init__.py.in index 2ee442b06f95..c67b4da0a7ef 100644 --- a/python/PyQt6/core/__init__.py.in +++ b/python/PyQt6/core/__init__.py.in @@ -591,29 +591,58 @@ try: QgsRasterLayer.as_numpy = _raster_layer_as_numpy def _qgsgeometry_as_numpy(self) -> _typing.Union[_numpy.ndarray, _typing.List[_numpy.ndarray]]: + wkb_type = self.wkbType() + hasM = QgsWkbTypes.hasM(wkb_type) + hasZ = QgsWkbTypes.hasZ(wkb_type) + geometry_type = self.type() + + def get_xyzm_coordinates(pt): + if hasZ and hasM: + return _numpy.array([pt.x(), pt.y(), pt.z(), pt.m()]) + elif hasZ: + return _numpy.array([pt.x(), pt.y(), pt.z()]) + elif hasM: + return _numpy.array([pt.x(), pt.y(), pt.m()]) + else: + return _numpy.array([pt.x(), pt.y()]) + + def fill_structure_with_elements(lst: _typing.List, elements: _typing.List, idx: int=0): + for i in range(len(lst)): + if isinstance(lst[i], list): + idx = fill_structure_with_elements(lst[i], elements, idx) + else: + lst[i] = _numpy.array(elements[idx]) + idx += 1 + return idx + if self.isMultipart(): - if self.type() == QgsWkbTypes.PointGeometry: - # MultiPoint - return [_numpy.array([pt.x(), pt.y()]) for pt in self.asMultiPoint()] - elif self.type() == QgsWkbTypes.LineGeometry: - # MultiLineString - return [_numpy.array([[pt.x(), pt.y()] for pt in line]) for line in self.asMultiPolyline()] - elif self.type() == QgsWkbTypes.PolygonGeometry: - # MultiPolygon - return [ - [_numpy.array([[pt.x(), pt.y()] for pt in ring]) for ring in polygon] - for polygon in self.asMultiPolygon() - ] + elements = [get_xyzm_coordinates(i) for i in self.vertices()] + + if geometry_type == QgsWkbTypes.PointGeometry: + skeleton = self.asMultiPoint() + fill_structure_with_elements(skeleton, elements) + return skeleton + + elif geometry_type == QgsWkbTypes.LineGeometry: + skeleton = self.asMultiPolyline() + fill_structure_with_elements(skeleton, elements) + return skeleton + + elif geometry_type == QgsWkbTypes.PolygonGeometry: + skeleton = self.asMultiPolygon() + fill_structure_with_elements(skeleton, elements) + return skeleton else: - if self.type() == QgsWkbTypes.PointGeometry: - point = self.asPoint() - return _numpy.array([point.x(), point.y()]) - elif self.type() == QgsWkbTypes.LineGeometry: - line = self.asPolyline() - return _numpy.array([[pt.x(), pt.y()] for pt in line]) - elif self.type() == QgsWkbTypes.PolygonGeometry: - polygon = self.asPolygon() - return _numpy.array([_numpy.array([[pt.x(), pt.y()] for pt in ring]) for ring in polygon]) + if geometry_type == QgsWkbTypes.PointGeometry: + return _numpy.array([get_xyzm_coordinates(i) for i in self.vertices()][0]) + elif geometry_type == QgsWkbTypes.LineGeometry: + line = self.vertices() + return _numpy.array([get_xyzm_coordinates(pt) for pt in line]) + elif geometry_type == QgsWkbTypes.PolygonGeometry: + skeleton = self.asPolygon() + elements = [get_xyzm_coordinates(i) for i in self.vertices()] + fill_structure_with_elements(skeleton, elements) + return _numpy.array(skeleton) QgsGeometry.as_numpy = _qgsgeometry_as_numpy diff --git a/python/core/__init__.py.in b/python/core/__init__.py.in index fc57a8676496..6e74b646d5b2 100644 --- a/python/core/__init__.py.in +++ b/python/core/__init__.py.in @@ -601,29 +601,58 @@ try: QgsRasterLayer.as_numpy = _raster_layer_as_numpy def _qgsgeometry_as_numpy(self) -> _typing.Union[_numpy.ndarray, _typing.List[_numpy.ndarray]]: + wkb_type = self.wkbType() + hasM = QgsWkbTypes.hasM(wkb_type) + hasZ = QgsWkbTypes.hasZ(wkb_type) + geometry_type = self.type() + + def get_xyzm_coordinates(pt): + if hasZ and hasM: + return _numpy.array([pt.x(), pt.y(), pt.z(), pt.m()]) + elif hasZ: + return _numpy.array([pt.x(), pt.y(), pt.z()]) + elif hasM: + return _numpy.array([pt.x(), pt.y(), pt.m()]) + else: + return _numpy.array([pt.x(), pt.y()]) + + def fill_structure_with_elements(lst: _typing.List, elements: _typing.List, idx: int=0): + for i in range(len(lst)): + if isinstance(lst[i], list): + idx = fill_structure_with_elements(lst[i], elements, idx) + else: + lst[i] = _numpy.array(elements[idx]) + idx += 1 + return idx + if self.isMultipart(): - if self.type() == QgsWkbTypes.PointGeometry: - # MultiPoint - return [_numpy.array([pt.x(), pt.y()]) for pt in self.asMultiPoint()] - elif self.type() == QgsWkbTypes.LineGeometry: - # MultiLineString - return [_numpy.array([[pt.x(), pt.y()] for pt in line]) for line in self.asMultiPolyline()] - elif self.type() == QgsWkbTypes.PolygonGeometry: - # MultiPolygon - return [ - [_numpy.array([[pt.x(), pt.y()] for pt in ring]) for ring in polygon] - for polygon in self.asMultiPolygon() - ] + elements = [get_xyzm_coordinates(i) for i in self.vertices()] + + if geometry_type == QgsWkbTypes.PointGeometry: + skeleton = self.asMultiPoint() + fill_structure_with_elements(skeleton, elements) + return skeleton + + elif geometry_type == QgsWkbTypes.LineGeometry: + skeleton = self.asMultiPolyline() + fill_structure_with_elements(skeleton, elements) + return skeleton + + elif geometry_type == QgsWkbTypes.PolygonGeometry: + skeleton = self.asMultiPolygon() + fill_structure_with_elements(skeleton, elements) + return skeleton else: - if self.type() == QgsWkbTypes.PointGeometry: - point = self.asPoint() - return _numpy.array([point.x(), point.y()]) - elif self.type() == QgsWkbTypes.LineGeometry: - line = self.asPolyline() - return _numpy.array([[pt.x(), pt.y()] for pt in line]) - elif self.type() == QgsWkbTypes.PolygonGeometry: - polygon = self.asPolygon() - return _numpy.array([_numpy.array([[pt.x(), pt.y()] for pt in ring]) for ring in polygon]) + if geometry_type == QgsWkbTypes.PointGeometry: + return _numpy.array([get_xyzm_coordinates(i) for i in self.vertices()][0]) + elif geometry_type == QgsWkbTypes.LineGeometry: + line = self.vertices() + return _numpy.array([get_xyzm_coordinates(pt) for pt in line]) + elif geometry_type == QgsWkbTypes.PolygonGeometry: + skeleton = self.asPolygon() + elements = [get_xyzm_coordinates(i) for i in self.vertices()] + fill_structure_with_elements(skeleton, elements) + return _numpy.array(skeleton) QgsGeometry.as_numpy = _qgsgeometry_as_numpy diff --git a/tests/src/python/test_qgsgeometry.py b/tests/src/python/test_qgsgeometry.py index fefb6cbcf0a0..d38ceda5b572 100644 --- a/tests/src/python/test_qgsgeometry.py +++ b/tests/src/python/test_qgsgeometry.py @@ -8037,6 +8037,169 @@ def testAsNumpy(self): self.assertEqual(len(array_multipolygon[1]), 1) # Second polygon has 1 ring self.assertTrue(numpy.allclose(array_multipolygon[1][0], [[10, 10], [14, 10], [14, 14], [10, 14], [10, 10]])) + # Test POINT with Z + geom_point_z = QgsGeometry.fromWkt("POINT Z (1 2 3)") + array_point_z = geom_point_z.as_numpy() + self.assertTrue(isinstance(array_point_z, numpy.ndarray)) + # self.assertEqual(array_point_z.shape, (3,)) + self.assertTrue(numpy.allclose(array_point_z, [1, 2, 3])) + + # Test POINT with M + geom_point_m = QgsGeometry.fromWkt("POINT M (1 2 4)") + array_point_m = geom_point_m.as_numpy() + self.assertTrue(isinstance(array_point_m, numpy.ndarray)) + self.assertEqual(array_point_m.shape, (3,)) + self.assertTrue(numpy.allclose(array_point_m, [1, 2, 4])) + + # Test POINT with Z and M + geom_point_zm = QgsGeometry.fromWkt("POINT ZM (1 2 3 4)") + array_point_zm = geom_point_zm.as_numpy() + self.assertTrue(isinstance(array_point_zm, numpy.ndarray)) + self.assertEqual(array_point_zm.shape, (4,)) + self.assertTrue(numpy.allclose(array_point_zm, [1, 2, 3, 4])) + + # Test LINESTRING with Z + geom_linestring_z = QgsGeometry.fromWkt("LINESTRING Z (0 0 1, 1 1 2, 1 2 3)") + array_linestring_z = geom_linestring_z.as_numpy() + self.assertTrue(isinstance(array_linestring_z, numpy.ndarray)) + self.assertEqual(array_linestring_z.shape, (3, 3)) + self.assertTrue(numpy.allclose(array_linestring_z, [[0, 0, 1], [1, 1, 2], [1, 2, 3]])) + + # Test LINESTRING with M + geom_linestring_m = QgsGeometry.fromWkt("LINESTRING M (0 0 1, 1 1 2, 1 2 3)") + array_linestring_m = geom_linestring_m.as_numpy() + self.assertTrue(isinstance(array_linestring_m, numpy.ndarray)) + self.assertEqual(array_linestring_m.shape, (3, 3)) + self.assertTrue(numpy.allclose(array_linestring_m, [[0, 0, 1], [1, 1, 2], [1, 2, 3]])) + + # Test LINESTRING with Z and M + geom_linestring_zm = QgsGeometry.fromWkt("LINESTRING ZM (0 0 1 2, 1 1 2 3, 1 2 3 4)") + array_linestring_zm = geom_linestring_zm.as_numpy() + self.assertTrue(isinstance(array_linestring_zm, numpy.ndarray)) + self.assertEqual(array_linestring_zm.shape, (3, 4)) + self.assertTrue(numpy.allclose(array_linestring_zm, [[0, 0, 1, 2], [1, 1, 2, 3], [1, 2, 3, 4]])) + + # Test POLYGON with Z + geom_polygon_z = QgsGeometry.fromWkt("POLYGON Z ((0 0 1, 4 0 2, 4 4 3, 0 4 4, 0 0 1))") + array_polygon_z = geom_polygon_z.as_numpy() + self.assertTrue(isinstance(array_polygon_z, numpy.ndarray)) + self.assertTrue(numpy.allclose(array_polygon_z[0], [[0, 0, 1], [4, 0, 2], [4, 4, 3], [0, 4, 4], [0, 0, 1]])) + + # Test POLYGON with M + geom_polygon_m = QgsGeometry.fromWkt("POLYGON M ((0 0 1, 4 0 2, 4 4 3, 0 4 4, 0 0 1))") + array_polygon_m = geom_polygon_m.as_numpy() + self.assertTrue(isinstance(array_polygon_m, numpy.ndarray)) + self.assertTrue(numpy.allclose(array_polygon_m[0], [[0, 0, 1], [4, 0, 2], [4, 4, 3], [0, 4, 4], [0, 0, 1]])) + + # Test POLYGON with Z and M + geom_polygon_zm = QgsGeometry.fromWkt("POLYGON ZM ((0 0 1 5, 4 0 2 6, 4 4 3 7, 0 4 4 8, 0 0 1 5))") + array_polygon_zm = geom_polygon_zm.as_numpy() + self.assertTrue(isinstance(array_polygon_zm, numpy.ndarray)) + self.assertTrue( + numpy.allclose(array_polygon_zm[0], [[0, 0, 1, 5], [4, 0, 2, 6], [4, 4, 3, 7], [0, 4, 4, 8], [0, 0, 1, 5]])) + + # Test MULTIPOINT with Z + geom_multipoint_z = QgsGeometry.fromWkt("MULTIPOINT Z ((1 2 3), (3 4 5))") + array_multipoint_z = geom_multipoint_z.as_numpy() + self.assertTrue(isinstance(array_multipoint_z, list)) + self.assertEqual(len(array_multipoint_z), 2) + self.assertTrue(numpy.allclose(array_multipoint_z[0], [1, 2, 3])) + self.assertTrue(numpy.allclose(array_multipoint_z[1], [3, 4, 5])) + + # Test MULTIPOINT with M + geom_multipoint_m = QgsGeometry.fromWkt("MULTIPOINT M ((1 2 6), (3 4 7))") + array_multipoint_m = geom_multipoint_m.as_numpy() + self.assertTrue(isinstance(array_multipoint_m, list)) + self.assertEqual(len(array_multipoint_m), 2) + self.assertTrue(numpy.allclose(array_multipoint_m[0], [1, 2, 6])) + self.assertTrue(numpy.allclose(array_multipoint_m[1], [3, 4, 7])) + + # Test MULTIPOINT with Z and M + geom_multipoint_zm = QgsGeometry.fromWkt("MULTIPOINT ZM ((1 2 3 8), (3 4 5 9))") + array_multipoint_zm = geom_multipoint_zm.as_numpy() + self.assertTrue(isinstance(array_multipoint_zm, list)) + self.assertEqual(len(array_multipoint_zm), 2) + self.assertTrue(numpy.allclose(array_multipoint_zm[0], [1, 2, 3, 8])) + self.assertTrue(numpy.allclose(array_multipoint_zm[1], [3, 4, 5, 9])) + + # Test MULTILINESTRING with Z + geom_multilinestring_z = QgsGeometry.fromWkt("MULTILINESTRING Z ((0 0 1, 1 1 2), (2 2 3, 3 3 4))") + array_multilinestring_z = geom_multilinestring_z.as_numpy() + self.assertTrue(isinstance(array_multilinestring_z, list)) + self.assertEqual(len(array_multilinestring_z), 2) + self.assertTrue(numpy.allclose(array_multilinestring_z[0], [[0, 0, 1], [1, 1, 2]])) + self.assertTrue(numpy.allclose(array_multilinestring_z[1], [[2, 2, 3], [3, 3, 4]])) + + # Test MULTILINESTRING with M + geom_multilinestring_m = QgsGeometry.fromWkt("MULTILINESTRING M ((0 0 5, 1 1 6), (2 2 7, 3 3 8))") + array_multilinestring_m = geom_multilinestring_m.as_numpy() + self.assertTrue(isinstance(array_multilinestring_m, list)) + self.assertEqual(len(array_multilinestring_m), 2) + self.assertTrue(numpy.allclose(array_multilinestring_m[0], [[0, 0, 5], [1, 1, 6]])) + self.assertTrue(numpy.allclose(array_multilinestring_m[1], [[2, 2, 7], [3, 3, 8]])) + + # Test MULTILINESTRING with Z and M + geom_multilinestring_zm = QgsGeometry.fromWkt("MULTILINESTRING ZM ((0 0 1 5, 1 1 2 6), (2 2 3 7, 3 3 4 8))") + array_multilinestring_zm = geom_multilinestring_zm.as_numpy() + self.assertTrue(isinstance(array_multilinestring_zm, list)) + self.assertEqual(len(array_multilinestring_zm), 2) + self.assertTrue(numpy.allclose(array_multilinestring_zm[0], [[0, 0, 1, 5], [1, 1, 2, 6]])) + self.assertTrue(numpy.allclose(array_multilinestring_zm[1], [[2, 2, 3, 7], [3, 3, 4, 8]])) + + # Test MULTIPOLYGON with Z, and a hole + geom_multipolygon_z = QgsGeometry.fromWkt( + "MULTIPOLYGON Z (((0 0 1, 4 0 2, 4 4 3, 0 4 4, 0 0 1)), ((1 1 1, 3 1 1.5, 3 3 2, 1 3 2.5, 1 1 1)))" + ) + array_multipolygon_z = geom_multipolygon_z.as_numpy() + self.assertTrue(isinstance(array_multipolygon_z, list)) + self.assertEqual(len(array_multipolygon_z), 2) + self.assertTrue( + numpy.allclose(array_multipolygon_z[0][0], [[0, 0, 1], [4, 0, 2], [4, 4, 3], [0, 4, 4], [0, 0, 1]])) + self.assertTrue( + numpy.allclose(array_multipolygon_z[1][0], [[1, 1, 1], [3, 1, 1.5], [3, 3, 2], [1, 3, 2.5], [1, 1, 1]])) + + # Test MULTIPOLYGON with M + geom_multipolygon_m = QgsGeometry.fromWkt( + "MULTIPOLYGON M (((0 0 5, 4 0 6, 4 4 7, 0 4 8, 0 0 5)), ((10 10 9, 14 10 10, 14 14 11, 10 14 12, 10 10 9)))" + ) + array_multipolygon_m = geom_multipolygon_m.as_numpy() + self.assertTrue(isinstance(array_multipolygon_m, list)) + self.assertEqual(len(array_multipolygon_m), 2) + self.assertTrue( + numpy.allclose(array_multipolygon_m[0][0], [[0, 0, 5], [4, 0, 6], [4, 4, 7], [0, 4, 8], [0, 0, 5]])) + self.assertTrue(numpy.allclose(array_multipolygon_m[1][0], + [[10, 10, 9], [14, 10, 10], [14, 14, 11], [10, 14, 12], [10, 10, 9]])) + + # Test MULTIPOLYGON with Z and M and holes + geom_multipolygon_zm = QgsGeometry.fromWkt( + "MultiPolygon ZM (((-2.43197278911564618 0.52380952380952384 0 0, -2.0986394557823127 -0.65986394557823114 0 0,-1.10544217687074831 -0.14965986394557818 0 0, -1.07142857142857162 0.29931972789115646 0 0,-2.43197278911564618 0.52380952380952384 0 0)),((-2.12399582948757359 0.08043619457833384 0 0, -1.33488018322907021 0.08043619457833384 0 0,-1.77025433288893463 -0.19167264895908098 0 0, -2.12399582948757359 0.08043619457833384 0 0)),((1.0 1.0 1 1, 2.0 1.0 2 2, 2.0 2.0 3 3, 1.0 2.0 4 4, 1.0 1.0 1 1)))" + ) + array_multipolygon_zm = geom_multipolygon_zm.as_numpy() + self.assertTrue(isinstance(array_multipolygon_zm, list)) + self.assertEqual(len(array_multipolygon_zm), 3) + self.assertTrue( + numpy.allclose(array_multipolygon_zm[0][0], + [[-2.43197278911564618, 0.52380952380952384, 0, 0], + [-2.0986394557823127, -0.65986394557823114, 0, 0], + [-1.10544217687074831, -0.14965986394557818, 0, 0], + [-1.07142857142857162, 0.29931972789115646, 0, 0], + [-2.43197278911564618, 0.52380952380952384, 0, 0]])) + + self.assertTrue( + numpy.allclose(array_multipolygon_zm[1][0], + [[-2.12399582948757359, 0.08043619457833384, 0, 0], + [-1.33488018322907021, 0.08043619457833384, 0, 0], + [-1.77025433288893463, -0.19167264895908098, 0, 0], + [-2.12399582948757359, 0.08043619457833384, 0, 0]])) + + self.assertTrue( + numpy.allclose(array_multipolygon_zm[2][0], + [[1.0, 1.0, 1, 1], + [2.0, 1.0, 2, 2], + [2.0, 2.0, 3, 3], + [1.0, 2.0, 4, 4], + [1.0, 1.0, 1, 1]])) + if __name__ == '__main__': unittest.main()