Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH: Add support for reading string lists #1141

Merged
merged 1 commit into from
Dec 8, 2022
Merged
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
1 change: 1 addition & 0 deletions fiona/gdal.pxi
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,7 @@ cdef extern from "ogr_api.h" nogil:
double OGR_F_GetFieldAsDouble(OGRFeatureH feature, int n)
int OGR_F_GetFieldAsInteger(OGRFeatureH feature, int n)
const char *OGR_F_GetFieldAsString(OGRFeatureH feature, int n)
char **OGR_F_GetFieldAsStringList( OGRFeatureH feature, int n)
int OGR_F_GetFieldCount(OGRFeatureH feature)
OGRFieldDefnH OGR_F_GetFieldDefnRef(OGRFeatureH feature, int n)
int OGR_F_GetFieldIndex(OGRFeatureH feature, const char *name)
Expand Down
18 changes: 17 additions & 1 deletion fiona/ogrext.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import os
import warnings
import math
from collections import namedtuple, OrderedDict
from typing import List
from uuid import uuid4

from six import integer_types, string_types, text_type
Expand Down Expand Up @@ -168,6 +169,8 @@ cdef class FeatureBuilder:
cdef void *fdefn = NULL
cdef int i
cdef unsigned char *data = NULL
cdef char **string_list = NULL
cdef int string_list_index = 0
cdef int l
cdef int retval
cdef int fieldsubtype
Expand Down Expand Up @@ -284,7 +287,20 @@ cdef class FeatureBuilder:
elif fieldtype is bytes:
data = OGR_F_GetFieldAsBinary(feature, i, &l)
props[key] = data[:l]

elif fieldtype is List[str]:
string_list = OGR_F_GetFieldAsStringList(feature, i)
string_list_index = 0
props[key] = []
while string_list[string_list_index] != NULL:
val = string_list[string_list_index]
try:
val = val.decode(encoding)
except UnicodeDecodeError:
log.warning(
"Failed to decode %s using %s codec", val, encoding
)
props[key].append(val)
string_list_index += 1
else:
props[key] = None

Expand Down
7 changes: 5 additions & 2 deletions fiona/schema.pyx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from six import text_type

from typing import List

from fiona.errors import SchemaError
from fiona.rfc3339 import FionaDateType, FionaDateTimeType, FionaTimeType

Expand All @@ -24,7 +26,7 @@ FIELD_TYPES = [
'float', # OFTReal, Double Precision floating point
None, # OFTRealList, List of doubles
'str', # OFTString, String of UTF-8 chars
None, # OFTStringList, Array of strings
'List[str]', # OFTStringList, Array of strings
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@snowman2 yes, I agree this is the right way to do it. Thank you!

None, # OFTWideString, deprecated
None, # OFTWideStringList, deprecated
'bytes', # OFTBinary, Raw Binary data
Expand All @@ -45,7 +47,8 @@ FIELD_TYPES_MAP = {
'datetime': FionaDateTimeType,
'bytes': bytes,
'int64': int,
'int': int
'int': int,
'List[str]': List[str],
}

FIELD_TYPES_MAP_REV = dict([(v, k) for k, v in FIELD_TYPES_MAP.items()])
Expand Down
31 changes: 31 additions & 0 deletions tests/test_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -384,3 +384,34 @@ def test_schema_default_fields_wrong_type(tmpdir):
driver="GPX",
schema=schema) as c:
pass


def test_schema_string_list(tmp_path):
output_file = tmp_path / "fio_test.geojson"
schema = {
"properties": {
"time_range": "str",
},
"geometry": "Point",
}
with fiona.open(
output_file, "w", driver="GeoJSON", schema=schema, crs="EPSG:4326"
) as fds:
fds.writerecords(
[
{
"id": 1,
"geometry": {"type": "Point", "coordinates": [0.0, 0.0]},
"properties": {
"time_range": '["2020-01-01", "2020-01-02"]',
},
}
]
)

with fiona.open(output_file) as fds:
assert fds.schema["properties"] == {"time_range": "List[str]"}
layers = list(fds)
assert layers[0]["properties"] == {
"time_range": ["2020-01-01", "2020-01-02"]
}