shapely 2.0.4__tar.gz → 2.0.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. {shapely-2.0.4 → shapely-2.0.6}/CHANGES.txt +22 -0
  2. {shapely-2.0.4 → shapely-2.0.6}/CITATION.cff +2 -2
  3. {shapely-2.0.4 → shapely-2.0.6}/PKG-INFO +1 -1
  4. {shapely-2.0.4 → shapely-2.0.6}/docs/geometry.rst +35 -0
  5. {shapely-2.0.4 → shapely-2.0.6}/docs/installation.rst +2 -2
  6. {shapely-2.0.4 → shapely-2.0.6}/docs/migration_pygeos.rst +2 -2
  7. {shapely-2.0.4 → shapely-2.0.6}/pyproject.toml +1 -1
  8. {shapely-2.0.4 → shapely-2.0.6}/setup.py +11 -13
  9. {shapely-2.0.4 → shapely-2.0.6}/shapely/_geometry.py +32 -23
  10. {shapely-2.0.4 → shapely-2.0.6}/shapely/_geometry_helpers.pyx +1 -1
  11. {shapely-2.0.4 → shapely-2.0.6}/shapely/_version.py +3 -3
  12. {shapely-2.0.4 → shapely-2.0.6}/shapely/affinity.py +23 -7
  13. {shapely-2.0.4 → shapely-2.0.6}/shapely/constructive.py +13 -12
  14. {shapely-2.0.4 → shapely-2.0.6}/shapely/coords.py +7 -2
  15. {shapely-2.0.4 → shapely-2.0.6}/shapely/creation.py +5 -5
  16. {shapely-2.0.4 → shapely-2.0.6}/shapely/geometry/point.py +2 -2
  17. {shapely-2.0.4 → shapely-2.0.6}/shapely/io.py +2 -2
  18. {shapely-2.0.4 → shapely-2.0.6}/shapely/plotting.py +5 -5
  19. {shapely-2.0.4 → shapely-2.0.6}/shapely/predicates.py +2 -2
  20. {shapely-2.0.4 → shapely-2.0.6}/shapely/set_operations.py +6 -2
  21. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/geometry/test_coords.py +16 -0
  22. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/geometry/test_format.py +1 -1
  23. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/geometry/test_point.py +3 -0
  24. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_operations.py +6 -2
  25. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_predicates.py +10 -2
  26. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/test_geometry.py +1 -1
  27. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/test_linear.py +1 -1
  28. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/test_predicates.py +1 -1
  29. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/test_set_operations.py +4 -4
  30. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/test_strtree.py +20 -8
  31. {shapely-2.0.4 → shapely-2.0.6}/shapely.egg-info/PKG-INFO +1 -1
  32. {shapely-2.0.4 → shapely-2.0.6}/src/coords.c +2 -2
  33. {shapely-2.0.4 → shapely-2.0.6}/src/geos.c +1 -1
  34. {shapely-2.0.4 → shapely-2.0.6}/src/strtree.c +2 -2
  35. {shapely-2.0.4 → shapely-2.0.6}/src/ufuncs.c +3 -3
  36. {shapely-2.0.4 → shapely-2.0.6}/CREDITS.txt +0 -0
  37. {shapely-2.0.4 → shapely-2.0.6}/LICENSE.txt +0 -0
  38. {shapely-2.0.4 → shapely-2.0.6}/README.rst +0 -0
  39. {shapely-2.0.4 → shapely-2.0.6}/docs/constructive.rst +0 -0
  40. {shapely-2.0.4 → shapely-2.0.6}/docs/coordinates.rst +0 -0
  41. {shapely-2.0.4 → shapely-2.0.6}/docs/creation.rst +0 -0
  42. {shapely-2.0.4 → shapely-2.0.6}/docs/design.rst +0 -0
  43. {shapely-2.0.4 → shapely-2.0.6}/docs/index.rst +0 -0
  44. {shapely-2.0.4 → shapely-2.0.6}/docs/io.rst +0 -0
  45. {shapely-2.0.4 → shapely-2.0.6}/docs/linear.rst +0 -0
  46. {shapely-2.0.4 → shapely-2.0.6}/docs/manual.rst +0 -0
  47. {shapely-2.0.4 → shapely-2.0.6}/docs/measurement.rst +0 -0
  48. {shapely-2.0.4 → shapely-2.0.6}/docs/migration.rst +0 -0
  49. {shapely-2.0.4 → shapely-2.0.6}/docs/predicates.rst +0 -0
  50. {shapely-2.0.4 → shapely-2.0.6}/docs/properties.rst +0 -0
  51. {shapely-2.0.4 → shapely-2.0.6}/docs/release.rst +0 -0
  52. {shapely-2.0.4 → shapely-2.0.6}/docs/set_operations.rst +0 -0
  53. {shapely-2.0.4 → shapely-2.0.6}/docs/strtree.rst +0 -0
  54. {shapely-2.0.4 → shapely-2.0.6}/docs/testing.rst +0 -0
  55. {shapely-2.0.4 → shapely-2.0.6}/setup.cfg +0 -0
  56. {shapely-2.0.4 → shapely-2.0.6}/shapely/__init__.py +0 -0
  57. {shapely-2.0.4 → shapely-2.0.6}/shapely/_enum.py +0 -0
  58. {shapely-2.0.4 → shapely-2.0.6}/shapely/_geos.pxd +0 -0
  59. {shapely-2.0.4 → shapely-2.0.6}/shapely/_geos.pyx +0 -0
  60. {shapely-2.0.4 → shapely-2.0.6}/shapely/_pygeos_api.pxd +0 -0
  61. {shapely-2.0.4 → shapely-2.0.6}/shapely/_ragged_array.py +0 -0
  62. {shapely-2.0.4 → shapely-2.0.6}/shapely/algorithms/__init__.py +0 -0
  63. {shapely-2.0.4 → shapely-2.0.6}/shapely/algorithms/_oriented_envelope.py +0 -0
  64. {shapely-2.0.4 → shapely-2.0.6}/shapely/algorithms/cga.py +0 -0
  65. {shapely-2.0.4 → shapely-2.0.6}/shapely/algorithms/polylabel.py +0 -0
  66. {shapely-2.0.4 → shapely-2.0.6}/shapely/coordinates.py +0 -0
  67. {shapely-2.0.4 → shapely-2.0.6}/shapely/decorators.py +0 -0
  68. {shapely-2.0.4 → shapely-2.0.6}/shapely/errors.py +0 -0
  69. {shapely-2.0.4 → shapely-2.0.6}/shapely/geometry/__init__.py +0 -0
  70. {shapely-2.0.4 → shapely-2.0.6}/shapely/geometry/base.py +0 -0
  71. {shapely-2.0.4 → shapely-2.0.6}/shapely/geometry/collection.py +0 -0
  72. {shapely-2.0.4 → shapely-2.0.6}/shapely/geometry/conftest.py +0 -0
  73. {shapely-2.0.4 → shapely-2.0.6}/shapely/geometry/geo.py +0 -0
  74. {shapely-2.0.4 → shapely-2.0.6}/shapely/geometry/linestring.py +0 -0
  75. {shapely-2.0.4 → shapely-2.0.6}/shapely/geometry/multilinestring.py +0 -0
  76. {shapely-2.0.4 → shapely-2.0.6}/shapely/geometry/multipoint.py +0 -0
  77. {shapely-2.0.4 → shapely-2.0.6}/shapely/geometry/multipolygon.py +0 -0
  78. {shapely-2.0.4 → shapely-2.0.6}/shapely/geometry/polygon.py +0 -0
  79. {shapely-2.0.4 → shapely-2.0.6}/shapely/geos.py +0 -0
  80. {shapely-2.0.4 → shapely-2.0.6}/shapely/linear.py +0 -0
  81. {shapely-2.0.4 → shapely-2.0.6}/shapely/measurement.py +0 -0
  82. {shapely-2.0.4 → shapely-2.0.6}/shapely/ops.py +0 -0
  83. {shapely-2.0.4 → shapely-2.0.6}/shapely/prepared.py +0 -0
  84. {shapely-2.0.4 → shapely-2.0.6}/shapely/speedups.py +0 -0
  85. {shapely-2.0.4 → shapely-2.0.6}/shapely/strtree.py +0 -0
  86. {shapely-2.0.4 → shapely-2.0.6}/shapely/testing.py +0 -0
  87. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/__init__.py +0 -0
  88. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/common.py +0 -0
  89. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/geometry/__init__.py +0 -0
  90. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/geometry/test_collection.py +0 -0
  91. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/geometry/test_decimal.py +0 -0
  92. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/geometry/test_emptiness.py +0 -0
  93. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/geometry/test_equality.py +0 -0
  94. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/geometry/test_geometry_base.py +0 -0
  95. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/geometry/test_hash.py +0 -0
  96. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/geometry/test_linestring.py +0 -0
  97. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/geometry/test_multi.py +0 -0
  98. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/geometry/test_multilinestring.py +0 -0
  99. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/geometry/test_multipoint.py +0 -0
  100. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/geometry/test_multipolygon.py +0 -0
  101. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/geometry/test_polygon.py +0 -0
  102. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/__init__.py +0 -0
  103. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/conftest.py +0 -0
  104. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_affinity.py +0 -0
  105. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_box.py +0 -0
  106. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_buffer.py +0 -0
  107. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_cga.py +0 -0
  108. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_clip_by_rect.py +0 -0
  109. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_create_inconsistent_dimensionality.py +0 -0
  110. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_delaunay.py +0 -0
  111. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_empty_polygons.py +0 -0
  112. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_equality.py +0 -0
  113. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_geointerface.py +0 -0
  114. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_invalid_geometries.py +0 -0
  115. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_linear_referencing.py +0 -0
  116. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_linemerge.py +0 -0
  117. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_locale.py +0 -0
  118. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_make_valid.py +0 -0
  119. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_mapping.py +0 -0
  120. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_minimum_clearance.py +0 -0
  121. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_ndarrays.py +0 -0
  122. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_nearest.py +0 -0
  123. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_operators.py +0 -0
  124. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_orient.py +0 -0
  125. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_parallel_offset.py +0 -0
  126. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_persist.py +0 -0
  127. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_pickle.py +0 -0
  128. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_polygonize.py +0 -0
  129. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_polylabel.py +0 -0
  130. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_prepared.py +0 -0
  131. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_products_z.py +0 -0
  132. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_shape.py +0 -0
  133. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_shared_paths.py +0 -0
  134. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_singularity.py +0 -0
  135. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_snap.py +0 -0
  136. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_split.py +0 -0
  137. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_substring.py +0 -0
  138. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_svg.py +0 -0
  139. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_transform.py +0 -0
  140. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_union.py +0 -0
  141. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_validation.py +0 -0
  142. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_vectorized.py +0 -0
  143. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_voronoi_diagram.py +0 -0
  144. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_wkb.py +0 -0
  145. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/test_wkt.py +0 -0
  146. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/legacy/threading_test.py +0 -0
  147. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/test_constructive.py +0 -0
  148. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/test_coordinates.py +0 -0
  149. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/test_creation.py +0 -0
  150. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/test_creation_indices.py +0 -0
  151. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/test_io.py +0 -0
  152. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/test_measurement.py +0 -0
  153. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/test_misc.py +0 -0
  154. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/test_plotting.py +0 -0
  155. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/test_ragged_array.py +0 -0
  156. {shapely-2.0.4 → shapely-2.0.6}/shapely/tests/test_testing.py +0 -0
  157. {shapely-2.0.4 → shapely-2.0.6}/shapely/validation.py +0 -0
  158. {shapely-2.0.4 → shapely-2.0.6}/shapely/vectorized/__init__.py +0 -0
  159. {shapely-2.0.4 → shapely-2.0.6}/shapely/wkb.py +0 -0
  160. {shapely-2.0.4 → shapely-2.0.6}/shapely/wkt.py +0 -0
  161. {shapely-2.0.4 → shapely-2.0.6}/shapely.egg-info/SOURCES.txt +0 -0
  162. {shapely-2.0.4 → shapely-2.0.6}/shapely.egg-info/dependency_links.txt +0 -0
  163. {shapely-2.0.4 → shapely-2.0.6}/shapely.egg-info/requires.txt +0 -0
  164. {shapely-2.0.4 → shapely-2.0.6}/shapely.egg-info/top_level.txt +0 -0
  165. {shapely-2.0.4 → shapely-2.0.6}/src/c_api.c +0 -0
  166. {shapely-2.0.4 → shapely-2.0.6}/src/c_api.h +0 -0
  167. {shapely-2.0.4 → shapely-2.0.6}/src/coords.h +0 -0
  168. {shapely-2.0.4 → shapely-2.0.6}/src/fast_loop_macros.h +0 -0
  169. {shapely-2.0.4 → shapely-2.0.6}/src/geos.h +0 -0
  170. {shapely-2.0.4 → shapely-2.0.6}/src/kvec.h +0 -0
  171. {shapely-2.0.4 → shapely-2.0.6}/src/lib.c +0 -0
  172. {shapely-2.0.4 → shapely-2.0.6}/src/pygeom.c +0 -0
  173. {shapely-2.0.4 → shapely-2.0.6}/src/pygeom.h +0 -0
  174. {shapely-2.0.4 → shapely-2.0.6}/src/strtree.h +0 -0
  175. {shapely-2.0.4 → shapely-2.0.6}/src/ufuncs.h +0 -0
  176. {shapely-2.0.4 → shapely-2.0.6}/src/vector.c +0 -0
  177. {shapely-2.0.4 → shapely-2.0.6}/src/vector.h +0 -0
  178. {shapely-2.0.4 → shapely-2.0.6}/versioneer.py +0 -0
@@ -1,6 +1,28 @@
1
1
  Changes
2
2
  =======
3
3
 
4
+ 2.0.6 (2024-08-19)
5
+ ------------------
6
+
7
+ Bug fixes:
8
+
9
+ - Fix compatibility with NumPy 2.1.0 (#2099).
10
+
11
+ Wheels are available for Python 3.13 (and still include GEOS 3.11.4).
12
+
13
+ 2.0.5 (2024-07-13)
14
+ ------------------
15
+
16
+ Binary wheels on PyPI include GEOS 3.11.4 from 2024-06-05. Furthermore,
17
+ universal2 wheels are removed for macOS since both x86_64 and arm64 wheels are
18
+ provided.
19
+
20
+ Bug fixes:
21
+
22
+ - Fix Point x/y/z attributes to return Python floats (#2074).
23
+ - Fix affinity for Apple silicon with NumPy 2.0 by reverting matmul, and
24
+ use direct matrix multiplication instead (#2085).
25
+
4
26
  2.0.4 (2024-04-16)
5
27
  ------------------
6
28
 
@@ -2,8 +2,8 @@ cff-version: 1.2.0
2
2
  message: "Please cite this software using these metadata."
3
3
  type: software
4
4
  title: Shapely
5
- version: "2.0.4"
6
- date-released: "2024-04-16"
5
+ version: "2.0.6"
6
+ date-released: "2024-08-19"
7
7
  doi: 10.5281/zenodo.5597138
8
8
  abstract: "Manipulation and analysis of geometric objects in the Cartesian plane."
9
9
  repository-artifact: https://pypi.org/project/Shapely
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: shapely
3
- Version: 2.0.4
3
+ Version: 2.0.6
4
4
  Summary: Manipulation and analysis of geometric objects
5
5
  Author: Sean Gillies
6
6
  Maintainer: Shapely contributors
@@ -173,3 +173,38 @@ specified, rounding precision will be disabled showing full precision.
173
173
  Format types ``'x'`` and ``'X'`` show a hex-encoded string representation of
174
174
  WKB or Well-Known Binary, with the case of the output matched the
175
175
  case of the format type character.
176
+
177
+ .. _canonical-form:
178
+
179
+ Canonical form
180
+ --------------
181
+ When operations are applied on geometries the result is returned according to
182
+ some conventions.
183
+
184
+ In most cases, geometries will be returned in "mild" canonical form. There is no
185
+ goal to keep this form stable, so it is expected to change in future versions of
186
+ GEOS:
187
+
188
+ - the coordinates of exterior rings follow a clockwise orientation and interior
189
+ rings have a counter-clockwise orientation. This is the opposite of the OGC
190
+ specifications because the choice was made before this was included in the
191
+ standard.
192
+ - the starting point of rings can be changed in the output, but the exact order
193
+ is undefined and should not be relied upon
194
+ - the order of geometry types in a collection can be changed, but the order is
195
+ undefined
196
+
197
+ When :func:`~shapely.normalize` is used, the "strict" canonical form is applied.
198
+ This type of normalization is meant to be stable, so changes to it will be
199
+ avoided if possible:
200
+
201
+ - the coordinates of exterior rings follow a clockwise orientation and interior
202
+ rings have a counter-clockwise orientation
203
+ - the starting point of rings is lower left
204
+ - elements in collections are ordered by geometry type: by descending dimension
205
+ and multi-types first (MultiPolygon, Polygon, MultiLineString, LineString,
206
+ MultiPoint, Point). Multiple elements from the same type are ordered from
207
+ right to left and from top to bottom.
208
+
209
+ It is important to note that input geometries do not have to follow these
210
+ conventions.
@@ -26,8 +26,8 @@ Shapely is available on the conda-forge channel. Install as follows::
26
26
  $ conda install shapely --channel conda-forge
27
27
 
28
28
 
29
- Installation from source with custom GEOS libary
30
- ------------------------------------------------
29
+ Installation from source with custom GEOS library
30
+ -------------------------------------------------
31
31
 
32
32
  You may want to use a specific GEOS version or a GEOS distribution that is
33
33
  already present on your system (for compatibility with other modules that
@@ -14,7 +14,7 @@ Therefore, everybody using PyGEOS is highly recommended to migrate to Shapely
14
14
 
15
15
  Generally speaking, this should be a smooth experience because all
16
16
  functionality of PyGEOS was added to Shapely. All vectorized functions
17
- availabe in ``pygeos`` have been added to the top-level ``shapely`` module,
17
+ available in ``pygeos`` have been added to the top-level ``shapely`` module,
18
18
  with only minor differences (see below). Migrating from PyGEOS to Shapely 2.0
19
19
  can thus be done by replacing the ``pygeos`` import and module calls::
20
20
 
@@ -87,4 +87,4 @@ Other differences
87
87
  - The behaviour of ``union_all()`` / ``intersection_all()`` / ``symmetric_difference_all``
88
88
  was changed to return an empty GeometryCollection for an empty or all-None
89
89
  sequence as input (instead of returning None).
90
- - The ``radius`` keyword of the ``buffer()`` funtion was renamed to ``distance``.
90
+ - The ``radius`` keyword of the ``buffer()`` function was renamed to ``distance``.
@@ -3,7 +3,7 @@ requires = [
3
3
  "Cython",
4
4
  # Starting with NumPy 1.25, NumPy is (by default) as far back compatible
5
5
  # as oldest-support-numpy was (customizable with a NPY_TARGET_VERSION
6
- # define). For older Python versions (where NumPy 1.25 is not yet avaiable)
6
+ # define). For older Python versions (where NumPy 1.25 is not yet available)
7
7
  # continue using oldest-support-numpy.
8
8
  "oldest-supported-numpy; python_version<'3.9'",
9
9
  "numpy>=1.25; python_version>='3.9'",
@@ -5,8 +5,7 @@ import subprocess
5
5
  import sys
6
6
  from pathlib import Path
7
7
 
8
- from pkg_resources import parse_version
9
- from setuptools import Extension, find_packages, setup
8
+ from setuptools import Extension, setup
10
9
  from setuptools.command.build_ext import build_ext as _build_ext
11
10
 
12
11
  # ensure the current directory is on sys.path so versioneer can be imported
@@ -42,15 +41,13 @@ def get_geos_config(option):
42
41
  """
43
42
  cmd = os.environ.get("GEOS_CONFIG", "geos-config")
44
43
  try:
45
- stdout, stderr = subprocess.Popen(
46
- [cmd, option], stdout=subprocess.PIPE, stderr=subprocess.PIPE
47
- ).communicate()
44
+ proc = subprocess.run([cmd, option], capture_output=True, text=True)
48
45
  except OSError:
49
46
  return
50
- if stderr and not stdout:
51
- log.warning("geos-config %s returned '%s'", option, stderr.decode().strip())
47
+ if proc.stderr and not proc.stdout:
48
+ log.warning("geos-config %s returned '%s'", option, proc.stderr.strip())
52
49
  return
53
- result = stdout.decode().strip()
50
+ result = proc.stdout.strip()
54
51
  log.debug("geos-config %s returned '%s'", option, result)
55
52
  return result
56
53
 
@@ -86,11 +83,12 @@ def get_geos_paths():
86
83
  )
87
84
  return {}
88
85
 
89
- if parse_version(geos_version) < parse_version(MIN_GEOS_VERSION):
86
+ def version_tuple(ver):
87
+ return tuple(int(itm) if itm.isnumeric() else itm for itm in ver.split("."))
88
+
89
+ if version_tuple(geos_version) < version_tuple(MIN_GEOS_VERSION):
90
90
  raise ImportError(
91
- "GEOS version should be >={}, found {}".format(
92
- MIN_GEOS_VERSION, geos_version
93
- )
91
+ f"GEOS version should be >={MIN_GEOS_VERSION}, found {geos_version}"
94
92
  )
95
93
 
96
94
  libraries = []
@@ -133,7 +131,7 @@ class build_ext(_build_ext):
133
131
 
134
132
  import numpy
135
133
 
136
- self.include_dirs.append(numpy.get_include())
134
+ self.include_dirs.insert(0, numpy.get_include())
137
135
 
138
136
 
139
137
  ext_modules = []
@@ -728,20 +728,26 @@ def set_precision(geometry, grid_size, mode="valid_output", **kwargs):
728
728
 
729
729
  By default, geometries use double precision coordinates (grid_size = 0).
730
730
 
731
- Coordinates will be rounded if a precision grid is less precise than the
732
- input geometry. Duplicated vertices will be dropped from lines and
731
+ Coordinates will be rounded if the precision grid specified is less precise
732
+ than the input geometry. Duplicated vertices will be dropped from lines and
733
733
  polygons for grid sizes greater than 0. Line and polygon geometries may
734
734
  collapse to empty geometries if all vertices are closer together than
735
- grid_size. Z values, if present, will not be modified.
736
-
737
- Note: subsequent operations will always be performed in the precision of
738
- the geometry with higher precision (smaller "grid_size"). That same
739
- precision will be attached to the operation outputs.
740
-
741
- Also note: input geometries should be geometrically valid; unexpected
742
- results may occur if input geometries are not.
743
-
744
- Returns None if geometry is None.
735
+ ``grid_size`` or if a polygon becomes significantly narrower than
736
+ ``grid_size``. Spikes or sections in polygons narrower than ``grid_size``
737
+ after rounding the vertices will be removed, which can lead to multipolygons
738
+ or empty geometries. Z values, if present, will not be modified.
739
+
740
+ Notes:
741
+
742
+ * subsequent operations will always be performed in the precision of the
743
+ geometry with higher precision (smaller "grid_size"). That same precision
744
+ will be attached to the operation outputs.
745
+ * input geometries should be geometrically valid; unexpected results may
746
+ occur if input geometries are not.
747
+ * the geometry returned will be in
748
+ :ref:`mild canonical form <canonical-form>`, and the order of vertices can
749
+ change and should not be relied upon.
750
+ * returns None if geometry is None.
745
751
 
746
752
  Parameters
747
753
  ----------
@@ -752,21 +758,24 @@ def set_precision(geometry, grid_size, mode="valid_output", **kwargs):
752
758
  value is more precise than input geometry, the input geometry will
753
759
  not be modified.
754
760
  mode : {'valid_output', 'pointwise', 'keep_collapsed'}, default 'valid_output'
755
- This parameter determines how to handle invalid output geometries. There are three modes:
761
+ This parameter determines the way a precision reduction is applied on
762
+ the geometry. There are three modes:
756
763
 
757
- 1. `'valid_output'` (default): The output is always valid. Collapsed geometry elements
758
- (including both polygons and lines) are removed. Duplicate vertices are removed.
759
- 2. `'pointwise'`: Precision reduction is performed pointwise. Output geometry
760
- may be invalid due to collapse or self-intersection. Duplicate vertices are not
761
- removed. In GEOS this option is called NO_TOPO.
764
+ 1. `'valid_output'` (default): The output is always valid. Collapsed
765
+ geometry elements (including both polygons and lines) are removed.
766
+ Duplicate vertices are removed.
767
+ 2. `'pointwise'`: Precision reduction is performed pointwise. Output
768
+ geometry may be invalid due to collapse or self-intersection.
769
+ Duplicate vertices are not removed. In GEOS this option is called
770
+ NO_TOPO.
762
771
 
763
772
  .. note::
764
773
 
765
- 'pointwise' mode requires at least GEOS 3.10. It is accepted in earlier versions,
766
- but the results may be unexpected.
767
- 3. `'keep_collapsed'`: Like the default mode, except that collapsed linear geometry
768
- elements are preserved. Collapsed polygonal input elements are removed. Duplicate
769
- vertices are removed.
774
+ 'pointwise' mode requires at least GEOS 3.10. It is accepted in
775
+ earlier versions, but the results may be unexpected.
776
+ 3. `'keep_collapsed'`: Like the default mode, except that collapsed
777
+ linear geometry elements are preserved. Collapsed polygonal input
778
+ elements are removed. Duplicate vertices are removed.
770
779
  **kwargs
771
780
  See :ref:`NumPy ufunc docs <ufuncs.kwargs>` for other keyword arguments.
772
781
 
@@ -302,7 +302,7 @@ def collections_1d(object geometries, object indices, int geometry_type = 7, obj
302
302
  cdef int[:] collection_size = np.bincount(indices).astype(np.int32)
303
303
 
304
304
  # A temporary array for the geometries that will be given to CreateCollection.
305
- # Its size equals max(collection_size) to accomodate the largest collection.
305
+ # Its size equals max(collection_size) to accommodate the largest collection.
306
306
  temp_geoms = np.empty(shape=(np.max(collection_size), ), dtype=np.intp)
307
307
  cdef np.intp_t[:] temp_geoms_view = temp_geoms
308
308
 
@@ -8,11 +8,11 @@ import json
8
8
 
9
9
  version_json = '''
10
10
  {
11
- "date": "2024-04-16T23:57:44+0200",
11
+ "date": "2024-08-19T23:29:00+0200",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "3853e1170173125b7e86cbb5cc8dab1c274c3999",
15
- "version": "2.0.4"
14
+ "full-revisionid": "5a4207d2fb74b25d7bb2fb5d04813f68a3a612f4",
15
+ "version": "2.0.6"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -61,15 +61,31 @@ def affine_transform(geom, matrix):
61
61
  ndim = 2
62
62
  else:
63
63
  raise ValueError("'matrix' expects either 6 or 12 coefficients")
64
- if ndim == 2:
65
- A = np.array([[a, b], [d, e]], dtype=float)
66
- off = np.array([xoff, yoff], dtype=float)
67
- else:
68
- A = np.array([[a, b, c], [d, e, f], [g, h, i]], dtype=float)
69
- off = np.array([xoff, yoff, zoff], dtype=float)
64
+
65
+ # if ndim == 2:
66
+ # A = np.array([[a, b], [d, e]], dtype=float)
67
+ # off = np.array([xoff, yoff], dtype=float)
68
+ # else:
69
+ # A = np.array([[a, b, c], [d, e, f], [g, h, i]], dtype=float)
70
+ # off = np.array([xoff, yoff, zoff], dtype=float)
70
71
 
71
72
  def _affine_coords(coords):
72
- return np.matmul(A, coords.T).T + off
73
+ # These are equivalent, but unfortunately not robust
74
+ # result = np.matmul(coords, A.T) + off
75
+ # result = np.matmul(A, coords.T).T + off
76
+ # Therefore, manual matrix multiplication is needed
77
+ if ndim == 2:
78
+ x, y = coords.T
79
+ xp = a * x + b * y + xoff
80
+ yp = d * x + e * y + yoff
81
+ result = np.stack([xp, yp]).T
82
+ elif ndim == 3:
83
+ x, y, z = coords.T
84
+ xp = a * x + b * y + c * z + xoff
85
+ yp = d * x + e * y + f * z + yoff
86
+ zp = g * x + h * y + i * z + zoff
87
+ result = np.stack([xp, yp, zp]).T
88
+ return result
73
89
 
74
90
  return shapely.transform(geom, _affine_coords, include_z=ndim == 3)
75
91
 
@@ -412,17 +412,18 @@ def delaunay_triangles(geometry, tolerance=0.0, only_edges=False, **kwargs):
412
412
  --------
413
413
  >>> from shapely import GeometryCollection, LineString, MultiPoint, Polygon
414
414
  >>> points = MultiPoint([(50, 30), (60, 30), (100, 100)])
415
- >>> delaunay_triangles(points)
416
- <GEOMETRYCOLLECTION (POLYGON ((50 30, 60 30, 100 100, 50 30)))>
415
+ >>> delaunay_triangles(points).normalize()
416
+ <GEOMETRYCOLLECTION (POLYGON ((50 30, 100 100, 60 30, 50 30)))>
417
417
  >>> delaunay_triangles(points, only_edges=True)
418
418
  <MULTILINESTRING ((50 30, 100 100), (50 30, 60 30), ...>
419
419
  >>> delaunay_triangles(MultiPoint([(50, 30), (51, 30), (60, 30), (100, 100)]), \
420
- tolerance=2)
421
- <GEOMETRYCOLLECTION (POLYGON ((50 30, 60 30, 100 100, 50 30)))>
422
- >>> delaunay_triangles(Polygon([(50, 30), (60, 30), (100, 100), (50, 30)]))
423
- <GEOMETRYCOLLECTION (POLYGON ((50 30, 60 30, 100 100, 50 30)))>
424
- >>> delaunay_triangles(LineString([(50, 30), (60, 30), (100, 100)]))
425
- <GEOMETRYCOLLECTION (POLYGON ((50 30, 60 30, 100 100, 50 30)))>
420
+ tolerance=2).normalize()
421
+ <GEOMETRYCOLLECTION (POLYGON ((50 30, 100 100, 60 30, 50 30)))>
422
+ >>> delaunay_triangles(Polygon([(50, 30), (60, 30), (100, 100), (50, 30)]))\
423
+ .normalize()
424
+ <GEOMETRYCOLLECTION (POLYGON ((50 30, 100 100, 60 30, 50 30)))>
425
+ >>> delaunay_triangles(LineString([(50, 30), (60, 30), (100, 100)])).normalize()
426
+ <GEOMETRYCOLLECTION (POLYGON ((50 30, 100 100, 60 30, 50 30)))>
426
427
  >>> delaunay_triangles(GeometryCollection([]))
427
428
  <GEOMETRYCOLLECTION EMPTY>
428
429
  """
@@ -533,11 +534,11 @@ def make_valid(geometry, **kwargs):
533
534
 
534
535
  @multithreading_enabled
535
536
  def normalize(geometry, **kwargs):
536
- """Converts Geometry to normal form (or canonical form).
537
+ """Converts Geometry to strict normal form (or canonical form).
537
538
 
538
- This method orders the coordinates, rings of a polygon and parts of
539
- multi geometries consistently. Typically useful for testing purposes
540
- (for example in combination with ``equals_exact``).
539
+ In :ref:`strict canonical form <canonical-form>`, the coordinates, rings of a polygon and
540
+ parts of multi geometries are ordered consistently. Typically useful for testing
541
+ purposes (for example in combination with ``equals_exact``).
541
542
 
542
543
  Parameters
543
544
  ----------
@@ -46,8 +46,13 @@ class CoordinateSequence:
46
46
  else:
47
47
  raise TypeError("key must be an index or slice")
48
48
 
49
- def __array__(self, dtype=None):
50
- return self._coords
49
+ def __array__(self, dtype=None, copy=None):
50
+ if copy is False:
51
+ raise ValueError("`copy=False` isn't supported. A copy is always created.")
52
+ elif copy is True:
53
+ return self._coords.copy()
54
+ else:
55
+ return self._coords
51
56
 
52
57
  @property
53
58
  def xy(self):
@@ -353,7 +353,7 @@ def multipoints(geometries, indices=None, out=None, **kwargs):
353
353
  ):
354
354
  geometries = points(geometries)
355
355
  if indices is None:
356
- return lib.create_collection(geometries, typ, out=out, **kwargs)
356
+ return lib.create_collection(geometries, np.intc(typ), out=out, **kwargs)
357
357
  else:
358
358
  return collections_1d(geometries, indices, typ, out=out)
359
359
 
@@ -390,7 +390,7 @@ def multilinestrings(geometries, indices=None, out=None, **kwargs):
390
390
  geometries = linestrings(geometries)
391
391
 
392
392
  if indices is None:
393
- return lib.create_collection(geometries, typ, out=out, **kwargs)
393
+ return lib.create_collection(geometries, np.intc(typ), out=out, **kwargs)
394
394
  else:
395
395
  return collections_1d(geometries, indices, typ, out=out)
396
396
 
@@ -426,7 +426,7 @@ def multipolygons(geometries, indices=None, out=None, **kwargs):
426
426
  ):
427
427
  geometries = polygons(geometries)
428
428
  if indices is None:
429
- return lib.create_collection(geometries, typ, out=out, **kwargs)
429
+ return lib.create_collection(geometries, np.intc(typ), out=out, **kwargs)
430
430
  else:
431
431
  return collections_1d(geometries, indices, typ, out=out)
432
432
 
@@ -457,7 +457,7 @@ def geometrycollections(geometries, indices=None, out=None, **kwargs):
457
457
  """
458
458
  typ = GeometryType.GEOMETRYCOLLECTION
459
459
  if indices is None:
460
- return lib.create_collection(geometries, typ, out=out, **kwargs)
460
+ return lib.create_collection(geometries, np.intc(typ), out=out, **kwargs)
461
461
  else:
462
462
  return collections_1d(geometries, indices, typ, out=out)
463
463
 
@@ -511,7 +511,7 @@ def destroy_prepared(geometry, **kwargs):
511
511
  Parameters
512
512
  ----------
513
513
  geometry : Geometry or array_like
514
- Geometries are changed inplace
514
+ Geometries are changed in-place
515
515
  **kwargs
516
516
  See :ref:`NumPy ufunc docs <ufuncs.kwargs>` for other keyword arguments.
517
517
 
@@ -85,12 +85,12 @@ class Point(BaseGeometry):
85
85
  @property
86
86
  def x(self):
87
87
  """Return x coordinate."""
88
- return shapely.get_x(self)
88
+ return float(shapely.get_x(self))
89
89
 
90
90
  @property
91
91
  def y(self):
92
92
  """Return y coordinate."""
93
- return shapely.get_y(self)
93
+ return float(shapely.get_y(self))
94
94
 
95
95
  @property
96
96
  def z(self):
@@ -139,12 +139,12 @@ def to_wkb(
139
139
  ----------
140
140
  geometry : Geometry or array_like
141
141
  hex : bool, default False
142
- If true, export the WKB as a hexidecimal string. The default is to
142
+ If true, export the WKB as a hexadecimal string. The default is to
143
143
  return a binary bytes object.
144
144
  output_dimension : int, default 3
145
145
  The output dimension for the WKB. Supported values are 2 and 3.
146
146
  Specifying 3 means that up to 3 dimensions will be written but 2D
147
- geometries will still be represented as 2D in the WKB represenation.
147
+ geometries will still be represented as 2D in the WKB representation.
148
148
  byte_order : int, default -1
149
149
  Defaults to native machine byte order (-1). Use 0 to force big endian
150
150
  and 1 for little endian.
@@ -1,7 +1,7 @@
1
1
  """
2
2
  Plot single geometries using Matplotlib.
3
3
 
4
- Note: this module is experimental, and mainly targetting (interactive)
4
+ Note: this module is experimental, and mainly targeting (interactive)
5
5
  exploration, debugging and illustration purposes.
6
6
 
7
7
  """
@@ -38,7 +38,7 @@ def patch_from_polygon(polygon, **kwargs):
38
38
  """
39
39
  Gets a Matplotlib patch from a (Multi)Polygon.
40
40
 
41
- Note: this function is experimental, and mainly targetting (interactive)
41
+ Note: this function is experimental, and mainly targeting (interactive)
42
42
  exploration, debugging and illustration purposes.
43
43
 
44
44
  Parameters
@@ -69,7 +69,7 @@ def plot_polygon(
69
69
  """
70
70
  Plot a (Multi)Polygon.
71
71
 
72
- Note: this function is experimental, and mainly targetting (interactive)
72
+ Note: this function is experimental, and mainly targeting (interactive)
73
73
  exploration, debugging and illustration purposes.
74
74
 
75
75
  Parameters
@@ -132,7 +132,7 @@ def plot_line(line, ax=None, add_points=True, color=None, linewidth=2, **kwargs)
132
132
  """
133
133
  Plot a (Multi)LineString/LinearRing.
134
134
 
135
- Note: this function is experimental, and mainly targetting (interactive)
135
+ Note: this function is experimental, and mainly targeting (interactive)
136
136
  exploration, debugging and illustration purposes.
137
137
 
138
138
  Parameters
@@ -144,7 +144,7 @@ def plot_line(line, ax=None, add_points=True, color=None, linewidth=2, **kwargs)
144
144
  add_points : bool, default True
145
145
  If True, also plot the coordinates (vertices) as points.
146
146
  color : matplotlib color specification
147
- Color for the line (edgecolor under the hood) and pointes.
147
+ Color for the line (edgecolor under the hood) and points.
148
148
  linewidth : float, default 2
149
149
  The line width for the polygon boundary.
150
150
  **kwargs
@@ -81,7 +81,7 @@ def is_ccw(geometry, **kwargs):
81
81
  Parameters
82
82
  ----------
83
83
  geometry : Geometry or array_like
84
- This function will return False for non-linear goemetries and for
84
+ This function will return False for non-linear geometries and for
85
85
  lines with fewer than 4 points (including the closing point).
86
86
  **kwargs
87
87
  See :ref:`NumPy ufunc docs <ufuncs.kwargs>` for other keyword arguments.
@@ -748,7 +748,7 @@ def equals(a, b, **kwargs):
748
748
  def intersects(a, b, **kwargs):
749
749
  """Returns True if A and B share any portion of space.
750
750
 
751
- Intersects implies that overlaps, touches and within are True.
751
+ Intersects implies that overlaps, touches, covers, or within are True.
752
752
 
753
753
  Parameters
754
754
  ----------
@@ -403,7 +403,9 @@ def union_all(geometries, grid_size=None, axis=None, **kwargs):
403
403
  geometries = np.rollaxis(geometries, axis=axis, start=geometries.ndim)
404
404
 
405
405
  # create_collection acts on the inner axis
406
- collections = lib.create_collection(geometries, GeometryType.GEOMETRYCOLLECTION)
406
+ collections = lib.create_collection(
407
+ geometries, np.intc(GeometryType.GEOMETRYCOLLECTION)
408
+ )
407
409
 
408
410
  if grid_size is not None:
409
411
  if lib.geos_version < (3, 9, 0):
@@ -501,5 +503,7 @@ def coverage_union_all(geometries, axis=None, **kwargs):
501
503
  np.asarray(geometries), axis=axis, start=geometries.ndim
502
504
  )
503
505
  # create_collection acts on the inner axis
504
- collections = lib.create_collection(geometries, GeometryType.GEOMETRYCOLLECTION)
506
+ collections = lib.create_collection(
507
+ geometries, np.intc(GeometryType.GEOMETRYCOLLECTION)
508
+ )
505
509
  return lib.coverage_union(collections, **kwargs)
@@ -2,6 +2,7 @@ import numpy as np
2
2
  import pytest
3
3
 
4
4
  from shapely import LineString
5
+ from shapely.tests.common import line_string, line_string_z, point, point_z
5
6
 
6
7
 
7
8
  class TestCoords:
@@ -84,3 +85,18 @@ class TestXY:
84
85
  assert list(x) == [0.0, 1.0]
85
86
  assert len(y) == 2
86
87
  assert list(y) == [0.0, 1.0]
88
+
89
+
90
+ @pytest.mark.parametrize("geom", [point, point_z, line_string, line_string_z])
91
+ def test_coords_array_copy(geom):
92
+ """Test CoordinateSequence.__array__ method."""
93
+ coord_seq = geom.coords
94
+ assert np.array(coord_seq) is not np.array(coord_seq)
95
+ assert np.array(coord_seq, copy=True) is not np.array(coord_seq, copy=True)
96
+
97
+ # Behaviour of copy=False is different between NumPy 1.x and 2.x
98
+ if int(np.version.short_version.split(".", 1)[0]) >= 2:
99
+ with pytest.raises(ValueError, match="A copy is always created"):
100
+ np.array(coord_seq, copy=False)
101
+ else:
102
+ assert np.array(coord_seq, copy=False) is np.array(coord_seq, copy=False)
@@ -47,7 +47,7 @@ def test_format_point():
47
47
  ("g", xy2, "POINT (-169.910918 -18.997564)", False),
48
48
  ("0.2g", xy2, "POINT (-169.91 -19)", False),
49
49
  ]
50
- # without precsions test GEOS rounding_precision=-1; different than Python
50
+ # without precisions test GEOS rounding_precision=-1; different than Python
51
51
  test_list += [
52
52
  ("f", (1, 2), f"POINT ({1:.16f} {2:.16f})", False),
53
53
  ("F", xyz3, "POINT Z ({:.16f} {:.16f} {:.16f})".format(*xyz3), False),
@@ -101,7 +101,9 @@ class TestPoint:
101
101
  # Test 2D points
102
102
  p = Point(1.0, 2.0)
103
103
  assert p.x == 1.0
104
+ assert type(p.x) is float
104
105
  assert p.y == 2.0
106
+ assert type(p.y) is float
105
107
  assert p.coords[:] == [(1.0, 2.0)]
106
108
  assert str(p) == p.wkt
107
109
  assert p.has_z is False
@@ -114,6 +116,7 @@ class TestPoint:
114
116
  assert str(p) == p.wkt
115
117
  assert p.has_z is True
116
118
  assert p.z == 3.0
119
+ assert type(p.z) is float
117
120
 
118
121
  # Coordinate access
119
122
  p = Point((3.0, 4.0))
@@ -3,6 +3,7 @@ import unittest
3
3
  import pytest
4
4
 
5
5
  import shapely
6
+ from shapely import geos_version
6
7
  from shapely.errors import TopologicalError
7
8
  from shapely.geometry import GeometryCollection, LineString, MultiPoint, Point, Polygon
8
9
  from shapely.wkt import loads
@@ -79,8 +80,11 @@ class OperationsTestCase(unittest.TestCase):
79
80
  "POLYGON ((40 100, 80 100, 80 60, 40 60, 40 100), (60 60, 80 60, 80 40, 60 40, 60 60))"
80
81
  )
81
82
  assert not invalid_polygon.is_valid
82
- with pytest.raises((TopologicalError, shapely.GEOSException)):
83
- invalid_polygon.relate(invalid_polygon)
83
+ if geos_version < (3, 13, 0):
84
+ with pytest.raises((TopologicalError, shapely.GEOSException)):
85
+ invalid_polygon.relate(invalid_polygon)
86
+ else: # resolved with RelateNG
87
+ assert invalid_polygon.relate(invalid_polygon) == "2FFF1FFF2"
84
88
 
85
89
  def test_hausdorff_distance(self):
86
90
  point = Point(1, 1)