test_creation_indices.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585
  1. import numpy as np
  2. import pytest
  3. from numpy.testing import assert_array_equal
  4. import shapely
  5. from shapely import LinearRing, Polygon
  6. from shapely.testing import assert_geometries_equal
  7. from shapely.tests.common import empty_point, line_string, linear_ring, point, polygon
  8. pnts = shapely.points
  9. lstrs = shapely.linestrings
  10. geom_coll = shapely.geometrycollections
  11. @pytest.mark.parametrize(
  12. "func", [shapely.points, shapely.linestrings, shapely.linearrings]
  13. )
  14. @pytest.mark.parametrize(
  15. "coordinates",
  16. [
  17. np.empty((2,)), # not enough dimensions
  18. np.empty((2, 4, 1)), # too many dimensions
  19. np.empty((2, 4)), # wrong inner dimension size
  20. None,
  21. np.full((2, 2), "foo", dtype=object), # wrong type
  22. ],
  23. )
  24. def test_invalid_coordinates(func, coordinates):
  25. with pytest.raises((TypeError, ValueError)):
  26. func(coordinates, indices=[0, 1])
  27. @pytest.mark.parametrize(
  28. "func",
  29. [
  30. shapely.multipoints,
  31. shapely.multilinestrings,
  32. shapely.multipolygons,
  33. shapely.geometrycollections,
  34. ],
  35. )
  36. @pytest.mark.parametrize(
  37. "geometries", [np.array([1, 2], dtype=np.intp), None, np.array([[point]]), "hello"]
  38. )
  39. def test_invalid_geometries(func, geometries):
  40. with pytest.raises((TypeError, ValueError)):
  41. func(geometries, indices=[0, 1])
  42. @pytest.mark.parametrize(
  43. "func", [shapely.points, shapely.linestrings, shapely.linearrings]
  44. )
  45. @pytest.mark.parametrize("indices", [[point], " hello", [0, 1], [-1]])
  46. def test_invalid_indices_simple(func, indices):
  47. with pytest.raises((TypeError, ValueError)):
  48. func([[0.2, 0.3]], indices=indices)
  49. non_writeable = np.empty(3, dtype=object)
  50. non_writeable.flags.writeable = False
  51. @pytest.mark.parametrize(
  52. "func", [shapely.points, shapely.linestrings, shapely.geometrycollections]
  53. )
  54. @pytest.mark.parametrize(
  55. "out",
  56. [
  57. [None, None, None], # not an ndarray
  58. np.empty(3), # wrong dtype
  59. non_writeable, # not writeable
  60. np.empty((3, 2), dtype=object), # too many dimensions
  61. np.empty((), dtype=object), # too few dimensions
  62. np.empty((2,), dtype=object), # too small
  63. ],
  64. )
  65. def test_invalid_out(func, out):
  66. if func is shapely.points:
  67. x = [[0.2, 0.3], [0.4, 0.5]]
  68. indices = [0, 2]
  69. elif func is shapely.linestrings:
  70. x = [[1, 1], [2, 1], [2, 2], [3, 3], [3, 4], [4, 4]]
  71. indices = [0, 0, 0, 2, 2, 2]
  72. else:
  73. x = [point, line_string]
  74. indices = [0, 2]
  75. with pytest.raises((TypeError, ValueError)):
  76. func(x, indices=indices, out=out)
  77. def test_points_invalid():
  78. # attempt to construct a point with 2 coordinates
  79. with pytest.raises(shapely.GEOSException):
  80. shapely.points([[1, 1], [2, 2]], indices=[0, 0])
  81. def test_points():
  82. actual = shapely.points(
  83. np.array([[2, 3], [2, 3]], dtype=float),
  84. indices=np.array([0, 1], dtype=np.intp),
  85. )
  86. assert_geometries_equal(actual, [point, point])
  87. def test_points_no_index_raises():
  88. with pytest.raises(ValueError):
  89. shapely.points(
  90. np.array([[2, 3], [2, 3]], dtype=float),
  91. indices=np.array([0, 2], dtype=np.intp),
  92. )
  93. @pytest.mark.parametrize(
  94. "indices,expected",
  95. [
  96. ([0, 1], [point, point, empty_point, None]),
  97. ([0, 3], [point, None, empty_point, point]),
  98. ([2, 3], [None, None, point, point]),
  99. ],
  100. )
  101. def test_points_out(indices, expected):
  102. out = np.empty(4, dtype=object)
  103. out[2] = empty_point
  104. actual = shapely.points(
  105. [[2, 3], [2, 3]],
  106. indices=indices,
  107. out=out,
  108. )
  109. assert_geometries_equal(out, expected)
  110. assert actual is out
  111. @pytest.mark.parametrize(
  112. "coordinates,indices,expected",
  113. [
  114. ([[1, 1], [2, 2]], [0, 0], [lstrs([[1, 1], [2, 2]])]),
  115. ([[1, 1, 1], [2, 2, 2]], [0, 0], [lstrs([[1, 1, 1], [2, 2, 2]])]),
  116. (
  117. [[1, 1], [2, 2], [2, 2], [3, 3]],
  118. [0, 0, 1, 1],
  119. [lstrs([[1, 1], [2, 2]]), lstrs([[2, 2], [3, 3]])],
  120. ),
  121. ],
  122. )
  123. def test_linestrings(coordinates, indices, expected):
  124. actual = shapely.linestrings(
  125. np.array(coordinates, dtype=float), indices=np.array(indices, dtype=np.intp)
  126. )
  127. assert_geometries_equal(actual, expected)
  128. def test_linestrings_invalid():
  129. # attempt to construct linestrings with 1 coordinate
  130. with pytest.raises(shapely.GEOSException):
  131. shapely.linestrings([[1, 1], [2, 2]], indices=[0, 1])
  132. @pytest.mark.parametrize(
  133. "indices,expected",
  134. [
  135. ([0, 0, 0, 1, 1, 1], [line_string, line_string, empty_point, None]),
  136. ([0, 0, 0, 3, 3, 3], [line_string, None, empty_point, line_string]),
  137. ([2, 2, 2, 3, 3, 3], [None, None, line_string, line_string]),
  138. ],
  139. )
  140. def test_linestrings_out(indices, expected):
  141. out = np.empty(4, dtype=object)
  142. out[2] = empty_point
  143. actual = shapely.linestrings(
  144. [(0, 0), (1, 0), (1, 1), (0, 0), (1, 0), (1, 1)],
  145. indices=indices,
  146. out=out,
  147. )
  148. assert_geometries_equal(out, expected)
  149. assert actual is out
  150. @pytest.mark.parametrize(
  151. "coordinates",
  152. [
  153. [[1, 1], [1, float("nan")], [2, 2]],
  154. ],
  155. )
  156. def test_linestrings_allow_nan(coordinates):
  157. actual = shapely.linestrings(
  158. np.array(coordinates, dtype=float),
  159. indices=np.zeros(len(coordinates), dtype=np.intp),
  160. )
  161. assert_array_equal(shapely.get_coordinates(actual), coordinates)
  162. @pytest.mark.parametrize(
  163. "coordinates,indices,expected",
  164. [
  165. ([[1, 1], [1, float("nan")], [2, 2]], [0, 0, 0], [lstrs([[1, 1], [2, 2]])]),
  166. ],
  167. )
  168. def test_linestrings_handle_nan_skip(coordinates, indices, expected):
  169. actual = shapely.linestrings(
  170. np.array(coordinates, dtype=float),
  171. indices=np.array(indices, dtype=np.intp),
  172. handle_nan="skip",
  173. )
  174. assert_geometries_equal(actual, expected)
  175. def test_linestrings_handle_nan_skip_invalid():
  176. # the NaN makes the linestring too short
  177. with pytest.raises(shapely.GEOSException):
  178. shapely.linestrings(
  179. [[1, 1], [2, float("nan")]], indices=[0, 0], handle_nan="skip"
  180. )
  181. def test_linestrings_handle_nan_skip_only_nan():
  182. actual = shapely.linestrings(
  183. np.full((3, 2), fill_value=np.nan), indices=[0, 0, 0], handle_nan="skip"
  184. )
  185. assert actual[0].is_empty
  186. def test_linestrings_handle_nan_error():
  187. with pytest.raises(ValueError, match=".*NaN.*"):
  188. shapely.linestrings(
  189. [[0, 0], [float("nan"), 0], [1, 1]], indices=[0, 0, 0], handle_nan="error"
  190. )
  191. @pytest.mark.parametrize(
  192. "coordinates", [([[1, 1], [2, 1], [2, 2], [1, 1]]), ([[1, 1], [2, 1], [2, 2]])]
  193. )
  194. def test_linearrings(coordinates):
  195. actual = shapely.linearrings(
  196. np.array(coordinates, dtype=np.float64),
  197. indices=np.zeros(len(coordinates), dtype=np.intp),
  198. )
  199. assert_geometries_equal(actual, shapely.linearrings(coordinates))
  200. @pytest.mark.parametrize(
  201. "coordinates",
  202. [
  203. ([[1, 1], [1, 1]]), # too short
  204. ([[1, np.nan], [2, 1], [2, 2], [1, 1]]), # starting with nan
  205. ],
  206. )
  207. def test_linearrings_invalid(coordinates):
  208. with pytest.raises((shapely.GEOSException, ValueError)):
  209. shapely.linearrings(coordinates, indices=np.zeros(len(coordinates)))
  210. def test_linearrings_unclosed_all_coords_equal():
  211. actual = shapely.linearrings([(0, 0), (0, 0), (0, 0)], indices=np.zeros(3))
  212. assert_geometries_equal(actual, LinearRing([(0, 0), (0, 0), (0, 0), (0, 0)]))
  213. @pytest.mark.parametrize(
  214. "indices,expected",
  215. [
  216. ([0, 0, 0, 0, 0], [linear_ring, None, None, empty_point]),
  217. ([1, 1, 1, 1, 1], [None, linear_ring, None, empty_point]),
  218. ([3, 3, 3, 3, 3], [None, None, None, linear_ring]),
  219. ],
  220. )
  221. def test_linearrings_out(indices, expected):
  222. out = np.empty(4, dtype=object)
  223. out[3] = empty_point
  224. actual = shapely.linearrings(
  225. [(0, 0), (1, 0), (1, 1), (0, 1), (0, 0)],
  226. indices=indices,
  227. out=out,
  228. )
  229. assert_geometries_equal(out, expected)
  230. assert actual is out
  231. @pytest.mark.parametrize("dim", [2, 3])
  232. @pytest.mark.parametrize("order", ["C", "F"])
  233. def test_linearrings_buffer(dim, order):
  234. coords = np.random.randn(10, 4, dim)
  235. coords1 = np.asarray(coords.reshape(10 * 4, dim), order=order)
  236. indices1 = np.repeat(range(10), 4)
  237. result1 = shapely.linearrings(coords1, indices=indices1)
  238. # with manual closure -> can directly copy from buffer if C order
  239. coords2 = np.hstack((coords, coords[:, [0], :]))
  240. coords2 = np.asarray(coords2.reshape(10 * 5, dim), order=order)
  241. indices2 = np.repeat(range(10), 5)
  242. result2 = shapely.linearrings(coords2, indices=indices2)
  243. assert_geometries_equal(result1, result2)
  244. @pytest.mark.parametrize(
  245. "coordinates",
  246. [
  247. [[1, 1], [2, 1], [2, float("nan")], [1, 1]],
  248. ],
  249. )
  250. def test_linearrings_allow_nan(coordinates):
  251. actual = shapely.linearrings(
  252. np.array(coordinates, dtype=float),
  253. indices=np.zeros(len(coordinates), dtype=np.intp),
  254. )
  255. assert_array_equal(shapely.get_coordinates(actual), coordinates)
  256. @pytest.mark.parametrize(
  257. "coordinates",
  258. [
  259. [[1, 1], [2, 1], [2, 2], [2, float("nan")], [1, 1]],
  260. [[1, 1], [2, 1], [2, float("nan")], [2, 2]],
  261. [[1, 1], [2, 1], [2, 2], [1, 1], [2, float("nan")]],
  262. [[2, float("nan")], [1, 1], [2, 1], [2, 2], [1, 1]],
  263. [[1, 1], [2, 1], [2, 2], [2, float("nan")]],
  264. [[2, float("nan")], [1, 1], [2, 1], [2, 2]],
  265. ],
  266. )
  267. def test_linearrings_handle_nan_skip(coordinates):
  268. actual = shapely.linearrings(
  269. np.array(coordinates, dtype=np.float64),
  270. indices=np.zeros(len(coordinates), dtype=np.intp),
  271. handle_nan="skip",
  272. )
  273. assert_geometries_equal(actual, shapely.linearrings(coordinates, handle_nan="skip"))
  274. def test_linearrings_handle_nan_skip_invalid():
  275. # the NaN makes the linearring too short
  276. with pytest.raises(ValueError):
  277. shapely.linearrings(
  278. [[1, 1], [float("nan"), 1], [1, 1]], indices=[0, 0, 0], handle_nan="skip"
  279. )
  280. def test_linearrings_handle_nan_skip_only_nan():
  281. actual = shapely.linearrings(
  282. np.full((5, 2), fill_value=np.nan), indices=[0] * 5, handle_nan="skip"
  283. )
  284. assert actual[0].is_empty
  285. def test_linearrings_handle_nan_error():
  286. with pytest.raises(ValueError, match=".*NaN.*"):
  287. shapely.linearrings(
  288. [[1, 1], [2, 1], [2, 2], [2, float("nan")], [1, 1]],
  289. indices=[0, 0, 0, 0, 0],
  290. handle_nan="error",
  291. )
  292. hole_1 = shapely.linearrings([(0.2, 0.2), (0.2, 0.4), (0.4, 0.4)])
  293. hole_2 = shapely.linearrings([(0.6, 0.6), (0.6, 0.8), (0.8, 0.8)])
  294. poly = shapely.polygons(linear_ring)
  295. poly_empty = Polygon()
  296. poly_hole_1 = shapely.polygons(linear_ring, holes=[hole_1])
  297. poly_hole_2 = shapely.polygons(linear_ring, holes=[hole_2])
  298. poly_hole_1_2 = shapely.polygons(linear_ring, holes=[hole_1, hole_2])
  299. @pytest.mark.parametrize(
  300. "rings,indices,expected",
  301. [
  302. ([linear_ring, linear_ring], [0, 1], [poly, poly]),
  303. ([None, linear_ring], [0, 1], [poly_empty, poly]),
  304. ([None, linear_ring, None, None], [0, 0, 1, 1], [poly, poly_empty]),
  305. ([linear_ring, hole_1, linear_ring], [0, 0, 1], [poly_hole_1, poly]),
  306. ([linear_ring, linear_ring, hole_1], [0, 1, 1], [poly, poly_hole_1]),
  307. ([None, linear_ring, linear_ring, hole_1], [0, 0, 1, 1], [poly, poly_hole_1]),
  308. ([linear_ring, None, linear_ring, hole_1], [0, 0, 1, 1], [poly, poly_hole_1]),
  309. ([linear_ring, None, linear_ring, hole_1], [0, 1, 1, 1], [poly, poly_hole_1]),
  310. ([linear_ring, linear_ring, None, hole_1], [0, 1, 1, 1], [poly, poly_hole_1]),
  311. ([linear_ring, linear_ring, hole_1, None], [0, 1, 1, 1], [poly, poly_hole_1]),
  312. (
  313. [linear_ring, hole_1, hole_2, linear_ring],
  314. [0, 0, 0, 1],
  315. [poly_hole_1_2, poly],
  316. ),
  317. (
  318. [linear_ring, hole_1, linear_ring, hole_2],
  319. [0, 0, 1, 1],
  320. [poly_hole_1, poly_hole_2],
  321. ),
  322. (
  323. [linear_ring, linear_ring, hole_1, hole_2],
  324. [0, 1, 1, 1],
  325. [poly, poly_hole_1_2],
  326. ),
  327. (
  328. [linear_ring, hole_1, None, hole_2, linear_ring],
  329. [0, 0, 0, 0, 1],
  330. [poly_hole_1_2, poly],
  331. ),
  332. (
  333. [linear_ring, hole_1, None, linear_ring, hole_2],
  334. [0, 0, 0, 1, 1],
  335. [poly_hole_1, poly_hole_2],
  336. ),
  337. (
  338. [linear_ring, hole_1, linear_ring, None, hole_2],
  339. [0, 0, 1, 1, 1],
  340. [poly_hole_1, poly_hole_2],
  341. ),
  342. ],
  343. )
  344. def test_polygons(rings, indices, expected):
  345. actual = shapely.polygons(
  346. np.array(rings, dtype=object), indices=np.array(indices, dtype=np.intp)
  347. )
  348. assert_geometries_equal(actual, expected)
  349. @pytest.mark.parametrize(
  350. "indices,expected",
  351. [
  352. ([0, 1], [poly, poly, empty_point, None]),
  353. ([0, 3], [poly, None, empty_point, poly]),
  354. ([2, 3], [None, None, poly, poly]),
  355. ],
  356. )
  357. def test_polygons_out(indices, expected):
  358. out = np.empty(4, dtype=object)
  359. out[2] = empty_point
  360. actual = shapely.polygons([linear_ring, linear_ring], indices=indices, out=out)
  361. assert_geometries_equal(out, expected)
  362. assert actual is out
  363. @pytest.mark.parametrize(
  364. "func",
  365. [
  366. shapely.polygons,
  367. shapely.multipoints,
  368. shapely.multilinestrings,
  369. shapely.multipolygons,
  370. shapely.geometrycollections,
  371. ],
  372. )
  373. @pytest.mark.parametrize("indices", [np.array([point]), " hello", [0, 1], [-1]])
  374. def test_invalid_indices_collections(func, indices):
  375. with pytest.raises((TypeError, ValueError)):
  376. func([point], indices=indices)
  377. @pytest.mark.parametrize(
  378. "geometries,indices,expected",
  379. [
  380. ([point, line_string], [0, 0], [geom_coll([point, line_string])]),
  381. ([point, line_string], [0, 1], [geom_coll([point]), geom_coll([line_string])]),
  382. ([point, None], [0, 0], [geom_coll([point])]),
  383. ([point, None], [0, 1], [geom_coll([point]), geom_coll([])]),
  384. ([None, point, None, None], [0, 0, 1, 1], [geom_coll([point]), geom_coll([])]),
  385. ([point, None, line_string], [0, 0, 0], [geom_coll([point, line_string])]),
  386. ],
  387. )
  388. def test_geometrycollections(geometries, indices, expected):
  389. actual = shapely.geometrycollections(
  390. np.array(geometries, dtype=object), indices=indices
  391. )
  392. assert_geometries_equal(actual, expected)
  393. def test_geometrycollections_no_index_raises():
  394. with pytest.raises(ValueError):
  395. shapely.geometrycollections(
  396. np.array([point, line_string], dtype=object), indices=[0, 2]
  397. )
  398. @pytest.mark.parametrize(
  399. "indices,expected",
  400. [
  401. ([0, 0], [geom_coll([point, line_string]), None, None, empty_point]),
  402. ([3, 3], [None, None, None, geom_coll([point, line_string])]),
  403. ],
  404. )
  405. def test_geometrycollections_out(indices, expected):
  406. out = np.empty(4, dtype=object)
  407. out[3] = empty_point
  408. actual = shapely.geometrycollections([point, line_string], indices=indices, out=out)
  409. assert_geometries_equal(out, expected)
  410. assert actual is out
  411. def test_multipoints():
  412. actual = shapely.multipoints(
  413. np.array([point], dtype=object), indices=np.zeros(1, dtype=np.intp)
  414. )
  415. assert_geometries_equal(actual, shapely.multipoints([point]))
  416. def test_multilinestrings():
  417. actual = shapely.multilinestrings(
  418. np.array([line_string], dtype=object), indices=np.zeros(1, dtype=np.intp)
  419. )
  420. assert_geometries_equal(actual, shapely.multilinestrings([line_string]))
  421. def test_multilinearrings():
  422. actual = shapely.multilinestrings(
  423. np.array([linear_ring], dtype=object), indices=np.zeros(1, dtype=np.intp)
  424. )
  425. assert_geometries_equal(actual, shapely.multilinestrings([linear_ring]))
  426. def test_multipolygons():
  427. actual = shapely.multipolygons(
  428. np.array([polygon], dtype=object), indices=np.zeros(1, dtype=np.intp)
  429. )
  430. assert_geometries_equal(actual, shapely.multipolygons([polygon]))
  431. @pytest.mark.parametrize(
  432. "geometries,func",
  433. [
  434. ([point], shapely.polygons),
  435. ([line_string], shapely.polygons),
  436. ([polygon], shapely.polygons),
  437. ([line_string], shapely.multipoints),
  438. ([polygon], shapely.multipoints),
  439. ([point], shapely.multilinestrings),
  440. ([polygon], shapely.multilinestrings),
  441. ([point], shapely.multipolygons),
  442. ([line_string], shapely.multipolygons),
  443. ],
  444. )
  445. def test_incompatible_types(geometries, func):
  446. with pytest.raises(TypeError):
  447. func(geometries, indices=[0])
  448. def test_points_deprecate_positional():
  449. with pytest.deprecated_call(
  450. match="positional argument `indices` for `points` is deprecated"
  451. ):
  452. shapely.points([[0, 1], [2, 3]], None, None, [0, 1])
  453. def test_linestrings_deprecate_positional():
  454. with pytest.deprecated_call(
  455. match="positional argument `indices` for `linestrings` is deprecated"
  456. ):
  457. shapely.linestrings([[0, 1], [2, 3], [4, 5], [6, 7]], None, None, [0, 0, 1, 1])
  458. def test_linearrings_deprecate_positional():
  459. with pytest.deprecated_call(
  460. match="positional argument `indices` for `linearrings` is deprecated"
  461. ):
  462. shapely.linearrings([[0, 1], [1, 1], [1, 0]], None, None, [0, 0, 0])
  463. def test_polygons_deprecate_positional():
  464. with pytest.deprecated_call(
  465. match="positional argument `indices` for `polygons` is deprecated"
  466. ):
  467. shapely.polygons([linear_ring, linear_ring], None, [0, 1])
  468. def test_multipoints_deprecate_positional():
  469. with pytest.deprecated_call(
  470. match="positional argument `indices` for `multipoints` is deprecated"
  471. ):
  472. shapely.multipoints([point, point], [0, 1])
  473. def test_multilinestrings_deprecate_positional():
  474. with pytest.deprecated_call(
  475. match="positional argument `indices` for `multilinestrings` is deprecated"
  476. ):
  477. shapely.multilinestrings([line_string, line_string], [0, 1])
  478. def test_multipolygons_deprecate_positional():
  479. with pytest.deprecated_call(
  480. match="positional argument `indices` for `multipolygons` is deprecated"
  481. ):
  482. shapely.multipolygons([polygon, polygon], [0, 1])
  483. def test_geometrycollections_deprecate_positional():
  484. with pytest.deprecated_call(
  485. match="positional argument `indices` for `geometrycollections` is deprecated"
  486. ):
  487. shapely.geometrycollections([point, polygon], [0, 1])