Skip to content

Commit

Permalink
Add an "int16" property type (#1358)
Browse files Browse the repository at this point in the history
* Add an "int16" property type

Resolves #1321

* Update reference in change log

* Code cleanup to improve readability
  • Loading branch information
sgillies authored Mar 29, 2024
1 parent b33bc56 commit 42ed681
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 40 deletions.
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"

0 comments on commit 42ed681

Please sign in to comment.