Skip to content
New issue

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

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

Already on GitHub? # to your account

Add an "int16" property type #1358

Merged
merged 3 commits into from
Mar 29, 2024
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
2 changes: 2 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ All issue numbers are relative to https://github.com/Toblerity/Fiona/issues.

Bug fixes:

- Add a 16-bit integer type "int16" based on OGR's OSFTInt16 integer sub-type
(#1358).
- Allow a GeoJSON collection's layer name to be set on opening in write mode
(#1352).
- The legacy crs.py module which was shadowed by the new crs.pyx module has
Expand Down
55 changes: 32 additions & 23 deletions fiona/ogrext.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,7 @@ cdef class Session:
continue

fieldtypename = FIELD_TYPES[OGR_Fld_GetType(cogr_fielddefn)]
fieldsubtype = OGR_Fld_GetSubType(cogr_fielddefn)

if not fieldtypename:
log.warning(
Expand All @@ -786,39 +787,44 @@ cdef class Session:
OGR_Fld_GetType(cogr_fielddefn))
continue

val = fieldtypename

if fieldtypename == 'float':
fmt = ""
if fieldtypename == "float":
width = OGR_Fld_GetWidth(cogr_fielddefn)

if width: # and width != 24:
fmt = f":{width:d}"

precision = OGR_Fld_GetPrecision(cogr_fielddefn)

if precision: # and precision != 15:
fmt = ""
if width:
fmt = f":{width:d}"
if precision:
fmt += f".{precision:d}"
val = f"float{fmt}"

val = "float" + fmt
elif fieldtypename == "int32":
if fieldsubtype == OFSTBoolean:
val = "bool"
elif fieldsubtype == OFSTInt16:
val = "int16"
else:
fmt = ""
width = OGR_Fld_GetWidth(cogr_fielddefn)
if width:
fmt = f":{width:d}"
val = f"int32{fmt}"

elif fieldtypename in ('int32', 'int64'):
elif fieldtypename == "int64":
fmt = ""
width = OGR_Fld_GetWidth(cogr_fielddefn)

if width:
fmt = f":{width:d}"
val = f"int{fmt}"

val = 'int' + fmt

elif fieldtypename == 'str':
elif fieldtypename == "str":
fmt = ""
width = OGR_Fld_GetWidth(cogr_fielddefn)

if width:
fmt = f":{width:d}"
val = f"str{fmt}"

val = fieldtypename + fmt
else:
val = fieldtypename

# Store the field name and description value.
props[key] = val
Expand Down Expand Up @@ -1287,15 +1293,17 @@ cdef class WritingSession(Session):

# Convert 'long' to 'int'. See
# https://github.com/Toblerity/Fiona/issues/101.
if fiona.gdal_version.major >= 2 and value in ('int', 'long'):
if value in ('int', 'long'):
value = 'int64'
elif value == 'int':
value = 'int32'

if value == 'bool':
elif value == 'bool':
value = 'int32'
field_subtype = OFSTBoolean

elif value == 'int16':
value = 'int32'
field_subtype = OFSTInt16

# Is there a field width/precision?
width = precision = None
if ':' in value:
Expand All @@ -1307,7 +1315,7 @@ cdef class WritingSession(Session):
width = int(fmt)

if value == 'int':
if GDAL_VERSION_NUM >= 2000000 and (width == 0 or width >= 10):
if width == 0 or width >= 10:
value = 'int64'
else:
value = 'int32'
Expand All @@ -1324,6 +1332,7 @@ cdef class WritingSession(Session):
if field_subtype != OFSTNone:
# subtypes are new in GDAL 2.x, ignored in 1.x
OGR_Fld_SetSubType(cogr_fielddefn, field_subtype)

exc_wrap_int(OGR_L_CreateField(self.cogr_layer, cogr_fielddefn, 1))

except (UnicodeEncodeError, CPLE_BaseError) as exc:
Expand Down
2 changes: 2 additions & 0 deletions fiona/schema.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ def normalize_field_type(ftype):
return ftype
elif ftype == 'bool':
return 'bool'
elif ftype == "int16":
return 'int32'
elif ftype.startswith('int'):
width = int((ftype.split(':')[1:] or ['0'])[0])
if GDAL_VERSION_NUM >= 2000000 and (width == 0 or width >= 10):
Expand Down
2 changes: 1 addition & 1 deletion tests/test_bytescollection.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def test_schema(self):
assert s["NAME"] == "str"
assert s["URL"] == "str"
assert s["STATE_FIPS"] == "str"
assert s["WILDRNP020"] == "int"
assert s["WILDRNP020"] == "int32"

def test_closed_schema(self):
# Schema is lazy too, never computed in this case. TODO?
Expand Down
60 changes: 44 additions & 16 deletions tests/test_subtypes.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
"""Tests of schema sub-types."""

import os

import fiona
from fiona.model import Feature


def test_read_bool_subtype(tmpdir):
def test_read_bool_subtype(tmp_path):
test_data = """{"type": "FeatureCollection", "features": [{"type": "Feature", "properties": {"bool": true, "not_bool": 1, "float": 42.5}, "geometry": null}]}"""
path = tmpdir.join("test_read_bool_subtype.geojson")
with open(str(path), "w") as f:
path = tmp_path.joinpath("test_read_bool_subtype.geojson")

with open(os.fspath(path), "w") as f:
f.write(test_data)

with fiona.open(str(path), "r") as src:
with fiona.open(path, "r") as src:
feature = next(iter(src))

if fiona.gdal_version.major >= 2:
assert type(feature["properties"]["bool"]) is bool
else:
assert type(feature["properties"]["bool"]) is int
assert type(feature["properties"]["bool"]) is bool
assert isinstance(feature["properties"]["not_bool"], int)
assert type(feature["properties"]["float"]) is float


def test_write_bool_subtype(tmpdir):
path = tmpdir.join("test_write_bool_subtype.geojson")
def test_write_bool_subtype(tmp_path):
path = tmp_path.joinpath("test_write_bool_subtype.geojson")

schema = {
"geometry": "Point",
Expand All @@ -42,14 +44,40 @@ def test_write_bool_subtype(tmpdir):
}
)

with fiona.open(str(path), "w", driver="GeoJSON", schema=schema) as dst:
with fiona.open(path, "w", driver="GeoJSON", schema=schema) as dst:
dst.write(feature)

with open(str(path)) as f:
with open(os.fspath(path)) as f:
data = f.read()

if fiona.gdal_version.major >= 2:
assert """"bool": true""" in data
else:
assert """"bool": 1""" in data
assert """"bool": true""" in data
assert """"not_bool": 1""" in data


def test_write_int16_subtype(tmp_path):
path = tmp_path.joinpath("test_write_bool_subtype.gpkg")

schema = {
"geometry": "Point",
"properties": {
"a": "int",
"b": "int16",
},
}

feature = Feature.from_dict(
**{
"geometry": None,
"properties": {
"a": 1,
"b": 2,
},
}
)

with fiona.open(path, "w", driver="GPKG", schema=schema) as colxn:
colxn.write(feature)

with fiona.open(path) as colxn:
assert colxn.schema["properties"]["a"] == "int"
assert colxn.schema["properties"]["b"] == "int16"
Loading