io.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. """Input/output functions for Shapely geometries."""
  2. import numpy as np
  3. from shapely import geos_version, lib
  4. from shapely._enum import ParamEnum
  5. # include ragged array functions here for reference documentation purpose
  6. from shapely._ragged_array import from_ragged_array, to_ragged_array
  7. from shapely.decorators import requires_geos
  8. from shapely.errors import UnsupportedGEOSVersionError
  9. __all__ = [
  10. "from_geojson",
  11. "from_ragged_array",
  12. "from_wkb",
  13. "from_wkt",
  14. "to_geojson",
  15. "to_ragged_array",
  16. "to_wkb",
  17. "to_wkt",
  18. ]
  19. # Allowed options for handling WKB/WKT decoding errors
  20. # Note: cannot use standard constructor since "raise" is a keyword
  21. DecodingErrorOptions = ParamEnum(
  22. "DecodingErrorOptions", {"ignore": 0, "warn": 1, "raise": 2, "fix": 3}
  23. )
  24. WKBFlavorOptions = ParamEnum("WKBFlavorOptions", {"extended": 1, "iso": 2})
  25. def to_wkt(
  26. geometry,
  27. rounding_precision=6,
  28. trim=True,
  29. output_dimension=None,
  30. old_3d=False,
  31. **kwargs,
  32. ):
  33. """Convert to the Well-Known Text (WKT) representation of a Geometry.
  34. The Well-known Text format is defined in the `OGC Simple Features
  35. Specification for SQL <https://www.opengeospatial.org/standards/sfs>`__.
  36. The following limitations apply to WKT serialization:
  37. - only simple empty geometries can be 3D, empty collections are always 2D
  38. Parameters
  39. ----------
  40. geometry : Geometry or array_like
  41. Geometry or geometries to convert to WKT.
  42. rounding_precision : int, default 6
  43. The rounding precision when writing the WKT string. Set to a value of
  44. -1 to indicate the full precision.
  45. trim : bool, default True
  46. If True, trim unnecessary decimals (trailing zeros). If False,
  47. use fixed-precision number formatting.
  48. output_dimension : int, default None
  49. The output dimension for the WKT string. Supported values are 2, 3 and
  50. 4 for GEOS 3.12+. Default None will automatically choose 3 or 4,
  51. depending on the version of GEOS.
  52. Specifying 3 means that up to 3 dimensions will be written but 2D
  53. geometries will still be represented as 2D in the WKT string.
  54. old_3d : bool, default False
  55. Enable old style 3D/4D WKT generation. By default, new style 3D/4D WKT
  56. (ie. "POINT Z (10 20 30)") is returned, but with ``old_3d=True``
  57. the WKT will be formatted in the style "POINT (10 20 30)".
  58. **kwargs
  59. See :ref:`NumPy ufunc docs <ufuncs.kwargs>` for other keyword arguments.
  60. Examples
  61. --------
  62. >>> import shapely
  63. >>> from shapely import Point
  64. >>> shapely.to_wkt(Point(0, 0))
  65. 'POINT (0 0)'
  66. >>> shapely.to_wkt(Point(0, 0), rounding_precision=3, trim=False)
  67. 'POINT (0.000 0.000)'
  68. >>> shapely.to_wkt(Point(0, 0), rounding_precision=-1, trim=False)
  69. 'POINT (0.0000000000000000 0.0000000000000000)'
  70. >>> shapely.to_wkt(Point(1, 2, 3), trim=True)
  71. 'POINT Z (1 2 3)'
  72. >>> shapely.to_wkt(Point(1, 2, 3), trim=True, output_dimension=2)
  73. 'POINT (1 2)'
  74. >>> shapely.to_wkt(Point(1, 2, 3), trim=True, old_3d=True)
  75. 'POINT (1 2 3)'
  76. Notes
  77. -----
  78. The defaults differ from the default of some GEOS versions. To mimic this for
  79. versions before GEOS 3.12, use::
  80. shapely.to_wkt(geometry, rounding_precision=-1, trim=False, output_dimension=2)
  81. """
  82. if not np.isscalar(rounding_precision):
  83. raise TypeError("rounding_precision only accepts scalar values")
  84. if not np.isscalar(trim):
  85. raise TypeError("trim only accepts scalar values")
  86. if output_dimension is None:
  87. output_dimension = 3 if geos_version < (3, 12, 0) else 4
  88. elif not np.isscalar(output_dimension):
  89. raise TypeError("output_dimension only accepts scalar values")
  90. if not np.isscalar(old_3d):
  91. raise TypeError("old_3d only accepts scalar values")
  92. return lib.to_wkt(
  93. geometry,
  94. np.intc(rounding_precision),
  95. np.bool_(trim),
  96. np.intc(output_dimension),
  97. np.bool_(old_3d),
  98. **kwargs,
  99. )
  100. def to_wkb(
  101. geometry,
  102. hex=False,
  103. output_dimension=None,
  104. byte_order=-1,
  105. include_srid=False,
  106. flavor="extended",
  107. **kwargs,
  108. ):
  109. r"""Convert to the Well-Known Binary (WKB) representation of a Geometry.
  110. The Well-Known Binary format is defined in the `OGC Simple Features
  111. Specification for SQL <https://www.opengeospatial.org/standards/sfs>`__.
  112. The following limitations apply to WKB serialization:
  113. - linearrings will be converted to linestrings
  114. - a point with only NaN coordinates is converted to an empty point
  115. Parameters
  116. ----------
  117. geometry : Geometry or array_like
  118. Geometry or geometries to convert to WKB.
  119. hex : bool, default False
  120. If true, export the WKB as a hexadecimal string. The default is to
  121. return a binary bytes object.
  122. output_dimension : int, default None
  123. The output dimension for the WKB. Supported values are 2, 3 and 4 for
  124. GEOS 3.12+. Default None will automatically choose 3 or 4, depending on
  125. the version of GEOS.
  126. Specifying 3 means that up to 3 dimensions will be written but 2D
  127. geometries will still be represented as 2D in the WKB representation.
  128. byte_order : int, default -1
  129. Defaults to native machine byte order (-1). Use 0 to force big endian
  130. and 1 for little endian.
  131. include_srid : bool, default False
  132. If True, the SRID is be included in WKB (this is an extension
  133. to the OGC WKB specification). Not allowed when flavor is "iso".
  134. flavor : {"iso", "extended"}, default "extended"
  135. Which flavor of WKB will be returned. The flavor determines how
  136. extra dimensionality is encoded with the type number, and whether
  137. SRID can be included in the WKB. ISO flavor is "more standard" for
  138. 3D output, and does not support SRID embedding.
  139. Both flavors are equivalent when ``output_dimension=2`` (or with 2D
  140. geometries) and ``include_srid=False``.
  141. The `from_wkb` function can read both flavors.
  142. **kwargs
  143. See :ref:`NumPy ufunc docs <ufuncs.kwargs>` for other keyword arguments.
  144. Examples
  145. --------
  146. >>> import shapely
  147. >>> from shapely import Point
  148. >>> point = Point(1, 1)
  149. >>> shapely.to_wkb(point, byte_order=1)
  150. b'\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\xf0?'
  151. >>> shapely.to_wkb(point, hex=True, byte_order=1)
  152. '0101000000000000000000F03F000000000000F03F'
  153. """
  154. if not np.isscalar(hex):
  155. raise TypeError("hex only accepts scalar values")
  156. if output_dimension is None:
  157. output_dimension = 3 if geos_version < (3, 12, 0) else 4
  158. elif not np.isscalar(output_dimension):
  159. raise TypeError("output_dimension only accepts scalar values")
  160. if not np.isscalar(byte_order):
  161. raise TypeError("byte_order only accepts scalar values")
  162. if not np.isscalar(include_srid):
  163. raise TypeError("include_srid only accepts scalar values")
  164. if not np.isscalar(flavor):
  165. raise TypeError("flavor only accepts scalar values")
  166. if lib.geos_version < (3, 10, 0) and flavor == "iso":
  167. raise UnsupportedGEOSVersionError(
  168. 'The "iso" option requires at least GEOS 3.10.0'
  169. )
  170. if flavor == "iso" and include_srid:
  171. raise ValueError('flavor="iso" and include_srid=True cannot be used together')
  172. flavor = WKBFlavorOptions.get_value(flavor)
  173. return lib.to_wkb(
  174. geometry,
  175. np.bool_(hex),
  176. np.intc(output_dimension),
  177. np.intc(byte_order),
  178. np.bool_(include_srid),
  179. np.intc(flavor),
  180. **kwargs,
  181. )
  182. @requires_geos("3.10.0")
  183. def to_geojson(geometry, indent=None, **kwargs):
  184. """Convert to the GeoJSON representation of a Geometry.
  185. The GeoJSON format is defined in the `RFC 7946 <https://geojson.org/>`__.
  186. NaN (not-a-number) coordinates will be written as 'null'.
  187. The following are currently unsupported:
  188. - Geometries of type LINEARRING: these are output as 'null'.
  189. - Three-dimensional geometries: the third dimension is ignored.
  190. Parameters
  191. ----------
  192. geometry : str, bytes or array_like
  193. Geometry or geometries to convert to GeoJSON.
  194. indent : int, optional
  195. If indent is a non-negative integer, then GeoJSON will be formatted.
  196. An indent level of 0 will only insert newlines. None (the default)
  197. selects the most compact representation.
  198. **kwargs
  199. See :ref:`NumPy ufunc docs <ufuncs.kwargs>` for other keyword arguments.
  200. Examples
  201. --------
  202. >>> import shapely
  203. >>> from shapely import Point
  204. >>> point = Point(1, 1)
  205. >>> shapely.to_geojson(point)
  206. '{"type":"Point","coordinates":[1.0,1.0]}'
  207. >>> print(shapely.to_geojson(point, indent=2))
  208. {
  209. "type": "Point",
  210. "coordinates": [
  211. 1.0,
  212. 1.0
  213. ]
  214. }
  215. """
  216. # GEOS Tickets:
  217. # - handle linearrings: https://trac.osgeo.org/geos/ticket/1140
  218. # - support 3D: https://trac.osgeo.org/geos/ticket/1141
  219. if indent is None:
  220. indent = -1
  221. elif not np.isscalar(indent):
  222. raise TypeError("indent only accepts scalar values")
  223. elif indent < 0:
  224. raise ValueError("indent cannot be negative")
  225. return lib.to_geojson(geometry, np.intc(indent), **kwargs)
  226. def from_wkt(geometry, on_invalid="raise", **kwargs):
  227. """Create geometries from the Well-Known Text (WKT) representation.
  228. The Well-known Text format is defined in the `OGC Simple Features
  229. Specification for SQL <https://www.opengeospatial.org/standards/sfs>`__.
  230. Parameters
  231. ----------
  232. geometry : str or array_like
  233. The WKT string(s) to convert.
  234. on_invalid : {"raise", "warn", "ignore", "fix"}, default "raise"
  235. Indicates what to do when an invalid WKT string is encountered. Note
  236. that the validations involved are very basic, e.g. the minimum number of
  237. points for the geometry type. For a thorough check, use
  238. :func:`is_valid` after conversion to geometries. Valid options are:
  239. - raise: an exception will be raised if any input geometry is invalid.
  240. - warn: a warning will be raised and invalid WKT geometries will be
  241. returned as ``None``.
  242. - ignore: invalid geometries will be returned as ``None`` without a
  243. warning.
  244. - fix: an effort is made to fix invalid input geometries (currently just
  245. unclosed rings). If this is not possible, they are returned as
  246. ``None`` without a warning. Requires GEOS >= 3.11.
  247. .. versionadded:: 2.1.0
  248. **kwargs
  249. See :ref:`NumPy ufunc docs <ufuncs.kwargs>` for other keyword arguments.
  250. Examples
  251. --------
  252. >>> import shapely
  253. >>> shapely.from_wkt('POINT (0 0)')
  254. <POINT (0 0)>
  255. """
  256. if not np.isscalar(on_invalid):
  257. raise TypeError("on_invalid only accepts scalar values")
  258. invalid_handler = np.uint8(DecodingErrorOptions.get_value(on_invalid))
  259. return lib.from_wkt(geometry, invalid_handler, **kwargs)
  260. def from_wkb(geometry, on_invalid="raise", **kwargs):
  261. r"""Create geometries from the Well-Known Binary (WKB) representation.
  262. The Well-Known Binary format is defined in the `OGC Simple Features
  263. Specification for SQL <https://www.opengeospatial.org/standards/sfs>`__.
  264. Parameters
  265. ----------
  266. geometry : str or array_like
  267. The WKB byte object(s) to convert.
  268. on_invalid : {"raise", "warn", "ignore", "fix"}, default "raise"
  269. Indicates what to do when an invalid WKB is encountered. Note that the
  270. validations involved are very basic, e.g. the minimum number of points
  271. for the geometry type. For a thorough check, use :func:`is_valid` after
  272. conversion to geometries. Valid options are:
  273. - raise: an exception will be raised if any input geometry is invalid.
  274. - warn: a warning will be raised and invalid WKT geometries will be
  275. returned as ``None``.
  276. - ignore: invalid geometries will be returned as ``None`` without a
  277. warning.
  278. - fix: an effort is made to fix invalid input geometries (currently just
  279. unclosed rings). If this is not possible, they are returned as
  280. ``None`` without a warning. Requires GEOS >= 3.11.
  281. .. versionadded:: 2.1.0
  282. **kwargs
  283. See :ref:`NumPy ufunc docs <ufuncs.kwargs>` for other keyword arguments.
  284. Examples
  285. --------
  286. >>> import shapely
  287. >>> shapely.from_wkb(b'\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\xf0?')
  288. <POINT (1 1)>
  289. """ # noqa: E501
  290. if not np.isscalar(on_invalid):
  291. raise TypeError("on_invalid only accepts scalar values")
  292. invalid_handler = np.uint8(DecodingErrorOptions.get_value(on_invalid))
  293. # ensure the input has object dtype, to avoid numpy inferring it as a
  294. # fixed-length string dtype (which removes trailing null bytes upon access
  295. # of array elements)
  296. geometry = np.asarray(geometry, dtype=object)
  297. return lib.from_wkb(geometry, invalid_handler, **kwargs)
  298. @requires_geos("3.10.1")
  299. def from_geojson(geometry, on_invalid="raise", **kwargs):
  300. """Create geometries from GeoJSON representations (strings).
  301. If a GeoJSON is a FeatureCollection, it is read as a single geometry
  302. (with type GEOMETRYCOLLECTION). This may be unpacked using
  303. :meth:`shapely.get_parts`. Properties are not read.
  304. The GeoJSON format is defined in `RFC 7946 <https://geojson.org/>`__.
  305. The following are currently unsupported:
  306. - Three-dimensional geometries: the third dimension is ignored.
  307. - Geometries having 'null' in the coordinates.
  308. Parameters
  309. ----------
  310. geometry : str, bytes or array_like
  311. The GeoJSON string or byte object(s) to convert.
  312. on_invalid : {"raise", "warn", "ignore"}, default "raise"
  313. - raise: an exception will be raised if an input GeoJSON is invalid.
  314. - warn: a warning will be raised and invalid input geometries will be
  315. returned as ``None``.
  316. - ignore: invalid input geometries will be returned as ``None`` without
  317. a warning.
  318. **kwargs
  319. See :ref:`NumPy ufunc docs <ufuncs.kwargs>` for other keyword arguments.
  320. See Also
  321. --------
  322. get_parts
  323. Examples
  324. --------
  325. >>> import shapely
  326. >>> shapely.from_geojson('{"type": "Point","coordinates": [1, 2]}')
  327. <POINT (1 2)>
  328. """
  329. # GEOS Tickets:
  330. # - support 3D: https://trac.osgeo.org/geos/ticket/1141
  331. # - handle null coordinates: https://trac.osgeo.org/geos/ticket/1142
  332. if not np.isscalar(on_invalid):
  333. raise TypeError("on_invalid only accepts scalar values")
  334. invalid_handler = np.uint8(DecodingErrorOptions.get_value(on_invalid))
  335. # ensure the input has object dtype, to avoid numpy inferring it as a
  336. # fixed-length string dtype (which removes trailing null bytes upon access
  337. # of array elements)
  338. geometry = np.asarray(geometry, dtype=object)
  339. return lib.from_geojson(geometry, invalid_handler, **kwargs)