Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make Z and M values accessible via QgsGeometry.as_numpy() #59367

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 50 additions & 21 deletions python/PyQt6/core/__init__.py.in
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
71 changes: 50 additions & 21 deletions python/core/__init__.py.in
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
163 changes: 163 additions & 0 deletions tests/src/python/test_qgsgeometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Loading