pyogrio 0.9.0__cp312-cp312-manylinux_2_28_aarch64.whl → 0.10.0__cp312-cp312-manylinux_2_28_aarch64.whl

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 (74) hide show
  1. pyogrio/__init__.py +20 -13
  2. pyogrio/_compat.py +7 -1
  3. pyogrio/_env.py +4 -6
  4. pyogrio/_err.cpython-312-aarch64-linux-gnu.so +0 -0
  5. pyogrio/_geometry.cpython-312-aarch64-linux-gnu.so +0 -0
  6. pyogrio/_io.cpython-312-aarch64-linux-gnu.so +0 -0
  7. pyogrio/_ogr.cpython-312-aarch64-linux-gnu.so +0 -0
  8. pyogrio/_version.py +3 -3
  9. pyogrio/_vsi.cpython-312-aarch64-linux-gnu.so +0 -0
  10. pyogrio/core.py +86 -20
  11. pyogrio/errors.py +9 -16
  12. pyogrio/gdal_data/GDAL-targets-release.cmake +3 -3
  13. pyogrio/gdal_data/GDAL-targets.cmake +1 -1
  14. pyogrio/gdal_data/GDALConfig.cmake +0 -1
  15. pyogrio/gdal_data/GDALConfigVersion.cmake +3 -3
  16. pyogrio/gdal_data/MM_m_idofic.csv +321 -0
  17. pyogrio/gdal_data/gdaltileindex.xsd +269 -0
  18. pyogrio/gdal_data/gdalvrt.xsd +130 -22
  19. pyogrio/gdal_data/ogrinfo_output.schema.json +23 -0
  20. pyogrio/gdal_data/ogrvrt.xsd +3 -0
  21. pyogrio/gdal_data/pci_datum.txt +222 -155
  22. pyogrio/gdal_data/pci_ellips.txt +90 -38
  23. pyogrio/gdal_data/vcpkg.spdx.json +26 -26
  24. pyogrio/gdal_data/vcpkg_abi_info.txt +25 -25
  25. pyogrio/geopandas.py +32 -24
  26. pyogrio/proj_data/proj-config-version.cmake +2 -2
  27. pyogrio/proj_data/proj-targets.cmake +1 -1
  28. pyogrio/proj_data/proj.db +0 -0
  29. pyogrio/proj_data/proj4-targets.cmake +1 -1
  30. pyogrio/proj_data/projjson.schema.json +1 -1
  31. pyogrio/proj_data/vcpkg.spdx.json +20 -20
  32. pyogrio/proj_data/vcpkg_abi_info.txt +13 -13
  33. pyogrio/raw.py +46 -30
  34. pyogrio/tests/conftest.py +206 -12
  35. pyogrio/tests/fixtures/README.md +32 -13
  36. pyogrio/tests/fixtures/curve.gpkg +0 -0
  37. pyogrio/tests/fixtures/{test_multisurface.gpkg → curvepolygon.gpkg} +0 -0
  38. pyogrio/tests/fixtures/line_zm.gpkg +0 -0
  39. pyogrio/tests/fixtures/multisurface.gpkg +0 -0
  40. pyogrio/tests/test_arrow.py +178 -24
  41. pyogrio/tests/test_core.py +162 -72
  42. pyogrio/tests/test_geopandas_io.py +239 -99
  43. pyogrio/tests/test_path.py +29 -17
  44. pyogrio/tests/test_raw_io.py +165 -54
  45. pyogrio/tests/test_util.py +56 -0
  46. pyogrio/util.py +54 -30
  47. {pyogrio-0.9.0.dist-info → pyogrio-0.10.0.dist-info}/LICENSE +1 -1
  48. {pyogrio-0.9.0.dist-info → pyogrio-0.10.0.dist-info}/METADATA +37 -8
  49. {pyogrio-0.9.0.dist-info → pyogrio-0.10.0.dist-info}/RECORD +198 -214
  50. {pyogrio-0.9.0.dist-info → pyogrio-0.10.0.dist-info}/WHEEL +1 -1
  51. pyogrio.libs/{libgdal-6ff0914e.so.34.3.8.5 → libgdal-b0847c7b.so.35.3.9.1} +0 -0
  52. pyogrio/_err.pxd +0 -4
  53. pyogrio/_err.pyx +0 -250
  54. pyogrio/_geometry.pxd +0 -4
  55. pyogrio/_geometry.pyx +0 -129
  56. pyogrio/_io.pxd +0 -0
  57. pyogrio/_io.pyx +0 -2742
  58. pyogrio/_ogr.pxd +0 -444
  59. pyogrio/_ogr.pyx +0 -346
  60. pyogrio/_vsi.pxd +0 -4
  61. pyogrio/_vsi.pyx +0 -140
  62. pyogrio/arrow_bridge.h +0 -115
  63. pyogrio/gdal_data/bag_template.xml +0 -201
  64. pyogrio/gdal_data/gmlasconf.xml +0 -169
  65. pyogrio/gdal_data/gmlasconf.xsd +0 -1066
  66. pyogrio/gdal_data/netcdf_config.xsd +0 -143
  67. pyogrio/gdal_data/template_tiles.mapml +0 -28
  68. pyogrio/tests/fixtures/poly_not_enough_points.shp.zip +0 -0
  69. pyogrio/tests/fixtures/test_datetime.geojson +0 -7
  70. pyogrio/tests/fixtures/test_datetime_tz.geojson +0 -8
  71. pyogrio/tests/fixtures/test_fgdb.gdb.zip +0 -0
  72. pyogrio/tests/fixtures/test_nested.geojson +0 -18
  73. pyogrio/tests/fixtures/test_ogr_types_list.geojson +0 -12
  74. {pyogrio-0.9.0.dist-info → pyogrio-0.10.0.dist-info}/top_level.txt +0 -0
@@ -1,29 +1,35 @@
1
+ from pathlib import Path
2
+
1
3
  import numpy as np
2
- from numpy import array_equal, allclose
3
- import pytest
4
+ from numpy import allclose, array_equal
4
5
 
5
6
  from pyogrio import (
6
- __gdal_version__,
7
7
  __gdal_geos_version__,
8
+ __gdal_version__,
9
+ detect_write_driver,
10
+ get_gdal_config_option,
11
+ get_gdal_data_path,
8
12
  list_drivers,
9
13
  list_layers,
10
14
  read_bounds,
11
15
  read_info,
12
16
  set_gdal_config_options,
13
- get_gdal_config_option,
14
- get_gdal_data_path,
17
+ vsi_listtree,
18
+ vsi_rmtree,
19
+ vsi_unlink,
15
20
  )
16
- from pyogrio.core import detect_write_driver
17
21
  from pyogrio._compat import GDAL_GE_38
18
- from pyogrio.errors import DataSourceError, DataLayerError
19
- from pyogrio.tests.conftest import HAS_SHAPELY, prepare_testfile
20
-
21
22
  from pyogrio._env import GDALEnv
23
+ from pyogrio.errors import DataLayerError, DataSourceError
24
+ from pyogrio.raw import read, write
25
+ from pyogrio.tests.conftest import START_FID, prepare_testfile, requires_shapely
26
+
27
+ import pytest
22
28
 
23
29
  with GDALEnv():
24
30
  # NOTE: this must be AFTER above imports, which init the GDAL and PROJ data
25
31
  # search paths
26
- from pyogrio._ogr import ogr_driver_supports_write, has_gdal_data, has_proj_data
32
+ from pyogrio._ogr import has_gdal_data, has_proj_data, ogr_driver_supports_write
27
33
 
28
34
 
29
35
  try:
@@ -151,7 +157,16 @@ def test_list_drivers():
151
157
  assert len(drivers) == len(expected)
152
158
 
153
159
 
154
- def test_list_layers(naturalearth_lowres, naturalearth_lowres_vsi, test_fgdb_vsi):
160
+ def test_list_layers(
161
+ naturalearth_lowres,
162
+ naturalearth_lowres_vsi,
163
+ naturalearth_lowres_vsimem,
164
+ line_zm_file,
165
+ curve_file,
166
+ curve_polygon_file,
167
+ multisurface_file,
168
+ no_geometry_file,
169
+ ):
155
170
  assert array_equal(
156
171
  list_layers(naturalearth_lowres), [["naturalearth_lowres", "Polygon"]]
157
172
  )
@@ -160,21 +175,27 @@ def test_list_layers(naturalearth_lowres, naturalearth_lowres_vsi, test_fgdb_vsi
160
175
  list_layers(naturalearth_lowres_vsi[1]), [["naturalearth_lowres", "Polygon"]]
161
176
  )
162
177
 
178
+ assert array_equal(
179
+ list_layers(naturalearth_lowres_vsimem),
180
+ [["naturalearth_lowres", "MultiPolygon"]],
181
+ )
182
+
163
183
  # Measured 3D is downgraded to plain 3D during read
164
184
  # Make sure this warning is raised
165
185
  with pytest.warns(
166
186
  UserWarning, match=r"Measured \(M\) geometry types are not supported"
167
187
  ):
168
- fgdb_layers = list_layers(test_fgdb_vsi)
169
- # GDAL >= 3.4.0 includes 'another_relationship' layer
170
- assert len(fgdb_layers) >= 7
188
+ assert array_equal(list_layers(line_zm_file), [["line_zm", "LineString Z"]])
171
189
 
172
- # Make sure that nonspatial layer has None for geometry
173
- assert array_equal(fgdb_layers[0], ["basetable_2", None])
190
+ # Curve / surface types are downgraded to plain types
191
+ assert array_equal(list_layers(curve_file), [["curve", "LineString"]])
192
+ assert array_equal(list_layers(curve_polygon_file), [["curvepolygon", "Polygon"]])
193
+ assert array_equal(
194
+ list_layers(multisurface_file), [["multisurface", "MultiPolygon"]]
195
+ )
174
196
 
175
- # Confirm that measured 3D is downgraded to plain 3D during read
176
- assert array_equal(fgdb_layers[3], ["test_lines", "MultiLineString Z"])
177
- assert array_equal(fgdb_layers[6], ["test_areas", "MultiPolygon Z"])
197
+ # Make sure that nonspatial layer has None for geometry
198
+ assert array_equal(list_layers(no_geometry_file), [["no_geometry", None]])
178
199
 
179
200
 
180
201
  def test_list_layers_bytes(geojson_bytes):
@@ -184,6 +205,13 @@ def test_list_layers_bytes(geojson_bytes):
184
205
  assert layers[0, 0] == "test"
185
206
 
186
207
 
208
+ def test_list_layers_nonseekable_bytes(nonseekable_bytes):
209
+ layers = list_layers(nonseekable_bytes)
210
+
211
+ assert layers.shape == (1, 2)
212
+ assert layers[0, 1] == "Point"
213
+
214
+
187
215
  def test_list_layers_filelike(geojson_filelike):
188
216
  layers = list_layers(geojson_filelike)
189
217
 
@@ -191,22 +219,18 @@ def test_list_layers_filelike(geojson_filelike):
191
219
  assert layers[0, 0] == "test"
192
220
 
193
221
 
194
- def test_read_bounds(naturalearth_lowres):
195
- fids, bounds = read_bounds(naturalearth_lowres)
196
- assert fids.shape == (177,)
197
- assert bounds.shape == (4, 177)
198
-
199
- assert fids[0] == 0
200
- # Fiji; wraps antimeridian
201
- assert allclose(bounds[:, 0], [-180.0, -18.28799, 180.0, -16.02088])
202
-
222
+ @pytest.mark.parametrize(
223
+ "testfile",
224
+ ["naturalearth_lowres", "naturalearth_lowres_vsimem", "naturalearth_lowres_vsi"],
225
+ )
226
+ def test_read_bounds(testfile, request):
227
+ path = request.getfixturevalue(testfile)
228
+ path = path if not isinstance(path, tuple) else path[1]
203
229
 
204
- def test_read_bounds_vsi(naturalearth_lowres_vsi):
205
- fids, bounds = read_bounds(naturalearth_lowres_vsi[1])
230
+ fids, bounds = read_bounds(path)
206
231
  assert fids.shape == (177,)
207
232
  assert bounds.shape == (4, 177)
208
-
209
- assert fids[0] == 0
233
+ assert fids[0] == START_FID[Path(path).suffix]
210
234
  # Fiji; wraps antimeridian
211
235
  assert allclose(bounds[:, 0], [-180.0, -18.28799, 180.0, -16.02088])
212
236
 
@@ -218,6 +242,13 @@ def test_read_bounds_bytes(geojson_bytes):
218
242
  assert allclose(bounds[:, 0], [-180.0, -18.28799, 180.0, -16.02088])
219
243
 
220
244
 
245
+ def test_read_bounds_nonseekable_bytes(nonseekable_bytes):
246
+ fids, bounds = read_bounds(nonseekable_bytes)
247
+ assert fids.shape == (1,)
248
+ assert bounds.shape == (4, 1)
249
+ assert allclose(bounds[:, 0], [1, 1, 1, 1])
250
+
251
+
221
252
  def test_read_bounds_filelike(geojson_filelike):
222
253
  fids, bounds = read_bounds(geojson_filelike)
223
254
  assert fids.shape == (3,)
@@ -285,12 +316,9 @@ def test_read_bounds_bbox(naturalearth_lowres_all_ext):
285
316
  fids, bounds = read_bounds(naturalearth_lowres_all_ext, bbox=(-85, 8, -80, 10))
286
317
 
287
318
  assert fids.shape == (2,)
288
- if naturalearth_lowres_all_ext.suffix == ".gpkg":
289
- # fid in gpkg is 1-based
290
- assert array_equal(fids, [34, 35]) # PAN, CRI
291
- else:
292
- # fid in other formats is 0-based
293
- assert array_equal(fids, [33, 34]) # PAN, CRI
319
+ fids_expected = np.array([33, 34]) # PAN, CRI
320
+ fids_expected += START_FID[naturalearth_lowres_all_ext.suffix]
321
+ assert array_equal(fids, fids_expected)
294
322
 
295
323
  assert bounds.shape == (4, 2)
296
324
  assert allclose(
@@ -302,9 +330,7 @@ def test_read_bounds_bbox(naturalearth_lowres_all_ext):
302
330
  )
303
331
 
304
332
 
305
- @pytest.mark.skipif(
306
- not HAS_SHAPELY, reason="Shapely is required for mask functionality"
307
- )
333
+ @requires_shapely
308
334
  @pytest.mark.parametrize(
309
335
  "mask",
310
336
  [
@@ -318,9 +344,7 @@ def test_read_bounds_mask_invalid(naturalearth_lowres, mask):
318
344
  read_bounds(naturalearth_lowres, mask=mask)
319
345
 
320
346
 
321
- @pytest.mark.skipif(
322
- not HAS_SHAPELY, reason="Shapely is required for mask functionality"
323
- )
347
+ @requires_shapely
324
348
  def test_read_bounds_bbox_mask_invalid(naturalearth_lowres):
325
349
  with pytest.raises(ValueError, match="cannot set both 'bbox' and 'mask'"):
326
350
  read_bounds(
@@ -328,9 +352,7 @@ def test_read_bounds_bbox_mask_invalid(naturalearth_lowres):
328
352
  )
329
353
 
330
354
 
331
- @pytest.mark.skipif(
332
- not HAS_SHAPELY, reason="Shapely is required for mask functionality"
333
- )
355
+ @requires_shapely
334
356
  @pytest.mark.parametrize(
335
357
  "mask,expected",
336
358
  [
@@ -361,12 +383,8 @@ def test_read_bounds_mask(naturalearth_lowres_all_ext, mask, expected):
361
383
 
362
384
  fids = read_bounds(naturalearth_lowres_all_ext, mask=mask)[0]
363
385
 
364
- if naturalearth_lowres_all_ext.suffix == ".gpkg":
365
- # fid in gpkg is 1-based
366
- assert array_equal(fids, np.array(expected) + 1)
367
- else:
368
- # fid in other formats is 0-based
369
- assert array_equal(fids, expected)
386
+ fids_expected = np.array(expected) + START_FID[naturalearth_lowres_all_ext.suffix]
387
+ assert array_equal(fids, fids_expected)
370
388
 
371
389
 
372
390
  @pytest.mark.skipif(
@@ -382,21 +400,15 @@ def test_read_bounds_bbox_intersects_vs_envelope_overlaps(naturalearth_lowres_al
382
400
  if __gdal_geos_version__ is None:
383
401
  # bboxes for CAN, RUS overlap but do not intersect geometries
384
402
  assert fids.shape == (4,)
385
- if naturalearth_lowres_all_ext.suffix == ".gpkg":
386
- # fid in gpkg is 1-based
387
- assert array_equal(fids, [4, 5, 19, 28]) # CAN, USA, RUS, MEX
388
- else:
389
- # fid in other formats is 0-based
390
- assert array_equal(fids, [3, 4, 18, 27]) # CAN, USA, RUS, MEX
403
+ fids_expected = np.array([3, 4, 18, 27]) # CAN, USA, RUS, MEX
404
+ fids_expected += START_FID[naturalearth_lowres_all_ext.suffix]
405
+ assert array_equal(fids, fids_expected)
391
406
 
392
407
  else:
393
408
  assert fids.shape == (2,)
394
- if naturalearth_lowres_all_ext.suffix == ".gpkg":
395
- # fid in gpkg is 1-based
396
- assert array_equal(fids, [5, 28]) # USA, MEX
397
- else:
398
- # fid in other formats is 0-based
399
- assert array_equal(fids, [4, 27]) # USA, MEX
409
+ fids_expected = np.array([4, 27]) # USA, MEX
410
+ fids_expected += START_FID[naturalearth_lowres_all_ext.suffix]
411
+ assert array_equal(fids, fids_expected)
400
412
 
401
413
 
402
414
  @pytest.mark.parametrize("naturalearth_lowres", [".shp", ".gpkg"], indirect=True)
@@ -426,7 +438,8 @@ def test_read_info(naturalearth_lowres):
426
438
  elif naturalearth_lowres.suffix == ".shp":
427
439
  # fid_column == "" for formats where fid is not physically stored
428
440
  assert meta["fid_column"] == ""
429
- # geometry_name == "" for formats where geometry column name cannot be customized
441
+ # geometry_name == "" for formats where geometry column name cannot be
442
+ # customized
430
443
  assert meta["geometry_name"] == ""
431
444
  assert meta["geometry_type"] == "Polygon"
432
445
  assert meta["driver"] == "ESRI Shapefile"
@@ -435,8 +448,14 @@ def test_read_info(naturalearth_lowres):
435
448
  raise ValueError(f"test not implemented for ext {naturalearth_lowres.suffix}")
436
449
 
437
450
 
438
- def test_read_info_vsi(naturalearth_lowres_vsi):
439
- meta = read_info(naturalearth_lowres_vsi[1])
451
+ @pytest.mark.parametrize(
452
+ "testfile", ["naturalearth_lowres_vsimem", "naturalearth_lowres_vsi"]
453
+ )
454
+ def test_read_info_vsi(testfile, request):
455
+ path = request.getfixturevalue(testfile)
456
+ path = path if not isinstance(path, tuple) else path[1]
457
+
458
+ meta = read_info(path)
440
459
 
441
460
  assert meta["fields"].shape == (5,)
442
461
  assert meta["features"] == 177
@@ -449,6 +468,13 @@ def test_read_info_bytes(geojson_bytes):
449
468
  assert meta["features"] == 3
450
469
 
451
470
 
471
+ def test_read_info_nonseekable_bytes(nonseekable_bytes):
472
+ meta = read_info(nonseekable_bytes)
473
+
474
+ assert meta["fields"].shape == (0,)
475
+ assert meta["features"] == 1
476
+
477
+
452
478
  def test_read_info_filelike(geojson_filelike):
453
479
  meta = read_info(geojson_filelike)
454
480
 
@@ -483,8 +509,8 @@ def test_read_info_filelike(geojson_filelike):
483
509
  ),
484
510
  ],
485
511
  )
486
- def test_read_info_dataset_kwargs(data_dir, dataset_kwargs, fields):
487
- meta = read_info(data_dir / "test_nested.geojson", **dataset_kwargs)
512
+ def test_read_info_dataset_kwargs(nested_geojson_file, dataset_kwargs, fields):
513
+ meta = read_info(nested_geojson_file, **dataset_kwargs)
488
514
  assert meta["fields"].tolist() == fields
489
515
 
490
516
 
@@ -543,8 +569,8 @@ def test_read_info_unspecified_layer_warning(data_dir):
543
569
  read_info(data_dir / "sample.osm.pbf")
544
570
 
545
571
 
546
- def test_read_info_without_geometry(test_fgdb_vsi):
547
- assert read_info(test_fgdb_vsi, layer="basetable_2")["total_bounds"] is None
572
+ def test_read_info_without_geometry(no_geometry_file):
573
+ assert read_info(no_geometry_file)["total_bounds"] is None
548
574
 
549
575
 
550
576
  @pytest.mark.parametrize(
@@ -586,3 +612,67 @@ def test_error_handling_warning(capfd, naturalearth_lowres):
586
612
  read_info(naturalearth_lowres, INVALID="YES")
587
613
 
588
614
  assert capfd.readouterr().err == ""
615
+
616
+
617
+ def test_vsimem_listtree_rmtree_unlink(naturalearth_lowres):
618
+ """Test all basic functionalities of file handling in /vsimem/."""
619
+ # Prepare test data in /vsimem
620
+ meta, _, geometry, field_data = read(naturalearth_lowres)
621
+ meta["spatial_index"] = False
622
+ meta["geometry_type"] = "MultiPolygon"
623
+ test_file_path = Path("/vsimem/pyogrio_test_naturalearth_lowres.gpkg")
624
+ test_dir_path = Path(f"/vsimem/pyogrio_dir_test/{naturalearth_lowres.stem}.gpkg")
625
+
626
+ write(test_file_path, geometry, field_data, **meta)
627
+ write(test_dir_path, geometry, field_data, **meta)
628
+
629
+ # Check if everything was created properly with listtree
630
+ files = vsi_listtree("/vsimem/")
631
+ assert test_file_path.as_posix() in files
632
+ assert test_dir_path.as_posix() in files
633
+
634
+ # Check listtree with pattern
635
+ files = vsi_listtree("/vsimem/", pattern="pyogrio_dir_test*.gpkg")
636
+ assert test_file_path.as_posix() not in files
637
+ assert test_dir_path.as_posix() in files
638
+
639
+ files = vsi_listtree("/vsimem/", pattern="pyogrio_test*.gpkg")
640
+ assert test_file_path.as_posix() in files
641
+ assert test_dir_path.as_posix() not in files
642
+
643
+ # Remove test_dir and its contents
644
+ vsi_rmtree(test_dir_path.parent)
645
+ files = vsi_listtree("/vsimem/")
646
+ assert test_file_path.as_posix() in files
647
+ assert test_dir_path.as_posix() not in files
648
+
649
+ # Remove test_file
650
+ vsi_unlink(test_file_path)
651
+
652
+
653
+ def test_vsimem_rmtree_error(naturalearth_lowres_vsimem):
654
+ with pytest.raises(NotADirectoryError, match="Path is not a directory"):
655
+ vsi_rmtree(naturalearth_lowres_vsimem)
656
+
657
+ with pytest.raises(FileNotFoundError, match="Path does not exist"):
658
+ vsi_rmtree("/vsimem/non-existent")
659
+
660
+ with pytest.raises(
661
+ OSError, match="path to in-memory file or directory is required"
662
+ ):
663
+ vsi_rmtree("/vsimem")
664
+ with pytest.raises(
665
+ OSError, match="path to in-memory file or directory is required"
666
+ ):
667
+ vsi_rmtree("/vsimem/")
668
+
669
+ # Verify that naturalearth_lowres_vsimem still exists.
670
+ assert naturalearth_lowres_vsimem.as_posix() in vsi_listtree("/vsimem")
671
+
672
+
673
+ def test_vsimem_unlink_error(naturalearth_lowres_vsimem):
674
+ with pytest.raises(IsADirectoryError, match="Path is a directory"):
675
+ vsi_unlink(naturalearth_lowres_vsimem.parent)
676
+
677
+ with pytest.raises(FileNotFoundError, match="Path does not exist"):
678
+ vsi_unlink("/vsimem/non-existent.gpkg")