Skip to content

Commit

Permalink
[Backport release/3.9] MiraMonVector: Fixing the way that Z are writt…
Browse files Browse the repository at this point in the history
…en in MiraMon layers (#9952)
  • Loading branch information
rouault committed May 16, 2024
1 parent 8af7095 commit e82d164
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 36 deletions.
15 changes: 12 additions & 3 deletions autotest/ogr/ogr_miramon_vector.py
Original file line number Diff line number Diff line change
Expand Up @@ -1173,7 +1173,16 @@ def test_ogr_miramon_write_basic_linestring(tmp_path):
ds = None


def test_ogr_miramon_write_basic_linestringZ(tmp_path):
# There are to ways of writing/reading repeated Z's in a linestring file.
# So let's test both ways (different Z's in each vertice or the same Z for all of them)
@pytest.mark.parametrize(
"LinestringZ",
[
"LINESTRING Z (0 0 4,0 1 3,1 1 2)",
"LINESTRING Z (0 0 4,0 1 4,1 1 4)",
],
)
def test_ogr_miramon_write_basic_linestringZ(tmp_path, LinestringZ):

filename = str(tmp_path / "DataSetLINESTRING")
ds = ogr.GetDriverByName("MiramonVector").CreateDataSource(filename)
Expand All @@ -1184,7 +1193,7 @@ def test_ogr_miramon_write_basic_linestringZ(tmp_path):
f = ogr.Feature(lyr.GetLayerDefn())
assign_common_attributes(f)

f.SetGeometry(ogr.CreateGeometryFromWkt("LINESTRING Z (0 0 4,0 1 3,1 1 2)"))
f.SetGeometry(ogr.CreateGeometryFromWkt(LinestringZ))
lyr.CreateFeature(f)
f = None
ds = None
Expand All @@ -1198,7 +1207,7 @@ def test_ogr_miramon_write_basic_linestringZ(tmp_path):
assert f["NODE_INI"] == [0, 0]
assert f["NODE_FI"] == [1, 1]
check_common_attributes(f)
assert f.GetGeometryRef().ExportToIsoWkt() == "LINESTRING Z (0 0 4,0 1 3,1 1 2)"
assert f.GetGeometryRef().ExportToIsoWkt() == LinestringZ
ds = None


Expand Down
19 changes: 11 additions & 8 deletions ogr/ogrsf_frmts/miramon/mm_gdal_driver_structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,14 +99,16 @@ CPL_C_START // Necessary for compiling in GDAL project
#define MM_LayerType_Node 7 // Layer of Nodes (internal)
#define MM_LayerType_Raster 8 // Layer of Raster Type

#define MM_FIRST_NUMBER_OF_POINTS 10000
#define MM_INCR_NUMBER_OF_POINTS 1000
#define MM_FIRST_NUMBER_OF_ARCS 10000
#define MM_INCR_NUMBER_OF_ARCS 1000
#define MM_FIRST_NUMBER_OF_NODES 20000 // 2*MM_FIRST_NUMBER_OF_ARCS
#define MM_INCR_NUMBER_OF_NODES 2000
#define MM_FIRST_NUMBER_OF_POLYGONS 10000
#define MM_INCR_NUMBER_OF_POLYGONS 1000
// FIRST are used for a first allocation and
// INCR for needed memory increase
#define MM_FIRST_NUMBER_OF_POINTS 100000 // 3.5 Mb
#define MM_INCR_NUMBER_OF_POINTS 100000 // 3.5 Mb
#define MM_FIRST_NUMBER_OF_ARCS 100000 // 5.3 Mb
#define MM_INCR_NUMBER_OF_ARCS 100000 // 5.3 Mb
#define MM_FIRST_NUMBER_OF_NODES 200000 // 2*MM_FIRST_NUMBER_OF_ARCS 1.5 Mb
#define MM_INCR_NUMBER_OF_NODES 200000 // 1.5 Mb
#define MM_FIRST_NUMBER_OF_POLYGONS 100000 // 6 Mb
#define MM_INCR_NUMBER_OF_POLYGONS 100000 // 6 Mb
#define MM_FIRST_NUMBER_OF_VERTICES 10000
#define MM_INCR_NUMBER_OF_VERTICES 1000

Expand Down Expand Up @@ -653,6 +655,7 @@ struct MiraMonFeature
// Number of used elements in *pZCoord
MM_N_VERTICES_TYPE nNumpZCoord;
MM_COORD_TYPE *pZCoord;
MM_BOOLEAN bAllZHaveSameValue;

// Records of the feature
MM_EXT_DBF_N_MULTIPLE_RECORDS nNumMRecords;
Expand Down
128 changes: 105 additions & 23 deletions ogr/ogrsf_frmts/miramon/mm_wrlayr.c
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,9 @@ void MMUpdateBoundingBoxXY(struct MMBoundingBox *dfBB,
void MMUpdateBoundingBox(struct MMBoundingBox *dfBBToBeAct,
struct MMBoundingBox *dfBBWithData);
int MMCheckVersionFor3DOffset(struct MiraMonVectLayerInfo *hMiraMonLayer,
MM_FILE_OFFSET nOffset,
MM_INTERNAL_FID nElemCount);
MM_INTERNAL_FID nElemCount,
MM_FILE_OFFSET nOffsetAL,
MM_FILE_OFFSET nZLOffset);
int MMCheckVersionOffset(struct MiraMonVectLayerInfo *hMiraMonLayer,
MM_FILE_OFFSET OffsetToCheck);
int MMCheckVersionForFID(struct MiraMonVectLayerInfo *hMiraMonLayer,
Expand Down Expand Up @@ -652,6 +653,15 @@ MMWriteZDescriptionHeaders(struct MiraMonVectLayerInfo *hMiraMonLayer,
}
}

// Overflow?
if (hMiraMonLayer->LayerVersion == MM_32BITS_VERSION &&
(pZDescription + nIndex)->nOffsetZ >
MAXIMUM_OFFSET_IN_2GB_VECTORS - nOffsetDiff)
{
MMCPLError(CE_Failure, CPLE_OpenFailed, "Offset Overflow in V1.1");
return 1;
}

if (MMAppendIntegerDependingOnVersion(
hMiraMonLayer, &FlushTMP, &nUL32,
(pZDescription + nIndex)->nOffsetZ + nOffsetDiff))
Expand Down Expand Up @@ -3921,16 +3931,43 @@ static int MMCreateFeaturePolOrArc(struct MiraMonVectLayerInfo *hMiraMonLayer,
// Where 3D part is going to start
if (hMiraMonLayer->TopHeader.bIs3d)
{
nArcOffset +=
hMMFeature->pNCoordRing[nIPart] * pMMArc->nALElementSize;
if (MMCheckVersionFor3DOffset(
hMiraMonLayer, nArcOffset,
hMiraMonLayer->TopHeader.nElemCount +
hMMFeature->nNRings))
if (nArcElemCount == 0)
{
MMCPLDebug("MiraMon",
"Error in MMCheckVersionFor3DOffset()");
return MM_STOP_WRITING_FEATURES;
if (MMCheckVersionFor3DOffset(hMiraMonLayer,
nArcElemCount + 1, 0, 0))
return MM_STOP_WRITING_FEATURES;
}
else
{
pZDesc = pMMArc->pZSection.pZDescription;
if (!pZDesc)
{
MMCPLError(
CE_Failure, CPLE_ObjectNull,
"Error: pZDescription should not be nullptr");
return MM_STOP_WRITING_FEATURES;
}

if (pZDesc[nArcElemCount - 1].nZCount < 0)
{
// One altitude was written on last element
if (MMCheckVersionFor3DOffset(
hMiraMonLayer, nArcElemCount + 1, nArcOffset,
pZDesc[nArcElemCount - 1].nOffsetZ +
sizeof(*pZ)))
return MM_STOP_WRITING_FEATURES;
}
else
{
// One for each vertice altitude was written on last element
if (MMCheckVersionFor3DOffset(
hMiraMonLayer, nArcElemCount + 1, nArcOffset,
pZDesc[nArcElemCount - 1].nOffsetZ +
sizeof(*pZ) * (pMMArc->pArcHeader +
(nArcElemCount - 1))
->nElemCount))
return MM_STOP_WRITING_FEATURES;
}
}
}
}
Expand Down Expand Up @@ -4187,6 +4224,16 @@ static int MMCreateFeaturePolOrArc(struct MiraMonVectLayerInfo *hMiraMonLayer,
STATISTICAL_UNDEF_VALUE;
pZDesc[pArcTopHeader->nElemCount].dfBBmaxz =
-STATISTICAL_UNDEF_VALUE;

// Number of arc altitudes. If the number of altitudes is
// positive this indicates the number of altitudes for
// each vertex of the arc, and all the altitudes of the
// vertex 0 are written first, then those of the vertex 1,
// etc. If the number of altitudes is negative this
// indicates the number of arc altitudes, understanding
// that all the vertices have the same altitude (it is
// the case of a contour line, for example).

for (nIVertice = 0; nIVertice < pCurrentArcHeader->nElemCount;
nIVertice++, pZ++)
{
Expand All @@ -4202,17 +4249,41 @@ static int MMCreateFeaturePolOrArc(struct MiraMonVectLayerInfo *hMiraMonLayer,
pZDesc[pArcTopHeader->nElemCount].dfBBminz = *pZ;
if (pZDesc[pArcTopHeader->nElemCount].dfBBmaxz < *pZ)
pZDesc[pArcTopHeader->nElemCount].dfBBmaxz = *pZ;

// Only one altitude (the same for all vertices) is written
if (hMMFeature->bAllZHaveSameValue)
break;
}
if (hMMFeature->bAllZHaveSameValue)
{
// Same altitude for all vertices
pZDesc[pArcTopHeader->nElemCount].nZCount = -1;
}
else
{
// One diferent altitude for each vertice
pZDesc[pArcTopHeader->nElemCount].nZCount = 1;
}
pZDesc[pArcTopHeader->nElemCount].nZCount = 1;

if (pArcTopHeader->nElemCount == 0)
pZDesc[pArcTopHeader->nElemCount].nOffsetZ = 0;
else
{
pLastArcHeader =
pMMArc->pArcHeader + pArcTopHeader->nElemCount - 1;
pZDesc[pArcTopHeader->nElemCount].nOffsetZ =
pZDesc[pArcTopHeader->nElemCount - 1].nOffsetZ +
sizeof(*pZ) * (pLastArcHeader->nElemCount);

if (pZDesc[pArcTopHeader->nElemCount - 1].nZCount < 0)
{
pZDesc[pArcTopHeader->nElemCount].nOffsetZ =
pZDesc[pArcTopHeader->nElemCount - 1].nOffsetZ +
sizeof(*pZ);
}
else
{
pZDesc[pArcTopHeader->nElemCount].nOffsetZ =
pZDesc[pArcTopHeader->nElemCount - 1].nOffsetZ +
sizeof(*pZ) * (pLastArcHeader->nElemCount);
}
}
}

Expand Down Expand Up @@ -4391,7 +4462,8 @@ static int MMCreateFeaturePoint(struct MiraMonVectLayerInfo *hMiraMonLayer,
{
if (nElemCount == 0)
{
if (MMCheckVersionFor3DOffset(hMiraMonLayer, 0, nElemCount + 1))
if (MMCheckVersionFor3DOffset(hMiraMonLayer, (nElemCount + 1),
0, 0))
return MM_STOP_WRITING_FEATURES;
}
else
Expand All @@ -4403,10 +4475,10 @@ static int MMCreateFeaturePoint(struct MiraMonVectLayerInfo *hMiraMonLayer,
"Error: pZDescription should not be nullptr");
return MM_STOP_WRITING_FEATURES;
}

if (MMCheckVersionFor3DOffset(
hMiraMonLayer,
pZDescription[nElemCount - 1].nOffsetZ + sizeof(*pZ),
nElemCount + 1))
hMiraMonLayer, nElemCount + 1, 0,
pZDescription[nElemCount - 1].nOffsetZ + sizeof(*pZ)))
return MM_STOP_WRITING_FEATURES;
}
}
Expand Down Expand Up @@ -4436,7 +4508,9 @@ static int MMCreateFeaturePoint(struct MiraMonVectLayerInfo *hMiraMonLayer,

pZDescription[nElemCount].dfBBminz = *pZ;
pZDescription[nElemCount].dfBBmaxz = *pZ;
pZDescription[nElemCount].nZCount = 1;

// Specification ask for a negative number.
pZDescription[nElemCount].nZCount = -1;
if (nElemCount == 0)
pZDescription[nElemCount].nOffsetZ = 0;
else
Expand Down Expand Up @@ -4557,8 +4631,9 @@ int MMCheckVersionOffset(struct MiraMonVectLayerInfo *hMiraMonLayer,
// Checks whether a given offset in 3D section exceeds the maximum allowed
// index for 2 GB vectors in a specific MiraMon layer.
int MMCheckVersionFor3DOffset(struct MiraMonVectLayerInfo *hMiraMonLayer,
MM_FILE_OFFSET nOffset,
MM_INTERNAL_FID nElemCount)
MM_INTERNAL_FID nElemCount,
MM_FILE_OFFSET nOffsetAL,
MM_FILE_OFFSET nZLOffset)
{
MM_FILE_OFFSET LastOffset;

Expand All @@ -4570,10 +4645,17 @@ int MMCheckVersionFor3DOffset(struct MiraMonVectLayerInfo *hMiraMonLayer,
return 0;

// User decided that if necessary, output version can be 2.0
LastOffset = nOffset + MM_HEADER_SIZE_32_BITS + nElemCount * MM_SIZE_OF_TL;
if (hMiraMonLayer->bIsPoint)
LastOffset = MM_HEADER_SIZE_32_BITS + nElemCount * MM_SIZE_OF_TL;
else // Arc case
{
LastOffset = MM_HEADER_SIZE_32_BITS +
nElemCount * MM_SIZE_OF_AH_32BITS + +nOffsetAL;
}

LastOffset += MM_SIZE_OF_ZH;
LastOffset += nElemCount * MM_SIZE_OF_ZD_32_BITS;
LastOffset += nZLOffset;

if (LastOffset < MAXIMUM_OFFSET_IN_2GB_VECTORS)
return 0;
Expand Down
16 changes: 14 additions & 2 deletions ogr/ogrsf_frmts/miramon/ogrmiramonlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1816,6 +1816,7 @@ OGRErr OGRMiraMonLayer::MMDumpVertices(OGRGeometryH hGeom,
MM_MEAN_NUMBER_OF_NCOORDS, 0))
return OGRERR_FAILURE;

hMMFeature.bAllZHaveSameValue = TRUE;
for (int iPoint = 0;
(MM_N_VERTICES_TYPE)iPoint < hMMFeature.pNCoordRing[hMMFeature.nIRing];
iPoint++)
Expand All @@ -1831,6 +1832,15 @@ OGRErr OGRMiraMonLayer::MMDumpVertices(OGRGeometryH hGeom,
phMiraMonLayer->bIsReal3d = 1;
}

// Asking if last Z-coordinate is the same than this one.
// If all Z-coordinates are the same, following MiraMon specification
// only the hMMFeature.pZCoord[0] value will be used and the number of
// vertices will be saved as a negative number on disk
if (iPoint > 0 &&
!CPLIsEqual(hMMFeature.pZCoord[hMMFeature.nICoord],
hMMFeature.pZCoord[hMMFeature.nICoord - 1]))
hMMFeature.bAllZHaveSameValue = FALSE;

hMMFeature.nICoord++;
}
hMMFeature.nIRing++;
Expand Down Expand Up @@ -1939,9 +1949,11 @@ OGRErr OGRMiraMonLayer::MMWriteGeometry()
{
CPLDebugOnly("MiraMon", "Error in MMAddFeature() "
"MM_STOP_WRITING_FEATURES");
CPLError(CE_Failure, CPLE_FileIO, "MiraMon format limitations.");
CPLError(CE_Failure, CPLE_FileIO,
"Try V2.0 option (-lco Version=V2.0).");
"MiraMon format limitations. Try V2.0 option (-lco "
"Version=V2.0). " sprintf_UINT64
" elements have been written correctly.",
phMiraMonLayer->TopHeader.nElemCount);
return OGRERR_FAILURE;
}

Expand Down

0 comments on commit e82d164

Please sign in to comment.