pyogrio 0.7.2__cp39-cp39-win_amd64.whl → 0.9.0__cp39-cp39-win_amd64.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.
- pyogrio/__init__.py +12 -7
- pyogrio/_compat.py +6 -1
- pyogrio/_err.c +855 -321
- pyogrio/_err.cp39-win_amd64.pyd +0 -0
- pyogrio/_err.pyx +7 -3
- pyogrio/_geometry.c +134 -75
- pyogrio/_geometry.cp39-win_amd64.pyd +0 -0
- pyogrio/_io.c +28462 -22609
- pyogrio/_io.cp39-win_amd64.pyd +0 -0
- pyogrio/_io.pyx +904 -242
- pyogrio/_ogr.c +1317 -1640
- pyogrio/_ogr.cp39-win_amd64.pyd +0 -0
- pyogrio/_ogr.pxd +69 -13
- pyogrio/_ogr.pyx +8 -24
- pyogrio/_version.py +3 -3
- pyogrio/_vsi.c +6815 -0
- pyogrio/_vsi.cp39-win_amd64.pyd +0 -0
- pyogrio/_vsi.pxd +4 -0
- pyogrio/_vsi.pyx +140 -0
- pyogrio/core.py +43 -44
- pyogrio/gdal_data/GDAL-targets-release.cmake +1 -1
- pyogrio/gdal_data/GDAL-targets.cmake +10 -6
- pyogrio/gdal_data/GDALConfigVersion.cmake +3 -3
- pyogrio/gdal_data/gdalinfo_output.schema.json +2 -0
- pyogrio/gdal_data/gdalvrt.xsd +163 -0
- pyogrio/gdal_data/ogrinfo_output.schema.json +12 -1
- pyogrio/gdal_data/vcpkg.spdx.json +23 -23
- pyogrio/gdal_data/vcpkg_abi_info.txt +29 -28
- pyogrio/geopandas.py +140 -34
- pyogrio/proj_data/ITRF2008 +2 -2
- pyogrio/proj_data/proj-config-version.cmake +2 -2
- pyogrio/proj_data/proj-config.cmake +2 -1
- pyogrio/proj_data/proj-targets-release.cmake +0 -1
- pyogrio/proj_data/proj-targets.cmake +10 -6
- pyogrio/proj_data/proj.db +0 -0
- pyogrio/proj_data/proj4-targets-release.cmake +0 -1
- pyogrio/proj_data/proj4-targets.cmake +10 -6
- pyogrio/proj_data/vcpkg.spdx.json +21 -43
- pyogrio/proj_data/vcpkg_abi_info.txt +16 -17
- pyogrio/raw.py +438 -116
- pyogrio/tests/conftest.py +75 -6
- pyogrio/tests/fixtures/poly_not_enough_points.shp.zip +0 -0
- pyogrio/tests/test_arrow.py +841 -7
- pyogrio/tests/test_core.py +99 -7
- pyogrio/tests/test_geopandas_io.py +827 -121
- pyogrio/tests/test_path.py +23 -3
- pyogrio/tests/test_raw_io.py +276 -50
- pyogrio/util.py +39 -19
- pyogrio-0.9.0.dist-info/DELVEWHEEL +2 -0
- {pyogrio-0.7.2.dist-info → pyogrio-0.9.0.dist-info}/METADATA +2 -2
- {pyogrio-0.7.2.dist-info → pyogrio-0.9.0.dist-info}/RECORD +73 -68
- {pyogrio-0.7.2.dist-info → pyogrio-0.9.0.dist-info}/WHEEL +1 -1
- pyogrio.libs/.load-order-pyogrio-0.9.0 +18 -0
- pyogrio.libs/Lerc-5e4d8cbeeabca06f95e2270792304dc3.dll +0 -0
- pyogrio.libs/{gdal-c3b1d8f66682071d0cd26d86e4182013.dll → gdal-b434963605a006e01c486c0df6dea4e0.dll} +0 -0
- pyogrio.libs/geos-f0622d0794b81c937a851b2e6fa9b712.dll +0 -0
- pyogrio.libs/geos_c-0e16bf70612fc3301d077b9d863a3fdb.dll +0 -0
- pyogrio.libs/{geotiff-e43cdab688866b59f8800cfcde836d16.dll → geotiff-772e7c705fb15ddf91b432adb4eb1f6c.dll} +0 -0
- pyogrio.libs/iconv-2-8fcc23ddc6f096c45871011b6e008b44.dll +0 -0
- pyogrio.libs/{jpeg62-567ab743ac805dfb57fe3867ba5788a4.dll → jpeg62-2f9b7af22d78338e8f0be0058503dc35.dll} +0 -0
- pyogrio.libs/json-c-e52a077545e4057de42beb4948289b41.dll +0 -0
- pyogrio.libs/libcurl-bc81cd8afe15b10c0821b181b6af8bd0.dll +0 -0
- pyogrio.libs/libexpat-fbe03ca8917dfda776562d4338b289b8.dll +0 -0
- pyogrio.libs/{liblzma-de7f4770d4e3715acd031ca93883f10c.dll → liblzma-6b36f24d54d3dd45f274a2aebef81085.dll} +0 -0
- pyogrio.libs/libpng16-13928571ad910705eae8d7dd8eef8b11.dll +0 -0
- pyogrio.libs/{msvcp140-83b6a1a2fa8b1735a358b2fe13cabe4e.dll → msvcp140-46db46e967c8db2cb7a20fc75872a57e.dll} +0 -0
- pyogrio.libs/proj-8a30239ef2dfc3b9dd2bb48e8abb330f.dll +0 -0
- pyogrio.libs/{qhull_r-99ae8a526357acc44b162cb4df2c3bb6.dll → qhull_r-c45abde5d0c92faf723cc2942138af77.dll} +0 -0
- pyogrio.libs/sqlite3-df30c3cf230727e23c43c40126a530f7.dll +0 -0
- pyogrio.libs/{tiff-7c2d4b204ec2db46c81f6a597895c2f7.dll → tiff-43630f30487a9015213475ae86ed3fa3.dll} +0 -0
- pyogrio.libs/{zlib1-824de9299616f0908aeeb9441a084848.dll → zlib1-e1272810861a13dd8d6cff3beac47f17.dll} +0 -0
- pyogrio/tests/win32.py +0 -86
- pyogrio-0.7.2.dist-info/DELVEWHEEL +0 -2
- pyogrio.libs/.load-order-pyogrio-0.7.2 +0 -17
- pyogrio.libs/Lerc-d5afc4101deffe7de21241ccd4d562f6.dll +0 -0
- pyogrio.libs/geos-1c764a1384537a0ad2995e83d23e8642.dll +0 -0
- pyogrio.libs/geos_c-0d7dfdcee49efa8df585e2fb993157aa.dll +0 -0
- pyogrio.libs/json-c-36c91e30c4410d41c22b2010c31183e3.dll +0 -0
- pyogrio.libs/libcurl-ebcc8c18195071a90e59f818902e10c6.dll +0 -0
- pyogrio.libs/libexpat-345379c9c11632130d8c383cbacde1a6.dll +0 -0
- pyogrio.libs/libpng16-2c30e6846653c47ef2ff9d7dec3338ba.dll +0 -0
- pyogrio.libs/proj-98758c96a6cb682b5cec7e8dc5e29a50.dll +0 -0
- pyogrio.libs/sqlite3-327ed7b38bfd91fb4a17544960e055e9.dll +0 -0
- {pyogrio-0.7.2.dist-info → pyogrio-0.9.0.dist-info}/LICENSE +0 -0
- {pyogrio-0.7.2.dist-info → pyogrio-0.9.0.dist-info}/top_level.txt +0 -0
pyogrio/tests/test_path.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import os
|
|
2
|
+
from pathlib import Path
|
|
2
3
|
import contextlib
|
|
3
4
|
from zipfile import ZipFile, ZIP_DEFLATED
|
|
4
5
|
|
|
@@ -6,7 +7,7 @@ import pytest
|
|
|
6
7
|
|
|
7
8
|
import pyogrio
|
|
8
9
|
import pyogrio.raw
|
|
9
|
-
from pyogrio.util import vsi_path
|
|
10
|
+
from pyogrio.util import vsi_path, get_vsi_path_or_buffer
|
|
10
11
|
|
|
11
12
|
try:
|
|
12
13
|
import geopandas # NOQA
|
|
@@ -34,6 +35,7 @@ def change_cwd(path):
|
|
|
34
35
|
("/home/user/data.gpkg", "/home/user/data.gpkg"),
|
|
35
36
|
(r"C:\User\Documents\data.gpkg", r"C:\User\Documents\data.gpkg"),
|
|
36
37
|
("file:///home/user/data.gpkg", "/home/user/data.gpkg"),
|
|
38
|
+
("/home/folder # with hash/data.gpkg", "/home/folder # with hash/data.gpkg"),
|
|
37
39
|
# cloud URIs
|
|
38
40
|
("https://testing/data.gpkg", "/vsicurl/https://testing/data.gpkg"),
|
|
39
41
|
("s3://testing/data.gpkg", "/vsis3/testing/data.gpkg"),
|
|
@@ -265,7 +267,7 @@ def test_detect_zip_path(tmp_path, naturalearth_lowres):
|
|
|
265
267
|
|
|
266
268
|
@pytest.mark.network
|
|
267
269
|
def test_url():
|
|
268
|
-
url = "https://raw.githubusercontent.com/geopandas/pyogrio/main/pyogrio/tests/fixtures/naturalearth_lowres/naturalearth_lowres.shp"
|
|
270
|
+
url = "https://raw.githubusercontent.com/geopandas/pyogrio/main/pyogrio/tests/fixtures/naturalearth_lowres/naturalearth_lowres.shp"
|
|
269
271
|
|
|
270
272
|
result = pyogrio.raw.read(url)
|
|
271
273
|
assert len(result[2]) == 177
|
|
@@ -279,7 +281,7 @@ def test_url():
|
|
|
279
281
|
|
|
280
282
|
@pytest.mark.skipif(not has_geopandas, reason="GeoPandas not available")
|
|
281
283
|
def test_url_dataframe():
|
|
282
|
-
url = "https://raw.githubusercontent.com/geopandas/pyogrio/main/pyogrio/tests/fixtures/naturalearth_lowres/naturalearth_lowres.shp"
|
|
284
|
+
url = "https://raw.githubusercontent.com/geopandas/pyogrio/main/pyogrio/tests/fixtures/naturalearth_lowres/naturalearth_lowres.shp"
|
|
283
285
|
|
|
284
286
|
assert len(pyogrio.read_dataframe(url)) == 177
|
|
285
287
|
|
|
@@ -330,3 +332,21 @@ def test_uri_s3(aws_env_setup):
|
|
|
330
332
|
def test_uri_s3_dataframe(aws_env_setup):
|
|
331
333
|
df = pyogrio.read_dataframe("zip+s3://fiona-testing/coutwildrnp.zip")
|
|
332
334
|
assert len(df) == 67
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def test_get_vsi_path_or_buffer_obj_to_string():
|
|
338
|
+
path = Path("/tmp/test.gpkg")
|
|
339
|
+
assert get_vsi_path_or_buffer(path) == str(path)
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
def test_get_vsi_path_or_buffer_fixtures_to_string(tmp_path):
|
|
343
|
+
path = tmp_path / "test.gpkg"
|
|
344
|
+
assert get_vsi_path_or_buffer(path) == str(path)
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
@pytest.mark.parametrize(
|
|
348
|
+
"raw_path", ["/vsimem/test.shp.zip", "/vsizip//vsimem/test.shp.zip"]
|
|
349
|
+
)
|
|
350
|
+
def test_vsimem_path_exception(raw_path):
|
|
351
|
+
with pytest.raises(ValueError, match=""):
|
|
352
|
+
vsi_path(raw_path)
|
pyogrio/tests/test_raw_io.py
CHANGED
|
@@ -1,26 +1,30 @@
|
|
|
1
1
|
import contextlib
|
|
2
|
+
import ctypes
|
|
3
|
+
from io import BytesIO
|
|
2
4
|
import json
|
|
3
|
-
import os
|
|
4
5
|
import sys
|
|
5
6
|
|
|
6
7
|
import numpy as np
|
|
7
8
|
from numpy import array_equal
|
|
8
9
|
import pytest
|
|
9
10
|
|
|
11
|
+
import pyogrio
|
|
10
12
|
from pyogrio import (
|
|
11
13
|
list_layers,
|
|
12
14
|
list_drivers,
|
|
13
15
|
read_info,
|
|
14
16
|
set_gdal_config_options,
|
|
17
|
+
get_gdal_config_option,
|
|
15
18
|
__gdal_version__,
|
|
16
19
|
)
|
|
17
|
-
from pyogrio._compat import HAS_SHAPELY
|
|
18
|
-
from pyogrio.raw import read, write
|
|
20
|
+
from pyogrio._compat import HAS_SHAPELY, HAS_PYARROW
|
|
21
|
+
from pyogrio.raw import read, write, open_arrow
|
|
19
22
|
from pyogrio.errors import DataSourceError, DataLayerError, FeatureError
|
|
20
23
|
from pyogrio.tests.conftest import (
|
|
21
24
|
DRIVERS,
|
|
22
25
|
DRIVER_EXT,
|
|
23
26
|
prepare_testfile,
|
|
27
|
+
requires_pyarrow_api,
|
|
24
28
|
requires_arrow_api,
|
|
25
29
|
)
|
|
26
30
|
|
|
@@ -79,6 +83,12 @@ def test_read_autodetect_driver(tmp_path, naturalearth_lowres, ext):
|
|
|
79
83
|
assert len(geometry) == len(fields[0])
|
|
80
84
|
|
|
81
85
|
|
|
86
|
+
def test_read_arrow_unspecified_layer_warning(data_dir):
|
|
87
|
+
"""Reading a multi-layer file without specifying a layer gives a warning."""
|
|
88
|
+
with pytest.warns(UserWarning, match="More than one layer found "):
|
|
89
|
+
read(data_dir / "sample.osm.pbf")
|
|
90
|
+
|
|
91
|
+
|
|
82
92
|
def test_read_invalid_layer(naturalearth_lowres):
|
|
83
93
|
with pytest.raises(DataLayerError, match="Layer 'invalid' could not be opened"):
|
|
84
94
|
read(naturalearth_lowres, layer="invalid")
|
|
@@ -414,35 +424,43 @@ def test_read_return_only_fids(naturalearth_lowres):
|
|
|
414
424
|
assert len(field_data) == 0
|
|
415
425
|
|
|
416
426
|
|
|
417
|
-
|
|
427
|
+
@pytest.mark.parametrize("encoding", [None, "ISO-8859-1"])
|
|
428
|
+
def test_write_shp(tmp_path, naturalearth_lowres, encoding):
|
|
418
429
|
meta, _, geometry, field_data = read(naturalearth_lowres)
|
|
419
430
|
|
|
420
|
-
filename =
|
|
431
|
+
filename = tmp_path / "test.shp"
|
|
432
|
+
meta["encoding"] = encoding
|
|
421
433
|
write(filename, geometry, field_data, **meta)
|
|
422
434
|
|
|
423
|
-
assert
|
|
435
|
+
assert filename.exists()
|
|
424
436
|
for ext in (".dbf", ".prj"):
|
|
425
|
-
assert
|
|
437
|
+
assert filename.with_suffix(ext).exists()
|
|
426
438
|
|
|
439
|
+
# We write shapefiles in UTF-8 by default on all platforms
|
|
440
|
+
expected_encoding = encoding if encoding is not None else "UTF-8"
|
|
441
|
+
with open(filename.with_suffix(".cpg")) as cpg_file:
|
|
442
|
+
result_encoding = cpg_file.read()
|
|
443
|
+
assert result_encoding == expected_encoding
|
|
427
444
|
|
|
428
|
-
|
|
445
|
+
|
|
446
|
+
def test_write_gpkg(tmp_path, naturalearth_lowres):
|
|
429
447
|
meta, _, geometry, field_data = read(naturalearth_lowres)
|
|
430
448
|
meta.update({"geometry_type": "MultiPolygon"})
|
|
431
449
|
|
|
432
|
-
filename =
|
|
450
|
+
filename = tmp_path / "test.gpkg"
|
|
433
451
|
write(filename, geometry, field_data, driver="GPKG", **meta)
|
|
434
452
|
|
|
435
|
-
assert
|
|
453
|
+
assert filename.exists()
|
|
436
454
|
|
|
437
455
|
|
|
438
|
-
def test_write_gpkg_multiple_layers(
|
|
456
|
+
def test_write_gpkg_multiple_layers(tmp_path, naturalearth_lowres):
|
|
439
457
|
meta, _, geometry, field_data = read(naturalearth_lowres)
|
|
440
458
|
meta["geometry_type"] = "MultiPolygon"
|
|
441
459
|
|
|
442
|
-
filename =
|
|
460
|
+
filename = tmp_path / "test.gpkg"
|
|
443
461
|
write(filename, geometry, field_data, driver="GPKG", layer="first", **meta)
|
|
444
462
|
|
|
445
|
-
assert
|
|
463
|
+
assert filename.exists()
|
|
446
464
|
|
|
447
465
|
assert np.array_equal(list_layers(filename), [["first", "MultiPolygon"]])
|
|
448
466
|
|
|
@@ -453,13 +471,13 @@ def test_write_gpkg_multiple_layers(tmpdir, naturalearth_lowres):
|
|
|
453
471
|
)
|
|
454
472
|
|
|
455
473
|
|
|
456
|
-
def test_write_geojson(
|
|
474
|
+
def test_write_geojson(tmp_path, naturalearth_lowres):
|
|
457
475
|
meta, _, geometry, field_data = read(naturalearth_lowres)
|
|
458
476
|
|
|
459
|
-
filename =
|
|
477
|
+
filename = tmp_path / "test.json"
|
|
460
478
|
write(filename, geometry, field_data, driver="GeoJSON", **meta)
|
|
461
479
|
|
|
462
|
-
assert
|
|
480
|
+
assert filename.exists()
|
|
463
481
|
|
|
464
482
|
data = json.loads(open(filename).read())
|
|
465
483
|
|
|
@@ -478,17 +496,21 @@ def test_write_no_fields(tmp_path, naturalearth_lowres):
|
|
|
478
496
|
meta, _, geometry, field_data = read(naturalearth_lowres)
|
|
479
497
|
field_data = None
|
|
480
498
|
meta["fields"] = None
|
|
499
|
+
# naturalearth_lowres actually contains MultiPolygons. A shapefile doesn't make the
|
|
500
|
+
# distinction, so the metadata just reports Polygon. GPKG does, so override here to
|
|
501
|
+
# avoid GDAL warnings.
|
|
502
|
+
meta["geometry_type"] = "MultiPolygon"
|
|
481
503
|
|
|
482
504
|
# Test
|
|
483
505
|
filename = tmp_path / "test.gpkg"
|
|
484
506
|
write(filename, geometry, field_data, driver="GPKG", **meta)
|
|
485
507
|
|
|
486
508
|
# Check result
|
|
487
|
-
assert
|
|
509
|
+
assert filename.exists()
|
|
488
510
|
meta, _, geometry, fields = read(filename)
|
|
489
511
|
|
|
490
512
|
assert meta["crs"] == "EPSG:4326"
|
|
491
|
-
assert meta["geometry_type"] == "
|
|
513
|
+
assert meta["geometry_type"] == "MultiPolygon"
|
|
492
514
|
assert meta["encoding"] == "UTF-8"
|
|
493
515
|
assert meta["fields"].shape == (0,)
|
|
494
516
|
assert len(fields) == 0
|
|
@@ -510,7 +532,7 @@ def test_write_no_geom(tmp_path, naturalearth_lowres):
|
|
|
510
532
|
write(filename, geometry, field_data, driver="GPKG", **meta)
|
|
511
533
|
|
|
512
534
|
# Check result
|
|
513
|
-
assert
|
|
535
|
+
assert filename.exists()
|
|
514
536
|
meta, _, geometry, fields = read(filename)
|
|
515
537
|
|
|
516
538
|
assert meta["crs"] is None
|
|
@@ -547,7 +569,7 @@ def test_write_no_geom_data(tmp_path, naturalearth_lowres):
|
|
|
547
569
|
write(filename, geometry, field_data, driver="GPKG", **meta)
|
|
548
570
|
|
|
549
571
|
# Check result
|
|
550
|
-
assert
|
|
572
|
+
assert filename.exists()
|
|
551
573
|
result_meta, _, result_geometry, result_field_data = read(filename)
|
|
552
574
|
|
|
553
575
|
assert result_meta["crs"] is None
|
|
@@ -581,17 +603,17 @@ def test_write_no_geom_no_fields():
|
|
|
581
603
|
__gdal_version__ < (3, 6, 0),
|
|
582
604
|
reason="OpenFileGDB write support only available for GDAL >= 3.6.0",
|
|
583
605
|
)
|
|
584
|
-
def test_write_openfilegdb(
|
|
606
|
+
def test_write_openfilegdb(tmp_path, naturalearth_lowres):
|
|
585
607
|
meta, _, geometry, field_data = read(naturalearth_lowres)
|
|
586
608
|
|
|
587
|
-
filename =
|
|
609
|
+
filename = tmp_path / "test.gdb"
|
|
588
610
|
write(filename, geometry, field_data, driver="OpenFileGDB", **meta)
|
|
589
611
|
|
|
590
|
-
assert
|
|
612
|
+
assert filename.exists()
|
|
591
613
|
|
|
592
614
|
|
|
593
615
|
@pytest.mark.parametrize("ext", DRIVERS)
|
|
594
|
-
def test_write_append(
|
|
616
|
+
def test_write_append(tmp_path, naturalearth_lowres, ext):
|
|
595
617
|
if ext == ".fgb" and __gdal_version__ <= (3, 5, 0):
|
|
596
618
|
pytest.skip("Append to FlatGeobuf fails for GDAL <= 3.5.0")
|
|
597
619
|
|
|
@@ -603,10 +625,10 @@ def test_write_append(tmpdir, naturalearth_lowres, ext):
|
|
|
603
625
|
# coerce output layer to MultiPolygon to avoid mixed type errors
|
|
604
626
|
meta["geometry_type"] = "MultiPolygon"
|
|
605
627
|
|
|
606
|
-
filename =
|
|
628
|
+
filename = tmp_path / f"test{ext}"
|
|
607
629
|
write(filename, geometry, field_data, **meta)
|
|
608
630
|
|
|
609
|
-
assert
|
|
631
|
+
assert filename.exists()
|
|
610
632
|
|
|
611
633
|
assert read_info(filename)["features"] == 177
|
|
612
634
|
|
|
@@ -617,17 +639,17 @@ def test_write_append(tmpdir, naturalearth_lowres, ext):
|
|
|
617
639
|
|
|
618
640
|
|
|
619
641
|
@pytest.mark.parametrize("driver,ext", [("GML", ".gml"), ("GeoJSONSeq", ".geojsons")])
|
|
620
|
-
def test_write_append_unsupported(
|
|
642
|
+
def test_write_append_unsupported(tmp_path, naturalearth_lowres, driver, ext):
|
|
621
643
|
if ext == ".geojsons" and __gdal_version__ >= (3, 6, 0):
|
|
622
644
|
pytest.skip("Append to GeoJSONSeq supported for GDAL >= 3.6.0")
|
|
623
645
|
|
|
624
646
|
meta, _, geometry, field_data = read(naturalearth_lowres)
|
|
625
647
|
|
|
626
648
|
# GML does not support append functionality
|
|
627
|
-
filename =
|
|
649
|
+
filename = tmp_path / f"test{ext}"
|
|
628
650
|
write(filename, geometry, field_data, driver=driver, **meta)
|
|
629
651
|
|
|
630
|
-
assert
|
|
652
|
+
assert filename.exists()
|
|
631
653
|
|
|
632
654
|
assert read_info(filename, force_feature_count=True)["features"] == 177
|
|
633
655
|
|
|
@@ -639,16 +661,16 @@ def test_write_append_unsupported(tmpdir, naturalearth_lowres, driver, ext):
|
|
|
639
661
|
__gdal_version__ > (3, 5, 0),
|
|
640
662
|
reason="segfaults on FlatGeobuf limited to GDAL <= 3.5.0",
|
|
641
663
|
)
|
|
642
|
-
def test_write_append_prevent_gdal_segfault(
|
|
664
|
+
def test_write_append_prevent_gdal_segfault(tmp_path, naturalearth_lowres):
|
|
643
665
|
"""GDAL <= 3.5.0 segfaults when appending to FlatGeobuf; this test
|
|
644
666
|
verifies that we catch that before segfault"""
|
|
645
667
|
meta, _, geometry, field_data = read(naturalearth_lowres)
|
|
646
668
|
meta["geometry_type"] = "MultiPolygon"
|
|
647
669
|
|
|
648
|
-
filename =
|
|
670
|
+
filename = tmp_path / "test.fgb"
|
|
649
671
|
write(filename, geometry, field_data, **meta)
|
|
650
672
|
|
|
651
|
-
assert
|
|
673
|
+
assert filename.exists()
|
|
652
674
|
|
|
653
675
|
with pytest.raises(
|
|
654
676
|
RuntimeError, # match="append to FlatGeobuf is not supported for GDAL <= 3.5.0"
|
|
@@ -664,7 +686,7 @@ def test_write_append_prevent_gdal_segfault(tmpdir, naturalearth_lowres):
|
|
|
664
686
|
if driver not in ("ESRI Shapefile", "GPKG", "GeoJSON")
|
|
665
687
|
},
|
|
666
688
|
)
|
|
667
|
-
def test_write_supported(
|
|
689
|
+
def test_write_supported(tmp_path, naturalearth_lowres, driver):
|
|
668
690
|
"""Test drivers known to work that are not specifically tested above"""
|
|
669
691
|
meta, _, geometry, field_data = read(naturalearth_lowres, columns=["iso_a3"])
|
|
670
692
|
|
|
@@ -673,7 +695,7 @@ def test_write_supported(tmpdir, naturalearth_lowres, driver):
|
|
|
673
695
|
# we take the first record only.
|
|
674
696
|
meta["geometry_type"] = "MultiPolygon"
|
|
675
697
|
|
|
676
|
-
filename =
|
|
698
|
+
filename = tmp_path / f"test{DRIVER_EXT[driver]}"
|
|
677
699
|
write(
|
|
678
700
|
filename,
|
|
679
701
|
geometry[:1],
|
|
@@ -688,10 +710,10 @@ def test_write_supported(tmpdir, naturalearth_lowres, driver):
|
|
|
688
710
|
@pytest.mark.skipif(
|
|
689
711
|
__gdal_version__ >= (3, 6, 0), reason="OpenFileGDB supports write for GDAL >= 3.6.0"
|
|
690
712
|
)
|
|
691
|
-
def test_write_unsupported(
|
|
713
|
+
def test_write_unsupported(tmp_path, naturalearth_lowres):
|
|
692
714
|
meta, _, geometry, field_data = read(naturalearth_lowres)
|
|
693
715
|
|
|
694
|
-
filename =
|
|
716
|
+
filename = tmp_path / "test.gdb"
|
|
695
717
|
|
|
696
718
|
with pytest.raises(DataSourceError, match="does not support write functionality"):
|
|
697
719
|
write(filename, geometry, field_data, driver="OpenFileGDB", **meta)
|
|
@@ -734,10 +756,10 @@ def assert_equal_result(result1, result2):
|
|
|
734
756
|
|
|
735
757
|
@pytest.mark.filterwarnings("ignore:File /vsimem:RuntimeWarning") # TODO
|
|
736
758
|
@pytest.mark.parametrize("driver,ext", [("GeoJSON", "geojson"), ("GPKG", "gpkg")])
|
|
737
|
-
def test_read_from_bytes(
|
|
759
|
+
def test_read_from_bytes(tmp_path, naturalearth_lowres, driver, ext):
|
|
738
760
|
meta, index, geometry, field_data = read(naturalearth_lowres)
|
|
739
761
|
meta.update({"geometry_type": "Unknown"})
|
|
740
|
-
filename =
|
|
762
|
+
filename = tmp_path / f"test.{ext}"
|
|
741
763
|
write(filename, geometry, field_data, driver=driver, **meta)
|
|
742
764
|
|
|
743
765
|
with open(filename, "rb") as f:
|
|
@@ -747,7 +769,7 @@ def test_read_from_bytes(tmpdir, naturalearth_lowres, driver, ext):
|
|
|
747
769
|
assert_equal_result((meta, index, geometry, field_data), result2)
|
|
748
770
|
|
|
749
771
|
|
|
750
|
-
def test_read_from_bytes_zipped(
|
|
772
|
+
def test_read_from_bytes_zipped(naturalearth_lowres_vsi):
|
|
751
773
|
path, vsi_path = naturalearth_lowres_vsi
|
|
752
774
|
meta, index, geometry, field_data = read(vsi_path)
|
|
753
775
|
|
|
@@ -760,10 +782,10 @@ def test_read_from_bytes_zipped(tmpdir, naturalearth_lowres_vsi):
|
|
|
760
782
|
|
|
761
783
|
@pytest.mark.filterwarnings("ignore:File /vsimem:RuntimeWarning") # TODO
|
|
762
784
|
@pytest.mark.parametrize("driver,ext", [("GeoJSON", "geojson"), ("GPKG", "gpkg")])
|
|
763
|
-
def test_read_from_file_like(
|
|
785
|
+
def test_read_from_file_like(tmp_path, naturalearth_lowres, driver, ext):
|
|
764
786
|
meta, index, geometry, field_data = read(naturalearth_lowres)
|
|
765
787
|
meta.update({"geometry_type": "Unknown"})
|
|
766
|
-
filename =
|
|
788
|
+
filename = tmp_path / f"test.{ext}"
|
|
767
789
|
write(filename, geometry, field_data, driver=driver, **meta)
|
|
768
790
|
|
|
769
791
|
with open(filename, "rb") as f:
|
|
@@ -972,11 +994,11 @@ def test_write_float_nan_null(tmp_path, dtype):
|
|
|
972
994
|
field_data = [np.array([1.5, np.nan], dtype=dtype)]
|
|
973
995
|
fields = ["col"]
|
|
974
996
|
meta = dict(geometry_type="Point", crs="EPSG:4326")
|
|
975
|
-
|
|
997
|
+
filename = tmp_path / "test.geojson"
|
|
976
998
|
|
|
977
999
|
# default nan_as_null=True
|
|
978
|
-
write(
|
|
979
|
-
with open(
|
|
1000
|
+
write(filename, geometry, field_data, fields, **meta)
|
|
1001
|
+
with open(filename, "r") as f:
|
|
980
1002
|
content = f.read()
|
|
981
1003
|
assert '{ "col": null }' in content
|
|
982
1004
|
|
|
@@ -987,14 +1009,14 @@ def test_write_float_nan_null(tmp_path, dtype):
|
|
|
987
1009
|
else:
|
|
988
1010
|
ctx = contextlib.nullcontext()
|
|
989
1011
|
with ctx:
|
|
990
|
-
write(
|
|
991
|
-
with open(
|
|
1012
|
+
write(filename, geometry, field_data, fields, **meta, nan_as_null=False)
|
|
1013
|
+
with open(filename, "r") as f:
|
|
992
1014
|
content = f.read()
|
|
993
1015
|
assert '"properties": { }' in content
|
|
994
1016
|
|
|
995
1017
|
# but can instruct GDAL to write NaN to json
|
|
996
1018
|
write(
|
|
997
|
-
|
|
1019
|
+
filename,
|
|
998
1020
|
geometry,
|
|
999
1021
|
field_data,
|
|
1000
1022
|
fields,
|
|
@@ -1002,12 +1024,12 @@ def test_write_float_nan_null(tmp_path, dtype):
|
|
|
1002
1024
|
nan_as_null=False,
|
|
1003
1025
|
WRITE_NON_FINITE_VALUES="YES",
|
|
1004
1026
|
)
|
|
1005
|
-
with open(
|
|
1027
|
+
with open(filename, "r") as f:
|
|
1006
1028
|
content = f.read()
|
|
1007
1029
|
assert '{ "col": NaN }' in content
|
|
1008
1030
|
|
|
1009
1031
|
|
|
1010
|
-
@
|
|
1032
|
+
@requires_pyarrow_api
|
|
1011
1033
|
@pytest.mark.skipif(
|
|
1012
1034
|
"Arrow" not in list_drivers(), reason="Arrow driver is not available"
|
|
1013
1035
|
)
|
|
@@ -1039,6 +1061,91 @@ def test_write_float_nan_null_arrow(tmp_path):
|
|
|
1039
1061
|
assert pc.is_nan(table["col"]).to_pylist() == [False, True]
|
|
1040
1062
|
|
|
1041
1063
|
|
|
1064
|
+
@pytest.mark.filterwarnings("ignore:File /vsimem:RuntimeWarning")
|
|
1065
|
+
@pytest.mark.parametrize("driver", ["GeoJSON", "GPKG"])
|
|
1066
|
+
def test_write_memory(naturalearth_lowres, driver):
|
|
1067
|
+
meta, _, geometry, field_data = read(naturalearth_lowres)
|
|
1068
|
+
meta.update({"geometry_type": "MultiPolygon"})
|
|
1069
|
+
|
|
1070
|
+
buffer = BytesIO()
|
|
1071
|
+
write(buffer, geometry, field_data, driver=driver, layer="test", **meta)
|
|
1072
|
+
|
|
1073
|
+
assert len(buffer.getbuffer()) > 0
|
|
1074
|
+
assert list_layers(buffer)[0][0] == "test"
|
|
1075
|
+
|
|
1076
|
+
actual_meta, _, actual_geometry, actual_field_data = read(buffer)
|
|
1077
|
+
|
|
1078
|
+
assert np.array_equal(actual_meta["fields"], meta["fields"])
|
|
1079
|
+
assert np.array_equal(actual_field_data, field_data)
|
|
1080
|
+
assert len(actual_geometry) == len(geometry)
|
|
1081
|
+
|
|
1082
|
+
|
|
1083
|
+
def test_write_memory_driver_required(naturalearth_lowres):
|
|
1084
|
+
meta, _, geometry, field_data = read(naturalearth_lowres)
|
|
1085
|
+
|
|
1086
|
+
buffer = BytesIO()
|
|
1087
|
+
with pytest.raises(
|
|
1088
|
+
ValueError,
|
|
1089
|
+
match="driver must be provided to write to in-memory file",
|
|
1090
|
+
):
|
|
1091
|
+
write(buffer, geometry, field_data, driver=None, layer="test", **meta)
|
|
1092
|
+
|
|
1093
|
+
|
|
1094
|
+
@pytest.mark.parametrize("driver", ["ESRI Shapefile", "OpenFileGDB"])
|
|
1095
|
+
def test_write_memory_unsupported_driver(naturalearth_lowres, driver):
|
|
1096
|
+
if driver == "OpenFileGDB" and __gdal_version__ < (3, 6, 0):
|
|
1097
|
+
pytest.skip("OpenFileGDB write support only available for GDAL >= 3.6.0")
|
|
1098
|
+
|
|
1099
|
+
meta, _, geometry, field_data = read(naturalearth_lowres)
|
|
1100
|
+
|
|
1101
|
+
buffer = BytesIO()
|
|
1102
|
+
|
|
1103
|
+
with pytest.raises(
|
|
1104
|
+
ValueError, match=f"writing to in-memory file is not supported for {driver}"
|
|
1105
|
+
):
|
|
1106
|
+
write(
|
|
1107
|
+
buffer,
|
|
1108
|
+
geometry,
|
|
1109
|
+
field_data,
|
|
1110
|
+
driver=driver,
|
|
1111
|
+
layer="test",
|
|
1112
|
+
append=True,
|
|
1113
|
+
**meta,
|
|
1114
|
+
)
|
|
1115
|
+
|
|
1116
|
+
|
|
1117
|
+
@pytest.mark.parametrize("driver", ["GeoJSON", "GPKG"])
|
|
1118
|
+
def test_write_memory_append_unsupported(naturalearth_lowres, driver):
|
|
1119
|
+
meta, _, geometry, field_data = read(naturalearth_lowres)
|
|
1120
|
+
meta.update({"geometry_type": "MultiPolygon"})
|
|
1121
|
+
|
|
1122
|
+
buffer = BytesIO()
|
|
1123
|
+
|
|
1124
|
+
with pytest.raises(
|
|
1125
|
+
NotImplementedError, match="append is not supported for in-memory files"
|
|
1126
|
+
):
|
|
1127
|
+
write(
|
|
1128
|
+
buffer,
|
|
1129
|
+
geometry,
|
|
1130
|
+
field_data,
|
|
1131
|
+
driver=driver,
|
|
1132
|
+
layer="test",
|
|
1133
|
+
append=True,
|
|
1134
|
+
**meta,
|
|
1135
|
+
)
|
|
1136
|
+
|
|
1137
|
+
|
|
1138
|
+
def test_write_memory_existing_unsupported(naturalearth_lowres):
|
|
1139
|
+
meta, _, geometry, field_data = read(naturalearth_lowres)
|
|
1140
|
+
|
|
1141
|
+
buffer = BytesIO(b"0000")
|
|
1142
|
+
with pytest.raises(
|
|
1143
|
+
NotImplementedError,
|
|
1144
|
+
match="writing to existing in-memory object is not supported",
|
|
1145
|
+
):
|
|
1146
|
+
write(buffer, geometry, field_data, driver="GeoJSON", layer="test", **meta)
|
|
1147
|
+
|
|
1148
|
+
|
|
1042
1149
|
@pytest.mark.parametrize("ext", ["fgb", "gpkg", "geojson"])
|
|
1043
1150
|
@pytest.mark.parametrize(
|
|
1044
1151
|
"read_encoding,write_encoding",
|
|
@@ -1141,7 +1248,7 @@ def test_encoding_io_shapefile(tmp_path, read_encoding, write_encoding):
|
|
|
1141
1248
|
# verify that if cpg file is not present, that user-provided encoding is used,
|
|
1142
1249
|
# otherwise it defaults to ISO-8859-1
|
|
1143
1250
|
if read_encoding is not None:
|
|
1144
|
-
|
|
1251
|
+
filename.with_suffix(".cpg").unlink()
|
|
1145
1252
|
actual_meta, _, _, actual_field_data = read(filename, encoding=read_encoding)
|
|
1146
1253
|
assert np.array_equal(fields, actual_meta["fields"])
|
|
1147
1254
|
assert np.array_equal(field_data, actual_field_data)
|
|
@@ -1150,6 +1257,97 @@ def test_encoding_io_shapefile(tmp_path, read_encoding, write_encoding):
|
|
|
1150
1257
|
)
|
|
1151
1258
|
|
|
1152
1259
|
|
|
1260
|
+
@pytest.mark.parametrize("ext", ["gpkg", "geojson"])
|
|
1261
|
+
def test_non_utf8_encoding_io(tmp_path, ext, encoded_text):
|
|
1262
|
+
"""Verify that we write non-UTF data to the data source
|
|
1263
|
+
|
|
1264
|
+
IMPORTANT: this may not be valid for the data source and will likely render
|
|
1265
|
+
them unusable in other tools, but should successfully roundtrip unless we
|
|
1266
|
+
disable writing using other encodings.
|
|
1267
|
+
|
|
1268
|
+
NOTE: FlatGeobuff driver cannot handle non-UTF data in GDAL >= 3.9
|
|
1269
|
+
"""
|
|
1270
|
+
encoding, text = encoded_text
|
|
1271
|
+
|
|
1272
|
+
# Point(0, 0)
|
|
1273
|
+
geometry = np.array(
|
|
1274
|
+
[bytes.fromhex("010100000000000000000000000000000000000000")], dtype=object
|
|
1275
|
+
)
|
|
1276
|
+
|
|
1277
|
+
field_data = [np.array([text], dtype=object)]
|
|
1278
|
+
|
|
1279
|
+
fields = [text]
|
|
1280
|
+
meta = dict(geometry_type="Point", crs="EPSG:4326", encoding=encoding)
|
|
1281
|
+
|
|
1282
|
+
filename = tmp_path / f"test.{ext}"
|
|
1283
|
+
write(filename, geometry, field_data, fields, **meta)
|
|
1284
|
+
|
|
1285
|
+
# cannot open these files without specifying encoding
|
|
1286
|
+
with pytest.raises(UnicodeDecodeError):
|
|
1287
|
+
read(filename)
|
|
1288
|
+
|
|
1289
|
+
with pytest.raises(UnicodeDecodeError):
|
|
1290
|
+
read_info(filename)
|
|
1291
|
+
|
|
1292
|
+
# must provide encoding to read these properly
|
|
1293
|
+
actual_meta, _, _, actual_field_data = read(filename, encoding=encoding)
|
|
1294
|
+
assert actual_meta["fields"][0] == text
|
|
1295
|
+
assert actual_field_data[0] == text
|
|
1296
|
+
assert read_info(filename, encoding=encoding)["fields"][0] == text
|
|
1297
|
+
|
|
1298
|
+
|
|
1299
|
+
def test_non_utf8_encoding_io_shapefile(tmp_path, encoded_text):
|
|
1300
|
+
encoding, text = encoded_text
|
|
1301
|
+
|
|
1302
|
+
# Point(0, 0)
|
|
1303
|
+
geometry = np.array(
|
|
1304
|
+
[bytes.fromhex("010100000000000000000000000000000000000000")], dtype=object
|
|
1305
|
+
)
|
|
1306
|
+
|
|
1307
|
+
field_data = [np.array([text], dtype=object)]
|
|
1308
|
+
|
|
1309
|
+
fields = [text]
|
|
1310
|
+
meta = dict(geometry_type="Point", crs="EPSG:4326", encoding=encoding)
|
|
1311
|
+
|
|
1312
|
+
filename = tmp_path / "test.shp"
|
|
1313
|
+
write(filename, geometry, field_data, fields, **meta)
|
|
1314
|
+
|
|
1315
|
+
# NOTE: GDAL automatically creates a cpg file with the encoding name, which
|
|
1316
|
+
# means that if we read this without specifying the encoding it uses the
|
|
1317
|
+
# correct one
|
|
1318
|
+
actual_meta, _, _, actual_field_data = read(filename)
|
|
1319
|
+
assert actual_meta["fields"][0] == text
|
|
1320
|
+
assert actual_field_data[0] == text
|
|
1321
|
+
assert read_info(filename)["fields"][0] == text
|
|
1322
|
+
|
|
1323
|
+
# verify that if cpg file is not present, that user-provided encoding must be used
|
|
1324
|
+
filename.with_suffix(".cpg").unlink()
|
|
1325
|
+
|
|
1326
|
+
# We will assume ISO-8859-1, which is wrong
|
|
1327
|
+
miscoded = text.encode(encoding).decode("ISO-8859-1")
|
|
1328
|
+
bad_meta, _, _, bad_field_data = read(filename)
|
|
1329
|
+
assert bad_meta["fields"][0] == miscoded
|
|
1330
|
+
assert bad_field_data[0] == miscoded
|
|
1331
|
+
assert read_info(filename)["fields"][0] == miscoded
|
|
1332
|
+
|
|
1333
|
+
# If encoding is provided, that should yield correct text
|
|
1334
|
+
actual_meta, _, _, actual_field_data = read(filename, encoding=encoding)
|
|
1335
|
+
assert actual_meta["fields"][0] == text
|
|
1336
|
+
assert actual_field_data[0] == text
|
|
1337
|
+
assert read_info(filename, encoding=encoding)["fields"][0] == text
|
|
1338
|
+
|
|
1339
|
+
# verify that setting encoding does not corrupt SHAPE_ENCODING option if set
|
|
1340
|
+
# globally (it is ignored during read when encoding is specified by user)
|
|
1341
|
+
try:
|
|
1342
|
+
set_gdal_config_options({"SHAPE_ENCODING": "CP1254"})
|
|
1343
|
+
_ = read(filename, encoding=encoding)
|
|
1344
|
+
assert get_gdal_config_option("SHAPE_ENCODING") == "CP1254"
|
|
1345
|
+
|
|
1346
|
+
finally:
|
|
1347
|
+
# reset to clear between tests
|
|
1348
|
+
set_gdal_config_options({"SHAPE_ENCODING": None})
|
|
1349
|
+
|
|
1350
|
+
|
|
1153
1351
|
def test_write_with_mask(tmp_path):
|
|
1154
1352
|
# Point(0, 0), null
|
|
1155
1353
|
geometry = np.array(
|
|
@@ -1176,3 +1374,31 @@ def test_write_with_mask(tmp_path):
|
|
|
1176
1374
|
field_mask = [np.array([False, True, False])] * 2
|
|
1177
1375
|
with pytest.raises(ValueError):
|
|
1178
1376
|
write(filename, geometry, field_data, fields, field_mask, **meta)
|
|
1377
|
+
|
|
1378
|
+
|
|
1379
|
+
@requires_arrow_api
|
|
1380
|
+
def test_open_arrow_capsule_protocol_without_pyarrow(naturalearth_lowres):
|
|
1381
|
+
# this test is included here instead of test_arrow.py to ensure we also run
|
|
1382
|
+
# it when pyarrow is not installed
|
|
1383
|
+
|
|
1384
|
+
with open_arrow(naturalearth_lowres) as (meta, reader):
|
|
1385
|
+
assert isinstance(meta, dict)
|
|
1386
|
+
assert isinstance(reader, pyogrio._io._ArrowStream)
|
|
1387
|
+
capsule = reader.__arrow_c_stream__()
|
|
1388
|
+
assert (
|
|
1389
|
+
ctypes.pythonapi.PyCapsule_IsValid(
|
|
1390
|
+
ctypes.py_object(capsule), b"arrow_array_stream"
|
|
1391
|
+
)
|
|
1392
|
+
== 1
|
|
1393
|
+
)
|
|
1394
|
+
|
|
1395
|
+
|
|
1396
|
+
@pytest.mark.skipif(HAS_PYARROW, reason="pyarrow is installed")
|
|
1397
|
+
@requires_arrow_api
|
|
1398
|
+
def test_open_arrow_error_no_pyarrow(naturalearth_lowres):
|
|
1399
|
+
# this test is included here instead of test_arrow.py to ensure we run
|
|
1400
|
+
# it when pyarrow is not installed
|
|
1401
|
+
|
|
1402
|
+
with pytest.raises(ImportError):
|
|
1403
|
+
with open_arrow(naturalearth_lowres, use_pyarrow=True) as _:
|
|
1404
|
+
pass
|