pyogrio 0.8.0__tar.gz → 0.9.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (54) hide show
  1. {pyogrio-0.8.0/pyogrio.egg-info → pyogrio-0.9.0}/PKG-INFO +1 -1
  2. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/_io.pyx +5 -1
  3. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/_ogr.pxd +4 -1
  4. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/_version.py +3 -3
  5. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/geopandas.py +14 -5
  6. pyogrio-0.9.0/pyogrio/tests/fixtures/poly_not_enough_points.shp.zip +0 -0
  7. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/tests/test_geopandas_io.py +106 -1
  8. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/tests/test_path.py +1 -0
  9. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/util.py +1 -1
  10. {pyogrio-0.8.0 → pyogrio-0.9.0/pyogrio.egg-info}/PKG-INFO +1 -1
  11. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio.egg-info/SOURCES.txt +1 -0
  12. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyproject.toml +1 -0
  13. {pyogrio-0.8.0 → pyogrio-0.9.0}/setup.py +9 -7
  14. {pyogrio-0.8.0 → pyogrio-0.9.0}/LICENSE +0 -0
  15. {pyogrio-0.8.0 → pyogrio-0.9.0}/MANIFEST.in +0 -0
  16. {pyogrio-0.8.0 → pyogrio-0.9.0}/README.md +0 -0
  17. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/__init__.py +0 -0
  18. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/_compat.py +0 -0
  19. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/_env.py +0 -0
  20. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/_err.pxd +0 -0
  21. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/_err.pyx +0 -0
  22. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/_geometry.pxd +0 -0
  23. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/_geometry.pyx +0 -0
  24. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/_io.pxd +0 -0
  25. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/_ogr.pyx +0 -0
  26. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/_vsi.pxd +0 -0
  27. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/_vsi.pyx +0 -0
  28. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/arrow_bridge.h +0 -0
  29. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/core.py +0 -0
  30. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/errors.py +0 -0
  31. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/raw.py +0 -0
  32. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/tests/__init__.py +0 -0
  33. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/tests/conftest.py +0 -0
  34. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/tests/fixtures/README.md +0 -0
  35. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/tests/fixtures/naturalearth_lowres/naturalearth_lowres.cpg +0 -0
  36. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/tests/fixtures/naturalearth_lowres/naturalearth_lowres.dbf +0 -0
  37. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/tests/fixtures/naturalearth_lowres/naturalearth_lowres.prj +0 -0
  38. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/tests/fixtures/naturalearth_lowres/naturalearth_lowres.shp +0 -0
  39. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/tests/fixtures/naturalearth_lowres/naturalearth_lowres.shx +0 -0
  40. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/tests/fixtures/sample.osm.pbf +0 -0
  41. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/tests/fixtures/test_datetime.geojson +0 -0
  42. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/tests/fixtures/test_datetime_tz.geojson +0 -0
  43. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/tests/fixtures/test_fgdb.gdb.zip +0 -0
  44. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/tests/fixtures/test_gpkg_nulls.gpkg +0 -0
  45. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/tests/fixtures/test_multisurface.gpkg +0 -0
  46. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/tests/fixtures/test_nested.geojson +0 -0
  47. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/tests/fixtures/test_ogr_types_list.geojson +0 -0
  48. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/tests/test_arrow.py +0 -0
  49. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/tests/test_core.py +0 -0
  50. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio/tests/test_raw_io.py +0 -0
  51. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio.egg-info/dependency_links.txt +0 -0
  52. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio.egg-info/requires.txt +0 -0
  53. {pyogrio-0.8.0 → pyogrio-0.9.0}/pyogrio.egg-info/top_level.txt +0 -0
  54. {pyogrio-0.8.0 → pyogrio-0.9.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyogrio
3
- Version: 0.8.0
3
+ Version: 0.9.0
4
4
  Summary: Vectorized spatial vector file format I/O using GDAL/OGR
5
5
  Home-page: https://github.com/geopandas/pyogrio
6
6
  Author: Brendan C. Ward
@@ -2134,6 +2134,10 @@ cdef create_ogr_dataset_layer(
2134
2134
  if crs is not None:
2135
2135
  try:
2136
2136
  ogr_crs = create_crs(crs)
2137
+ # force geographic CRS to use lon, lat order and ignore axis order specified by CRS, in order
2138
+ # to correctly write KML and GeoJSON coordinates in correct order
2139
+ OSRSetAxisMappingStrategy(ogr_crs, OAMS_TRADITIONAL_GIS_ORDER)
2140
+
2137
2141
 
2138
2142
  except Exception as exc:
2139
2143
  if dataset_options != NULL:
@@ -2735,4 +2739,4 @@ cdef create_fields_from_arrow_schema(
2735
2739
  f"Error while creating field from Arrow for field {i} with name "
2736
2740
  f"'{get_string(child.name)}' and type {get_string(child.format)}"
2737
2741
  f"{gdal_msg}."
2738
- )
2742
+ )
@@ -196,7 +196,10 @@ cdef extern from "ogr_srs_api.h":
196
196
  const char* OSRGetAuthorityName(OGRSpatialReferenceH srs, const char *key)
197
197
  const char* OSRGetAuthorityCode(OGRSpatialReferenceH srs, const char *key)
198
198
  OGRErr OSRImportFromEPSG(OGRSpatialReferenceH srs, int code)
199
-
199
+ ctypedef enum OSRAxisMappingStrategy:
200
+ OAMS_TRADITIONAL_GIS_ORDER
201
+
202
+ void OSRSetAxisMappingStrategy(OGRSpatialReferenceH hSRS, OSRAxisMappingStrategy)
200
203
  int OSRSetFromUserInput(OGRSpatialReferenceH srs, const char *pszDef)
201
204
  void OSRSetPROJSearchPaths(const char *const *paths)
202
205
  OGRSpatialReferenceH OSRNewSpatialReference(const char *wkt)
@@ -8,11 +8,11 @@ import json
8
8
 
9
9
  version_json = '''
10
10
  {
11
- "date": "2024-05-06T14:34:51-0700",
11
+ "date": "2024-06-17T12:28:58-0700",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "46c35a7e98d85923cfefe73dae7285404e72d9a6",
15
- "version": "0.8.0"
14
+ "full-revisionid": "568f87062a65e0452c679624cc33fbf1af535454",
15
+ "version": "0.9.0"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -87,6 +87,7 @@ def read_dataframe(
87
87
  sql_dialect=None,
88
88
  fid_as_index=False,
89
89
  use_arrow=None,
90
+ on_invalid="raise",
90
91
  arrow_to_pandas_kwargs=None,
91
92
  **kwargs,
92
93
  ):
@@ -197,6 +198,15 @@ def read_dataframe(
197
198
  installed). When enabled, this provides a further speed-up.
198
199
  Defaults to False, but this default can also be globally overridden
199
200
  by setting the ``PYOGRIO_USE_ARROW=1`` environment variable.
201
+ on_invalid : str, optional (default: "raise")
202
+
203
+ - **raise**: an exception will be raised if a WKB input geometry is
204
+ invalid.
205
+ - **warn**: a warning will be raised and invalid WKB geometries will be
206
+ returned as ``None``.
207
+ - **ignore**: invalid WKB geometries will be returned as ``None``
208
+ without a warning.
209
+
200
210
  arrow_to_pandas_kwargs : dict, optional (default: None)
201
211
  When `use_arrow` is True, these kwargs will be passed to the `to_pandas`_
202
212
  call for the arrow to pandas conversion.
@@ -234,7 +244,6 @@ def read_dataframe(
234
244
 
235
245
  import pandas as pd
236
246
  import geopandas as gp
237
- from geopandas.array import from_wkb
238
247
  import shapely # if geopandas is present, shapely is expected to be present
239
248
 
240
249
  path_or_buffer = _stringify_path(path_or_buffer)
@@ -292,10 +301,10 @@ def read_dataframe(
292
301
  if PANDAS_GE_15 and wkb_values.dtype != object:
293
302
  # for example ArrowDtype will otherwise create numpy array with pd.NA
294
303
  wkb_values = wkb_values.to_numpy(na_value=None)
295
- df["geometry"] = from_wkb(wkb_values, crs=meta["crs"])
304
+ df["geometry"] = shapely.from_wkb(wkb_values, on_invalid=on_invalid)
296
305
  if force_2d:
297
306
  df["geometry"] = shapely.force_2d(df["geometry"])
298
- return gp.GeoDataFrame(df, geometry="geometry")
307
+ return gp.GeoDataFrame(df, geometry="geometry", crs=meta["crs"])
299
308
  else:
300
309
  return df
301
310
 
@@ -315,9 +324,9 @@ def read_dataframe(
315
324
  if geometry is None or not read_geometry:
316
325
  return df
317
326
 
318
- geometry = from_wkb(geometry, crs=meta["crs"])
327
+ geometry = shapely.from_wkb(geometry, on_invalid=on_invalid)
319
328
 
320
- return gp.GeoDataFrame(df, geometry=geometry)
329
+ return gp.GeoDataFrame(df, geometry=geometry, crs=meta["crs"])
321
330
 
322
331
 
323
332
  # TODO: handle index properly
@@ -6,7 +6,7 @@ import locale
6
6
  import numpy as np
7
7
  import pytest
8
8
 
9
- from pyogrio import list_layers, read_info, __gdal_version__
9
+ from pyogrio import list_layers, list_drivers, read_info, __gdal_version__
10
10
  from pyogrio.errors import DataLayerError, DataSourceError, FeatureError, GeometryError
11
11
  from pyogrio.geopandas import read_dataframe, write_dataframe, PANDAS_GE_20
12
12
  from pyogrio.raw import (
@@ -1641,6 +1641,37 @@ def test_write_geometry_z_types_auto(
1641
1641
  assert_geodataframe_equal(gdf, result_gdf)
1642
1642
 
1643
1643
 
1644
+ @pytest.mark.parametrize(
1645
+ "on_invalid, message",
1646
+ [
1647
+ (
1648
+ "warn",
1649
+ "Invalid WKB: geometry is returned as None. IllegalArgumentException: "
1650
+ "Invalid number of points in LinearRing found 2 - must be 0 or >=",
1651
+ ),
1652
+ ("raise", "Invalid number of points in LinearRing found 2 - must be 0 or >="),
1653
+ ("ignore", None),
1654
+ ],
1655
+ )
1656
+ def test_read_invalid_shp(data_dir, use_arrow, on_invalid, message):
1657
+ if on_invalid == "raise":
1658
+ handler = pytest.raises(shapely.errors.GEOSException, match=message)
1659
+ elif on_invalid == "warn":
1660
+ handler = pytest.warns(match=message)
1661
+ elif on_invalid == "ignore":
1662
+ handler = contextlib.nullcontext()
1663
+ else:
1664
+ raise ValueError(f"unknown value for on_invalid: {on_invalid}")
1665
+
1666
+ with handler:
1667
+ df = read_dataframe(
1668
+ data_dir / "poly_not_enough_points.shp.zip",
1669
+ use_arrow=use_arrow,
1670
+ on_invalid=on_invalid,
1671
+ )
1672
+ df.geometry.isnull().all()
1673
+
1674
+
1644
1675
  def test_read_multisurface(data_dir, use_arrow):
1645
1676
  if use_arrow:
1646
1677
  with pytest.raises(shapely.errors.GEOSException):
@@ -2067,3 +2098,77 @@ def test_non_utf8_encoding_shapefile_sql(tmp_path, use_arrow):
2067
2098
  )
2068
2099
  assert actual.columns[0] == mandarin
2069
2100
  assert actual[mandarin].values[0] == mandarin
2101
+
2102
+
2103
+ @pytest.mark.requires_arrow_write_api
2104
+ 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 specifies otherwise
2106
+ points = [Point(10, 20), Point(30, 40), Point(50, 60)]
2107
+ gdf = gp.GeoDataFrame(geometry=points, crs="EPSG:4326")
2108
+ output_path = tmp_path / "test.kml"
2109
+ write_dataframe(
2110
+ gdf, output_path, layer="tmp_layer", driver="KML", use_arrow=use_arrow
2111
+ )
2112
+
2113
+ gdf_in = read_dataframe(output_path, use_arrow=use_arrow)
2114
+
2115
+ assert np.array_equal(gdf_in.geometry.values, points)
2116
+
2117
+ if "LIBKML" in list_drivers():
2118
+ # test appending to the existing file only if LIBKML is available
2119
+ # as it appears to fall back on LIBKML driver when appending.
2120
+ points_append = [Point(70, 80), Point(90, 100), Point(110, 120)]
2121
+ gdf_append = gp.GeoDataFrame(geometry=points_append, crs="EPSG:4326")
2122
+
2123
+ write_dataframe(
2124
+ gdf_append,
2125
+ output_path,
2126
+ layer="tmp_layer",
2127
+ driver="KML",
2128
+ use_arrow=use_arrow,
2129
+ append=True,
2130
+ )
2131
+ # force_2d used to only compare xy geometry as z-dimension is undesirably
2132
+ # introduced when the kml file is over-written.
2133
+ gdf_in_appended = read_dataframe(
2134
+ output_path, use_arrow=use_arrow, force_2d=True
2135
+ )
2136
+
2137
+ assert np.array_equal(gdf_in_appended.geometry.values, points + points_append)
2138
+
2139
+
2140
+ @pytest.mark.requires_arrow_write_api
2141
+ def test_write_geojson_rfc7946_coordinates(tmp_path, use_arrow):
2142
+ points = [Point(10, 20), Point(30, 40), Point(50, 60)]
2143
+ gdf = gp.GeoDataFrame(geometry=points, crs="EPSG:4326")
2144
+ output_path = tmp_path / "test.geojson"
2145
+ write_dataframe(
2146
+ gdf,
2147
+ output_path,
2148
+ layer="tmp_layer",
2149
+ driver="GeoJSON",
2150
+ RFC7946=True,
2151
+ use_arrow=use_arrow,
2152
+ )
2153
+
2154
+ gdf_in = read_dataframe(output_path, use_arrow=use_arrow)
2155
+
2156
+ assert np.array_equal(gdf_in.geometry.values, points)
2157
+
2158
+ # test appending to the existing file
2159
+
2160
+ points_append = [Point(70, 80), Point(90, 100), Point(110, 120)]
2161
+ gdf_append = gp.GeoDataFrame(geometry=points_append, crs="EPSG:4326")
2162
+
2163
+ write_dataframe(
2164
+ gdf_append,
2165
+ output_path,
2166
+ layer="tmp_layer",
2167
+ driver="GeoJSON",
2168
+ RFC7946=True,
2169
+ use_arrow=use_arrow,
2170
+ append=True,
2171
+ )
2172
+
2173
+ gdf_in_appended = read_dataframe(output_path, use_arrow=use_arrow)
2174
+ assert np.array_equal(gdf_in_appended.geometry.values, points + points_append)
@@ -35,6 +35,7 @@ def change_cwd(path):
35
35
  ("/home/user/data.gpkg", "/home/user/data.gpkg"),
36
36
  (r"C:\User\Documents\data.gpkg", r"C:\User\Documents\data.gpkg"),
37
37
  ("file:///home/user/data.gpkg", "/home/user/data.gpkg"),
38
+ ("/home/folder # with hash/data.gpkg", "/home/folder # with hash/data.gpkg"),
38
39
  # cloud URIs
39
40
  ("https://testing/data.gpkg", "/vsicurl/https://testing/data.gpkg"),
40
41
  ("s3://testing/data.gpkg", "/vsis3/testing/data.gpkg"),
@@ -115,7 +115,7 @@ def _parse_uri(path: str):
115
115
  scheme : str
116
116
  URI scheme such as "https" or "zip+s3".
117
117
  """
118
- parts = urlparse(path)
118
+ parts = urlparse(path, allow_fragments=False)
119
119
 
120
120
  # if the scheme is not one of GDAL's supported schemes, return raw path
121
121
  if parts.scheme and not all(p in SCHEMES for p in parts.scheme.split("+")):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyogrio
3
- Version: 0.8.0
3
+ Version: 0.9.0
4
4
  Summary: Vectorized spatial vector file format I/O using GDAL/OGR
5
5
  Home-page: https://github.com/geopandas/pyogrio
6
6
  Author: Brendan C. Ward
@@ -37,6 +37,7 @@ pyogrio/tests/test_geopandas_io.py
37
37
  pyogrio/tests/test_path.py
38
38
  pyogrio/tests/test_raw_io.py
39
39
  pyogrio/tests/fixtures/README.md
40
+ pyogrio/tests/fixtures/poly_not_enough_points.shp.zip
40
41
  pyogrio/tests/fixtures/sample.osm.pbf
41
42
  pyogrio/tests/fixtures/test_datetime.geojson
42
43
  pyogrio/tests/fixtures/test_datetime_tz.geojson
@@ -38,6 +38,7 @@ GDAL_VERSION = "3.8.5"
38
38
  PYOGRIO_PACKAGE_DATA = 1
39
39
  GDAL_DATA = "$VCPKG_INSTALL/share/gdal"
40
40
  PROJ_LIB = "$VCPKG_INSTALL/share/proj"
41
+ MACOSX_DEPLOYMENT_TARGET = "12.0"
41
42
 
42
43
  [tool.cibuildwheel.windows]
43
44
  before-build = "pip install delvewheel"
@@ -1,3 +1,4 @@
1
+ import logging
1
2
  import os
2
3
  from pathlib import Path
3
4
  import platform
@@ -5,9 +6,7 @@ import shutil
5
6
  import subprocess
6
7
  import sys
7
8
 
8
- from distutils import log
9
- from setuptools import setup, find_packages
10
- from setuptools.extension import Extension
9
+ from setuptools import Extension, setup, find_packages
11
10
  import versioneer
12
11
 
13
12
  # import Cython if available
@@ -18,6 +17,9 @@ except ImportError:
18
17
  cythonize = None
19
18
 
20
19
 
20
+ logger = logging.getLogger(__name__)
21
+
22
+
21
23
  MIN_PYTHON_VERSION = (3, 8, 0)
22
24
  MIN_GDAL_VERSION = (2, 4, 0)
23
25
 
@@ -71,7 +73,7 @@ def get_gdal_config():
71
73
  }, gdal_version_str
72
74
 
73
75
  if include_dir or library_dir or gdal_version_str:
74
- log.warn(
76
+ logger.warning(
75
77
  "If specifying the GDAL_INCLUDE_PATH, GDAL_LIBRARY_PATH, or GDAL_VERSION "
76
78
  "environment variables, you need to specify all of them."
77
79
  )
@@ -117,7 +119,7 @@ def get_gdal_config():
117
119
  )
118
120
  sys.exit(1)
119
121
 
120
- log.info(
122
+ logger.info(
121
123
  "Building on Windows requires extra options to setup.py to locate "
122
124
  "GDAL files. See the installation documentation."
123
125
  )
@@ -174,7 +176,7 @@ else:
174
176
  if os.environ.get("PYOGRIO_PACKAGE_DATA"):
175
177
  gdal_data = os.environ.get("GDAL_DATA")
176
178
  if gdal_data and os.path.exists(gdal_data):
177
- log.info(f"Copying gdal data from {gdal_data}")
179
+ logger.info(f"Copying gdal data from {gdal_data}")
178
180
  copy_data_tree(gdal_data, "pyogrio/gdal_data")
179
181
  else:
180
182
  raise Exception(
@@ -184,7 +186,7 @@ else:
184
186
 
185
187
  proj_data = os.environ.get("PROJ_LIB")
186
188
  if proj_data and os.path.exists(proj_data):
187
- log.info(f"Copying proj data from {proj_data}")
189
+ logger.info(f"Copying proj data from {proj_data}")
188
190
  copy_data_tree(proj_data, "pyogrio/proj_data")
189
191
  else:
190
192
  raise Exception(
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes