pyogrio 0.8.0__cp311-cp311-win_amd64.whl → 0.10.0__cp311-cp311-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 +26 -18
- pyogrio/_compat.py +7 -1
- pyogrio/_env.py +4 -6
- pyogrio/_err.c +460 -445
- pyogrio/_err.cp311-win_amd64.pyd +0 -0
- pyogrio/_geometry.c +645 -612
- pyogrio/_geometry.cp311-win_amd64.pyd +0 -0
- pyogrio/_io.c +7764 -7602
- pyogrio/_io.cp311-win_amd64.pyd +0 -0
- pyogrio/_ogr.c +601 -609
- pyogrio/_ogr.cp311-win_amd64.pyd +0 -0
- pyogrio/_version.py +3 -3
- pyogrio/_vsi.c +7570 -2514
- pyogrio/_vsi.cp311-win_amd64.pyd +0 -0
- pyogrio/core.py +86 -20
- pyogrio/errors.py +9 -16
- pyogrio/gdal_data/GDAL-targets.cmake +1 -1
- pyogrio/gdal_data/GDALConfig.cmake +0 -1
- pyogrio/gdal_data/GDALConfigVersion.cmake +3 -3
- pyogrio/gdal_data/MM_m_idofic.csv +321 -0
- pyogrio/gdal_data/gdaltileindex.xsd +269 -0
- pyogrio/gdal_data/gdalvrt.xsd +130 -22
- pyogrio/gdal_data/ogrinfo_output.schema.json +23 -0
- pyogrio/gdal_data/ogrvrt.xsd +3 -0
- pyogrio/gdal_data/pci_datum.txt +222 -155
- pyogrio/gdal_data/pci_ellips.txt +90 -38
- pyogrio/gdal_data/vcpkg.spdx.json +21 -21
- pyogrio/gdal_data/vcpkg_abi_info.txt +27 -27
- pyogrio/geopandas.py +44 -27
- pyogrio/proj_data/proj-config-version.cmake +2 -2
- pyogrio/proj_data/proj-targets.cmake +1 -1
- pyogrio/proj_data/proj.db +0 -0
- pyogrio/proj_data/proj4-targets.cmake +1 -1
- pyogrio/proj_data/projjson.schema.json +1 -1
- pyogrio/proj_data/vcpkg.spdx.json +17 -17
- pyogrio/proj_data/vcpkg_abi_info.txt +15 -15
- pyogrio/raw.py +46 -30
- pyogrio/tests/conftest.py +206 -12
- pyogrio/tests/fixtures/README.md +32 -13
- pyogrio/tests/fixtures/curve.gpkg +0 -0
- pyogrio/tests/fixtures/{test_multisurface.gpkg → curvepolygon.gpkg} +0 -0
- pyogrio/tests/fixtures/line_zm.gpkg +0 -0
- pyogrio/tests/fixtures/multisurface.gpkg +0 -0
- pyogrio/tests/test_arrow.py +178 -24
- pyogrio/tests/test_core.py +162 -72
- pyogrio/tests/test_geopandas_io.py +341 -96
- pyogrio/tests/test_path.py +30 -17
- pyogrio/tests/test_raw_io.py +165 -54
- pyogrio/tests/test_util.py +56 -0
- pyogrio/util.py +55 -31
- pyogrio-0.10.0.dist-info/DELVEWHEEL +2 -0
- {pyogrio-0.8.0.dist-info → pyogrio-0.10.0.dist-info}/LICENSE +1 -1
- {pyogrio-0.8.0.dist-info → pyogrio-0.10.0.dist-info}/METADATA +37 -8
- {pyogrio-0.8.0.dist-info → pyogrio-0.10.0.dist-info}/RECORD +74 -89
- {pyogrio-0.8.0.dist-info → pyogrio-0.10.0.dist-info}/WHEEL +1 -1
- pyogrio.libs/{Lerc-62a2c1c74500e7815994b3e49b36750c.dll → Lerc-089e3fef3df84b17326dcddbf1dedaa4.dll} +0 -0
- pyogrio.libs/{gdal-2bfc6a9f962a8953b0640db9a272d797.dll → gdal-debee5933f0da7bb90b4bcd009023377.dll} +0 -0
- pyogrio.libs/{geos-289d7171bd083dfed1f8a90e4ae57442.dll → geos-ace4c5b5c1f569bb4213e7bbd0b0322e.dll} +0 -0
- pyogrio.libs/{geos_c-2a12859cd876719c648f1eb950b7d94c.dll → geos_c-7478ca0a86136b280d9b2d245c6f6627.dll} +0 -0
- pyogrio.libs/geotiff-c8fe8a095520a4ea4e465d27e06add3a.dll +0 -0
- pyogrio.libs/{iconv-2-f2d9304f8dc4cdd981024b520b73a099.dll → iconv-2-27352d156a5467ca5383d3951093ea5a.dll} +0 -0
- pyogrio.libs/{jpeg62-a67b2bf7fd32d34c565ae5bb6d47c224.dll → jpeg62-e56b6f95a95af498f4623b8da4cebd46.dll} +0 -0
- pyogrio.libs/{json-c-79a8df7e59952f5c5d594620e4b66c13.dll → json-c-c84940e2654a4f8468bfcf2ce992aa93.dll} +0 -0
- pyogrio.libs/libcurl-d69cfd4ad487d53d58743b6778ec85e7.dll +0 -0
- pyogrio.libs/{libexpat-fa55f107b678de136400c6d953c3cdde.dll → libexpat-6576a8d02641b6a3dbad35901ec200a7.dll} +0 -0
- pyogrio.libs/liblzma-9ee4accb476ec1ae24e924953140273d.dll +0 -0
- pyogrio.libs/{libpng16-6227e9a35c2a350ae6b0586079c10b9e.dll → libpng16-7c36142dda59f186f6bb683e8dae2bfe.dll} +0 -0
- pyogrio.libs/{msvcp140-46db46e967c8db2cb7a20fc75872a57e.dll → msvcp140-98b3e5b80de1e5e9d1703b786d795623.dll} +0 -0
- pyogrio.libs/proj-a408c5327f3fd2f5fabe8c56815beed7.dll +0 -0
- pyogrio.libs/{qhull_r-d8840f4ed1f7d452ff9a30237320bcfd.dll → qhull_r-516897f855568caab1ab1fe37912766c.dll} +0 -0
- pyogrio.libs/sqlite3-9bc109d8536d5ed9666332fec94485fc.dll +0 -0
- pyogrio.libs/{tiff-ffca1ff19d0e95dad39df0078fb037af.dll → tiff-9b3f605fffe0bccc0a964c374ee4f820.dll} +0 -0
- pyogrio.libs/{zlib1-aaba6ea052f6d3fa3d84a301e3eb3d30.dll → zlib1-e5af16a15c63f05bd82d90396807ae5b.dll} +0 -0
- pyogrio/_err.pxd +0 -4
- pyogrio/_err.pyx +0 -250
- pyogrio/_geometry.pxd +0 -4
- pyogrio/_geometry.pyx +0 -129
- pyogrio/_io.pxd +0 -0
- pyogrio/_io.pyx +0 -2738
- pyogrio/_ogr.pxd +0 -441
- pyogrio/_ogr.pyx +0 -346
- pyogrio/_vsi.pxd +0 -4
- pyogrio/_vsi.pyx +0 -140
- pyogrio/arrow_bridge.h +0 -115
- pyogrio/gdal_data/bag_template.xml +0 -201
- pyogrio/gdal_data/gmlasconf.xml +0 -169
- pyogrio/gdal_data/gmlasconf.xsd +0 -1066
- pyogrio/gdal_data/netcdf_config.xsd +0 -143
- pyogrio/gdal_data/template_tiles.mapml +0 -28
- pyogrio/tests/fixtures/test_datetime.geojson +0 -7
- pyogrio/tests/fixtures/test_datetime_tz.geojson +0 -8
- pyogrio/tests/fixtures/test_fgdb.gdb.zip +0 -0
- pyogrio/tests/fixtures/test_nested.geojson +0 -18
- pyogrio/tests/fixtures/test_ogr_types_list.geojson +0 -12
- pyogrio-0.8.0.dist-info/DELVEWHEEL +0 -2
- pyogrio.libs/geotiff-d1c0fcc3c454409ad8be61ff04a7422c.dll +0 -0
- pyogrio.libs/libcurl-7fef9869f6520a5fbdb2bc9ce4c496cc.dll +0 -0
- pyogrio.libs/liblzma-5a1f648afc3d4cf36e3aef2266d55143.dll +0 -0
- pyogrio.libs/proj-74051a73897c9fa6d7bfef4561688568.dll +0 -0
- pyogrio.libs/sqlite3-fe7a86058d1c5658d1f9106228a7fd83.dll +0 -0
- {pyogrio-0.8.0.dist-info → pyogrio-0.10.0.dist-info}/top_level.txt +0 -0
pyogrio/tests/test_path.py
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
|
+
import contextlib
|
|
1
2
|
import os
|
|
2
3
|
from pathlib import Path
|
|
3
|
-
import
|
|
4
|
-
from zipfile import ZipFile, ZIP_DEFLATED
|
|
5
|
-
|
|
6
|
-
import pytest
|
|
4
|
+
from zipfile import ZIP_DEFLATED, ZipFile
|
|
7
5
|
|
|
8
6
|
import pyogrio
|
|
9
7
|
import pyogrio.raw
|
|
10
|
-
from pyogrio.
|
|
8
|
+
from pyogrio._compat import HAS_PYPROJ
|
|
9
|
+
from pyogrio.util import get_vsi_path_or_buffer, vsi_path
|
|
10
|
+
|
|
11
|
+
import pytest
|
|
11
12
|
|
|
12
13
|
try:
|
|
13
|
-
import geopandas #
|
|
14
|
+
import geopandas # noqa: F401
|
|
14
15
|
|
|
15
16
|
has_geopandas = True
|
|
16
17
|
except ImportError:
|
|
@@ -32,9 +33,11 @@ def change_cwd(path):
|
|
|
32
33
|
[
|
|
33
34
|
# local file paths that should be passed through as is
|
|
34
35
|
("data.gpkg", "data.gpkg"),
|
|
36
|
+
(Path("data.gpkg"), "data.gpkg"),
|
|
35
37
|
("/home/user/data.gpkg", "/home/user/data.gpkg"),
|
|
36
38
|
(r"C:\User\Documents\data.gpkg", r"C:\User\Documents\data.gpkg"),
|
|
37
39
|
("file:///home/user/data.gpkg", "/home/user/data.gpkg"),
|
|
40
|
+
("/home/folder # with hash/data.gpkg", "/home/folder # with hash/data.gpkg"),
|
|
38
41
|
# cloud URIs
|
|
39
42
|
("https://testing/data.gpkg", "/vsicurl/https://testing/data.gpkg"),
|
|
40
43
|
("s3://testing/data.gpkg", "/vsis3/testing/data.gpkg"),
|
|
@@ -83,6 +86,8 @@ def change_cwd(path):
|
|
|
83
86
|
"s3://testing/test.zip!a/b/item.shp",
|
|
84
87
|
"/vsizip/vsis3/testing/test.zip/a/b/item.shp",
|
|
85
88
|
),
|
|
89
|
+
("/vsimem/data.gpkg", "/vsimem/data.gpkg"),
|
|
90
|
+
(Path("/vsimem/data.gpkg"), "/vsimem/data.gpkg"),
|
|
86
91
|
],
|
|
87
92
|
)
|
|
88
93
|
def test_vsi_path(path, expected):
|
|
@@ -237,6 +242,9 @@ def test_detect_zip_path(tmp_path, naturalearth_lowres):
|
|
|
237
242
|
path = tmp_path / "test.zip"
|
|
238
243
|
with ZipFile(path, mode="w", compression=ZIP_DEFLATED, compresslevel=5) as out:
|
|
239
244
|
for ext in ["dbf", "prj", "shp", "shx"]:
|
|
245
|
+
if not HAS_PYPROJ and ext == "prj":
|
|
246
|
+
continue
|
|
247
|
+
|
|
240
248
|
filename = f"test1.{ext}"
|
|
241
249
|
out.write(tmp_path / filename, filename)
|
|
242
250
|
|
|
@@ -278,6 +286,7 @@ def test_url():
|
|
|
278
286
|
assert len(result[0]) == 177
|
|
279
287
|
|
|
280
288
|
|
|
289
|
+
@pytest.mark.network
|
|
281
290
|
@pytest.mark.skipif(not has_geopandas, reason="GeoPandas not available")
|
|
282
291
|
def test_url_dataframe():
|
|
283
292
|
url = "https://raw.githubusercontent.com/geopandas/pyogrio/main/pyogrio/tests/fixtures/naturalearth_lowres/naturalearth_lowres.shp"
|
|
@@ -333,19 +342,23 @@ def test_uri_s3_dataframe(aws_env_setup):
|
|
|
333
342
|
assert len(df) == 67
|
|
334
343
|
|
|
335
344
|
|
|
336
|
-
|
|
337
|
-
path
|
|
338
|
-
|
|
345
|
+
@pytest.mark.parametrize(
|
|
346
|
+
"path, expected",
|
|
347
|
+
[
|
|
348
|
+
(Path("/tmp/test.gpkg"), str(Path("/tmp/test.gpkg"))),
|
|
349
|
+
(Path("/vsimem/test.gpkg"), "/vsimem/test.gpkg"),
|
|
350
|
+
],
|
|
351
|
+
)
|
|
352
|
+
def test_get_vsi_path_or_buffer_obj_to_string(path, expected):
|
|
353
|
+
"""Verify that get_vsi_path_or_buffer retains forward slashes in /vsimem paths.
|
|
354
|
+
|
|
355
|
+
The /vsimem paths should keep forward slashes for GDAL to recognize them as such.
|
|
356
|
+
However, on Windows systems, forward slashes are by default replaced by backslashes,
|
|
357
|
+
so this test verifies that this doesn't happen for /vsimem paths.
|
|
358
|
+
"""
|
|
359
|
+
assert get_vsi_path_or_buffer(path) == expected
|
|
339
360
|
|
|
340
361
|
|
|
341
362
|
def test_get_vsi_path_or_buffer_fixtures_to_string(tmp_path):
|
|
342
363
|
path = tmp_path / "test.gpkg"
|
|
343
364
|
assert get_vsi_path_or_buffer(path) == str(path)
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
@pytest.mark.parametrize(
|
|
347
|
-
"raw_path", ["/vsimem/test.shp.zip", "/vsizip//vsimem/test.shp.zip"]
|
|
348
|
-
)
|
|
349
|
-
def test_vsimem_path_exception(raw_path):
|
|
350
|
-
with pytest.raises(ValueError, match=""):
|
|
351
|
-
vsi_path(raw_path)
|
pyogrio/tests/test_raw_io.py
CHANGED
|
@@ -1,33 +1,36 @@
|
|
|
1
1
|
import contextlib
|
|
2
2
|
import ctypes
|
|
3
|
-
from io import BytesIO
|
|
4
3
|
import json
|
|
5
4
|
import sys
|
|
5
|
+
from io import BytesIO
|
|
6
|
+
from zipfile import ZipFile
|
|
6
7
|
|
|
7
8
|
import numpy as np
|
|
8
9
|
from numpy import array_equal
|
|
9
|
-
import pytest
|
|
10
10
|
|
|
11
11
|
import pyogrio
|
|
12
12
|
from pyogrio import (
|
|
13
|
-
|
|
13
|
+
__gdal_version__,
|
|
14
|
+
get_gdal_config_option,
|
|
14
15
|
list_drivers,
|
|
16
|
+
list_layers,
|
|
15
17
|
read_info,
|
|
16
18
|
set_gdal_config_options,
|
|
17
|
-
get_gdal_config_option,
|
|
18
|
-
__gdal_version__,
|
|
19
19
|
)
|
|
20
|
-
from pyogrio._compat import
|
|
21
|
-
from pyogrio.
|
|
22
|
-
from pyogrio.
|
|
20
|
+
from pyogrio._compat import HAS_PYARROW, HAS_SHAPELY
|
|
21
|
+
from pyogrio.errors import DataLayerError, DataSourceError, FeatureError
|
|
22
|
+
from pyogrio.raw import open_arrow, read, write
|
|
23
23
|
from pyogrio.tests.conftest import (
|
|
24
|
-
DRIVERS,
|
|
25
24
|
DRIVER_EXT,
|
|
25
|
+
DRIVERS,
|
|
26
26
|
prepare_testfile,
|
|
27
|
-
requires_pyarrow_api,
|
|
28
27
|
requires_arrow_api,
|
|
28
|
+
requires_pyarrow_api,
|
|
29
|
+
requires_shapely,
|
|
29
30
|
)
|
|
30
31
|
|
|
32
|
+
import pytest
|
|
33
|
+
|
|
31
34
|
try:
|
|
32
35
|
import shapely
|
|
33
36
|
except ImportError:
|
|
@@ -116,6 +119,29 @@ def test_read_no_geometry(naturalearth_lowres):
|
|
|
116
119
|
assert geometry is None
|
|
117
120
|
|
|
118
121
|
|
|
122
|
+
@requires_shapely
|
|
123
|
+
def test_read_no_geometry__mask(naturalearth_lowres):
|
|
124
|
+
geometry, fields = read(
|
|
125
|
+
naturalearth_lowres,
|
|
126
|
+
read_geometry=False,
|
|
127
|
+
mask=shapely.Point(-105, 55),
|
|
128
|
+
)[2:]
|
|
129
|
+
|
|
130
|
+
assert np.array_equal(fields[3], ["CAN"])
|
|
131
|
+
assert geometry is None
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def test_read_no_geometry__bbox(naturalearth_lowres):
|
|
135
|
+
geometry, fields = read(
|
|
136
|
+
naturalearth_lowres,
|
|
137
|
+
read_geometry=False,
|
|
138
|
+
bbox=(-109.0, 55.0, -109.0, 55.0),
|
|
139
|
+
)[2:]
|
|
140
|
+
|
|
141
|
+
assert np.array_equal(fields[3], ["CAN"])
|
|
142
|
+
assert geometry is None
|
|
143
|
+
|
|
144
|
+
|
|
119
145
|
def test_read_no_geometry_no_columns_no_fids(naturalearth_lowres):
|
|
120
146
|
with pytest.raises(
|
|
121
147
|
ValueError,
|
|
@@ -255,9 +281,7 @@ def test_read_bbox_where(naturalearth_lowres_all_ext):
|
|
|
255
281
|
assert np.array_equal(fields[3], ["CAN"])
|
|
256
282
|
|
|
257
283
|
|
|
258
|
-
@
|
|
259
|
-
not HAS_SHAPELY, reason="Shapely is required for mask functionality"
|
|
260
|
-
)
|
|
284
|
+
@requires_shapely
|
|
261
285
|
@pytest.mark.parametrize(
|
|
262
286
|
"mask",
|
|
263
287
|
[
|
|
@@ -271,17 +295,13 @@ def test_read_mask_invalid(naturalearth_lowres, mask):
|
|
|
271
295
|
read(naturalearth_lowres, mask=mask)
|
|
272
296
|
|
|
273
297
|
|
|
274
|
-
@
|
|
275
|
-
not HAS_SHAPELY, reason="Shapely is required for mask functionality"
|
|
276
|
-
)
|
|
298
|
+
@requires_shapely
|
|
277
299
|
def test_read_bbox_mask_invalid(naturalearth_lowres):
|
|
278
300
|
with pytest.raises(ValueError, match="cannot set both 'bbox' and 'mask'"):
|
|
279
301
|
read(naturalearth_lowres, bbox=(-85, 8, -80, 10), mask=shapely.Point(-105, 55))
|
|
280
302
|
|
|
281
303
|
|
|
282
|
-
@
|
|
283
|
-
not HAS_SHAPELY, reason="Shapely is required for mask functionality"
|
|
284
|
-
)
|
|
304
|
+
@requires_shapely
|
|
285
305
|
@pytest.mark.parametrize(
|
|
286
306
|
"mask,expected",
|
|
287
307
|
[
|
|
@@ -316,9 +336,7 @@ def test_read_mask(naturalearth_lowres_all_ext, mask, expected):
|
|
|
316
336
|
assert len(geometry) == len(expected)
|
|
317
337
|
|
|
318
338
|
|
|
319
|
-
@
|
|
320
|
-
not HAS_SHAPELY, reason="Shapely is required for mask functionality"
|
|
321
|
-
)
|
|
339
|
+
@requires_shapely
|
|
322
340
|
def test_read_mask_sql(naturalearth_lowres_all_ext):
|
|
323
341
|
fields = read(
|
|
324
342
|
naturalearth_lowres_all_ext,
|
|
@@ -329,9 +347,7 @@ def test_read_mask_sql(naturalearth_lowres_all_ext):
|
|
|
329
347
|
assert np.array_equal(fields[3], ["CAN"])
|
|
330
348
|
|
|
331
349
|
|
|
332
|
-
@
|
|
333
|
-
not HAS_SHAPELY, reason="Shapely is required for mask functionality"
|
|
334
|
-
)
|
|
350
|
+
@requires_shapely
|
|
335
351
|
def test_read_mask_where(naturalearth_lowres_all_ext):
|
|
336
352
|
fields = read(
|
|
337
353
|
naturalearth_lowres_all_ext,
|
|
@@ -603,13 +619,80 @@ def test_write_no_geom_no_fields():
|
|
|
603
619
|
__gdal_version__ < (3, 6, 0),
|
|
604
620
|
reason="OpenFileGDB write support only available for GDAL >= 3.6.0",
|
|
605
621
|
)
|
|
606
|
-
|
|
607
|
-
|
|
622
|
+
@pytest.mark.parametrize(
|
|
623
|
+
"write_int64",
|
|
624
|
+
[
|
|
625
|
+
False,
|
|
626
|
+
pytest.param(
|
|
627
|
+
True,
|
|
628
|
+
marks=pytest.mark.skipif(
|
|
629
|
+
__gdal_version__ < (3, 9, 0),
|
|
630
|
+
reason="OpenFileGDB write support for int64 values for GDAL >= 3.9.0",
|
|
631
|
+
),
|
|
632
|
+
),
|
|
633
|
+
],
|
|
634
|
+
)
|
|
635
|
+
def test_write_openfilegdb(tmp_path, write_int64):
|
|
636
|
+
# Point(0, 0)
|
|
637
|
+
expected_geometry = np.array(
|
|
638
|
+
[bytes.fromhex("010100000000000000000000000000000000000000")] * 3, dtype=object
|
|
639
|
+
)
|
|
640
|
+
expected_field_data = [
|
|
641
|
+
np.array([True, False, True], dtype="bool"),
|
|
642
|
+
np.array([1, 2, 3], dtype="int16"),
|
|
643
|
+
np.array([1, 2, 3], dtype="int32"),
|
|
644
|
+
np.array([1, 2, 3], dtype="int64"),
|
|
645
|
+
np.array([1, 2, 3], dtype="float32"),
|
|
646
|
+
np.array([1, 2, 3], dtype="float64"),
|
|
647
|
+
]
|
|
648
|
+
expected_fields = ["bool", "int16", "int32", "int64", "float32", "float64"]
|
|
649
|
+
expected_meta = {
|
|
650
|
+
"geometry_type": "Point",
|
|
651
|
+
"crs": "EPSG:4326",
|
|
652
|
+
"fields": expected_fields,
|
|
653
|
+
}
|
|
608
654
|
|
|
609
655
|
filename = tmp_path / "test.gdb"
|
|
610
|
-
write(filename, geometry, field_data, driver="OpenFileGDB", **meta)
|
|
611
656
|
|
|
612
|
-
|
|
657
|
+
# int64 is not supported without additional config: https://gdal.org/en/latest/drivers/vector/openfilegdb.html#bit-integer-field-support
|
|
658
|
+
# it is converted to float64 by default and raises a warning
|
|
659
|
+
# (for GDAL >= 3.9.0 only)
|
|
660
|
+
write_params = (
|
|
661
|
+
{"TARGET_ARCGIS_VERSION": "ARCGIS_PRO_3_2_OR_LATER"} if write_int64 else {}
|
|
662
|
+
)
|
|
663
|
+
|
|
664
|
+
if write_int64 or __gdal_version__ < (3, 9, 0):
|
|
665
|
+
ctx = contextlib.nullcontext()
|
|
666
|
+
else:
|
|
667
|
+
ctx = pytest.warns(
|
|
668
|
+
RuntimeWarning, match="Integer64 will be written as a Float64"
|
|
669
|
+
)
|
|
670
|
+
|
|
671
|
+
with ctx:
|
|
672
|
+
write(
|
|
673
|
+
filename,
|
|
674
|
+
expected_geometry,
|
|
675
|
+
expected_field_data,
|
|
676
|
+
driver="OpenFileGDB",
|
|
677
|
+
**expected_meta,
|
|
678
|
+
**write_params,
|
|
679
|
+
)
|
|
680
|
+
|
|
681
|
+
meta, _, geometry, field_data = read(filename)
|
|
682
|
+
|
|
683
|
+
if not write_int64:
|
|
684
|
+
expected_field_data[3] = expected_field_data[3].astype("float64")
|
|
685
|
+
|
|
686
|
+
# bool types are converted to int32
|
|
687
|
+
expected_field_data[0] = expected_field_data[0].astype("int32")
|
|
688
|
+
|
|
689
|
+
assert meta["crs"] == expected_meta["crs"]
|
|
690
|
+
assert np.array_equal(meta["fields"], expected_meta["fields"])
|
|
691
|
+
|
|
692
|
+
assert np.array_equal(geometry, expected_geometry)
|
|
693
|
+
for i in range(len(expected_field_data)):
|
|
694
|
+
assert field_data[i].dtype == expected_field_data[i].dtype
|
|
695
|
+
assert np.array_equal(field_data[i], expected_field_data[i])
|
|
613
696
|
|
|
614
697
|
|
|
615
698
|
@pytest.mark.parametrize("ext", DRIVERS)
|
|
@@ -743,7 +826,7 @@ def assert_equal_result(result1, result2):
|
|
|
743
826
|
|
|
744
827
|
assert np.array_equal(meta1["fields"], meta2["fields"])
|
|
745
828
|
assert np.array_equal(index1, index2)
|
|
746
|
-
assert all(
|
|
829
|
+
assert all(np.array_equal(f1, f2) for f1, f2 in zip(field_data1, field_data2))
|
|
747
830
|
|
|
748
831
|
if HAS_SHAPELY:
|
|
749
832
|
# a plain `assert np.array_equal(geometry1, geometry2)` doesn't work
|
|
@@ -794,6 +877,12 @@ def test_read_from_file_like(tmp_path, naturalearth_lowres, driver, ext):
|
|
|
794
877
|
assert_equal_result((meta, index, geometry, field_data), result2)
|
|
795
878
|
|
|
796
879
|
|
|
880
|
+
def test_read_from_nonseekable_bytes(nonseekable_bytes):
|
|
881
|
+
meta, _, geometry, _ = read(nonseekable_bytes)
|
|
882
|
+
assert meta["fields"].shape == (0,)
|
|
883
|
+
assert len(geometry) == 1
|
|
884
|
+
|
|
885
|
+
|
|
797
886
|
@pytest.mark.parametrize("ext", ["gpkg", "fgb"])
|
|
798
887
|
def test_read_write_data_types_numeric(tmp_path, ext):
|
|
799
888
|
# Point(0, 0)
|
|
@@ -809,13 +898,13 @@ def test_read_write_data_types_numeric(tmp_path, ext):
|
|
|
809
898
|
np.array([1, 2, 3], dtype="float64"),
|
|
810
899
|
]
|
|
811
900
|
fields = ["bool", "int16", "int32", "int64", "float32", "float64"]
|
|
812
|
-
meta =
|
|
901
|
+
meta = {"geometry_type": "Point", "crs": "EPSG:4326", "spatial_index": False}
|
|
813
902
|
|
|
814
903
|
filename = tmp_path / f"test.{ext}"
|
|
815
904
|
write(filename, geometry, field_data, fields, **meta)
|
|
816
905
|
result = read(filename)[3]
|
|
817
|
-
assert all(
|
|
818
|
-
assert all(
|
|
906
|
+
assert all(np.array_equal(f1, f2) for f1, f2 in zip(result, field_data))
|
|
907
|
+
assert all(f1.dtype == f2.dtype for f1, f2 in zip(result, field_data))
|
|
819
908
|
|
|
820
909
|
# other integer data types that don't roundtrip exactly
|
|
821
910
|
# these are generally promoted to a larger integer type except for uint64
|
|
@@ -866,7 +955,7 @@ def test_read_write_datetime(tmp_path):
|
|
|
866
955
|
geometry = np.array(
|
|
867
956
|
[bytes.fromhex("010100000000000000000000000000000000000000")] * 2, dtype=object
|
|
868
957
|
)
|
|
869
|
-
meta =
|
|
958
|
+
meta = {"geometry_type": "Point", "crs": "EPSG:4326", "spatial_index": False}
|
|
870
959
|
|
|
871
960
|
filename = tmp_path / "test.gpkg"
|
|
872
961
|
write(filename, geometry, field_data, fields, **meta)
|
|
@@ -889,7 +978,7 @@ def test_read_write_int64_large(tmp_path, ext):
|
|
|
889
978
|
)
|
|
890
979
|
field_data = [np.array([1, 2192502720, -5], dtype="int64")]
|
|
891
980
|
fields = ["overflow_int64"]
|
|
892
|
-
meta =
|
|
981
|
+
meta = {"geometry_type": "Point", "crs": "EPSG:4326", "spatial_index": False}
|
|
893
982
|
|
|
894
983
|
filename = tmp_path / f"test.{ext}"
|
|
895
984
|
write(filename, geometry, field_data, fields, **meta)
|
|
@@ -912,17 +1001,17 @@ def test_read_data_types_numeric_with_null(test_gpkg_nulls):
|
|
|
912
1001
|
assert field.dtype == "float64"
|
|
913
1002
|
|
|
914
1003
|
|
|
915
|
-
def test_read_unsupported_types(
|
|
916
|
-
fields = read(
|
|
1004
|
+
def test_read_unsupported_types(list_field_values_file):
|
|
1005
|
+
fields = read(list_field_values_file)[3]
|
|
917
1006
|
# list field gets skipped, only integer field is read
|
|
918
1007
|
assert len(fields) == 1
|
|
919
1008
|
|
|
920
|
-
fields = read(
|
|
1009
|
+
fields = read(list_field_values_file, columns=["int64"])[3]
|
|
921
1010
|
assert len(fields) == 1
|
|
922
1011
|
|
|
923
1012
|
|
|
924
|
-
def test_read_datetime_millisecond(
|
|
925
|
-
field = read(
|
|
1013
|
+
def test_read_datetime_millisecond(datetime_file):
|
|
1014
|
+
field = read(datetime_file)[3][0]
|
|
926
1015
|
assert field.dtype == "datetime64[ms]"
|
|
927
1016
|
assert field[0] == np.datetime64("2020-01-01 09:00:00.123")
|
|
928
1017
|
assert field[1] == np.datetime64("2020-01-01 10:00:00.000")
|
|
@@ -951,13 +1040,14 @@ def test_read_unsupported_ext_with_prefix(tmp_path):
|
|
|
951
1040
|
assert field_data[0] == "data1"
|
|
952
1041
|
|
|
953
1042
|
|
|
954
|
-
def test_read_datetime_as_string(
|
|
955
|
-
field = read(
|
|
1043
|
+
def test_read_datetime_as_string(datetime_tz_file):
|
|
1044
|
+
field = read(datetime_tz_file)[3][0]
|
|
956
1045
|
assert field.dtype == "datetime64[ms]"
|
|
957
1046
|
# timezone is ignored in numpy layer
|
|
958
1047
|
assert field[0] == np.datetime64("2020-01-01 09:00:00.123")
|
|
959
1048
|
assert field[1] == np.datetime64("2020-01-01 10:00:00.000")
|
|
960
|
-
|
|
1049
|
+
|
|
1050
|
+
field = read(datetime_tz_file, datetime_as_string=True)[3][0]
|
|
961
1051
|
assert field.dtype == "object"
|
|
962
1052
|
# GDAL doesn't return strings in ISO format (yet)
|
|
963
1053
|
assert field[0] == "2020/01/01 09:00:00.123-05"
|
|
@@ -973,7 +1063,7 @@ def test_read_write_null_geometry(tmp_path, ext):
|
|
|
973
1063
|
)
|
|
974
1064
|
field_data = [np.array([1, 2], dtype="int32")]
|
|
975
1065
|
fields = ["col"]
|
|
976
|
-
meta =
|
|
1066
|
+
meta = {"geometry_type": "Point", "crs": "EPSG:4326"}
|
|
977
1067
|
if ext == "gpkg":
|
|
978
1068
|
meta["spatial_index"] = False
|
|
979
1069
|
|
|
@@ -993,12 +1083,12 @@ def test_write_float_nan_null(tmp_path, dtype):
|
|
|
993
1083
|
)
|
|
994
1084
|
field_data = [np.array([1.5, np.nan], dtype=dtype)]
|
|
995
1085
|
fields = ["col"]
|
|
996
|
-
meta =
|
|
1086
|
+
meta = {"geometry_type": "Point", "crs": "EPSG:4326"}
|
|
997
1087
|
filename = tmp_path / "test.geojson"
|
|
998
1088
|
|
|
999
1089
|
# default nan_as_null=True
|
|
1000
1090
|
write(filename, geometry, field_data, fields, **meta)
|
|
1001
|
-
with open(filename
|
|
1091
|
+
with open(filename) as f:
|
|
1002
1092
|
content = f.read()
|
|
1003
1093
|
assert '{ "col": null }' in content
|
|
1004
1094
|
|
|
@@ -1010,7 +1100,7 @@ def test_write_float_nan_null(tmp_path, dtype):
|
|
|
1010
1100
|
ctx = contextlib.nullcontext()
|
|
1011
1101
|
with ctx:
|
|
1012
1102
|
write(filename, geometry, field_data, fields, **meta, nan_as_null=False)
|
|
1013
|
-
with open(filename
|
|
1103
|
+
with open(filename) as f:
|
|
1014
1104
|
content = f.read()
|
|
1015
1105
|
assert '"properties": { }' in content
|
|
1016
1106
|
|
|
@@ -1024,7 +1114,7 @@ def test_write_float_nan_null(tmp_path, dtype):
|
|
|
1024
1114
|
nan_as_null=False,
|
|
1025
1115
|
WRITE_NON_FINITE_VALUES="YES",
|
|
1026
1116
|
)
|
|
1027
|
-
with open(filename
|
|
1117
|
+
with open(filename) as f:
|
|
1028
1118
|
content = f.read()
|
|
1029
1119
|
assert '{ "col": NaN }' in content
|
|
1030
1120
|
|
|
@@ -1043,7 +1133,7 @@ def test_write_float_nan_null_arrow(tmp_path):
|
|
|
1043
1133
|
)
|
|
1044
1134
|
field_data = [np.array([1.5, np.nan], dtype="float64")]
|
|
1045
1135
|
fields = ["col"]
|
|
1046
|
-
meta =
|
|
1136
|
+
meta = {"geometry_type": "Point", "crs": "EPSG:4326"}
|
|
1047
1137
|
fname = tmp_path / "test.arrow"
|
|
1048
1138
|
|
|
1049
1139
|
# default nan_as_null=True
|
|
@@ -1146,6 +1236,27 @@ def test_write_memory_existing_unsupported(naturalearth_lowres):
|
|
|
1146
1236
|
write(buffer, geometry, field_data, driver="GeoJSON", layer="test", **meta)
|
|
1147
1237
|
|
|
1148
1238
|
|
|
1239
|
+
def test_write_open_file_handle(tmp_path, naturalearth_lowres):
|
|
1240
|
+
"""Verify that writing to an open file handle is not currently supported"""
|
|
1241
|
+
|
|
1242
|
+
meta, _, geometry, field_data = read(naturalearth_lowres)
|
|
1243
|
+
|
|
1244
|
+
# verify it fails for regular file handle
|
|
1245
|
+
with pytest.raises(
|
|
1246
|
+
NotImplementedError, match="writing to an open file handle is not yet supported"
|
|
1247
|
+
):
|
|
1248
|
+
with open(tmp_path / "test.geojson", "wb") as f:
|
|
1249
|
+
write(f, geometry, field_data, driver="GeoJSON", layer="test", **meta)
|
|
1250
|
+
|
|
1251
|
+
# verify it fails for ZipFile
|
|
1252
|
+
with pytest.raises(
|
|
1253
|
+
NotImplementedError, match="writing to an open file handle is not yet supported"
|
|
1254
|
+
):
|
|
1255
|
+
with ZipFile(tmp_path / "test.geojson.zip", "w") as z:
|
|
1256
|
+
with z.open("test.geojson", "w") as f:
|
|
1257
|
+
write(f, geometry, field_data, driver="GeoJSON", layer="test", **meta)
|
|
1258
|
+
|
|
1259
|
+
|
|
1149
1260
|
@pytest.mark.parametrize("ext", ["fgb", "gpkg", "geojson"])
|
|
1150
1261
|
@pytest.mark.parametrize(
|
|
1151
1262
|
"read_encoding,write_encoding",
|
|
@@ -1182,7 +1293,7 @@ def test_encoding_io(tmp_path, ext, read_encoding, write_encoding):
|
|
|
1182
1293
|
np.array([mandarin], dtype=object),
|
|
1183
1294
|
]
|
|
1184
1295
|
fields = [arabic, cree, mandarin]
|
|
1185
|
-
meta =
|
|
1296
|
+
meta = {"geometry_type": "Point", "crs": "EPSG:4326", "encoding": write_encoding}
|
|
1186
1297
|
|
|
1187
1298
|
filename = tmp_path / f"test.{ext}"
|
|
1188
1299
|
write(filename, geometry, field_data, fields, **meta)
|
|
@@ -1232,7 +1343,7 @@ def test_encoding_io_shapefile(tmp_path, read_encoding, write_encoding):
|
|
|
1232
1343
|
# character level) by GDAL when output to shapefile, so we have to truncate
|
|
1233
1344
|
# before writing
|
|
1234
1345
|
fields = [arabic[:5], cree[:3], mandarin]
|
|
1235
|
-
meta =
|
|
1346
|
+
meta = {"geometry_type": "Point", "crs": "EPSG:4326", "encoding": "UTF-8"}
|
|
1236
1347
|
|
|
1237
1348
|
filename = tmp_path / "test.shp"
|
|
1238
1349
|
# NOTE: GDAL automatically creates a cpg file with the encoding name, which
|
|
@@ -1277,7 +1388,7 @@ def test_non_utf8_encoding_io(tmp_path, ext, encoded_text):
|
|
|
1277
1388
|
field_data = [np.array([text], dtype=object)]
|
|
1278
1389
|
|
|
1279
1390
|
fields = [text]
|
|
1280
|
-
meta =
|
|
1391
|
+
meta = {"geometry_type": "Point", "crs": "EPSG:4326", "encoding": encoding}
|
|
1281
1392
|
|
|
1282
1393
|
filename = tmp_path / f"test.{ext}"
|
|
1283
1394
|
write(filename, geometry, field_data, fields, **meta)
|
|
@@ -1307,7 +1418,7 @@ def test_non_utf8_encoding_io_shapefile(tmp_path, encoded_text):
|
|
|
1307
1418
|
field_data = [np.array([text], dtype=object)]
|
|
1308
1419
|
|
|
1309
1420
|
fields = [text]
|
|
1310
|
-
meta =
|
|
1421
|
+
meta = {"geometry_type": "Point", "crs": "EPSG:4326", "encoding": encoding}
|
|
1311
1422
|
|
|
1312
1423
|
filename = tmp_path / "test.shp"
|
|
1313
1424
|
write(filename, geometry, field_data, fields, **meta)
|
|
@@ -1357,7 +1468,7 @@ def test_write_with_mask(tmp_path):
|
|
|
1357
1468
|
field_data = [np.array([1, 2, 3], dtype="int32")]
|
|
1358
1469
|
field_mask = [np.array([False, True, False])]
|
|
1359
1470
|
fields = ["col"]
|
|
1360
|
-
meta =
|
|
1471
|
+
meta = {"geometry_type": "Point", "crs": "EPSG:4326"}
|
|
1361
1472
|
|
|
1362
1473
|
filename = tmp_path / "test.geojson"
|
|
1363
1474
|
write(filename, geometry, field_data, fields, field_mask, **meta)
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from pyogrio import vsi_listtree, vsi_unlink
|
|
4
|
+
from pyogrio.raw import read, write
|
|
5
|
+
from pyogrio.util import vsimem_rmtree_toplevel
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_vsimem_rmtree_toplevel(naturalearth_lowres):
|
|
11
|
+
# Prepare test data in /vsimem/
|
|
12
|
+
meta, _, geometry, field_data = read(naturalearth_lowres)
|
|
13
|
+
meta["spatial_index"] = False
|
|
14
|
+
meta["geometry_type"] = "MultiPolygon"
|
|
15
|
+
test_dir_path = Path(f"/vsimem/test/{naturalearth_lowres.stem}.gpkg")
|
|
16
|
+
test_dir2_path = Path(f"/vsimem/test2/test2/{naturalearth_lowres.stem}.gpkg")
|
|
17
|
+
|
|
18
|
+
write(test_dir_path, geometry, field_data, **meta)
|
|
19
|
+
write(test_dir2_path, geometry, field_data, **meta)
|
|
20
|
+
|
|
21
|
+
# Check if everything was created properly with listtree
|
|
22
|
+
files = vsi_listtree("/vsimem/")
|
|
23
|
+
assert test_dir_path.as_posix() in files
|
|
24
|
+
assert test_dir2_path.as_posix() in files
|
|
25
|
+
|
|
26
|
+
# Test deleting parent dir of file in single directory
|
|
27
|
+
vsimem_rmtree_toplevel(test_dir_path)
|
|
28
|
+
files = vsi_listtree("/vsimem/")
|
|
29
|
+
assert test_dir_path.parent.as_posix() not in files
|
|
30
|
+
assert test_dir2_path.as_posix() in files
|
|
31
|
+
|
|
32
|
+
# Test deleting top-level dir of file in a subdirectory
|
|
33
|
+
vsimem_rmtree_toplevel(test_dir2_path)
|
|
34
|
+
assert test_dir2_path.as_posix() not in vsi_listtree("/vsimem/")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_vsimem_rmtree_toplevel_error(naturalearth_lowres):
|
|
38
|
+
# Prepare test data in /vsimem
|
|
39
|
+
meta, _, geometry, field_data = read(naturalearth_lowres)
|
|
40
|
+
meta["spatial_index"] = False
|
|
41
|
+
meta["geometry_type"] = "MultiPolygon"
|
|
42
|
+
test_file_path = Path(f"/vsimem/pyogrio_test_{naturalearth_lowres.stem}.gpkg")
|
|
43
|
+
|
|
44
|
+
write(test_file_path, geometry, field_data, **meta)
|
|
45
|
+
assert test_file_path.as_posix() in vsi_listtree("/vsimem/")
|
|
46
|
+
|
|
47
|
+
# Deleting parent dir of non-existent file should raise an error.
|
|
48
|
+
with pytest.raises(FileNotFoundError, match="Path does not exist"):
|
|
49
|
+
vsimem_rmtree_toplevel("/vsimem/test/non-existent.gpkg")
|
|
50
|
+
|
|
51
|
+
# File should still be there
|
|
52
|
+
assert test_file_path.as_posix() in vsi_listtree("/vsimem/")
|
|
53
|
+
|
|
54
|
+
# Cleanup.
|
|
55
|
+
vsi_unlink(test_file_path)
|
|
56
|
+
assert test_file_path not in vsi_listtree("/vsimem/")
|