From 5805d0a93d6a784738aa0617d841993497cf37ea Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Wed, 20 Mar 2024 17:11:41 +0100 Subject: [PATCH] Shapefile: add read/write support for DBF Logical field type mapped to OGR OFSTBoolean --- autotest/ogr/ogr_shape.py | 46 +++++++++++++++++ ogr/ogrsf_frmts/shape/ogrshapedriver.cpp | 1 + ogr/ogrsf_frmts/shape/ogrshapelayer.cpp | 16 ++++-- ogr/ogrsf_frmts/shape/shape2ogr.cpp | 66 +++++++++++++++++------- 4 files changed, 106 insertions(+), 23 deletions(-) diff --git a/autotest/ogr/ogr_shape.py b/autotest/ogr/ogr_shape.py index bfc5be622ae5..2e68659af244 100755 --- a/autotest/ogr/ogr_shape.py +++ b/autotest/ogr/ogr_shape.py @@ -6066,3 +6066,49 @@ def test_ogr_shape_arrow_stream_fid_optim(tmp_vsimem): == "YES" ) assert len(batches) == 0 + + +############################################################################### +# Test DBF Logical field type + + +@gdaltest.enable_exceptions() +def test_ogr_shape_logical_field(tmp_vsimem): + + filename = tmp_vsimem / "test_ogr_shape_logical_field.shp" + ds = gdal.GetDriverByName("ESRI Shapefile").Create( + filename, 0, 0, 0, gdal.GDT_Unknown + ) + lyr = ds.CreateLayer("test") + fld_defn = ogr.FieldDefn("bool_field", ogr.OFTInteger) + fld_defn.SetSubType(ogr.OFSTBoolean) + lyr.CreateField(fld_defn) + lyr.CreateField(ogr.FieldDefn("int_field", ogr.OFTInteger)) + f = ogr.Feature(lyr.GetLayerDefn()) + f["bool_field"] = True + f["int_field"] = 1234 + lyr.CreateFeature(f) + f = ogr.Feature(lyr.GetLayerDefn()) + f["bool_field"] = False + f["int_field"] = -1234 + lyr.CreateFeature(f) + f = ogr.Feature(lyr.GetLayerDefn()) + f["bool_field"] = None + lyr.CreateFeature(f) + f = None + ds.Close() + + ds = ogr.Open(filename) + lyr = ds.GetLayer(0) + fld_defn = lyr.GetLayerDefn().GetFieldDefn(0) + assert fld_defn.GetType() == ogr.OFTInteger + assert fld_defn.GetSubType() == ogr.OFSTBoolean + assert fld_defn.GetWidth() == 1 + f = lyr.GetNextFeature() + assert f["bool_field"] == True + assert f["int_field"] == 1234 + f = lyr.GetNextFeature() + assert f["bool_field"] == False + assert f["int_field"] == -1234 + f = lyr.GetNextFeature() + assert f["bool_field"] is None diff --git a/ogr/ogrsf_frmts/shape/ogrshapedriver.cpp b/ogr/ogrsf_frmts/shape/ogrshapedriver.cpp index 69d671b29750..1bd877924412 100644 --- a/ogr/ogrsf_frmts/shape/ogrshapedriver.cpp +++ b/ogr/ogrsf_frmts/shape/ogrshapedriver.cpp @@ -427,6 +427,7 @@ void RegisterOGRShape() poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATATYPES, "Integer Integer64 Real String Date"); + poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATASUBTYPES, "Boolean"); poDriver->SetMetadataItem(GDAL_DMD_CREATION_FIELD_DEFN_FLAGS, "WidthPrecision"); poDriver->SetMetadataItem(GDAL_DMD_ALTER_FIELD_DEFN_FLAGS, diff --git a/ogr/ogrsf_frmts/shape/ogrshapelayer.cpp b/ogr/ogrsf_frmts/shape/ogrshapelayer.cpp index 08b7cf28fdab..2a1507cea652 100644 --- a/ogr/ogrsf_frmts/shape/ogrshapelayer.cpp +++ b/ogr/ogrsf_frmts/shape/ogrshapelayer.cpp @@ -2042,10 +2042,18 @@ OGRErr OGRShapeLayer::CreateField(const OGRFieldDefn *poFieldDefn, switch (oModFieldDefn.GetType()) { case OFTInteger: - chType = 'N'; - nWidth = oModFieldDefn.GetWidth(); - if (nWidth == 0) - nWidth = 9; + if (oModFieldDefn.GetSubType() == OFSTBoolean) + { + chType = 'L'; + nWidth = 1; + } + else + { + chType = 'N'; + nWidth = oModFieldDefn.GetWidth(); + if (nWidth == 0) + nWidth = 9; + } break; case OFTInteger64: diff --git a/ogr/ogrsf_frmts/shape/shape2ogr.cpp b/ogr/ogrsf_frmts/shape/shape2ogr.cpp index 452f549bf916..d2577b63fbae 100644 --- a/ogr/ogrsf_frmts/shape/shape2ogr.cpp +++ b/ogr/ogrsf_frmts/shape/shape2ogr.cpp @@ -1172,6 +1172,11 @@ OGRFeatureDefn *SHPReadOGRFeatureDefn(const char *pszName, SHPHandle hSHP, } else if (eDBFType == FTInteger) oField.SetType(OFTInteger); + else if (eDBFType == FTLogical) + { + oField.SetType(OFTInteger); + oField.SetSubType(OFSTBoolean); + } else oField.SetType(OFTString); @@ -1431,8 +1436,22 @@ OGRFeature *SHPReadOGRFeature(SHPHandle hSHP, DBFHandle hDBF, } else { - poFeature->SetField( - iField, DBFReadStringAttribute(hDBF, iShape, iField)); + if (poFieldDefn->GetSubType() == OFSTBoolean) + { + const char *pszVal = + DBFReadLogicalAttribute(hDBF, iShape, iField); + poFeature->SetField( + iField, pszVal[0] == 'T' || pszVal[0] == 't' || + pszVal[0] == 'Y' || pszVal[0] == 'y' + ? 1 + : 0); + } + else + { + const char *pszVal = + DBFReadStringAttribute(hDBF, iShape, iField); + poFeature->SetField(iField, pszVal); + } } break; } @@ -1682,27 +1701,36 @@ OGRErr SHPWriteOGRFeature(SHPHandle hSHP, DBFHandle hDBF, case OFTInteger: case OFTInteger64: { - char szValue[32] = {}; - const int nFieldWidth = poFieldDefn->GetWidth(); - snprintf(szValue, sizeof(szValue), - "%*" CPL_FRMT_GB_WITHOUT_PREFIX "d", - std::min(nFieldWidth, - static_cast(sizeof(szValue)) - 1), - poFeature->GetFieldAsInteger64(iField)); - - const int nStrLen = static_cast(strlen(szValue)); - if (nStrLen > nFieldWidth) + if (poFieldDefn->GetSubType() == OFSTBoolean) { - if (GrowField(hDBF, iField, poFieldDefn, nStrLen) != - OGRERR_NONE) + DBFWriteAttributeDirectly( + hDBF, static_cast(poFeature->GetFID()), iField, + poFeature->GetFieldAsInteger(iField) ? "T" : "F"); + } + else + { + char szValue[32] = {}; + const int nFieldWidth = poFieldDefn->GetWidth(); + snprintf(szValue, sizeof(szValue), + "%*" CPL_FRMT_GB_WITHOUT_PREFIX "d", + std::min(nFieldWidth, + static_cast(sizeof(szValue)) - 1), + poFeature->GetFieldAsInteger64(iField)); + + const int nStrLen = static_cast(strlen(szValue)); + if (nStrLen > nFieldWidth) { - return OGRERR_FAILURE; + if (GrowField(hDBF, iField, poFieldDefn, nStrLen) != + OGRERR_NONE) + { + return OGRERR_FAILURE; + } } - } - DBFWriteAttributeDirectly(hDBF, - static_cast(poFeature->GetFID()), - iField, szValue); + DBFWriteAttributeDirectly( + hDBF, static_cast(poFeature->GetFID()), iField, + szValue); + } break; }