pyogrio 0.9.0__cp39-cp39-win_amd64.whl → 0.10.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 +33 -22
- pyogrio/_compat.py +7 -1
- pyogrio/_env.py +4 -6
- pyogrio/_err.c +460 -445
- pyogrio/_err.cp39-win_amd64.pyd +0 -0
- pyogrio/_geometry.c +645 -612
- pyogrio/_geometry.cp39-win_amd64.pyd +0 -0
- pyogrio/_io.c +7749 -7596
- pyogrio/_io.cp39-win_amd64.pyd +0 -0
- pyogrio/_ogr.c +601 -609
- pyogrio/_ogr.cp39-win_amd64.pyd +0 -0
- pyogrio/_version.py +3 -3
- pyogrio/_vsi.c +7570 -2514
- pyogrio/_vsi.cp39-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 +32 -24
- 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 +239 -99
- pyogrio/tests/test_path.py +29 -17
- pyogrio/tests/test_raw_io.py +165 -54
- pyogrio/tests/test_util.py +56 -0
- pyogrio/util.py +54 -30
- pyogrio-0.10.0.dist-info/DELVEWHEEL +2 -0
- {pyogrio-0.9.0.dist-info → pyogrio-0.10.0.dist-info}/LICENSE +1 -1
- {pyogrio-0.9.0.dist-info → pyogrio-0.10.0.dist-info}/METADATA +37 -8
- {pyogrio-0.9.0.dist-info → pyogrio-0.10.0.dist-info}/RECORD +75 -91
- {pyogrio-0.9.0.dist-info → pyogrio-0.10.0.dist-info}/WHEEL +1 -1
- pyogrio.libs/.load-order-pyogrio-0.10.0 +18 -0
- pyogrio.libs/{Lerc-5e4d8cbeeabca06f95e2270792304dc3.dll → Lerc-089e3fef3df84b17326dcddbf1dedaa4.dll} +0 -0
- pyogrio.libs/{gdal-b434963605a006e01c486c0df6dea4e0.dll → gdal-debee5933f0da7bb90b4bcd009023377.dll} +0 -0
- pyogrio.libs/{geos-f0622d0794b81c937a851b2e6fa9b712.dll → geos-ace4c5b5c1f569bb4213e7bbd0b0322e.dll} +0 -0
- pyogrio.libs/{geos_c-0e16bf70612fc3301d077b9d863a3fdb.dll → geos_c-7478ca0a86136b280d9b2d245c6f6627.dll} +0 -0
- pyogrio.libs/geotiff-c8fe8a095520a4ea4e465d27e06add3a.dll +0 -0
- pyogrio.libs/{iconv-2-8fcc23ddc6f096c45871011b6e008b44.dll → iconv-2-27352d156a5467ca5383d3951093ea5a.dll} +0 -0
- pyogrio.libs/{jpeg62-2f9b7af22d78338e8f0be0058503dc35.dll → jpeg62-e56b6f95a95af498f4623b8da4cebd46.dll} +0 -0
- pyogrio.libs/{json-c-e52a077545e4057de42beb4948289b41.dll → json-c-c84940e2654a4f8468bfcf2ce992aa93.dll} +0 -0
- pyogrio.libs/libcurl-d69cfd4ad487d53d58743b6778ec85e7.dll +0 -0
- pyogrio.libs/{libexpat-fbe03ca8917dfda776562d4338b289b8.dll → libexpat-6576a8d02641b6a3dbad35901ec200a7.dll} +0 -0
- pyogrio.libs/liblzma-9ee4accb476ec1ae24e924953140273d.dll +0 -0
- pyogrio.libs/{libpng16-13928571ad910705eae8d7dd8eef8b11.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-c45abde5d0c92faf723cc2942138af77.dll → qhull_r-516897f855568caab1ab1fe37912766c.dll} +0 -0
- pyogrio.libs/sqlite3-9bc109d8536d5ed9666332fec94485fc.dll +0 -0
- pyogrio.libs/{tiff-43630f30487a9015213475ae86ed3fa3.dll → tiff-9b3f605fffe0bccc0a964c374ee4f820.dll} +0 -0
- pyogrio.libs/{zlib1-e1272810861a13dd8d6cff3beac47f17.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 -2742
- pyogrio/_ogr.pxd +0 -444
- 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/poly_not_enough_points.shp.zip +0 -0
- 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.9.0.dist-info/DELVEWHEEL +0 -2
- pyogrio.libs/.load-order-pyogrio-0.9.0 +0 -18
- pyogrio.libs/geotiff-772e7c705fb15ddf91b432adb4eb1f6c.dll +0 -0
- pyogrio.libs/libcurl-bc81cd8afe15b10c0821b181b6af8bd0.dll +0 -0
- pyogrio.libs/liblzma-6b36f24d54d3dd45f274a2aebef81085.dll +0 -0
- pyogrio.libs/proj-8a30239ef2dfc3b9dd2bb48e8abb330f.dll +0 -0
- pyogrio.libs/sqlite3-df30c3cf230727e23c43c40126a530f7.dll +0 -0
- {pyogrio-0.9.0.dist-info → pyogrio-0.10.0.dist-info}/top_level.txt +0 -0
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
import contextlib
|
|
2
|
+
import locale
|
|
3
|
+
import warnings
|
|
2
4
|
from datetime import datetime
|
|
3
5
|
from io import BytesIO
|
|
4
|
-
import
|
|
6
|
+
from zipfile import ZipFile
|
|
5
7
|
|
|
6
8
|
import numpy as np
|
|
7
|
-
import pytest
|
|
8
9
|
|
|
9
|
-
from pyogrio import
|
|
10
|
+
from pyogrio import (
|
|
11
|
+
__gdal_version__,
|
|
12
|
+
list_drivers,
|
|
13
|
+
list_layers,
|
|
14
|
+
read_info,
|
|
15
|
+
vsi_listtree,
|
|
16
|
+
vsi_unlink,
|
|
17
|
+
)
|
|
18
|
+
from pyogrio._compat import HAS_ARROW_WRITE_API, HAS_PYPROJ, PANDAS_GE_15
|
|
10
19
|
from pyogrio.errors import DataLayerError, DataSourceError, FeatureError, GeometryError
|
|
11
|
-
from pyogrio.geopandas import read_dataframe, write_dataframe
|
|
20
|
+
from pyogrio.geopandas import PANDAS_GE_20, read_dataframe, write_dataframe
|
|
12
21
|
from pyogrio.raw import (
|
|
13
22
|
DRIVERS_NO_MIXED_DIMENSIONS,
|
|
14
23
|
DRIVERS_NO_MIXED_SINGLE_MULTI,
|
|
@@ -16,27 +25,29 @@ from pyogrio.raw import (
|
|
|
16
25
|
from pyogrio.tests.conftest import (
|
|
17
26
|
ALL_EXTS,
|
|
18
27
|
DRIVERS,
|
|
19
|
-
|
|
28
|
+
START_FID,
|
|
20
29
|
requires_arrow_write_api,
|
|
21
30
|
requires_gdal_geos,
|
|
31
|
+
requires_pyarrow_api,
|
|
32
|
+
requires_pyproj,
|
|
22
33
|
)
|
|
23
|
-
from pyogrio._compat import PANDAS_GE_15, HAS_ARROW_WRITE_API
|
|
24
34
|
|
|
25
|
-
|
|
26
|
-
import pandas as pd
|
|
27
|
-
from pandas.testing import (
|
|
28
|
-
assert_frame_equal,
|
|
29
|
-
assert_index_equal,
|
|
30
|
-
assert_series_equal,
|
|
31
|
-
)
|
|
35
|
+
import pytest
|
|
32
36
|
|
|
37
|
+
try:
|
|
33
38
|
import geopandas as gp
|
|
39
|
+
import pandas as pd
|
|
34
40
|
from geopandas.array import from_wkt
|
|
35
|
-
from geopandas.testing import assert_geodataframe_equal
|
|
36
41
|
|
|
37
42
|
import shapely # if geopandas is present, shapely is expected to be present
|
|
38
43
|
from shapely.geometry import Point
|
|
39
44
|
|
|
45
|
+
from geopandas.testing import assert_geodataframe_equal
|
|
46
|
+
from pandas.testing import (
|
|
47
|
+
assert_index_equal,
|
|
48
|
+
assert_series_equal,
|
|
49
|
+
)
|
|
50
|
+
|
|
40
51
|
except ImportError:
|
|
41
52
|
pass
|
|
42
53
|
|
|
@@ -124,7 +135,8 @@ def test_read_csv_platform_encoding(tmp_path):
|
|
|
124
135
|
def test_read_dataframe(naturalearth_lowres_all_ext):
|
|
125
136
|
df = read_dataframe(naturalearth_lowres_all_ext)
|
|
126
137
|
|
|
127
|
-
|
|
138
|
+
if HAS_PYPROJ:
|
|
139
|
+
assert df.crs == "EPSG:4326"
|
|
128
140
|
assert len(df) == 177
|
|
129
141
|
assert df.columns.tolist() == [
|
|
130
142
|
"pop_est",
|
|
@@ -142,14 +154,13 @@ def test_read_dataframe_vsi(naturalearth_lowres_vsi, use_arrow):
|
|
|
142
154
|
|
|
143
155
|
|
|
144
156
|
@pytest.mark.parametrize(
|
|
145
|
-
"columns, fid_as_index, exp_len", [(None, False,
|
|
157
|
+
"columns, fid_as_index, exp_len", [(None, False, 3), ([], True, 3), ([], False, 0)]
|
|
146
158
|
)
|
|
147
159
|
def test_read_layer_without_geometry(
|
|
148
|
-
|
|
160
|
+
no_geometry_file, columns, fid_as_index, use_arrow, exp_len
|
|
149
161
|
):
|
|
150
162
|
result = read_dataframe(
|
|
151
|
-
|
|
152
|
-
layer="basetable",
|
|
163
|
+
no_geometry_file,
|
|
153
164
|
columns=columns,
|
|
154
165
|
fid_as_index=fid_as_index,
|
|
155
166
|
use_arrow=use_arrow,
|
|
@@ -195,38 +206,62 @@ def test_read_no_geometry_no_columns_no_fids(naturalearth_lowres, use_arrow):
|
|
|
195
206
|
)
|
|
196
207
|
|
|
197
208
|
|
|
198
|
-
def test_read_force_2d(
|
|
199
|
-
|
|
200
|
-
UserWarning, match=r"Measured \(M\) geometry types are not supported"
|
|
201
|
-
):
|
|
202
|
-
df = read_dataframe(test_fgdb_vsi, layer="test_lines", max_features=1)
|
|
203
|
-
assert df.iloc[0].geometry.has_z
|
|
209
|
+
def test_read_force_2d(tmp_path, use_arrow):
|
|
210
|
+
filename = tmp_path / "test.gpkg"
|
|
204
211
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
212
|
+
# create a GPKG with 3D point values
|
|
213
|
+
expected = gp.GeoDataFrame(
|
|
214
|
+
geometry=[Point(0, 0, 0), Point(1, 1, 0)], crs="EPSG:4326"
|
|
215
|
+
)
|
|
216
|
+
write_dataframe(expected, filename)
|
|
217
|
+
|
|
218
|
+
df = read_dataframe(filename)
|
|
219
|
+
assert df.iloc[0].geometry.has_z
|
|
220
|
+
|
|
221
|
+
df = read_dataframe(
|
|
222
|
+
filename,
|
|
223
|
+
force_2d=True,
|
|
224
|
+
max_features=1,
|
|
225
|
+
use_arrow=use_arrow,
|
|
226
|
+
)
|
|
227
|
+
assert not df.iloc[0].geometry.has_z
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def test_read_layer(tmp_path, use_arrow):
|
|
231
|
+
filename = tmp_path / "test.gpkg"
|
|
213
232
|
|
|
233
|
+
# create a multilayer GPKG
|
|
234
|
+
expected1 = gp.GeoDataFrame(geometry=[Point(0, 0)], crs="EPSG:4326")
|
|
235
|
+
write_dataframe(
|
|
236
|
+
expected1,
|
|
237
|
+
filename,
|
|
238
|
+
layer="layer1",
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
expected2 = gp.GeoDataFrame(geometry=[Point(1, 1)], crs="EPSG:4326")
|
|
242
|
+
write_dataframe(expected2, filename, layer="layer2", append=True)
|
|
243
|
+
|
|
244
|
+
assert np.array_equal(
|
|
245
|
+
list_layers(filename), [["layer1", "Point"], ["layer2", "Point"]]
|
|
246
|
+
)
|
|
214
247
|
|
|
215
|
-
|
|
216
|
-
@pytest.mark.filterwarnings("ignore: More than one layer found in")
|
|
217
|
-
def test_read_layer(test_fgdb_vsi, use_arrow):
|
|
218
|
-
layers = list_layers(test_fgdb_vsi)
|
|
219
|
-
kwargs = {"use_arrow": use_arrow, "read_geometry": False, "max_features": 1}
|
|
248
|
+
kwargs = {"use_arrow": use_arrow, "max_features": 1}
|
|
220
249
|
|
|
221
|
-
# The first layer is read by default
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
250
|
+
# The first layer is read by default, which will warn when there are multiple
|
|
251
|
+
# layers
|
|
252
|
+
with pytest.warns(UserWarning, match="More than one layer found"):
|
|
253
|
+
df = read_dataframe(filename, **kwargs)
|
|
225
254
|
|
|
226
|
-
|
|
255
|
+
assert_geodataframe_equal(df, expected1)
|
|
256
|
+
|
|
257
|
+
# Reading a specific layer by name should return that layer.
|
|
227
258
|
# Detected here by a known column.
|
|
228
|
-
df = read_dataframe(
|
|
229
|
-
|
|
259
|
+
df = read_dataframe(filename, layer="layer2", **kwargs)
|
|
260
|
+
assert_geodataframe_equal(df, expected2)
|
|
261
|
+
|
|
262
|
+
# Reading a specific layer by index should return that layer
|
|
263
|
+
df = read_dataframe(filename, layer=1, **kwargs)
|
|
264
|
+
assert_geodataframe_equal(df, expected2)
|
|
230
265
|
|
|
231
266
|
|
|
232
267
|
def test_read_layer_invalid(naturalearth_lowres_all_ext, use_arrow):
|
|
@@ -234,22 +269,19 @@ def test_read_layer_invalid(naturalearth_lowres_all_ext, use_arrow):
|
|
|
234
269
|
read_dataframe(naturalearth_lowres_all_ext, layer="wrong", use_arrow=use_arrow)
|
|
235
270
|
|
|
236
271
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
df = read_dataframe(
|
|
240
|
-
test_fgdb_vsi, layer="test_lines", use_arrow=use_arrow, max_features=1
|
|
241
|
-
)
|
|
272
|
+
def test_read_datetime(datetime_file, use_arrow):
|
|
273
|
+
df = read_dataframe(datetime_file, use_arrow=use_arrow)
|
|
242
274
|
if PANDAS_GE_20:
|
|
243
275
|
# starting with pandas 2.0, it preserves the passed datetime resolution
|
|
244
|
-
assert df.
|
|
276
|
+
assert df.col.dtype.name == "datetime64[ms]"
|
|
245
277
|
else:
|
|
246
|
-
assert df.
|
|
278
|
+
assert df.col.dtype.name == "datetime64[ns]"
|
|
247
279
|
|
|
248
280
|
|
|
249
281
|
@pytest.mark.filterwarnings("ignore: Non-conformant content for record 1 in column ")
|
|
250
282
|
@pytest.mark.requires_arrow_write_api
|
|
251
|
-
def test_read_datetime_tz(
|
|
252
|
-
df = read_dataframe(
|
|
283
|
+
def test_read_datetime_tz(datetime_tz_file, tmp_path, use_arrow):
|
|
284
|
+
df = read_dataframe(datetime_tz_file)
|
|
253
285
|
# Make the index non-consecutive to test this case as well. Added for issue
|
|
254
286
|
# https://github.com/geopandas/pyogrio/issues/324
|
|
255
287
|
df = df.set_index(np.array([0, 2]))
|
|
@@ -319,14 +351,17 @@ def test_read_write_datetime_tz_with_nulls(tmp_path, use_arrow):
|
|
|
319
351
|
assert_geodataframe_equal(df, result)
|
|
320
352
|
|
|
321
353
|
|
|
322
|
-
def test_read_null_values(
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
354
|
+
def test_read_null_values(tmp_path, use_arrow):
|
|
355
|
+
filename = tmp_path / "test_null_values_no_geometry.gpkg"
|
|
356
|
+
|
|
357
|
+
# create a GPKG with no geometries and only null values
|
|
358
|
+
expected = pd.DataFrame({"col": [None, None]})
|
|
359
|
+
write_dataframe(expected, filename)
|
|
360
|
+
|
|
361
|
+
df = read_dataframe(filename, use_arrow=use_arrow, read_geometry=False)
|
|
326
362
|
|
|
327
363
|
# make sure that Null values are preserved
|
|
328
|
-
assert df.
|
|
329
|
-
assert df.loc[df.SEGMENT_NAME.isnull()].SEGMENT_NAME.iloc[0] is None
|
|
364
|
+
assert np.array_equal(df.col.values, expected.col.values)
|
|
330
365
|
|
|
331
366
|
|
|
332
367
|
def test_read_fid_as_index(naturalearth_lowres_all_ext, use_arrow):
|
|
@@ -344,12 +379,9 @@ def test_read_fid_as_index(naturalearth_lowres_all_ext, use_arrow):
|
|
|
344
379
|
fid_as_index=True,
|
|
345
380
|
**kwargs,
|
|
346
381
|
)
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
else:
|
|
351
|
-
# File format where fid starts at 0
|
|
352
|
-
assert_index_equal(df.index, pd.Index([2, 3], name="fid"))
|
|
382
|
+
fids_expected = pd.Index([2, 3], name="fid")
|
|
383
|
+
fids_expected += START_FID[naturalearth_lowres_all_ext.suffix]
|
|
384
|
+
assert_index_equal(df.index, fids_expected)
|
|
353
385
|
|
|
354
386
|
|
|
355
387
|
def test_read_fid_as_index_only(naturalearth_lowres, use_arrow):
|
|
@@ -605,17 +637,22 @@ def test_read_fids_arrow_warning_old_gdal(naturalearth_lowres_all_ext):
|
|
|
605
637
|
assert len(df) == 1
|
|
606
638
|
|
|
607
639
|
|
|
608
|
-
def test_read_fids_force_2d(
|
|
609
|
-
|
|
610
|
-
UserWarning, match=r"Measured \(M\) geometry types are not supported"
|
|
611
|
-
):
|
|
612
|
-
df = read_dataframe(test_fgdb_vsi, layer="test_lines", fids=[22])
|
|
613
|
-
assert len(df) == 1
|
|
614
|
-
assert df.iloc[0].geometry.has_z
|
|
640
|
+
def test_read_fids_force_2d(tmp_path):
|
|
641
|
+
filename = tmp_path / "test.gpkg"
|
|
615
642
|
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
643
|
+
# create a GPKG with 3D point values
|
|
644
|
+
expected = gp.GeoDataFrame(
|
|
645
|
+
geometry=[Point(0, 0, 0), Point(1, 1, 0)], crs="EPSG:4326"
|
|
646
|
+
)
|
|
647
|
+
write_dataframe(expected, filename)
|
|
648
|
+
|
|
649
|
+
df = read_dataframe(filename, fids=[1])
|
|
650
|
+
assert_geodataframe_equal(df, expected.iloc[:1])
|
|
651
|
+
|
|
652
|
+
df = read_dataframe(filename, force_2d=True, fids=[1])
|
|
653
|
+
assert np.array_equal(
|
|
654
|
+
df.geometry.values, shapely.force_2d(expected.iloc[:1].geometry.values)
|
|
655
|
+
)
|
|
619
656
|
|
|
620
657
|
|
|
621
658
|
@pytest.mark.parametrize("skip_features", [10, 200])
|
|
@@ -769,7 +806,7 @@ def test_read_sql_invalid(naturalearth_lowres_all_ext, use_arrow):
|
|
|
769
806
|
)
|
|
770
807
|
|
|
771
808
|
with pytest.raises(
|
|
772
|
-
ValueError, match="'sql'
|
|
809
|
+
ValueError, match="'sql' parameter cannot be combined with 'layer'"
|
|
773
810
|
):
|
|
774
811
|
read_dataframe(
|
|
775
812
|
naturalearth_lowres_all_ext,
|
|
@@ -924,9 +961,9 @@ def test_write_csv_encoding(tmp_path, encoding):
|
|
|
924
961
|
write_dataframe(df, csv_pyogrio_path, encoding=encoding)
|
|
925
962
|
|
|
926
963
|
# Check if the text files written both ways can be read again and give same result.
|
|
927
|
-
with open(csv_path,
|
|
964
|
+
with open(csv_path, encoding=encoding) as csv:
|
|
928
965
|
csv_str = csv.read()
|
|
929
|
-
with open(csv_pyogrio_path,
|
|
966
|
+
with open(csv_pyogrio_path, encoding=encoding) as csv_pyogrio:
|
|
930
967
|
csv_pyogrio_str = csv_pyogrio.read()
|
|
931
968
|
assert csv_str == csv_pyogrio_str
|
|
932
969
|
|
|
@@ -960,7 +997,7 @@ def test_write_dataframe(tmp_path, naturalearth_lowres, ext, use_arrow):
|
|
|
960
997
|
if DRIVERS[ext] in DRIVERS_NO_MIXED_SINGLE_MULTI:
|
|
961
998
|
assert list(geometry_types) == ["MultiPolygon"]
|
|
962
999
|
else:
|
|
963
|
-
assert set(geometry_types) ==
|
|
1000
|
+
assert set(geometry_types) == {"MultiPolygon", "Polygon"}
|
|
964
1001
|
|
|
965
1002
|
# Coordinates are not precisely equal when written to JSON
|
|
966
1003
|
# dtypes do not necessarily round-trip precisely through JSON
|
|
@@ -1062,6 +1099,23 @@ def test_write_empty_dataframe(tmp_path, ext, use_arrow):
|
|
|
1062
1099
|
assert_geodataframe_equal(df, expected)
|
|
1063
1100
|
|
|
1064
1101
|
|
|
1102
|
+
def test_write_empty_geometry(tmp_path):
|
|
1103
|
+
expected = gp.GeoDataFrame({"x": [0]}, geometry=from_wkt(["POINT EMPTY"]), crs=4326)
|
|
1104
|
+
filename = tmp_path / "test.gpkg"
|
|
1105
|
+
|
|
1106
|
+
# Check that no warning is raised with GeoSeries.notna()
|
|
1107
|
+
with warnings.catch_warnings():
|
|
1108
|
+
warnings.simplefilter("error", UserWarning)
|
|
1109
|
+
if not HAS_PYPROJ:
|
|
1110
|
+
warnings.filterwarnings("ignore", message="'crs' was not provided.")
|
|
1111
|
+
write_dataframe(expected, filename)
|
|
1112
|
+
assert filename.exists()
|
|
1113
|
+
|
|
1114
|
+
# Xref GH-436: round-tripping possible with GPKG but not others
|
|
1115
|
+
df = read_dataframe(filename)
|
|
1116
|
+
assert_geodataframe_equal(df, expected)
|
|
1117
|
+
|
|
1118
|
+
|
|
1065
1119
|
@pytest.mark.parametrize("ext", [".geojsonl", ".geojsons"])
|
|
1066
1120
|
@pytest.mark.requires_arrow_write_api
|
|
1067
1121
|
def test_write_read_empty_dataframe_unsupported(tmp_path, ext, use_arrow):
|
|
@@ -1161,7 +1215,7 @@ def test_write_dataframe_gdal_options(
|
|
|
1161
1215
|
df,
|
|
1162
1216
|
outfilename2,
|
|
1163
1217
|
use_arrow=use_arrow,
|
|
1164
|
-
layer_options=
|
|
1218
|
+
layer_options={"spatial_index": spatial_index},
|
|
1165
1219
|
)
|
|
1166
1220
|
assert outfilename2.exists() is True
|
|
1167
1221
|
index_filename2 = tmp_path / "test2.qix"
|
|
@@ -1207,7 +1261,7 @@ def test_write_dataframe_gdal_options_dataset(tmp_path, naturalearth_lowres, use
|
|
|
1207
1261
|
df,
|
|
1208
1262
|
test_no_contents_filename2,
|
|
1209
1263
|
use_arrow=use_arrow,
|
|
1210
|
-
dataset_options=
|
|
1264
|
+
dataset_options={"add_gpkg_ogr_contents": False},
|
|
1211
1265
|
)
|
|
1212
1266
|
assert "gpkg_ogr_contents" not in _get_gpkg_table_names(test_no_contents_filename2)
|
|
1213
1267
|
|
|
@@ -1320,7 +1374,8 @@ def test_write_dataframe_promote_to_multi_layer_geom_type(
|
|
|
1320
1374
|
".shp",
|
|
1321
1375
|
None,
|
|
1322
1376
|
"Point",
|
|
1323
|
-
"Could not add feature to layer at index|Error while writing batch to OGR
|
|
1377
|
+
"Could not add feature to layer at index|Error while writing batch to OGR "
|
|
1378
|
+
"layer",
|
|
1324
1379
|
),
|
|
1325
1380
|
],
|
|
1326
1381
|
)
|
|
@@ -1443,6 +1498,7 @@ def test_write_dataframe_infer_geometry_with_nulls(tmp_path, geoms, ext, use_arr
|
|
|
1443
1498
|
"ignore: You will likely lose important projection information"
|
|
1444
1499
|
)
|
|
1445
1500
|
@pytest.mark.requires_arrow_write_api
|
|
1501
|
+
@requires_pyproj
|
|
1446
1502
|
def test_custom_crs_io(tmp_path, naturalearth_lowres_all_ext, use_arrow):
|
|
1447
1503
|
df = read_dataframe(naturalearth_lowres_all_ext)
|
|
1448
1504
|
# project Belgium to a custom Albers Equal Area projection
|
|
@@ -1517,6 +1573,22 @@ def test_write_read_null(tmp_path, use_arrow):
|
|
|
1517
1573
|
assert result_gdf["object_str"][2] is None
|
|
1518
1574
|
|
|
1519
1575
|
|
|
1576
|
+
@pytest.mark.requires_arrow_write_api
|
|
1577
|
+
def test_write_read_vsimem(naturalearth_lowres_vsi, use_arrow):
|
|
1578
|
+
path, _ = naturalearth_lowres_vsi
|
|
1579
|
+
mem_path = f"/vsimem/{path.name}"
|
|
1580
|
+
|
|
1581
|
+
input = read_dataframe(path, use_arrow=use_arrow)
|
|
1582
|
+
assert len(input) == 177
|
|
1583
|
+
|
|
1584
|
+
try:
|
|
1585
|
+
write_dataframe(input, mem_path, use_arrow=use_arrow)
|
|
1586
|
+
result = read_dataframe(mem_path, use_arrow=use_arrow)
|
|
1587
|
+
assert len(result) == 177
|
|
1588
|
+
finally:
|
|
1589
|
+
vsi_unlink(mem_path)
|
|
1590
|
+
|
|
1591
|
+
|
|
1520
1592
|
@pytest.mark.parametrize(
|
|
1521
1593
|
"wkt,geom_types",
|
|
1522
1594
|
[
|
|
@@ -1529,7 +1601,7 @@ def test_write_read_null(tmp_path, use_arrow):
|
|
|
1529
1601
|
["2.5D MultiLineString", "MultiLineString Z"],
|
|
1530
1602
|
),
|
|
1531
1603
|
(
|
|
1532
|
-
"MultiPolygon Z (((0 0 0, 0 1 0, 1 1 0, 0 0 0)), ((1 1 1, 1 2 1, 2 2 1, 1 1 1)))",
|
|
1604
|
+
"MultiPolygon Z (((0 0 0, 0 1 0, 1 1 0, 0 0 0)), ((1 1 1, 1 2 1, 2 2 1, 1 1 1)))", # noqa: E501
|
|
1533
1605
|
["2.5D MultiPolygon", "MultiPolygon Z"],
|
|
1534
1606
|
),
|
|
1535
1607
|
(
|
|
@@ -1572,7 +1644,7 @@ def test_write_geometry_z_types(tmp_path, wkt, geom_types, use_arrow):
|
|
|
1572
1644
|
"MultiPolygon Z",
|
|
1573
1645
|
False,
|
|
1574
1646
|
[
|
|
1575
|
-
"MultiPolygon Z (((0 0 0, 0 1 0, 1 1 0, 0 0 0)), ((1 1 1, 1 2 1, 2 2 1, 1 1 1)))"
|
|
1647
|
+
"MultiPolygon Z (((0 0 0, 0 1 0, 1 1 0, 0 0 0)), ((1 1 1, 1 2 1, 2 2 1, 1 1 1)))" # noqa: E501
|
|
1576
1648
|
],
|
|
1577
1649
|
),
|
|
1578
1650
|
(
|
|
@@ -1653,7 +1725,7 @@ def test_write_geometry_z_types_auto(
|
|
|
1653
1725
|
("ignore", None),
|
|
1654
1726
|
],
|
|
1655
1727
|
)
|
|
1656
|
-
def
|
|
1728
|
+
def test_read_invalid_poly_ring(tmp_path, use_arrow, on_invalid, message):
|
|
1657
1729
|
if on_invalid == "raise":
|
|
1658
1730
|
handler = pytest.raises(shapely.errors.GEOSException, match=message)
|
|
1659
1731
|
elif on_invalid == "warn":
|
|
@@ -1663,33 +1735,54 @@ def test_read_invalid_shp(data_dir, use_arrow, on_invalid, message):
|
|
|
1663
1735
|
else:
|
|
1664
1736
|
raise ValueError(f"unknown value for on_invalid: {on_invalid}")
|
|
1665
1737
|
|
|
1738
|
+
# create a GeoJSON file with an invalid exterior ring
|
|
1739
|
+
invalid_geojson = """{
|
|
1740
|
+
"type": "FeatureCollection",
|
|
1741
|
+
"features": [
|
|
1742
|
+
{
|
|
1743
|
+
"type": "Feature",
|
|
1744
|
+
"properties": {},
|
|
1745
|
+
"geometry": {
|
|
1746
|
+
"type": "Polygon",
|
|
1747
|
+
"coordinates": [ [ [0, 0], [0, 0] ] ]
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
]
|
|
1751
|
+
}"""
|
|
1752
|
+
|
|
1753
|
+
filename = tmp_path / "test.geojson"
|
|
1754
|
+
with open(filename, "w") as f:
|
|
1755
|
+
_ = f.write(invalid_geojson)
|
|
1756
|
+
|
|
1666
1757
|
with handler:
|
|
1667
1758
|
df = read_dataframe(
|
|
1668
|
-
|
|
1759
|
+
filename,
|
|
1669
1760
|
use_arrow=use_arrow,
|
|
1670
1761
|
on_invalid=on_invalid,
|
|
1671
1762
|
)
|
|
1672
1763
|
df.geometry.isnull().all()
|
|
1673
1764
|
|
|
1674
1765
|
|
|
1675
|
-
def test_read_multisurface(
|
|
1766
|
+
def test_read_multisurface(multisurface_file, use_arrow):
|
|
1676
1767
|
if use_arrow:
|
|
1768
|
+
# TODO: revisit once https://github.com/geopandas/pyogrio/issues/478
|
|
1769
|
+
# is resolved.
|
|
1770
|
+
pytest.skip("Shapely + GEOS 3.13 crashes in from_wkb for this case")
|
|
1771
|
+
|
|
1677
1772
|
with pytest.raises(shapely.errors.GEOSException):
|
|
1678
1773
|
# TODO(Arrow)
|
|
1679
1774
|
# shapely fails parsing the WKB
|
|
1680
|
-
read_dataframe(
|
|
1775
|
+
read_dataframe(multisurface_file, use_arrow=True)
|
|
1681
1776
|
else:
|
|
1682
|
-
df = read_dataframe(
|
|
1777
|
+
df = read_dataframe(multisurface_file)
|
|
1683
1778
|
|
|
1684
1779
|
# MultiSurface should be converted to MultiPolygon
|
|
1685
1780
|
assert df.geometry.type.tolist() == ["MultiPolygon"]
|
|
1686
1781
|
|
|
1687
1782
|
|
|
1688
|
-
def test_read_dataset_kwargs(
|
|
1689
|
-
filename = data_dir / "test_nested.geojson"
|
|
1690
|
-
|
|
1783
|
+
def test_read_dataset_kwargs(nested_geojson_file, use_arrow):
|
|
1691
1784
|
# by default, nested data are not flattened
|
|
1692
|
-
df = read_dataframe(
|
|
1785
|
+
df = read_dataframe(nested_geojson_file, use_arrow=use_arrow)
|
|
1693
1786
|
|
|
1694
1787
|
expected = gp.GeoDataFrame(
|
|
1695
1788
|
{
|
|
@@ -1702,7 +1795,9 @@ def test_read_dataset_kwargs(data_dir, use_arrow):
|
|
|
1702
1795
|
|
|
1703
1796
|
assert_geodataframe_equal(df, expected)
|
|
1704
1797
|
|
|
1705
|
-
df = read_dataframe(
|
|
1798
|
+
df = read_dataframe(
|
|
1799
|
+
nested_geojson_file, use_arrow=use_arrow, FLATTEN_NESTED_ATTRIBUTES="YES"
|
|
1800
|
+
)
|
|
1706
1801
|
|
|
1707
1802
|
expected = gp.GeoDataFrame(
|
|
1708
1803
|
{
|
|
@@ -1904,6 +1999,9 @@ def test_write_memory(naturalearth_lowres, driver):
|
|
|
1904
1999
|
check_dtype=not is_json,
|
|
1905
2000
|
)
|
|
1906
2001
|
|
|
2002
|
+
# Check temp file was cleaned up. Filter, as gdal keeps cache files in /vsimem/.
|
|
2003
|
+
assert vsi_listtree("/vsimem/", pattern="pyogrio_*") == []
|
|
2004
|
+
|
|
1907
2005
|
|
|
1908
2006
|
def test_write_memory_driver_required(naturalearth_lowres):
|
|
1909
2007
|
df = read_dataframe(naturalearth_lowres)
|
|
@@ -1916,6 +2014,9 @@ def test_write_memory_driver_required(naturalearth_lowres):
|
|
|
1916
2014
|
):
|
|
1917
2015
|
write_dataframe(df.head(1), buffer, driver=None, layer="test")
|
|
1918
2016
|
|
|
2017
|
+
# Check temp file was cleaned up. Filter, as gdal keeps cache files in /vsimem/.
|
|
2018
|
+
assert vsi_listtree("/vsimem/", pattern="pyogrio_*") == []
|
|
2019
|
+
|
|
1919
2020
|
|
|
1920
2021
|
@pytest.mark.parametrize("driver", ["ESRI Shapefile", "OpenFileGDB"])
|
|
1921
2022
|
def test_write_memory_unsupported_driver(naturalearth_lowres, driver):
|
|
@@ -1931,6 +2032,9 @@ def test_write_memory_unsupported_driver(naturalearth_lowres, driver):
|
|
|
1931
2032
|
):
|
|
1932
2033
|
write_dataframe(df, buffer, driver=driver, layer="test")
|
|
1933
2034
|
|
|
2035
|
+
# Check temp file was cleaned up. Filter, as gdal keeps cache files in /vsimem/.
|
|
2036
|
+
assert vsi_listtree("/vsimem/", pattern="pyogrio_*") == []
|
|
2037
|
+
|
|
1934
2038
|
|
|
1935
2039
|
@pytest.mark.parametrize("driver", ["GeoJSON", "GPKG"])
|
|
1936
2040
|
def test_write_memory_append_unsupported(naturalearth_lowres, driver):
|
|
@@ -1943,6 +2047,9 @@ def test_write_memory_append_unsupported(naturalearth_lowres, driver):
|
|
|
1943
2047
|
):
|
|
1944
2048
|
write_dataframe(df.head(1), buffer, driver=driver, layer="test", append=True)
|
|
1945
2049
|
|
|
2050
|
+
# Check temp file was cleaned up. Filter, as gdal keeps cache files in /vsimem/.
|
|
2051
|
+
assert vsi_listtree("/vsimem/", pattern="pyogrio_*") == []
|
|
2052
|
+
|
|
1946
2053
|
|
|
1947
2054
|
def test_write_memory_existing_unsupported(naturalearth_lowres):
|
|
1948
2055
|
df = read_dataframe(naturalearth_lowres)
|
|
@@ -1954,6 +2061,33 @@ def test_write_memory_existing_unsupported(naturalearth_lowres):
|
|
|
1954
2061
|
):
|
|
1955
2062
|
write_dataframe(df.head(1), buffer, driver="GeoJSON", layer="test")
|
|
1956
2063
|
|
|
2064
|
+
# Check temp file was cleaned up. Filter, as gdal keeps cache files in /vsimem/.
|
|
2065
|
+
assert vsi_listtree("/vsimem/", pattern="pyogrio_*") == []
|
|
2066
|
+
|
|
2067
|
+
|
|
2068
|
+
def test_write_open_file_handle(tmp_path, naturalearth_lowres):
|
|
2069
|
+
"""Verify that writing to an open file handle is not currently supported"""
|
|
2070
|
+
|
|
2071
|
+
df = read_dataframe(naturalearth_lowres)
|
|
2072
|
+
|
|
2073
|
+
# verify it fails for regular file handle
|
|
2074
|
+
with pytest.raises(
|
|
2075
|
+
NotImplementedError, match="writing to an open file handle is not yet supported"
|
|
2076
|
+
):
|
|
2077
|
+
with open(tmp_path / "test.geojson", "wb") as f:
|
|
2078
|
+
write_dataframe(df.head(1), f)
|
|
2079
|
+
|
|
2080
|
+
# verify it fails for ZipFile
|
|
2081
|
+
with pytest.raises(
|
|
2082
|
+
NotImplementedError, match="writing to an open file handle is not yet supported"
|
|
2083
|
+
):
|
|
2084
|
+
with ZipFile(tmp_path / "test.geojson.zip", "w") as z:
|
|
2085
|
+
with z.open("test.geojson", "w") as f:
|
|
2086
|
+
write_dataframe(df.head(1), f)
|
|
2087
|
+
|
|
2088
|
+
# Check temp file was cleaned up. Filter, as gdal keeps cache files in /vsimem/.
|
|
2089
|
+
assert vsi_listtree("/vsimem/", pattern="pyogrio_*") == []
|
|
2090
|
+
|
|
1957
2091
|
|
|
1958
2092
|
@pytest.mark.parametrize("ext", ["gpkg", "geojson"])
|
|
1959
2093
|
def test_non_utf8_encoding_io(tmp_path, ext, encoded_text):
|
|
@@ -2045,7 +2179,8 @@ def test_non_utf8_encoding_io_shapefile(tmp_path, encoded_text, use_arrow):
|
|
|
2045
2179
|
|
|
2046
2180
|
|
|
2047
2181
|
def test_encoding_read_option_collision_shapefile(naturalearth_lowres, use_arrow):
|
|
2048
|
-
"""Providing both encoding parameter and ENCODING open option
|
|
2182
|
+
"""Providing both encoding parameter and ENCODING open option
|
|
2183
|
+
(even if blank) is not allowed."""
|
|
2049
2184
|
|
|
2050
2185
|
with pytest.raises(
|
|
2051
2186
|
ValueError, match='cannot provide both encoding parameter and "ENCODING" option'
|
|
@@ -2056,7 +2191,8 @@ def test_encoding_read_option_collision_shapefile(naturalearth_lowres, use_arrow
|
|
|
2056
2191
|
|
|
2057
2192
|
|
|
2058
2193
|
def test_encoding_write_layer_option_collision_shapefile(tmp_path, encoded_text):
|
|
2059
|
-
"""Providing both encoding parameter and ENCODING layer creation option
|
|
2194
|
+
"""Providing both encoding parameter and ENCODING layer creation option
|
|
2195
|
+
(even if blank) is not allowed."""
|
|
2060
2196
|
encoding, text = encoded_text
|
|
2061
2197
|
|
|
2062
2198
|
output_path = tmp_path / "test.shp"
|
|
@@ -2064,7 +2200,10 @@ def test_encoding_write_layer_option_collision_shapefile(tmp_path, encoded_text)
|
|
|
2064
2200
|
|
|
2065
2201
|
with pytest.raises(
|
|
2066
2202
|
ValueError,
|
|
2067
|
-
match=
|
|
2203
|
+
match=(
|
|
2204
|
+
'cannot provide both encoding parameter and "ENCODING" layer creation '
|
|
2205
|
+
"option"
|
|
2206
|
+
),
|
|
2068
2207
|
):
|
|
2069
2208
|
write_dataframe(
|
|
2070
2209
|
df, output_path, encoding=encoding, layer_options={"ENCODING": ""}
|
|
@@ -2102,7 +2241,8 @@ def test_non_utf8_encoding_shapefile_sql(tmp_path, use_arrow):
|
|
|
2102
2241
|
|
|
2103
2242
|
@pytest.mark.requires_arrow_write_api
|
|
2104
2243
|
def test_write_kml_file_coordinate_order(tmp_path, use_arrow):
|
|
2105
|
-
# confirm KML coordinates are written in lon, lat order even if CRS axis
|
|
2244
|
+
# confirm KML coordinates are written in lon, lat order even if CRS axis
|
|
2245
|
+
# specifies otherwise
|
|
2106
2246
|
points = [Point(10, 20), Point(30, 40), Point(50, 60)]
|
|
2107
2247
|
gdf = gp.GeoDataFrame(geometry=points, crs="EPSG:4326")
|
|
2108
2248
|
output_path = tmp_path / "test.kml"
|