Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
- Fix decode error reading an sqlite file on windows (#568).
- Fix wrong layername when creating .gpkg.zip file (#570).

### Packaging

- Add libkml driver to the wheels for more recent Linux platforms supported
by manylinux_2_28, MacOS, and Windows (#561).

## 0.11.1 (2025-08-02)

### Bug fixes
Expand Down
2 changes: 1 addition & 1 deletion ci/manylinux2014_x86_64-vcpkg-gdal.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ RUN bootstrap-vcpkg.sh && \

COPY ci/custom-triplets/x64-linux-dynamic-release.cmake opt/vcpkg/custom-triplets/x64-linux-dynamic-release.cmake
COPY ci/vcpkg-custom-ports/ opt/vcpkg/custom-ports/
COPY ci/vcpkg.json opt/vcpkg/
COPY ci/vcpkg-manylinux2014.json opt/vcpkg/vcpkg.json

ENV LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:/opt/vcpkg/installed/x64-linux-dynamic-release/lib"
RUN vcpkg install --overlay-triplets=opt/vcpkg/custom-triplets \
Expand Down
12 changes: 12 additions & 0 deletions ci/vcpkg-manylinux2014.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "pyogrio",
"version": "0.12.0",
"dependencies": [
{
"name": "gdal",
"default-features": false,
"features": ["recommended-features", "curl", "geos", "iconv", "openssl"]
}
],
"builtin-baseline": "66c1c9852bb30bd87285e77cc775072046d51fc6"
}
4 changes: 3 additions & 1 deletion ci/vcpkg.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
{
"name": "gdal",
"default-features": false,
"features": ["recommended-features", "curl", "geos", "iconv", "openssl"]
"features": [
"recommended-features", "curl", "geos", "iconv", "libkml", "openssl"
]
}
],
"builtin-baseline": "66c1c9852bb30bd87285e77cc775072046d51fc6"
Expand Down
2 changes: 1 addition & 1 deletion pyogrio/_io.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -886,7 +886,7 @@ cdef process_fields(
cdef int field_index
cdef int ret_length
cdef int *ints_c
cdef int64_t *int64s_c
cdef GIntBig *int64s_c
cdef double *doubles_c
cdef char **strings_c
cdef GByte *bin_value
Expand Down
7 changes: 4 additions & 3 deletions pyogrio/_ogr.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ cdef extern from "arrow_bridge.h" nogil:


cdef extern from "ogr_api.h":
ctypedef signed long long GIntBig
int OGRGetDriverCount()
OGRSFDriverH OGRGetDriver(int)

Expand Down Expand Up @@ -284,11 +285,11 @@ cdef extern from "ogr_api.h":
int64_t OGR_F_GetFieldAsInteger64(OGRFeatureH feature, int n)
const char* OGR_F_GetFieldAsString(OGRFeatureH feature, int n)
char ** OGR_F_GetFieldAsStringList(OGRFeatureH feature, int n)
int * OGR_F_GetFieldAsIntegerList(
const int * OGR_F_GetFieldAsIntegerList(
OGRFeatureH feature, int n, int* pnCount)
int64_t * OGR_F_GetFieldAsInteger64List(
const GIntBig * OGR_F_GetFieldAsInteger64List(
OGRFeatureH feature, int n, int* pnCount)
double * OGR_F_GetFieldAsDoubleList(
const double * OGR_F_GetFieldAsDoubleList(
OGRFeatureH feature, int n, int* pnCount)

int OGR_F_IsFieldSetAndNotNull(OGRFeatureH feature, int n)
Expand Down
28 changes: 28 additions & 0 deletions pyogrio/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,34 @@ def geojson_filelike(tmp_path):
yield f


@pytest.fixture(scope="function")
def kml_file(tmp_path):
# create KML file
kml_data = """<?xml version="1.0" encoding="utf-8" ?>
<kml xmlns="http://www.opengis.net/kml/2.2">
<Document id="root_doc">
<Schema name="interfaces1" id="interfaces1">
<SimpleField name="id" type="float"></SimpleField>
<SimpleField name="formation" type="string"></SimpleField>
</Schema>
<Folder><name>interfaces1</name>
<Placemark>
<ExtendedData><SchemaData schemaUrl="#interfaces1">
<SimpleData name="formation">Ton</SimpleData>
</SchemaData></ExtendedData>
<Point><coordinates>19.1501280458077,293.313485355882</coordinates></Point>
</Placemark>
</Folder>
</Document>
</kml>
"""
filename = tmp_path / "test.kml"
with open(filename, "w") as f:
_ = f.write(kml_data)

return filename


@pytest.fixture(scope="function")
def nonseekable_bytes(tmp_path):
# mock a non-seekable byte stream, such as a zstandard handle
Expand Down
70 changes: 51 additions & 19 deletions pyogrio/tests/test_geopandas_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,22 @@ def test_read_geojson_error(naturalearth_lowres_geojson, use_arrow):
set_gdal_config_options({"OGR_GEOJSON_MAX_OBJ_SIZE": None})


@pytest.mark.skipif(
"LIBKML" not in list_drivers(),
reason="LIBKML driver is not available and is needed to read simpledata element",
)
def test_read_kml_simpledata(kml_file, use_arrow):
"""Test reading a KML file with a simpledata element.

Simpledata elements are only read by the LibKML driver, not the KML driver.
"""
gdf = read_dataframe(kml_file, use_arrow=use_arrow)

# Check if the simpledata column is present.
assert "formation" in gdf.columns
assert gdf["formation"].iloc[0] == "Ton"


def test_read_layer(tmp_path, use_arrow):
filename = tmp_path / "test.gpkg"

Expand Down Expand Up @@ -2556,27 +2572,43 @@ def test_write_kml_file_coordinate_order(tmp_path, use_arrow):

assert np.array_equal(gdf_in.geometry.values, points)

if "LIBKML" in list_drivers():
# test appending to the existing file only if LIBKML is available
# as it appears to fall back on LIBKML driver when appending.
points_append = [Point(7, 8), Point(9, 10), Point(11, 12)]
gdf_append = gp.GeoDataFrame(geometry=points_append, crs="EPSG:4326")

write_dataframe(
gdf_append,
output_path,
layer="tmp_layer",
driver="KML",
use_arrow=use_arrow,
append=True,
)
# force_2d used to only compare xy geometry as z-dimension is undesirably
# introduced when the kml file is over-written.
gdf_in_appended = read_dataframe(
output_path, use_arrow=use_arrow, force_2d=True
)
@pytest.mark.requires_arrow_write_api
@pytest.mark.skipif(
"LIBKML" not in list_drivers(),
reason="LIBKML driver is not available and is needed to append to .kml",
)
def test_write_kml_append(tmp_path, use_arrow):
"""Append features to an existing KML file.

Appending is only supported by the LIBKML driver, and the driver isn't
included in the GDAL ubuntu-small images, so skip if not available.
"""
points = [Point(10, 20), Point(30, 40), Point(50, 60)]
gdf = gp.GeoDataFrame(geometry=points, crs="EPSG:4326")
output_path = tmp_path / "test.kml"
write_dataframe(
gdf, output_path, layer="tmp_layer", driver="KML", use_arrow=use_arrow
)

# test appending to the existing file only if LIBKML is available
# as it appears to fall back on LIBKML driver when appending.
points_append = [Point(7, 8), Point(9, 10), Point(11, 12)]
gdf_append = gp.GeoDataFrame(geometry=points_append, crs="EPSG:4326")

assert np.array_equal(gdf_in_appended.geometry.values, points + points_append)
write_dataframe(
gdf_append,
output_path,
layer="tmp_layer",
driver="KML",
use_arrow=use_arrow,
append=True,
)
# force_2d is used to only compare the xy dimensions of the geometry, as the LIBKML
# driver always adds the z-dimension when the kml file is over-written.
gdf_in_appended = read_dataframe(output_path, use_arrow=use_arrow, force_2d=True)

assert np.array_equal(gdf_in_appended.geometry.values, points + points_append)


@pytest.mark.requires_arrow_write_api
Expand Down
Loading