test_ragged_array.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665
  1. import numpy as np
  2. import pytest
  3. from numpy.testing import assert_allclose
  4. import shapely
  5. from shapely import MultiLineString, MultiPoint, MultiPolygon
  6. from shapely.testing import assert_geometries_equal
  7. from shapely.tests.common import (
  8. empty_line_string,
  9. empty_line_string_m,
  10. empty_line_string_z,
  11. empty_line_string_zm,
  12. empty_multi_polygon_m,
  13. empty_multi_polygon_z,
  14. empty_multi_polygon_zm,
  15. geometry_collection,
  16. line_string,
  17. line_string_m,
  18. line_string_z,
  19. line_string_zm,
  20. linear_ring,
  21. multi_line_string,
  22. multi_line_string_m,
  23. multi_line_string_z,
  24. multi_line_string_zm,
  25. multi_point,
  26. multi_point_m,
  27. multi_point_z,
  28. multi_point_zm,
  29. multi_polygon,
  30. multi_polygon_m,
  31. multi_polygon_z,
  32. multi_polygon_zm,
  33. point,
  34. point_m,
  35. point_z,
  36. point_zm,
  37. polygon,
  38. polygon_m,
  39. polygon_z,
  40. polygon_zm,
  41. )
  42. all_types = (
  43. point,
  44. line_string,
  45. polygon,
  46. multi_point,
  47. multi_line_string,
  48. multi_polygon,
  49. )
  50. all_types_z = (
  51. point_z,
  52. line_string_z,
  53. polygon_z,
  54. multi_point_z,
  55. multi_line_string_z,
  56. multi_polygon_z,
  57. )
  58. all_types_m = (
  59. point_m,
  60. line_string_m,
  61. polygon_m,
  62. multi_point_m,
  63. multi_line_string_m,
  64. multi_polygon_m,
  65. )
  66. all_types_zm = (
  67. point_zm,
  68. line_string_zm,
  69. polygon_zm,
  70. multi_point_zm,
  71. multi_line_string_zm,
  72. multi_polygon_zm,
  73. )
  74. all_types_dims_combos = all_types + all_types_z
  75. if shapely.geos_version >= (3, 12, 0):
  76. all_types_dims_combos = all_types_dims_combos + all_types_m + all_types_zm
  77. all_types_not_supported = (
  78. linear_ring,
  79. geometry_collection,
  80. )
  81. @pytest.mark.parametrize("geom", all_types + all_types_z)
  82. def test_roundtrip(geom):
  83. actual = shapely.from_ragged_array(*shapely.to_ragged_array([geom, geom]))
  84. assert_geometries_equal(actual, [geom, geom])
  85. @pytest.mark.parametrize("include_m", [None, True, False])
  86. @pytest.mark.parametrize("include_z", [None, True, False])
  87. @pytest.mark.parametrize("geom", all_types_dims_combos)
  88. def test_to_ragged_array(geom, include_z, include_m):
  89. _, coords, _ = shapely.to_ragged_array(
  90. [geom, geom], include_z=include_z, include_m=include_m
  91. )
  92. nan_dims = np.all(np.isnan(coords), axis=0).tolist()
  93. expected = [False, False] # XY
  94. has_z = geom.has_z
  95. if include_z or (include_z is None and has_z):
  96. expected.append(not has_z) # XYZ or XYZM
  97. if shapely.geos_version >= (3, 12, 0):
  98. has_m = geom.has_m
  99. else:
  100. has_m = False
  101. if include_m or (include_m is None and has_m):
  102. expected.append(not has_m) # XYM or XYZM
  103. assert nan_dims == expected
  104. def test_include_z_default():
  105. # corner cases for inferring dimensionality
  106. # mixed XY and XYZ -> XYZ
  107. _, coords, _ = shapely.to_ragged_array([line_string, line_string_z])
  108. assert coords.shape[1] == 3
  109. # only empties -> always 2D
  110. _, coords, _ = shapely.to_ragged_array([empty_line_string])
  111. assert coords.shape[1] == 2
  112. _, coords, _ = shapely.to_ragged_array([empty_line_string_z])
  113. assert coords.shape[1] == 2
  114. # empty collection -> GEOS indicates 2D
  115. _, coords, _ = shapely.to_ragged_array([empty_multi_polygon_z])
  116. assert coords.shape[1] == 2
  117. @pytest.mark.skipif(shapely.geos_version < (3, 12, 0), reason="GEOS < 3.12")
  118. def test_include_m_default():
  119. # a few other corner cases for inferring dimensionality
  120. # mixed XY and XYM -> XYM
  121. _, coords, _ = shapely.to_ragged_array([line_string, line_string_m])
  122. assert coords.shape[1] == 3
  123. # mixed XY, XYM, and XYZM -> XYZM
  124. _, coords, _ = shapely.to_ragged_array([line_string, line_string_m, line_string_zm])
  125. assert coords.shape[1] == 4
  126. # only empties -> always 2D
  127. _, coords, _ = shapely.to_ragged_array([empty_line_string_m])
  128. assert coords.shape[1] == 2
  129. _, coords, _ = shapely.to_ragged_array([empty_line_string_zm])
  130. assert coords.shape[1] == 2
  131. # empty collection -> GEOS indicates 2D
  132. _, coords, _ = shapely.to_ragged_array([empty_multi_polygon_m])
  133. assert coords.shape[1] == 2
  134. _, coords, _ = shapely.to_ragged_array([empty_multi_polygon_zm])
  135. assert coords.shape[1] == 2
  136. @pytest.mark.parametrize("geom", all_types)
  137. def test_read_only_arrays(geom):
  138. # https://github.com/shapely/shapely/pull/1744
  139. typ, coords, offsets = shapely.to_ragged_array([geom, geom])
  140. coords.flags.writeable = False
  141. for arr in offsets:
  142. arr.flags.writeable = False
  143. result = shapely.from_ragged_array(typ, coords, offsets)
  144. assert_geometries_equal(result, [geom, geom])
  145. @pytest.mark.parametrize("geom", all_types_not_supported)
  146. def test_raise_geometry_type(geom):
  147. with pytest.raises(ValueError):
  148. shapely.to_ragged_array([geom, geom])
  149. def test_points():
  150. arr = shapely.from_wkt(
  151. [
  152. "POINT (0 0)",
  153. "POINT (1 1)",
  154. "POINT EMPTY",
  155. "POINT EMPTY",
  156. "POINT (4 4)",
  157. None,
  158. "POINT EMPTY",
  159. ]
  160. )
  161. typ, result, offsets = shapely.to_ragged_array(arr)
  162. expected = np.array(
  163. [
  164. [0, 0],
  165. [1, 1],
  166. [np.nan, np.nan],
  167. [np.nan, np.nan],
  168. [4, 4],
  169. [np.nan, np.nan],
  170. [np.nan, np.nan],
  171. ]
  172. )
  173. assert typ == shapely.GeometryType.POINT
  174. assert len(result) == len(arr)
  175. assert_allclose(result, expected)
  176. assert len(offsets) == 0
  177. geoms = shapely.from_ragged_array(typ, result)
  178. # in a roundtrip, missing geometries come back as empty
  179. arr[-2] = shapely.from_wkt("POINT EMPTY")
  180. assert_geometries_equal(geoms, arr)
  181. def test_linestrings():
  182. arr = shapely.from_wkt(
  183. [
  184. "LINESTRING (30 10, 10 30, 40 40)",
  185. "LINESTRING (40 40, 30 30, 40 20, 30 10)",
  186. "LINESTRING EMPTY",
  187. "LINESTRING EMPTY",
  188. "LINESTRING (10 10, 20 20, 10 40)",
  189. None,
  190. "LINESTRING EMPTY",
  191. ]
  192. )
  193. typ, coords, offsets = shapely.to_ragged_array(arr)
  194. expected = np.array(
  195. [
  196. [30.0, 10.0],
  197. [10.0, 30.0],
  198. [40.0, 40.0],
  199. [40.0, 40.0],
  200. [30.0, 30.0],
  201. [40.0, 20.0],
  202. [30.0, 10.0],
  203. [10.0, 10.0],
  204. [20.0, 20.0],
  205. [10.0, 40.0],
  206. ]
  207. )
  208. expected_offsets = np.array([0, 3, 7, 7, 7, 10, 10, 10], dtype="int32")
  209. assert typ == shapely.GeometryType.LINESTRING
  210. assert_allclose(coords, expected)
  211. assert len(offsets) == 1
  212. assert offsets[0].dtype == np.int32
  213. assert_allclose(offsets[0], expected_offsets)
  214. result = shapely.from_ragged_array(typ, coords, offsets)
  215. # in a roundtrip, missing geometries come back as empty
  216. arr[-2] = shapely.from_wkt("LINESTRING EMPTY")
  217. assert_geometries_equal(result, arr)
  218. # sliced
  219. offsets_sliced = (offsets[0][1:],)
  220. result = shapely.from_ragged_array(typ, coords, offsets_sliced)
  221. assert_geometries_equal(result, arr[1:])
  222. offsets_sliced = (offsets[0][:-1],)
  223. result = shapely.from_ragged_array(typ, coords, offsets_sliced)
  224. assert_geometries_equal(result, arr[:-1])
  225. def test_polygons():
  226. arr = shapely.from_wkt(
  227. [
  228. "POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))",
  229. "POLYGON ((35 10, 45 45, 15 40, 10 20, 35 10), (20 30, 35 35, 30 20, 20 30))", # noqa: E501
  230. "POLYGON EMPTY",
  231. "POLYGON EMPTY",
  232. "POLYGON ((30 10, 40 40, 20 40, 10 20, 30 10))",
  233. None,
  234. "POLYGON EMPTY",
  235. ]
  236. )
  237. typ, coords, offsets = shapely.to_ragged_array(arr)
  238. expected = np.array(
  239. [
  240. [30.0, 10.0],
  241. [40.0, 40.0],
  242. [20.0, 40.0],
  243. [10.0, 20.0],
  244. [30.0, 10.0],
  245. [35.0, 10.0],
  246. [45.0, 45.0],
  247. [15.0, 40.0],
  248. [10.0, 20.0],
  249. [35.0, 10.0],
  250. [20.0, 30.0],
  251. [35.0, 35.0],
  252. [30.0, 20.0],
  253. [20.0, 30.0],
  254. [30.0, 10.0],
  255. [40.0, 40.0],
  256. [20.0, 40.0],
  257. [10.0, 20.0],
  258. [30.0, 10.0],
  259. ]
  260. )
  261. expected_offsets1 = np.array([0, 5, 10, 14, 19])
  262. expected_offsets2 = np.array([0, 1, 3, 3, 3, 4, 4, 4])
  263. assert typ == shapely.GeometryType.POLYGON
  264. assert_allclose(coords, expected)
  265. assert len(offsets) == 2
  266. assert offsets[0].dtype == np.int32
  267. assert offsets[1].dtype == np.int32
  268. assert_allclose(offsets[0], expected_offsets1)
  269. assert_allclose(offsets[1], expected_offsets2)
  270. result = shapely.from_ragged_array(typ, coords, offsets)
  271. # in a roundtrip, missing geometries come back as empty
  272. arr[-2] = shapely.from_wkt("POLYGON EMPTY")
  273. assert_geometries_equal(result, arr)
  274. # sliced:
  275. # - indices into the coordinate array for the whole buffer
  276. # - indices into the ring array for *just* the sliced part
  277. offsets_sliced = (offsets[0], offsets[1][1:])
  278. result = shapely.from_ragged_array(typ, coords, offsets_sliced)
  279. assert_geometries_equal(result, arr[1:])
  280. offsets_sliced = (offsets[0], offsets[1][:-1])
  281. result = shapely.from_ragged_array(typ, coords, offsets_sliced)
  282. assert_geometries_equal(result, arr[:-1])
  283. def test_multipoints():
  284. arr = shapely.from_wkt(
  285. [
  286. "MULTIPOINT (10 40, 40 30, 20 20, 30 10)",
  287. "MULTIPOINT (30 10)",
  288. "MULTIPOINT EMPTY",
  289. "MULTIPOINT EMPTY",
  290. "MULTIPOINT (30 10, 10 30, 40 40)",
  291. None,
  292. "MULTIPOINT EMPTY",
  293. ]
  294. )
  295. typ, coords, offsets = shapely.to_ragged_array(arr)
  296. expected = np.array(
  297. [
  298. [10.0, 40.0],
  299. [40.0, 30.0],
  300. [20.0, 20.0],
  301. [30.0, 10.0],
  302. [30.0, 10.0],
  303. [30.0, 10.0],
  304. [10.0, 30.0],
  305. [40.0, 40.0],
  306. ]
  307. )
  308. expected_offsets = np.array([0, 4, 5, 5, 5, 8, 8, 8])
  309. assert typ == shapely.GeometryType.MULTIPOINT
  310. assert_allclose(coords, expected)
  311. assert len(offsets) == 1
  312. assert offsets[0].dtype == np.int32
  313. assert_allclose(offsets[0], expected_offsets)
  314. result = shapely.from_ragged_array(typ, coords, offsets)
  315. # in a roundtrip, missing geometries come back as empty
  316. arr[-2] = shapely.from_wkt("MULTIPOINT EMPTY")
  317. assert_geometries_equal(result, arr)
  318. # sliced:
  319. offsets_sliced = (offsets[0][1:],)
  320. result = shapely.from_ragged_array(typ, coords, offsets_sliced)
  321. assert_geometries_equal(result, arr[1:])
  322. offsets_sliced = (offsets[0][:-1],)
  323. result = shapely.from_ragged_array(typ, coords, offsets_sliced)
  324. assert_geometries_equal(result, arr[:-1])
  325. def test_multilinestrings():
  326. arr = shapely.from_wkt(
  327. [
  328. "MULTILINESTRING ((30 10, 10 30, 40 40))",
  329. "MULTILINESTRING ((10 10, 20 20, 10 40), (40 40, 30 30, 40 20, 30 10))",
  330. "MULTILINESTRING EMPTY",
  331. "MULTILINESTRING EMPTY",
  332. "MULTILINESTRING ((35 10, 45 45), (15 40, 10 20), (30 10, 10 30, 40 40))",
  333. None,
  334. "MULTILINESTRING EMPTY",
  335. ]
  336. )
  337. typ, coords, offsets = shapely.to_ragged_array(arr)
  338. expected = np.array(
  339. [
  340. [30.0, 10.0],
  341. [10.0, 30.0],
  342. [40.0, 40.0],
  343. [10.0, 10.0],
  344. [20.0, 20.0],
  345. [10.0, 40.0],
  346. [40.0, 40.0],
  347. [30.0, 30.0],
  348. [40.0, 20.0],
  349. [30.0, 10.0],
  350. [35.0, 10.0],
  351. [45.0, 45.0],
  352. [15.0, 40.0],
  353. [10.0, 20.0],
  354. [30.0, 10.0],
  355. [10.0, 30.0],
  356. [40.0, 40.0],
  357. ]
  358. )
  359. expected_offsets1 = np.array([0, 3, 6, 10, 12, 14, 17])
  360. expected_offsets2 = np.array([0, 1, 3, 3, 3, 6, 6, 6])
  361. assert typ == shapely.GeometryType.MULTILINESTRING
  362. assert_allclose(coords, expected)
  363. assert len(offsets) == 2
  364. assert offsets[0].dtype == np.int32
  365. assert offsets[1].dtype == np.int32
  366. assert_allclose(offsets[0], expected_offsets1)
  367. assert_allclose(offsets[1], expected_offsets2)
  368. result = shapely.from_ragged_array(typ, coords, offsets)
  369. # in a roundtrip, missing geometries come back as empty
  370. arr[-2] = shapely.from_wkt("MULTILINESTRING EMPTY")
  371. assert_geometries_equal(result, arr)
  372. # sliced:
  373. # - indices into the coordinate array for the whole buffer
  374. # - indices into the line parts for *just* the sliced part
  375. offsets_sliced = (offsets[0], offsets[1][1:])
  376. result = shapely.from_ragged_array(typ, coords, offsets_sliced)
  377. assert_geometries_equal(result, arr[1:])
  378. offsets_sliced = (offsets[0], offsets[1][:-1])
  379. result = shapely.from_ragged_array(typ, coords, offsets_sliced)
  380. assert_geometries_equal(result, arr[:-1])
  381. def test_multipolygons():
  382. arr = shapely.from_wkt(
  383. [
  384. "MULTIPOLYGON (((35 10, 45 45, 15 40, 10 20, 35 10), (20 30, 35 35, 30 20, 20 30)))", # noqa: E501
  385. "MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)), ((20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20)))", # noqa: E501
  386. "MULTIPOLYGON EMPTY",
  387. "MULTIPOLYGON EMPTY",
  388. "MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)))",
  389. None,
  390. "MULTIPOLYGON EMPTY",
  391. ]
  392. )
  393. typ, coords, offsets = shapely.to_ragged_array(arr)
  394. expected = np.array(
  395. [
  396. [35.0, 10.0],
  397. [45.0, 45.0],
  398. [15.0, 40.0],
  399. [10.0, 20.0],
  400. [35.0, 10.0],
  401. [20.0, 30.0],
  402. [35.0, 35.0],
  403. [30.0, 20.0],
  404. [20.0, 30.0],
  405. [40.0, 40.0],
  406. [20.0, 45.0],
  407. [45.0, 30.0],
  408. [40.0, 40.0],
  409. [20.0, 35.0],
  410. [10.0, 30.0],
  411. [10.0, 10.0],
  412. [30.0, 5.0],
  413. [45.0, 20.0],
  414. [20.0, 35.0],
  415. [30.0, 20.0],
  416. [20.0, 15.0],
  417. [20.0, 25.0],
  418. [30.0, 20.0],
  419. [40.0, 40.0],
  420. [20.0, 45.0],
  421. [45.0, 30.0],
  422. [40.0, 40.0],
  423. ]
  424. )
  425. expected_offsets1 = np.array([0, 5, 9, 13, 19, 23, 27])
  426. expected_offsets2 = np.array([0, 2, 3, 5, 6])
  427. expected_offsets3 = np.array([0, 1, 3, 3, 3, 4, 4, 4])
  428. assert typ == shapely.GeometryType.MULTIPOLYGON
  429. assert_allclose(coords, expected)
  430. assert len(offsets) == 3
  431. assert offsets[0].dtype == np.int32
  432. assert offsets[1].dtype == np.int32
  433. assert offsets[2].dtype == np.int32
  434. assert_allclose(offsets[0], expected_offsets1)
  435. assert_allclose(offsets[1], expected_offsets2)
  436. assert_allclose(offsets[2], expected_offsets3)
  437. result = shapely.from_ragged_array(typ, coords, offsets)
  438. # in a roundtrip, missing geometries come back as empty
  439. arr[-2] = shapely.from_wkt("MULTIPOLYGON EMPTY")
  440. assert_geometries_equal(result, arr)
  441. # sliced:
  442. offsets_sliced = (offsets[0], offsets[1], offsets[2][1:])
  443. result = shapely.from_ragged_array(typ, coords, offsets_sliced)
  444. assert_geometries_equal(result, arr[1:])
  445. offsets_sliced = (offsets[0], offsets[1], offsets[2][:-3])
  446. result = shapely.from_ragged_array(typ, coords, offsets_sliced)
  447. assert_geometries_equal(result, arr[:-3])
  448. print(result)
  449. def test_mixture_point_multipoint():
  450. typ, coords, offsets = shapely.to_ragged_array([point, multi_point])
  451. assert typ == shapely.GeometryType.MULTIPOINT
  452. result = shapely.from_ragged_array(typ, coords, offsets)
  453. expected = np.array([MultiPoint([point]), multi_point])
  454. assert_geometries_equal(result, expected)
  455. def test_mixture_linestring_multilinestring():
  456. typ, coords, offsets = shapely.to_ragged_array([line_string, multi_line_string])
  457. assert typ == shapely.GeometryType.MULTILINESTRING
  458. result = shapely.from_ragged_array(typ, coords, offsets)
  459. expected = np.array([MultiLineString([line_string]), multi_line_string])
  460. assert_geometries_equal(result, expected)
  461. def test_mixture_polygon_multipolygon():
  462. typ, coords, offsets = shapely.to_ragged_array([polygon, multi_polygon])
  463. assert typ == shapely.GeometryType.MULTIPOLYGON
  464. result = shapely.from_ragged_array(typ, coords, offsets)
  465. expected = np.array([MultiPolygon([polygon]), multi_polygon])
  466. assert_geometries_equal(result, expected)
  467. def test_from_ragged_incorrect_rings_short():
  468. # too few coordinates for linearring
  469. coords = np.array([[0, 0], [1, 1]], dtype="float64")
  470. offsets1 = np.array([0, 2])
  471. offsets2 = np.array([0, 1])
  472. offsets3 = np.array([0, 1])
  473. with pytest.raises(
  474. ValueError, match="A linearring requires at least 4 coordinates"
  475. ):
  476. shapely.from_ragged_array(
  477. shapely.GeometryType.MULTIPOLYGON, coords, (offsets1, offsets2, offsets3)
  478. )
  479. with pytest.raises(
  480. ValueError, match="A linearring requires at least 4 coordinates"
  481. ):
  482. shapely.from_ragged_array(
  483. shapely.GeometryType.POLYGON, coords, (offsets1, offsets2)
  484. )
  485. def test_from_ragged_incorrect_rings_unclosed():
  486. # NaNs cause the ring to be unclosed
  487. coords = np.full((4, 2), np.nan)
  488. offsets1 = np.array([0, 4])
  489. offsets2 = np.array([0, 1])
  490. offsets3 = np.array([0, 1])
  491. with pytest.raises(
  492. shapely.GEOSException,
  493. match="Points of LinearRing do not form a closed linestring",
  494. ):
  495. shapely.from_ragged_array(
  496. shapely.GeometryType.MULTIPOLYGON, coords, (offsets1, offsets2, offsets3)
  497. )
  498. with pytest.raises(
  499. shapely.GEOSException,
  500. match="Points of LinearRing do not form a closed linestring",
  501. ):
  502. shapely.from_ragged_array(
  503. shapely.GeometryType.POLYGON, coords, (offsets1, offsets2)
  504. )
  505. def test_from_ragged_wrong_offsets():
  506. with pytest.raises(ValueError, match="'offsets' must be provided"):
  507. shapely.from_ragged_array(
  508. shapely.GeometryType.LINESTRING, np.array([[0, 0], [0, 1]])
  509. )
  510. with pytest.raises(ValueError, match="'offsets' should not be provided"):
  511. shapely.from_ragged_array(
  512. shapely.GeometryType.POINT,
  513. np.array([[0, 0], [0, 1]]),
  514. offsets=(np.array([0, 1]),),
  515. )
  516. def test_from_ragged_crash_2284():
  517. # caused segfault in shapely 2.1.0
  518. # https://github.com/shapely/shapely/discussions/2284
  519. # one of the geometries has more rings than the total number of geometries
  520. coords = np.random.default_rng().random(120).reshape((60, 2))
  521. offsets1 = np.array([0, 10, 20, 30, 40, 50, 60])
  522. offsets2 = np.array([0, 1, 5, 6])
  523. for _ in range(10):
  524. polygons = shapely.from_ragged_array(
  525. shapely.GeometryType.POLYGON, coords, (offsets1, offsets2)
  526. )
  527. # just ensure it didn't crash
  528. assert len(polygons) == 3
  529. offsets3 = np.array([0, 3])
  530. for _ in range(10):
  531. polygons = shapely.from_ragged_array(
  532. shapely.GeometryType.MULTIPOLYGON, coords, (offsets1, offsets2, offsets3)
  533. )
  534. # just ensure it didn't crash
  535. assert len(polygons) == 1
  536. def test_from_ragged_wrong_offsets_values():
  537. # caused segfault in shapely 2.1.0
  538. # outer offsets indicates more rings than the shape of the ring offsets
  539. coords = np.random.default_rng().random(70).reshape((35, 2))
  540. offsets1 = np.array([0, 10, 20], dtype=np.uint32)
  541. offsets2 = np.array([0, 1, 5], dtype=np.uint32)
  542. offsets3 = np.array([0, 2])
  543. with pytest.raises(
  544. ValueError, match="Number of rings indicated by the geometry offsets"
  545. ):
  546. shapely.from_ragged_array(
  547. shapely.GeometryType.POLYGON, coords, (offsets1, offsets2)
  548. )
  549. with pytest.raises(
  550. ValueError, match="Number of rings indicated by the part offsets"
  551. ):
  552. shapely.from_ragged_array(
  553. shapely.GeometryType.MULTIPOLYGON, coords, (offsets1, offsets2, offsets3)
  554. )
  555. # inner offsets indicating more coordinats that the shape of the coordinates
  556. coords = np.random.default_rng().random(70).reshape((35, 2))
  557. offsets1 = np.array([0, 10, 40], dtype=np.uint32)
  558. offsets2 = np.array([0, 1, 2], dtype=np.uint32)
  559. with pytest.raises(
  560. ValueError, match="Number of coordinates indicated by the linear offsets"
  561. ):
  562. shapely.from_ragged_array(
  563. shapely.GeometryType.POLYGON, coords, (offsets1, offsets2)
  564. )
  565. with pytest.raises(
  566. ValueError, match="Number of coordinates indicated by the linear offsets"
  567. ):
  568. shapely.from_ragged_array(
  569. shapely.GeometryType.MULTIPOLYGON, coords, (offsets1, offsets2, offsets3)
  570. )
  571. # outer multipolygon offsets indicating too many parts
  572. offsets3 = np.array([0, 3])
  573. with pytest.raises(
  574. ValueError, match="Number of geometry parts indicated by the geometry offsets"
  575. ):
  576. shapely.from_ragged_array(
  577. shapely.GeometryType.MULTIPOLYGON, coords, (offsets1, offsets2, offsets3)
  578. )