Skip to content

Commit

Permalink
MapInfo .mif/.tab: implement read/write support for MapInfo logical t…
Browse files Browse the repository at this point in the history
…ype to OGR OFSTBoolean

Fixes qgis/QGIS#57359
  • Loading branch information
rouault committed May 8, 2024
1 parent c27a689 commit bd9d8d4
Show file tree
Hide file tree
Showing 12 changed files with 124 additions and 73 deletions.
Binary file modified autotest/ogr/data/mitab/all_possible_fields.dat
Binary file not shown.
3 changes: 2 additions & 1 deletion autotest/ogr/data/mitab/all_possible_fields.mid
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
"test",120,12345,123456789012345,12.34,12.34,20221231,235900000,20220323145600000,T
"test",120,12345,123456789012345,12.34,12.34,20221231,235900000,20220323145600000,T
"test",120,12345,123456789012345,12.34,12.34,20221231,235900000,20220323145600000,F
35 changes: 18 additions & 17 deletions autotest/ogr/data/mitab/all_possible_fields.mif
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
Version 900
Charset "WindowsLatin1"
Delimiter ","
Columns 10
field1 Char(10)
Field2 Integer
Field3 Smallint
Field4 LargeInt
Field5 Float
Field6 Decimal(10, 2)
Field7 Date
Field8 Time
Field9 DateTime
Field10 Logical
Data

none
Version 900
Charset "WindowsLatin1"
Delimiter ","
Columns 10
field1 Char(10)
Field2 Integer
Field3 Smallint
Field4 LargeInt
Field5 Float
Field6 Decimal(10, 2)
Field7 Date
Field8 Time
Field9 DateTime
Field10 Logical
Data

none
none
56 changes: 33 additions & 23 deletions autotest/ogr/ogr_mitab.py
Original file line number Diff line number Diff line change
Expand Up @@ -2507,11 +2507,11 @@ def test_ogr_mitab_read_multi_line_mid():
f = lyr.GetNextFeature()
assert f["Name"] == "NAME1"
assert f["Notes"] == "MULTI\n\nLINE"
assert f["Awesome"] == "F"
assert f["Awesome"] is False
f = lyr.GetNextFeature()
assert f["Name"] == "NAME2"
assert f["Notes"] == "MULTI\nLINE2"
assert f["Awesome"] == "F"
assert f["Awesome"] is False


###############################################################################
Expand All @@ -2536,38 +2536,48 @@ def test_ogr_mitab_read_single_field_mid():

@pytest.mark.parametrize("ext", ["mif", "tab"])
def test_ogr_mitab_read_write_all_data_types(tmp_vsimem, ext):
def check_features(lyr):
f = lyr.GetNextFeature()
assert f["field1"] == "test"
assert f["Field2"] == 120
assert f["Field3"] == 12345
assert (
lyr.GetLayerDefn()
.GetFieldDefn(lyr.GetLayerDefn().GetFieldIndex("Field4"))
.GetType()
== ogr.OFTInteger64
)
assert f["Field4"] == 123456789012345
assert f["Field5"] == 12.34
assert f["Field6"] == 12.34
assert f["Field7"] == "2022/12/31"
assert f["Field8"] == "23:59:00"
assert f["Field9"] == "2022/03/23 14:56:00"
assert f["Field10"] is True

f = lyr.GetNextFeature()
assert f["Field10"] is False

ds = ogr.Open("data/mitab/all_possible_fields." + ext)
lyr = ds.GetLayer(0)
f = lyr.GetNextFeature()
assert f["field1"] == "test"
assert f["Field2"] == 120
assert f["Field3"] == 12345
assert (
lyr.GetLayerDefn()
.GetFieldDefn(lyr.GetLayerDefn().GetFieldIndex("Field4"))
.GetType()
== ogr.OFTInteger64
)
assert f["Field4"] == 123456789012345
assert f["Field5"] == 12.34
assert f["Field6"] == 12.34
assert f["Field7"] == "2022/12/31"
assert f["Field8"] == "23:59:00"
assert f["Field9"] == "2022/03/23 14:56:00"
assert f["Field10"] == "T"
check_features(lyr)

filename = tmp_vsimem / f"test_ogr_mitab_read_write_all_data_types.{ext}"
out_ds = ogr.GetDriverByName("MapInfo File").CreateDataSource(filename)
out_lyr = out_ds.CreateLayer("test", geom_type=ogr.wkbNone)
for i in range(lyr.GetLayerDefn().GetFieldCount()):
out_lyr.CreateField(lyr.GetLayerDefn().GetFieldDefn(i))
out_f = ogr.Feature(out_lyr.GetLayerDefn())
out_f.SetFrom(f)
assert out_lyr.CreateFeature(out_f) == ogr.OGRERR_NONE
out_f = None
for f in lyr:
out_f = ogr.Feature(out_lyr.GetLayerDefn())
out_f.SetFrom(f)
assert out_lyr.CreateFeature(out_f) == ogr.OGRERR_NONE
out_f = None
out_ds = None

ds = ogr.Open(filename)
lyr = ds.GetLayer(0)
check_features(lyr)


###############################################################################

Expand Down
26 changes: 11 additions & 15 deletions ogr/ogrsf_frmts/mitab/mitab_datfile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1442,7 +1442,8 @@ int TABDATFile::AlterFieldDefn(int iField, OGRFieldDefn *poNewFieldDefn,
else if (m_pasFieldDef[iField].eTABType == TABFLogical)
{
strncpy(pabyNewField,
ReadLogicalField(m_pasFieldDef[iField].byLength),
ReadLogicalField(m_pasFieldDef[iField].byLength) ? "T"
: "F",
sFieldDef.byLength);
}
else if (m_pasFieldDef[iField].eTABType == TABFDate)
Expand Down Expand Up @@ -1745,25 +1746,22 @@ double TABDATFile::ReadFloatField(int nWidth)
* Read the logical field value at the current position in the data
* block.
*
* The file contains either 0 or 1, and we return a string with
* "F" (false) or "T" (true)
*
* Note: nWidth is used only with TABTableDBF types.
*
* CPLError() will have been called if something fails.
**********************************************************************/
const char *TABDATFile::ReadLogicalField(int nWidth)
bool TABDATFile::ReadLogicalField(int nWidth)
{
// If current record has been deleted, then return an acceptable
// default value.
if (m_bCurRecordDeletedFlag)
return "F";
return false;

if (m_poRecordBlock == nullptr)
{
CPLError(CE_Failure, CPLE_AssertionFailed,
"Can't read field value: file is not opened.");
return "";
return false;
}

bool bValue = false;
Expand All @@ -1778,7 +1776,7 @@ const char *TABDATFile::ReadLogicalField(int nWidth)
bValue = CPL_TO_BOOL(m_poRecordBlock->ReadByte());
}

return bValue ? "T" : "F";
return bValue;
}

/**********************************************************************
Expand Down Expand Up @@ -2217,12 +2215,11 @@ int TABDATFile::WriteFloatField(double dValue, TABINDFile *poINDFile,
* Write the logical field value at the current position in the data
* block.
*
* The value written to the file is either 0 or 1, but this function
* takes as input a string with "F" (false) or "T" (true)
* The value written to the file is either 0 or 1.
*
* CPLError() will have been called if something fails.
**********************************************************************/
int TABDATFile::WriteLogicalField(const char *pszValue, TABINDFile *poINDFile,
int TABDATFile::WriteLogicalField(bool bValue, TABINDFile *poINDFile,
int nIndexNo)
{
if (m_poRecordBlock == nullptr)
Expand All @@ -2233,18 +2230,17 @@ int TABDATFile::WriteLogicalField(const char *pszValue, TABINDFile *poINDFile,
return -1;
}

// TODO(schwehr): bValue should be a bool.
const GByte bValue = STARTS_WITH_CI(pszValue, "T") ? 1 : 0;
const GByte byValue = bValue ? 1 : 0;

// Update Index
if (poINDFile && nIndexNo > 0)
{
GByte *pKey = poINDFile->BuildKey(nIndexNo, static_cast<int>(bValue));
GByte *pKey = poINDFile->BuildKey(nIndexNo, static_cast<int>(byValue));
if (poINDFile->AddEntry(nIndexNo, pKey, m_nCurRecordId) != 0)
return -1;
}

return m_poRecordBlock->WriteByte(bValue);
return m_poRecordBlock->WriteByte(byValue);
}

/**********************************************************************
Expand Down
9 changes: 5 additions & 4 deletions ogr/ogrsf_frmts/mitab/mitab_feature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -390,9 +390,9 @@ int TABFeature::ReadRecordFromDATFile(TABDATFile *poDATFile)
}
case TABFLogical:
{
const char *pszValue = poDATFile->ReadLogicalField(
const bool bValue = poDATFile->ReadLogicalField(
poDATFile->GetFieldWidth(iField));
SetField(iField, pszValue);
SetField(iField, bValue ? 1 : 0);
break;
}
case TABFDate:
Expand Down Expand Up @@ -557,8 +557,9 @@ int TABFeature::WriteRecordToDATFile(TABDATFile *poDATFile,
GetFieldAsDouble(iField), poINDFile, panIndexNo[iField]);
break;
case TABFLogical:
nStatus = poDATFile->WriteLogicalField(
GetFieldAsString(iField), poINDFile, panIndexNo[iField]);
nStatus =
poDATFile->WriteLogicalField(GetFieldAsInteger(iField) == 1,
poINDFile, panIndexNo[iField]);
break;
case TABFDate:
#ifdef MITAB_USE_OFTDATETIME
Expand Down
31 changes: 29 additions & 2 deletions ogr/ogrsf_frmts/mitab/mitab_feature_mif.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,9 @@ int TABFeature::ReadRecordFromMIDFile(MIDDATAFile *fp)
return -1;
}

OGRFieldDefn *poFDefn = nullptr;
for (int i = 0; i < nFields; i++)
{
poFDefn = GetFieldDefnRef(i);
const auto poFDefn = GetFieldDefnRef(i);
switch (poFDefn->GetType())
{
#ifdef MITAB_USE_OFTDATETIME
Expand Down Expand Up @@ -141,6 +140,22 @@ int TABFeature::ReadRecordFromMIDFile(MIDDATAFile *fp)
break;
}
#endif
case OFTInteger:
{
if (poFDefn->GetSubType() == OFSTBoolean)
{
char ch = papszToken[i][0];
SetField(i, (ch == 'T' || ch == 't' || ch == 'Y' ||
ch == 'y' || ch == '1')
? 1
: 0);
}
else
{
SetField(i, papszToken[i]);
}
break;
}
case OFTString:
{
CPLString osValue(papszToken[i]);
Expand Down Expand Up @@ -289,6 +304,18 @@ int TABFeature::WriteRecordToMIDFile(MIDDATAFile *fp)
break;
}
#endif
case OFTInteger:
{
if (poFDefn->GetSubType() == OFSTBoolean)
{
fp->WriteLine("%c", GetFieldAsInteger(iField) ? 'T' : 'F');
}
else
{
fp->WriteLine("%s", GetFieldAsString(iField));
}
break;
}
default:
fp->WriteLine("%s", GetFieldAsString(iField));
}
Expand Down
14 changes: 11 additions & 3 deletions ogr/ogrsf_frmts/mitab/mitab_imapinfofile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -445,9 +445,17 @@ int IMapInfoFile::GetTABType(const OGRFieldDefn *poField,

if (poField->GetType() == OFTInteger)
{
eTABType = TABFInteger;
if (nWidth == 0)
nWidth = 12;
if (poField->GetSubType() == OFSTBoolean)
{
eTABType = TABFLogical;
nWidth = 1;
}
else
{
eTABType = TABFInteger;
if (nWidth == 0)
nWidth = 12;
}
}
else if (poField->GetType() == OFTInteger64)
{
Expand Down
7 changes: 5 additions & 2 deletions ogr/ogrsf_frmts/mitab/mitab_miffile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1576,7 +1576,9 @@ int MIFFile::SetFeatureDefn(
switch (poFieldDefn->GetType())
{
case OFTInteger:
eMapInfoType = TABFInteger;
eMapInfoType = poFieldDefn->GetSubType() == OFSTBoolean
? TABFLogical
: TABFInteger;
break;
case OFTReal:
eMapInfoType = TABFFloat;
Expand Down Expand Up @@ -1764,7 +1766,8 @@ int MIFFile::AddFieldNative(const char *pszName, TABFieldType eMapInfoType,
/*-------------------------------------------------
* LOGICAL type (value "T" or "F")
*------------------------------------------------*/
poFieldDefn = new OGRFieldDefn(osName.c_str(), OFTString);
poFieldDefn = new OGRFieldDefn(osName.c_str(), OFTInteger);
poFieldDefn->SetSubType(OFSTBoolean);
poFieldDefn->SetWidth(1);
break;
default:
Expand Down
1 change: 1 addition & 0 deletions ogr/ogrsf_frmts/mitab/mitab_ogr_driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ void RegisterOGRTAB()
poDriver->SetMetadataItem(
GDAL_DMD_CREATIONFIELDDATATYPES,
"Integer Integer64 Real String Date DateTime Time");
poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATASUBTYPES, "Boolean");
poDriver->SetMetadataItem(GDAL_DMD_CREATION_FIELD_DEFN_FLAGS,
"WidthPrecision");
poDriver->SetMetadataItem(GDAL_DMD_ALTER_FIELD_DEFN_FLAGS,
Expand Down
5 changes: 2 additions & 3 deletions ogr/ogrsf_frmts/mitab/mitab_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -1899,7 +1899,7 @@ class TABDATFile
GInt64 ReadLargeIntField(int nWidth);
double ReadFloatField(int nWidth);
double ReadDecimalField(int nWidth);
const char *ReadLogicalField(int nWidth);
bool ReadLogicalField(int nWidth);
const char *ReadDateField(int nWidth);
int ReadDateField(int nWidth, int *nYear, int *nMonth, int *nDay);
const char *ReadTimeField(int nWidth);
Expand All @@ -1917,8 +1917,7 @@ class TABDATFile
int WriteFloatField(double dValue, TABINDFile *poINDFile, int nIndexNo);
int WriteDecimalField(double dValue, int nWidth, int nPrecision,
TABINDFile *poINDFile, int nIndexNo);
int WriteLogicalField(const char *pszValue, TABINDFile *poINDFile,
int nIndexNo);
int WriteLogicalField(bool bValue, TABINDFile *poINDFile, int nIndexNo);
int WriteDateField(const char *pszValue, TABINDFile *poINDFile,
int nIndexNo);
int WriteDateField(int nYear, int nMonth, int nDay, TABINDFile *poINDFile,
Expand Down
10 changes: 7 additions & 3 deletions ogr/ogrsf_frmts/mitab/mitab_tabfile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -996,7 +996,8 @@ int TABFile::ParseTABFileFields()
*------------------------------------------------*/
nStatus = m_poDATFile->ValidateFieldInfoFromTAB(
iField, osFieldName, TABFLogical, 0, 0);
poFieldDefn = new OGRFieldDefn(osFieldName, OFTString);
poFieldDefn = new OGRFieldDefn(osFieldName, OFTInteger);
poFieldDefn->SetSubType(OFSTBoolean);
poFieldDefn->SetWidth(1);
}
else
Expand Down Expand Up @@ -2044,7 +2045,9 @@ int TABFile::SetFeatureDefn(
switch (poFieldDefn->GetType())
{
case OFTInteger:
eMapInfoType = TABFInteger;
eMapInfoType = poFieldDefn->GetSubType() == OFSTBoolean
? TABFLogical
: TABFInteger;
break;
case OFTReal:
if (poFieldDefn->GetWidth() > 0 ||
Expand Down Expand Up @@ -2226,7 +2229,8 @@ int TABFile::AddFieldNative(const char *pszName, TABFieldType eMapInfoType,
/*-------------------------------------------------
* LOGICAL type (value "T" or "F")
*------------------------------------------------*/
poFieldDefn = new OGRFieldDefn(osName.c_str(), OFTString);
poFieldDefn = new OGRFieldDefn(osName.c_str(), OFTInteger);
poFieldDefn->SetSubType(OFSTBoolean);
poFieldDefn->SetWidth(1);
break;
default:
Expand Down

0 comments on commit bd9d8d4

Please sign in to comment.