pyogrio 0.8.0__tar.gz → 0.10.0__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.

Potentially problematic release.


This version of pyogrio might be problematic. Click here for more details.

Files changed (60) hide show
  1. {pyogrio-0.8.0 → pyogrio-0.10.0}/LICENSE +1 -1
  2. {pyogrio-0.8.0/pyogrio.egg-info → pyogrio-0.10.0}/PKG-INFO +37 -8
  3. {pyogrio-0.8.0 → pyogrio-0.10.0}/README.md +1 -1
  4. {pyogrio-0.8.0 → pyogrio-0.10.0}/pyogrio/__init__.py +20 -13
  5. {pyogrio-0.8.0 → pyogrio-0.10.0}/pyogrio/_compat.py +7 -1
  6. {pyogrio-0.8.0 → pyogrio-0.10.0}/pyogrio/_env.py +4 -6
  7. {pyogrio-0.8.0 → pyogrio-0.10.0}/pyogrio/_io.pyx +57 -44
  8. {pyogrio-0.8.0 → pyogrio-0.10.0}/pyogrio/_ogr.pxd +13 -2
  9. {pyogrio-0.8.0 → pyogrio-0.10.0}/pyogrio/_version.py +3 -3
  10. {pyogrio-0.8.0 → pyogrio-0.10.0}/pyogrio/_vsi.pxd +2 -2
  11. pyogrio-0.10.0/pyogrio/_vsi.pyx +289 -0
  12. {pyogrio-0.8.0 → pyogrio-0.10.0}/pyogrio/core.py +86 -20
  13. {pyogrio-0.8.0 → pyogrio-0.10.0}/pyogrio/errors.py +9 -16
  14. {pyogrio-0.8.0 → pyogrio-0.10.0}/pyogrio/geopandas.py +44 -27
  15. {pyogrio-0.8.0 → pyogrio-0.10.0}/pyogrio/raw.py +46 -30
  16. pyogrio-0.10.0/pyogrio/tests/conftest.py +398 -0
  17. {pyogrio-0.8.0 → pyogrio-0.10.0}/pyogrio/tests/fixtures/README.md +32 -13
  18. pyogrio-0.10.0/pyogrio/tests/fixtures/curve.gpkg +0 -0
  19. pyogrio-0.8.0/pyogrio/tests/fixtures/test_multisurface.gpkg → pyogrio-0.10.0/pyogrio/tests/fixtures/curvepolygon.gpkg +0 -0
  20. pyogrio-0.10.0/pyogrio/tests/fixtures/line_zm.gpkg +0 -0
  21. pyogrio-0.10.0/pyogrio/tests/fixtures/multisurface.gpkg +0 -0
  22. {pyogrio-0.8.0 → pyogrio-0.10.0}/pyogrio/tests/test_arrow.py +178 -24
  23. {pyogrio-0.8.0 → pyogrio-0.10.0}/pyogrio/tests/test_core.py +162 -72
  24. {pyogrio-0.8.0 → pyogrio-0.10.0}/pyogrio/tests/test_geopandas_io.py +341 -96
  25. {pyogrio-0.8.0 → pyogrio-0.10.0}/pyogrio/tests/test_path.py +30 -17
  26. {pyogrio-0.8.0 → pyogrio-0.10.0}/pyogrio/tests/test_raw_io.py +165 -54
  27. pyogrio-0.10.0/pyogrio/tests/test_util.py +56 -0
  28. {pyogrio-0.8.0 → pyogrio-0.10.0}/pyogrio/util.py +55 -31
  29. {pyogrio-0.8.0 → pyogrio-0.10.0/pyogrio.egg-info}/PKG-INFO +37 -8
  30. {pyogrio-0.8.0 → pyogrio-0.10.0}/pyogrio.egg-info/SOURCES.txt +5 -6
  31. {pyogrio-0.8.0 → pyogrio-0.10.0}/pyogrio.egg-info/requires.txt +1 -1
  32. pyogrio-0.10.0/pyproject.toml +208 -0
  33. {pyogrio-0.8.0 → pyogrio-0.10.0}/setup.py +12 -25
  34. pyogrio-0.8.0/pyogrio/_vsi.pyx +0 -140
  35. pyogrio-0.8.0/pyogrio/tests/conftest.py +0 -204
  36. pyogrio-0.8.0/pyogrio/tests/fixtures/test_datetime.geojson +0 -7
  37. pyogrio-0.8.0/pyogrio/tests/fixtures/test_datetime_tz.geojson +0 -8
  38. pyogrio-0.8.0/pyogrio/tests/fixtures/test_fgdb.gdb.zip +0 -0
  39. pyogrio-0.8.0/pyogrio/tests/fixtures/test_nested.geojson +0 -18
  40. pyogrio-0.8.0/pyogrio/tests/fixtures/test_ogr_types_list.geojson +0 -12
  41. pyogrio-0.8.0/pyproject.toml +0 -65
  42. {pyogrio-0.8.0 → pyogrio-0.10.0}/MANIFEST.in +0 -0
  43. {pyogrio-0.8.0 → pyogrio-0.10.0}/pyogrio/_err.pxd +0 -0
  44. {pyogrio-0.8.0 → pyogrio-0.10.0}/pyogrio/_err.pyx +0 -0
  45. {pyogrio-0.8.0 → pyogrio-0.10.0}/pyogrio/_geometry.pxd +0 -0
  46. {pyogrio-0.8.0 → pyogrio-0.10.0}/pyogrio/_geometry.pyx +0 -0
  47. {pyogrio-0.8.0 → pyogrio-0.10.0}/pyogrio/_io.pxd +0 -0
  48. {pyogrio-0.8.0 → pyogrio-0.10.0}/pyogrio/_ogr.pyx +0 -0
  49. {pyogrio-0.8.0 → pyogrio-0.10.0}/pyogrio/arrow_bridge.h +0 -0
  50. {pyogrio-0.8.0 → pyogrio-0.10.0}/pyogrio/tests/__init__.py +0 -0
  51. {pyogrio-0.8.0 → pyogrio-0.10.0}/pyogrio/tests/fixtures/naturalearth_lowres/naturalearth_lowres.cpg +0 -0
  52. {pyogrio-0.8.0 → pyogrio-0.10.0}/pyogrio/tests/fixtures/naturalearth_lowres/naturalearth_lowres.dbf +0 -0
  53. {pyogrio-0.8.0 → pyogrio-0.10.0}/pyogrio/tests/fixtures/naturalearth_lowres/naturalearth_lowres.prj +0 -0
  54. {pyogrio-0.8.0 → pyogrio-0.10.0}/pyogrio/tests/fixtures/naturalearth_lowres/naturalearth_lowres.shp +0 -0
  55. {pyogrio-0.8.0 → pyogrio-0.10.0}/pyogrio/tests/fixtures/naturalearth_lowres/naturalearth_lowres.shx +0 -0
  56. {pyogrio-0.8.0 → pyogrio-0.10.0}/pyogrio/tests/fixtures/sample.osm.pbf +0 -0
  57. {pyogrio-0.8.0 → pyogrio-0.10.0}/pyogrio/tests/fixtures/test_gpkg_nulls.gpkg +0 -0
  58. {pyogrio-0.8.0 → pyogrio-0.10.0}/pyogrio.egg-info/dependency_links.txt +0 -0
  59. {pyogrio-0.8.0 → pyogrio-0.10.0}/pyogrio.egg-info/top_level.txt +0 -0
  60. {pyogrio-0.8.0 → pyogrio-0.10.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2020-2021 Brendan C. Ward and pyogrio contributors
3
+ Copyright (c) 2020-2024 Brendan C. Ward and pyogrio contributors
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -1,19 +1,48 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyogrio
3
- Version: 0.8.0
3
+ Version: 0.10.0
4
4
  Summary: Vectorized spatial vector file format I/O using GDAL/OGR
5
- Home-page: https://github.com/geopandas/pyogrio
6
- Author: Brendan C. Ward
7
- Author-email: bcward@astutespruce.com
8
- License: MIT
9
- Requires-Python: >=3.8
5
+ Author: pyogrio contributors
6
+ Author-email: "Brendan C. Ward" <bcward@astutespruce.com>
7
+ Maintainer: pyogrio contributors
8
+ License: MIT License
9
+
10
+ Copyright (c) 2020-2024 Brendan C. Ward and pyogrio contributors
11
+
12
+ Permission is hereby granted, free of charge, to any person obtaining a copy
13
+ of this software and associated documentation files (the "Software"), to deal
14
+ in the Software without restriction, including without limitation the rights
15
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16
+ copies of the Software, and to permit persons to whom the Software is
17
+ furnished to do so, subject to the following conditions:
18
+
19
+ The above copyright notice and this permission notice shall be included in all
20
+ copies or substantial portions of the Software.
21
+
22
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
+ SOFTWARE.
29
+
30
+ Project-URL: Home, https://pyogrio.readthedocs.io/
31
+ Project-URL: Repository, https://github.com/geopandas/pyogrio
32
+ Classifier: Development Status :: 5 - Production/Stable
33
+ Classifier: Intended Audience :: Science/Research
34
+ Classifier: License :: OSI Approved :: MIT License
35
+ Classifier: Operating System :: OS Independent
36
+ Classifier: Programming Language :: Python :: 3
37
+ Classifier: Topic :: Scientific/Engineering :: GIS
38
+ Requires-Python: >=3.9
10
39
  Description-Content-Type: text/markdown
11
40
  License-File: LICENSE
12
41
  Requires-Dist: certifi
13
42
  Requires-Dist: numpy
14
43
  Requires-Dist: packaging
15
44
  Provides-Extra: dev
16
- Requires-Dist: Cython; extra == "dev"
45
+ Requires-Dist: cython; extra == "dev"
17
46
  Provides-Extra: test
18
47
  Requires-Dist: pytest; extra == "test"
19
48
  Requires-Dist: pytest-cov; extra == "test"
@@ -56,7 +85,7 @@ substantial change. Please see [CHANGES](CHANGES.md).
56
85
 
57
86
  ## Requirements
58
87
 
59
- Supports Python 3.8 - 3.11 and GDAL 3.4.x - 3.8.x.
88
+ Supports Python 3.9 - 3.13 and GDAL 3.4.x - 3.9.x.
60
89
 
61
90
  Reading to GeoDataFrames requires `geopandas>=0.12` with `shapely>=2`.
62
91
 
@@ -32,7 +32,7 @@ substantial change. Please see [CHANGES](CHANGES.md).
32
32
 
33
33
  ## Requirements
34
34
 
35
- Supports Python 3.8 - 3.11 and GDAL 3.4.x - 3.8.x.
35
+ Supports Python 3.9 - 3.13 and GDAL 3.4.x - 3.9.x.
36
36
 
37
37
  Reading to GeoDataFrames requires `geopandas>=0.12` with `shapely>=2`.
38
38
 
@@ -1,28 +1,32 @@
1
+ """Vectorized vector I/O using OGR."""
2
+
1
3
  try:
2
4
  # we try importing shapely, to ensure it is imported (and it can load its
3
5
  # own GEOS copy) before we load GDAL and its linked GEOS
4
- import shapely # noqa
5
- import shapely.geos # noqa
6
+ import shapely
7
+ import shapely.geos # noqa: F401
6
8
  except Exception:
7
9
  pass
8
10
 
11
+ from pyogrio._version import get_versions
9
12
  from pyogrio.core import (
10
- list_drivers,
13
+ __gdal_geos_version__,
14
+ __gdal_version__,
15
+ __gdal_version_string__,
11
16
  detect_write_driver,
17
+ get_gdal_config_option,
18
+ get_gdal_data_path,
19
+ list_drivers,
12
20
  list_layers,
13
21
  read_bounds,
14
22
  read_info,
15
23
  set_gdal_config_options,
16
- get_gdal_config_option,
17
- get_gdal_data_path,
18
- __gdal_version__,
19
- __gdal_version_string__,
20
- __gdal_geos_version__,
24
+ vsi_listtree,
25
+ vsi_rmtree,
26
+ vsi_unlink,
21
27
  )
22
- from pyogrio.raw import read_arrow, open_arrow, write_arrow
23
28
  from pyogrio.geopandas import read_dataframe, write_dataframe
24
- from pyogrio._version import get_versions
25
-
29
+ from pyogrio.raw import open_arrow, read_arrow, write_arrow
26
30
 
27
31
  __version__ = get_versions()["version"]
28
32
  del get_versions
@@ -36,10 +40,13 @@ __all__ = [
36
40
  "set_gdal_config_options",
37
41
  "get_gdal_config_option",
38
42
  "get_gdal_data_path",
39
- "read_arrow",
40
43
  "open_arrow",
41
- "write_arrow",
44
+ "read_arrow",
42
45
  "read_dataframe",
46
+ "vsi_listtree",
47
+ "vsi_rmtree",
48
+ "vsi_unlink",
49
+ "write_arrow",
43
50
  "write_dataframe",
44
51
  "__gdal_version__",
45
52
  "__gdal_version_string__",
@@ -1,6 +1,6 @@
1
1
  from packaging.version import Version
2
2
 
3
- from pyogrio.core import __gdal_version__, __gdal_geos_version__
3
+ from pyogrio.core import __gdal_geos_version__, __gdal_version__
4
4
 
5
5
  # detect optional dependencies
6
6
  try:
@@ -8,6 +8,11 @@ try:
8
8
  except ImportError:
9
9
  pyarrow = None
10
10
 
11
+ try:
12
+ import pyproj
13
+ except ImportError:
14
+ pyproj = None
15
+
11
16
  try:
12
17
  import shapely
13
18
  except ImportError:
@@ -27,6 +32,7 @@ except ImportError:
27
32
  HAS_ARROW_API = __gdal_version__ >= (3, 6, 0)
28
33
  HAS_ARROW_WRITE_API = __gdal_version__ >= (3, 8, 0)
29
34
  HAS_PYARROW = pyarrow is not None
35
+ HAS_PYPROJ = pyproj is not None
30
36
 
31
37
  HAS_GEOPANDAS = geopandas is not None
32
38
 
@@ -4,13 +4,11 @@
4
4
  # adapted from Fiona: https://github.com/Toblerity/Fiona/pull/875
5
5
 
6
6
 
7
- from contextlib import contextmanager
8
7
  import logging
9
8
  import os
10
- from pathlib import Path
11
9
  import platform
12
- import sys
13
-
10
+ from contextlib import contextmanager
11
+ from pathlib import Path
14
12
 
15
13
  log = logging.getLogger(__name__)
16
14
  log.addHandler(logging.NullHandler())
@@ -29,10 +27,10 @@ except ImportError:
29
27
 
30
28
  gdal_dll_dir = None
31
29
 
32
- if platform.system() == "Windows" and sys.version_info >= (3, 8):
30
+ if platform.system() == "Windows":
33
31
  # if loading of extension modules fails, search for gdal dll directory
34
32
  try:
35
- import pyogrio._io # NOQA
33
+ import pyogrio._io # noqa: F401
36
34
 
37
35
  except ImportError:
38
36
  for path in os.getenv("PATH", "").split(os.pathsep):
@@ -12,6 +12,7 @@ import math
12
12
  import os
13
13
  import sys
14
14
  import warnings
15
+ from pathlib import Path
15
16
 
16
17
  from libc.stdint cimport uint8_t, uintptr_t
17
18
  from libc.stdlib cimport malloc, free
@@ -1184,7 +1185,7 @@ def ogr_read(
1184
1185
  ):
1185
1186
 
1186
1187
  cdef int err = 0
1187
- cdef bint is_vsimem = isinstance(path_or_buffer, bytes)
1188
+ cdef bint use_tmp_vsimem = isinstance(path_or_buffer, bytes)
1188
1189
  cdef const char *path_c = NULL
1189
1190
  cdef char **dataset_options = NULL
1190
1191
  cdef const char *where_c = NULL
@@ -1206,7 +1207,7 @@ def ogr_read(
1206
1207
  fids = np.asarray(fids, dtype=np.intc)
1207
1208
 
1208
1209
  if sql is not None and layer is not None:
1209
- raise ValueError("'sql' paramater cannot be combined with 'layer'")
1210
+ raise ValueError("'sql' parameter cannot be combined with 'layer'")
1210
1211
 
1211
1212
  if not (read_geometry or return_fids or columns is None or len(columns) > 0):
1212
1213
  raise ValueError(
@@ -1224,7 +1225,7 @@ def ogr_read(
1224
1225
  raise ValueError("'max_features' must be >= 0")
1225
1226
 
1226
1227
  try:
1227
- path = read_buffer_to_vsimem(path_or_buffer) if is_vsimem else path_or_buffer
1228
+ path = read_buffer_to_vsimem(path_or_buffer) if use_tmp_vsimem else path_or_buffer
1228
1229
 
1229
1230
  if encoding:
1230
1231
  # for shapefiles, SHAPE_ENCODING must be set before opening the file
@@ -1274,7 +1275,7 @@ def ogr_read(
1274
1275
  idx = np.intersect1d(fields[:,2], columns, return_indices=True)[1]
1275
1276
  fields = fields[idx, :]
1276
1277
 
1277
- if not read_geometry:
1278
+ if not read_geometry and bbox is None and mask is None:
1278
1279
  ignored_fields.append("OGR_GEOMETRY")
1279
1280
 
1280
1281
  # Instruct GDAL to ignore reading fields not
@@ -1362,8 +1363,8 @@ def ogr_read(
1362
1363
  CPLFree(<void*>prev_shape_encoding)
1363
1364
  prev_shape_encoding = NULL
1364
1365
 
1365
- if is_vsimem:
1366
- delete_vsimem_file(path)
1366
+ if use_tmp_vsimem:
1367
+ vsimem_rmtree_toplevel(path)
1367
1368
 
1368
1369
  return (
1369
1370
  meta,
@@ -1424,7 +1425,7 @@ def ogr_open_arrow(
1424
1425
  ):
1425
1426
 
1426
1427
  cdef int err = 0
1427
- cdef bint is_vsimem = isinstance(path_or_buffer, bytes)
1428
+ cdef bint use_tmp_vsimem = isinstance(path_or_buffer, bytes)
1428
1429
  cdef const char *path_c = NULL
1429
1430
  cdef char **dataset_options = NULL
1430
1431
  cdef const char *where_c = NULL
@@ -1467,7 +1468,7 @@ def ogr_open_arrow(
1467
1468
  )
1468
1469
 
1469
1470
  if sql is not None and layer is not None:
1470
- raise ValueError("'sql' paramater cannot be combined with 'layer'")
1471
+ raise ValueError("'sql' parameter cannot be combined with 'layer'")
1471
1472
 
1472
1473
  if not (read_geometry or return_fids or columns is None or len(columns) > 0):
1473
1474
  raise ValueError(
@@ -1480,7 +1481,7 @@ def ogr_open_arrow(
1480
1481
 
1481
1482
  reader = None
1482
1483
  try:
1483
- path = read_buffer_to_vsimem(path_or_buffer) if is_vsimem else path_or_buffer
1484
+ path = read_buffer_to_vsimem(path_or_buffer) if use_tmp_vsimem else path_or_buffer
1484
1485
 
1485
1486
  if encoding:
1486
1487
  override_shape_encoding = True
@@ -1679,8 +1680,8 @@ def ogr_open_arrow(
1679
1680
  CPLFree(<void*>prev_shape_encoding)
1680
1681
  prev_shape_encoding = NULL
1681
1682
 
1682
- if is_vsimem:
1683
- delete_vsimem_file(path)
1683
+ if use_tmp_vsimem:
1684
+ vsimem_rmtree_toplevel(path)
1684
1685
 
1685
1686
 
1686
1687
  def ogr_read_bounds(
@@ -1697,7 +1698,7 @@ def ogr_read_bounds(
1697
1698
  object mask=None):
1698
1699
 
1699
1700
  cdef int err = 0
1700
- cdef bint is_vsimem = isinstance(path_or_buffer, bytes)
1701
+ cdef bint use_tmp_vsimem = isinstance(path_or_buffer, bytes)
1701
1702
  cdef const char *path_c = NULL
1702
1703
  cdef const char *where_c = NULL
1703
1704
  cdef OGRDataSourceH ogr_dataset = NULL
@@ -1715,7 +1716,7 @@ def ogr_read_bounds(
1715
1716
  raise ValueError("'max_features' must be >= 0")
1716
1717
 
1717
1718
  try:
1718
- path = read_buffer_to_vsimem(path_or_buffer) if is_vsimem else path_or_buffer
1719
+ path = read_buffer_to_vsimem(path_or_buffer) if use_tmp_vsimem else path_or_buffer
1719
1720
  ogr_dataset = ogr_open(path.encode('UTF-8'), 0, NULL)
1720
1721
 
1721
1722
  if layer is None:
@@ -1744,8 +1745,8 @@ def ogr_read_bounds(
1744
1745
  GDALClose(ogr_dataset)
1745
1746
  ogr_dataset = NULL
1746
1747
 
1747
- if is_vsimem:
1748
- delete_vsimem_file(path)
1748
+ if use_tmp_vsimem:
1749
+ vsimem_rmtree_toplevel(path)
1749
1750
 
1750
1751
  return bounds
1751
1752
 
@@ -1758,7 +1759,7 @@ def ogr_read_info(
1758
1759
  int force_feature_count=False,
1759
1760
  int force_total_bounds=False):
1760
1761
 
1761
- cdef bint is_vsimem = isinstance(path_or_buffer, bytes)
1762
+ cdef bint use_tmp_vsimem = isinstance(path_or_buffer, bytes)
1762
1763
  cdef const char *path_c = NULL
1763
1764
  cdef char **dataset_options = NULL
1764
1765
  cdef OGRDataSourceH ogr_dataset = NULL
@@ -1767,7 +1768,7 @@ def ogr_read_info(
1767
1768
  cdef bint override_shape_encoding = False
1768
1769
 
1769
1770
  try:
1770
- path = read_buffer_to_vsimem(path_or_buffer) if is_vsimem else path_or_buffer
1771
+ path = read_buffer_to_vsimem(path_or_buffer) if use_tmp_vsimem else path_or_buffer
1771
1772
 
1772
1773
  if encoding:
1773
1774
  override_shape_encoding = True
@@ -1826,19 +1827,19 @@ def ogr_read_info(
1826
1827
  if prev_shape_encoding != NULL:
1827
1828
  CPLFree(<void*>prev_shape_encoding)
1828
1829
 
1829
- if is_vsimem:
1830
- delete_vsimem_file(path)
1830
+ if use_tmp_vsimem:
1831
+ vsimem_rmtree_toplevel(path)
1831
1832
 
1832
1833
  return meta
1833
1834
 
1834
1835
 
1835
1836
  def ogr_list_layers(object path_or_buffer):
1836
- cdef bint is_vsimem = isinstance(path_or_buffer, bytes)
1837
+ cdef bint use_tmp_vsimem = isinstance(path_or_buffer, bytes)
1837
1838
  cdef const char *path_c = NULL
1838
1839
  cdef OGRDataSourceH ogr_dataset = NULL
1839
1840
 
1840
1841
  try:
1841
- path = read_buffer_to_vsimem(path_or_buffer) if is_vsimem else path_or_buffer
1842
+ path = read_buffer_to_vsimem(path_or_buffer) if use_tmp_vsimem else path_or_buffer
1842
1843
  ogr_dataset = ogr_open(path.encode('UTF-8'), 0, NULL)
1843
1844
  layers = get_layer_names(ogr_dataset)
1844
1845
 
@@ -1847,8 +1848,8 @@ def ogr_list_layers(object path_or_buffer):
1847
1848
  GDALClose(ogr_dataset)
1848
1849
  ogr_dataset = NULL
1849
1850
 
1850
- if is_vsimem:
1851
- delete_vsimem_file(path)
1851
+ if use_tmp_vsimem:
1852
+ vsimem_rmtree_toplevel(path)
1852
1853
 
1853
1854
  return layers
1854
1855
 
@@ -1931,6 +1932,16 @@ cdef void * ogr_create(const char* path_c, const char* driver_c, char** options)
1931
1932
  except CPLE_BaseError as exc:
1932
1933
  raise DataSourceError(str(exc))
1933
1934
 
1935
+ # For /vsimem/ files, with GDAL >= 3.8 parent directories are created automatically.
1936
+ IF CTE_GDAL_VERSION < (3, 8, 0):
1937
+ path = path_c.decode("UTF-8")
1938
+ if "/vsimem/" in path:
1939
+ parent = str(Path(path).parent.as_posix())
1940
+ if not parent.endswith("/vsimem"):
1941
+ retcode = VSIMkdirRecursive(parent.encode("UTF-8"), 0666)
1942
+ if retcode != 0:
1943
+ raise OSError(f"Could not create parent directory '{parent}'")
1944
+
1934
1945
  # Create the dataset
1935
1946
  try:
1936
1947
  ogr_dataset = exc_wrap_pointer(GDALCreate(ogr_driver, path_c, 0, 0, 0, GDT_Unknown, options))
@@ -2014,7 +2025,7 @@ cdef infer_field_types(list dtypes):
2014
2025
 
2015
2026
  cdef create_ogr_dataset_layer(
2016
2027
  str path,
2017
- bint is_vsi,
2028
+ bint use_tmp_vsimem,
2018
2029
  str layer,
2019
2030
  str driver,
2020
2031
  str crs,
@@ -2048,6 +2059,8 @@ cdef create_ogr_dataset_layer(
2048
2059
  encoding : str
2049
2060
  Only used if `driver` is "ESRI Shapefile". If not None, it overrules the default
2050
2061
  shapefile encoding, which is "UTF-8" in pyogrio.
2062
+ use_tmp_vsimem : bool
2063
+ Whether the file path is meant to save a temporary memory file to.
2051
2064
 
2052
2065
  Returns
2053
2066
  -------
@@ -2075,8 +2088,8 @@ cdef create_ogr_dataset_layer(
2075
2088
  driver_b = driver.encode('UTF-8')
2076
2089
  driver_c = driver_b
2077
2090
 
2078
- # in-memory dataset is always created from scratch
2079
- path_exists = os.path.exists(path) if not is_vsi else False
2091
+ # temporary in-memory dataset is always created from scratch
2092
+ path_exists = os.path.exists(path) if not use_tmp_vsimem else False
2080
2093
 
2081
2094
  if not layer:
2082
2095
  layer = os.path.splitext(os.path.split(path)[1])[0]
@@ -2112,10 +2125,7 @@ cdef create_ogr_dataset_layer(
2112
2125
  raise exc
2113
2126
 
2114
2127
  # otherwise create from scratch
2115
- if is_vsi:
2116
- VSIUnlink(path_c)
2117
- else:
2118
- os.unlink(path)
2128
+ os.unlink(path)
2119
2129
 
2120
2130
  ogr_dataset = NULL
2121
2131
 
@@ -2134,6 +2144,10 @@ cdef create_ogr_dataset_layer(
2134
2144
  if crs is not None:
2135
2145
  try:
2136
2146
  ogr_crs = create_crs(crs)
2147
+ # force geographic CRS to use lon, lat order and ignore axis order specified by CRS, in order
2148
+ # to correctly write KML and GeoJSON coordinates in correct order
2149
+ OSRSetAxisMappingStrategy(ogr_crs, OAMS_TRADITIONAL_GIS_ORDER)
2150
+
2137
2151
 
2138
2152
  except Exception as exc:
2139
2153
  if dataset_options != NULL:
@@ -2246,7 +2260,7 @@ def ogr_write(
2246
2260
  cdef int num_records = -1
2247
2261
  cdef int num_field_data = len(field_data) if field_data is not None else 0
2248
2262
  cdef int num_fields = len(fields) if fields is not None else 0
2249
- cdef bint is_vsi = False
2263
+ cdef bint use_tmp_vsimem = False
2250
2264
 
2251
2265
  if num_fields != num_field_data:
2252
2266
  raise ValueError("field_data array needs to be same length as fields array")
@@ -2287,12 +2301,11 @@ def ogr_write(
2287
2301
 
2288
2302
  try:
2289
2303
  # Setup in-memory handler if needed
2290
- path = get_ogr_vsimem_write_path(path_or_fp, driver)
2291
- is_vsi = path.startswith('/vsimem/')
2304
+ path, use_tmp_vsimem = get_ogr_vsimem_write_path(path_or_fp, driver)
2292
2305
 
2293
2306
  # Setup dataset and layer
2294
2307
  layer_created = create_ogr_dataset_layer(
2295
- path, is_vsi, layer, driver, crs, geometry_type, encoding,
2308
+ path, use_tmp_vsimem, layer, driver, crs, geometry_type, encoding,
2296
2309
  dataset_kwargs, layer_kwargs, append,
2297
2310
  dataset_metadata, layer_metadata,
2298
2311
  &ogr_dataset, &ogr_layer,
@@ -2497,7 +2510,7 @@ def ogr_write(
2497
2510
  raise DataSourceError(f"Failed to write features to dataset {path}; {exc}")
2498
2511
 
2499
2512
  # copy in-memory file back to path_or_fp object
2500
- if is_vsi:
2513
+ if use_tmp_vsimem:
2501
2514
  read_vsimem_to_buffer(path, path_or_fp)
2502
2515
 
2503
2516
  finally:
@@ -2519,8 +2532,8 @@ def ogr_write(
2519
2532
  if ogr_dataset != NULL:
2520
2533
  ogr_close(ogr_dataset)
2521
2534
 
2522
- if is_vsi:
2523
- delete_vsimem_file(path)
2535
+ if use_tmp_vsimem:
2536
+ vsimem_rmtree_toplevel(path)
2524
2537
 
2525
2538
 
2526
2539
  def ogr_write_arrow(
@@ -2544,7 +2557,7 @@ def ogr_write_arrow(
2544
2557
  cdef OGRDataSourceH ogr_dataset = NULL
2545
2558
  cdef OGRLayerH ogr_layer = NULL
2546
2559
  cdef char **options = NULL
2547
- cdef bint is_vsi = False
2560
+ cdef bint use_tmp_vsimem = False
2548
2561
  cdef ArrowArrayStream* stream = NULL
2549
2562
  cdef ArrowSchema schema
2550
2563
  cdef ArrowArray array
@@ -2553,11 +2566,11 @@ def ogr_write_arrow(
2553
2566
  array.release = NULL
2554
2567
 
2555
2568
  try:
2556
- path = get_ogr_vsimem_write_path(path_or_fp, driver)
2557
- is_vsi = path.startswith('/vsimem/')
2569
+ # Setup in-memory handler if needed
2570
+ path, use_tmp_vsimem = get_ogr_vsimem_write_path(path_or_fp, driver)
2558
2571
 
2559
2572
  layer_created = create_ogr_dataset_layer(
2560
- path, is_vsi, layer, driver, crs, geometry_type, encoding,
2573
+ path, use_tmp_vsimem, layer, driver, crs, geometry_type, encoding,
2561
2574
  dataset_kwargs, layer_kwargs, append,
2562
2575
  dataset_metadata, layer_metadata,
2563
2576
  &ogr_dataset, &ogr_layer,
@@ -2618,7 +2631,7 @@ def ogr_write_arrow(
2618
2631
  raise DataSourceError(f"Failed to write features to dataset {path}; {exc}")
2619
2632
 
2620
2633
  # copy in-memory file back to path_or_fp object
2621
- if is_vsi:
2634
+ if use_tmp_vsimem:
2622
2635
  read_vsimem_to_buffer(path, path_or_fp)
2623
2636
 
2624
2637
  finally:
@@ -2638,8 +2651,8 @@ def ogr_write_arrow(
2638
2651
  if ogr_dataset != NULL:
2639
2652
  ogr_close(ogr_dataset)
2640
2653
 
2641
- if is_vsi:
2642
- delete_vsimem_file(path)
2654
+ if use_tmp_vsimem:
2655
+ vsimem_rmtree_toplevel(path)
2643
2656
 
2644
2657
 
2645
2658
  cdef get_arrow_extension_metadata(const ArrowSchema* schema):
@@ -36,6 +36,10 @@ cdef extern from "cpl_error.h" nogil:
36
36
  void CPLPopErrorHandler()
37
37
 
38
38
 
39
+ cdef extern from "cpl_port.h":
40
+ ctypedef char **CSLConstList
41
+
42
+
39
43
  cdef extern from "cpl_string.h":
40
44
  char** CSLAddNameValue(char **list, const char *name, const char *value)
41
45
  char** CSLSetNameValue(char **list, const char *name, const char *value)
@@ -53,6 +57,9 @@ cdef extern from "cpl_vsi.h" nogil:
53
57
  long st_mode
54
58
  int st_mtime
55
59
 
60
+ int VSIStatL(const char *path, VSIStatBufL *psStatBuf)
61
+ int VSI_ISDIR(int mode)
62
+ char** VSIReadDirRecursive(const char *path)
56
63
  int VSIFCloseL(VSILFILE *fp)
57
64
  int VSIFFlushL(VSILFILE *fp)
58
65
  int VSIUnlink(const char *path)
@@ -61,7 +68,8 @@ cdef extern from "cpl_vsi.h" nogil:
61
68
  unsigned char *VSIGetMemFileBuffer(const char *path, vsi_l_offset *data_len, int take_ownership)
62
69
 
63
70
  int VSIMkdir(const char *path, long mode)
64
- int VSIRmdirRecursive(const char *pszDirname)
71
+ int VSIMkdirRecursive(const char *path, long mode)
72
+ int VSIRmdirRecursive(const char *path)
65
73
 
66
74
 
67
75
  cdef extern from "ogr_core.h":
@@ -196,7 +204,10 @@ cdef extern from "ogr_srs_api.h":
196
204
  const char* OSRGetAuthorityName(OGRSpatialReferenceH srs, const char *key)
197
205
  const char* OSRGetAuthorityCode(OGRSpatialReferenceH srs, const char *key)
198
206
  OGRErr OSRImportFromEPSG(OGRSpatialReferenceH srs, int code)
199
-
207
+ ctypedef enum OSRAxisMappingStrategy:
208
+ OAMS_TRADITIONAL_GIS_ORDER
209
+
210
+ void OSRSetAxisMappingStrategy(OGRSpatialReferenceH hSRS, OSRAxisMappingStrategy)
200
211
  int OSRSetFromUserInput(OGRSpatialReferenceH srs, const char *pszDef)
201
212
  void OSRSetPROJSearchPaths(const char *const *paths)
202
213
  OGRSpatialReferenceH OSRNewSpatialReference(const char *wkt)
@@ -8,11 +8,11 @@ import json
8
8
 
9
9
  version_json = '''
10
10
  {
11
- "date": "2024-05-06T14:34:51-0700",
11
+ "date": "2024-09-28T11:22:57-0700",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "46c35a7e98d85923cfefe73dae7285404e72d9a6",
15
- "version": "0.8.0"
14
+ "full-revisionid": "eb8e7889224155ffa0f779360db29f07f370eef1",
15
+ "version": "0.10.0"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -1,4 +1,4 @@
1
- cdef str get_ogr_vsimem_write_path(object path_or_fp, str driver)
1
+ cdef tuple get_ogr_vsimem_write_path(object path_or_fp, str driver)
2
2
  cdef str read_buffer_to_vsimem(bytes bytes_buffer)
3
3
  cdef read_vsimem_to_buffer(str path, object out_buffer)
4
- cdef delete_vsimem_file(str path)
4
+ cpdef vsimem_rmtree_toplevel(str path)