diff --git a/autotest/ogr/data/miramon/Arcs/3dArcs/linies_3d_WGS84.arc b/autotest/ogr/data/miramon/Arcs/3dArcs/linies_3d_WGS84.arc new file mode 100644 index 000000000000..e6e5b38820de Binary files /dev/null and b/autotest/ogr/data/miramon/Arcs/3dArcs/linies_3d_WGS84.arc differ diff --git a/autotest/ogr/data/miramon/Arcs/3dArcs/linies_3d_WGS84.nod b/autotest/ogr/data/miramon/Arcs/3dArcs/linies_3d_WGS84.nod new file mode 100644 index 000000000000..de26c1fb8e0b Binary files /dev/null and b/autotest/ogr/data/miramon/Arcs/3dArcs/linies_3d_WGS84.nod differ diff --git a/autotest/ogr/data/miramon/Arcs/3dArcs/linies_3d_WGS84A.dbf b/autotest/ogr/data/miramon/Arcs/3dArcs/linies_3d_WGS84A.dbf new file mode 100644 index 000000000000..b1460b1b4216 Binary files /dev/null and b/autotest/ogr/data/miramon/Arcs/3dArcs/linies_3d_WGS84A.dbf differ diff --git a/autotest/ogr/data/miramon/Arcs/3dArcs/linies_3d_WGS84A.rel b/autotest/ogr/data/miramon/Arcs/3dArcs/linies_3d_WGS84A.rel new file mode 100644 index 000000000000..c5e02b72eda0 --- /dev/null +++ b/autotest/ogr/data/miramon/Arcs/3dArcs/linies_3d_WGS84A.rel @@ -0,0 +1,161 @@ +[VERSIO] +Vers=4 +SubVers=3 +VersMetaDades=5 +SubVersMetaDades=0 + +[TAULA_PRINCIPAL] +IdGrafic=ID_GRAFIC +TipusRelacio=RELACIO_1_N_DICC + +[TAULA_PRINCIPAL:ID_GRAFIC] +visible=0 +MostrarUnitats=0 +descriptor=Identificador Gràfic intern + +[TAULA_PRINCIPAL:N_VERTEXS] +visible=0 +MostrarUnitats=0 +descriptor=Nombre de vèrtexs + +[TAULA_PRINCIPAL:LONG_ARC] +visible=0 +descriptor=Longitud de l'arc (projecció) + +[TAULA_PRINCIPAL:LONG_ARCE] +unitats=m +descriptor=Longitud de l'arc (el·lipsoide) + +[TAULA_PRINCIPAL:NODE_INI] +visible=0 +MostrarUnitats=0 +descriptor=Node inicial + +[TAULA_PRINCIPAL:NODE_FI] +visible=0 +MostrarUnitats=0 +descriptor=Node final + +[METADADES] +language=cat +MDIdiom=cat +dateStamp=20240311 09500830 +characterSet=006 +nOrganismes=2 +FileIdentifier=linies_3d_29042 + +[METADADES:ORGANISME_1] +role=009 +OrganisationName=CREAF +IndividualName=Abel Pau +PositionName=Tècnic en SIG + +[IDENTIFICATION] +code=linies_3d_29042 +codeSpace= +DatasetTitle=Linies + +[SPATIAL_REFERENCE_SYSTEM:HORIZONTAL] +HorizontalSystemIdentifier=lat/long-WGS84 + +[EXTENT] +MinX=1.28287097369422 +MaxX=1.98292118178235 +MinY=41.1902658152019 +MaxY=41.6776900669325 +toler_env=0 + +[OVERVIEW] +CreationDate=20240311 09500477 + +[OVERVIEW:ASPECTES_TECNICS] +comment1=Nombre d'arcs: 6 +comment2=El fitxer era anteriorment en la projecció UTM-31N-ETRS89 + +[METADADES:ORGANISME_2] +role=009 +OrganisationName=Students and educational institutions + +[QUALITY:LINEAGE:PROCESS1] +nOrganismes=1 +history=C:\MiraMon\MM64.exe +date=20230912 16505195+0200 + +[QUALITY:LINEAGE:PROCESS1:ORGANISME_1] +OrganisationName=Students and educational institutions + +[QUALITY:LINEAGE:PROCESS2] +nOrganismes=1 +history=Vec3D.exe 1 C:\Mapes\ColleccionsPreferides\Catalunya-ETRS89\Altimetria30m\MDE30m_ICC_Aster_mar0.img D:\dades\GDAL_V\KML\multi\+\linies.arc D:\dades\GDAL_V\KML\multi\+\linies_3d.arc 0 +purpose=Incorpora la 3a dimensió en capes vectorials +date=20231031 13263780+0100 + +[QUALITY:LINEAGE:PROCESS2:ORGANISME_1] +OrganisationName=Students and educational institutions + +[QUALITY:LINEAGE:PROCESS3] +nOrganismes=1 +history=CanviPrj_64.exe D:\dades\GDAL_V\KML\multi\+\linies_3d.arc D:\dades\GDAL_V\KML\multi\+\linies_3d_WGS84.arc lat/long-WGS84 +purpose=Permet fer la transformació per a vectors estructurats de punts (PNT), d'arcs (ARC) i polígons (POL). Per a transformar fitxers de nodes (NOD) cal transformar el fitxer d'arcs associat. +date=20231113 11502174+0100 +NomFitxer=C:\miramon\CanviPrj_64.exe + +[QUALITY:LINEAGE:PROCESS3:ORGANISME_1] +OrganisationName=Students and educational institutions + +[QUALITY:LINEAGE:PROCESS3:SOFTWARE_REFERENCE] +Titol= +Edition= +CollectiveTitle= +ISBN= +ISSN= + +[QUALITY:LINEAGE:PROCESS3:INOUT1] +identifier=Param1 +TypeValues=S +ResultUnits= +source=1 + +[QUALITY:LINEAGE:SOURCE1] +NomFitxer=linies_3d.arc +processes=4,5 + +[QUALITY:LINEAGE:PROCESS4] +nOrganismes=1 +history=C:\MiraMon\MM64.exe +date=20230912 16505195+0200 + +[QUALITY:LINEAGE:PROCESS4:ORGANISME_1] +OrganisationName=Students and educational institutions + +[QUALITY:LINEAGE:PROCESS5] +nOrganismes=1 +history=Vec3D.exe 1 C:\Mapes\ColleccionsPreferides\Catalunya-ETRS89\Altimetria30m\MDE30m_ICC_Aster_mar0.img D:\dades\GDAL_V\KML\multi\+\linies.arc D:\dades\GDAL_V\KML\multi\+\linies_3d.arc 0 +purpose=Incorpora la 3a dimensió en capes vectorials +date=20231031 13263780+0100 + +[QUALITY:LINEAGE:PROCESS5:ORGANISME_1] +OrganisationName=Students and educational institutions + +[QUALITY:LINEAGE:PROCESS3:INOUT2] +identifier=Param2 +sentit=1 +TypeValues=S +ResultUnits= +source= + +[QUALITY:LINEAGE:PROCESS3:INOUT3] +identifier=Param3 +TypeValues=C +ResultValue=lat/long-WGS84 +ResultUnits= + +[QUALITY:LINEAGE] +processes=1,2,3 + +[GEOMETRIA_I_TOPOLOGIA] +NomCampNVertexs=N_VERTEXS +NomCampLongitudArc=LONG_ARC +NomCampLongitudArcEllipsoidal=LONG_ARCE +NomCampNodeIni=NODE_INI +NomCampNodeFi=NODE_FI diff --git a/autotest/ogr/data/miramon/Arcs/3dArcs/linies_3d_WGS84N.dbf b/autotest/ogr/data/miramon/Arcs/3dArcs/linies_3d_WGS84N.dbf new file mode 100644 index 000000000000..9376c48a6ae8 Binary files /dev/null and b/autotest/ogr/data/miramon/Arcs/3dArcs/linies_3d_WGS84N.dbf differ diff --git a/autotest/ogr/data/miramon/Arcs/3dArcs/linies_3d_WGS84N.rel b/autotest/ogr/data/miramon/Arcs/3dArcs/linies_3d_WGS84N.rel new file mode 100644 index 000000000000..750942aead92 --- /dev/null +++ b/autotest/ogr/data/miramon/Arcs/3dArcs/linies_3d_WGS84N.rel @@ -0,0 +1,83 @@ +[VERSIO] +Vers=4 +SubVers=3 +VersMetaDades=5 +SubVersMetaDades=0 + +[TAULA_PRINCIPAL] +IdGrafic=ID_GRAFIC +TipusRelacio=RELACIO_1_N_DICC + +[TAULA_PRINCIPAL:ID_GRAFIC] +visible=0 +MostrarUnitats=0 +descriptor=Identificador Gràfic intern + +[TAULA_PRINCIPAL:ARCS_A_NOD] +MostrarUnitats=0 +descriptor=Nombre d'arcs al node + +[TAULA_PRINCIPAL:TIPUS_NODE] +MostrarUnitats=0 +descriptor=Tipus de node + +[METADADES] +language=cat +MDIdiom=cat +dateStamp=20240311 09500993 +characterSet=006 +nOrganismes=2 +FileIdentifier=linies_3d_29042 + +[METADADES:ORGANISME_1] +role=009 +OrganisationName=CREAF +IndividualName=Abel Pau +PositionName=Tècnic en SIG + +[IDENTIFICATION] +code=linies_3d_29042 +codeSpace= +DatasetTitle=Linies + +[OVERVIEW:ASPECTES_TECNICS] +comment1=Nombre d'arcs: 6 +comment2=El fitxer era anteriorment en la projecció UTM-31N-ETRS89 + +[EXTENT] +MinX=1.28287097369422 +MaxX=1.98292118178235 +MinY=41.1902658152019 +MaxY=41.6776900669325 +toler_env=0 + +[OVERVIEW] +CreationDate=20240311 09500616 + +[METADADES:ORGANISME_2] +role=009 +OrganisationName=Students and educational institutions + +[QUALITY:LINEAGE:PROCESS1] +nOrganismes=1 +history=C:\MiraMon\MM64.exe +date=20230912 16505195+0200 + +[QUALITY:LINEAGE:PROCESS1:ORGANISME_1] +OrganisationName=Students and educational institutions + +[QUALITY:LINEAGE:PROCESS2] +nOrganismes=1 +history=Vec3D.exe 1 C:\Mapes\ColleccionsPreferides\Catalunya-ETRS89\Altimetria30m\MDE30m_ICC_Aster_mar0.img D:\dades\GDAL_V\KML\multi\+\linies.arc D:\dades\GDAL_V\KML\multi\+\linies_3d.arc 0 +purpose=Incorpora la 3a dimensió en capes vectorials +date=20231031 13263780+0100 + +[QUALITY:LINEAGE:PROCESS2:ORGANISME_1] +OrganisationName=Students and educational institutions + +[QUALITY:LINEAGE] +processes=1,2 + +[GEOMETRIA_I_TOPOLOGIA] +NomCampArcsANode=ARCS_A_NOD +NomCampTipusNode=TIPUS_NODE diff --git a/autotest/ogr/data/miramon/Arcs/EmptyArcs/Empty_ARC.arc b/autotest/ogr/data/miramon/Arcs/EmptyArcs/Empty_ARC.arc new file mode 100644 index 000000000000..e089cf12d059 Binary files /dev/null and b/autotest/ogr/data/miramon/Arcs/EmptyArcs/Empty_ARC.arc differ diff --git a/autotest/ogr/data/miramon/Arcs/EmptyArcs/Empty_ARC.nod b/autotest/ogr/data/miramon/Arcs/EmptyArcs/Empty_ARC.nod new file mode 100644 index 000000000000..88197ddde3eb Binary files /dev/null and b/autotest/ogr/data/miramon/Arcs/EmptyArcs/Empty_ARC.nod differ diff --git a/autotest/ogr/data/miramon/Arcs/EmptyArcs/Empty_ARCA.dbf b/autotest/ogr/data/miramon/Arcs/EmptyArcs/Empty_ARCA.dbf new file mode 100644 index 000000000000..683995629988 Binary files /dev/null and b/autotest/ogr/data/miramon/Arcs/EmptyArcs/Empty_ARCA.dbf differ diff --git a/autotest/ogr/data/miramon/Arcs/EmptyArcs/Empty_ARCA.rel b/autotest/ogr/data/miramon/Arcs/EmptyArcs/Empty_ARCA.rel new file mode 100644 index 000000000000..429b66fee63c --- /dev/null +++ b/autotest/ogr/data/miramon/Arcs/EmptyArcs/Empty_ARCA.rel @@ -0,0 +1,41 @@ +[VERSIO] +VersMetaDades=5 +SubVersMetaDades=0 +Vers=4 +SubVers=3 + +[SPATIAL_REFERENCE_SYSTEM:HORIZONTAL] +HorizontalSystemIdentifier=UTM-31N-ETRS89 + +[TAULA_PRINCIPAL] +IdGrafic=ID_GRAFIC +TipusRelacio=RELACIO_1_N_DICC + +[TAULA_PRINCIPAL:ID_GRAFIC] +visible=0 +descriptor=Identificador Gràfic intern +MostrarUnitats=0 + +[TAULA_PRINCIPAL:N_VERTEXS] +visible=0 +MostrarUnitats=0 +descriptor=Nombre de vèrtexs + +[TAULA_PRINCIPAL:LONG_ARC] +descriptor=Longitud de l'arc + +[TAULA_PRINCIPAL:NODE_INI] +visible=0 +MostrarUnitats=0 +descriptor=Node inicial + +[TAULA_PRINCIPAL:NODE_FI] +visible=0 +MostrarUnitats=0 +descriptor=Node final + +[GEOMETRIA_I_TOPOLOGIA] +NomCampNVertexs=N_VERTEXS +NomCampLongitudArc=LONG_ARC +NomCampNodeIni=NODE_INI +NomCampNodeFi=NODE_FI diff --git a/autotest/ogr/data/miramon/Arcs/EmptyArcs/Empty_ARCN.dbf b/autotest/ogr/data/miramon/Arcs/EmptyArcs/Empty_ARCN.dbf new file mode 100644 index 000000000000..95689ae5c695 Binary files /dev/null and b/autotest/ogr/data/miramon/Arcs/EmptyArcs/Empty_ARCN.dbf differ diff --git a/autotest/ogr/data/miramon/Arcs/EmptyArcs/Empty_ARCN.rel b/autotest/ogr/data/miramon/Arcs/EmptyArcs/Empty_ARCN.rel new file mode 100644 index 000000000000..832186cce6c1 --- /dev/null +++ b/autotest/ogr/data/miramon/Arcs/EmptyArcs/Empty_ARCN.rel @@ -0,0 +1,26 @@ +[VERSIO] +VersMetaDades=5 +SubVersMetaDades=0 +Vers=4 +SubVers=3 + +[TAULA_PRINCIPAL] +IdGrafic=ID_GRAFIC +TipusRelacio=RELACIO_1_N_DICC + +[TAULA_PRINCIPAL:ID_GRAFIC] +visible=0 +descriptor=Identificador Gràfic intern +MostrarUnitats=0 + +[TAULA_PRINCIPAL:ARCS_A_NOD] +MostrarUnitats=0 +descriptor=Nombre d'arcs al node + +[TAULA_PRINCIPAL:TIPUS_NODE] +MostrarUnitats=0 +descriptor=Tipus de node + +[GEOMETRIA_I_TOPOLOGIA] +NomCampArcsANode=ARCS_A_NOD +NomCampTipusNode=TIPUS_NODE diff --git a/autotest/ogr/data/miramon/Arcs/SimpleArcs/SimpleArcFile.arc b/autotest/ogr/data/miramon/Arcs/SimpleArcs/SimpleArcFile.arc new file mode 100644 index 000000000000..a5084dcc5b72 Binary files /dev/null and b/autotest/ogr/data/miramon/Arcs/SimpleArcs/SimpleArcFile.arc differ diff --git a/autotest/ogr/data/miramon/Arcs/SimpleArcs/SimpleArcFile.nod b/autotest/ogr/data/miramon/Arcs/SimpleArcs/SimpleArcFile.nod new file mode 100644 index 000000000000..2b3fa6ee1e50 Binary files /dev/null and b/autotest/ogr/data/miramon/Arcs/SimpleArcs/SimpleArcFile.nod differ diff --git a/autotest/ogr/data/miramon/Arcs/SimpleArcs/SimpleArcFileA.dbf b/autotest/ogr/data/miramon/Arcs/SimpleArcs/SimpleArcFileA.dbf new file mode 100644 index 000000000000..cb3e15a7209b Binary files /dev/null and b/autotest/ogr/data/miramon/Arcs/SimpleArcs/SimpleArcFileA.dbf differ diff --git a/autotest/ogr/data/miramon/Arcs/SimpleArcs/SimpleArcFileA.rel b/autotest/ogr/data/miramon/Arcs/SimpleArcs/SimpleArcFileA.rel new file mode 100644 index 000000000000..9f92d9a62f2c --- /dev/null +++ b/autotest/ogr/data/miramon/Arcs/SimpleArcs/SimpleArcFileA.rel @@ -0,0 +1,102 @@ +[VERSIO] +VersMetaDades=5 +SubVersMetaDades=0 +Vers=4 +SubVers=3 + +[METADADES] +language=cat,spa,eng +MDIdiom=cat,spa,eng +dateStamp=20230628 16235471+0200 +characterSet=006 +nOrganismes=1 +FileIdentifier=16b4eae3-8f74-4145-95db-babb7f0feb0f_SimpleArcFileA + +[METADADES:ORGANISME_1] +role=009 +IndividualName=Abel Pau +PositionName=Tècnic SIG +OrganisationName=Students and educational institutions + +[IDENTIFICATION] +code=16b4eae3-8f74-4145-95db-babb7f0feb0f_SimpleArcFileA +codeSpace= +DatasetTitle=Simple Arc File + +[SPATIAL_REFERENCE_SYSTEM:HORIZONTAL] +HorizontalSystemDefinition=Local +HorizontalSystemIdentifier=plane +unitats=STB#T_pixels +unitatsY=? + +[EXTENT] +toler_env=0 +MinX=351.333967649907 +MaxX=1369.30161750719 +MinY=201.191246431919 +MaxY=931.88582302564 + +[OVERVIEW] +CreationDate=20230628 16235470+0200 + +[TAULA_PRINCIPAL] +IdGrafic=ID_GRAFIC +TipusRelacio=RELACIO_1_N_DICC + +[TAULA_PRINCIPAL:ID_GRAFIC] +visible=0 +descriptor=Identificador Gràfic intern +descriptor_spa=Identificador Gráfico interno +descriptor_eng=Internal Graphic identifier +MostrarUnitats=0 + +[TAULA_PRINCIPAL:N_VERTEXS] +visible=0 +MostrarUnitats=0 +descriptor=Nombre de vèrtexs +descriptor_spa=Número de vertices +descriptor_eng=Number of vertices + +[TAULA_PRINCIPAL:LONG_ARC] +descriptor=Longitud de l'arc +descriptor_spa=Longitud del arco +descriptor_eng=Lenght of arc + +[TAULA_PRINCIPAL:NODE_INI] +visible=0 +MostrarUnitats=0 +descriptor=Node inicial +descriptor_spa=Nodo inicial +descriptor_eng=Initial node + +[TAULA_PRINCIPAL:NODE_FI] +visible=0 +MostrarUnitats=0 +descriptor=Node final +descriptor_spa=Nodo final +descriptor_eng=Final node + +[TAULA_PRINCIPAL:ATT1] +descriptor=Atributte1 + +[TAULA_PRINCIPAL:ATT2] +descriptor=Attribute2 + +[QUALITY:LINEAGE:PROCESS1] +nOrganismes=1 +history=C:\MiraMon\MM64.exe +date=20230628 16235471+0200 + +[QUALITY:LINEAGE:PROCESS1:ORGANISME_1] +IndividualName=Abel Pau +PositionName=Tècnic SIG +OrganisationName=Students and educational institutions + +[QUALITY:LINEAGE] +processes=1 + +[GEOMETRIA_I_TOPOLOGIA] +NomCampNVertexs=N_VERTEXS +NomCampLongitudArc=LONG_ARC +NomCampNodeIni=NODE_INI +NomCampNodeFi=NODE_FI diff --git a/autotest/ogr/data/miramon/Arcs/SimpleArcs/SimpleArcFileN.dbf b/autotest/ogr/data/miramon/Arcs/SimpleArcs/SimpleArcFileN.dbf new file mode 100644 index 000000000000..93a6c3f4a05e Binary files /dev/null and b/autotest/ogr/data/miramon/Arcs/SimpleArcs/SimpleArcFileN.dbf differ diff --git a/autotest/ogr/data/miramon/Arcs/SimpleArcs/SimpleArcFileN.rel b/autotest/ogr/data/miramon/Arcs/SimpleArcs/SimpleArcFileN.rel new file mode 100644 index 000000000000..81b6ce35bc31 --- /dev/null +++ b/autotest/ogr/data/miramon/Arcs/SimpleArcs/SimpleArcFileN.rel @@ -0,0 +1,64 @@ +[VERSIO] +VersMetaDades=5 +SubVersMetaDades=0 +Vers=4 +SubVers=3 + +[METADADES] +language=cat +MDIdiom=cat +dateStamp=20230628 16235470+0200 +characterSet=006 +nOrganismes=1 +FileIdentifier=e4365dc3-82f4-4da8-ae1f-3f73922adc27_SimpleArcFileN + +[METADADES:ORGANISME_1] +role=009 +IndividualName=Abel Pau +PositionName=Tècnic SIG +OrganisationName=Students and educational institutions + +[IDENTIFICATION] +code=e4365dc3-82f4-4da8-ae1f-3f73922adc27_SimpleArcFileN +codeSpace= +DatasetTitle=Simple Arc File [píxels] + +[EXTENT] +toler_env=0 + +[OVERVIEW] +CreationDate=20230628 16235469+0200 + +[TAULA_PRINCIPAL] +IdGrafic=ID_GRAFIC +TipusRelacio=RELACIO_1_N_DICC + +[TAULA_PRINCIPAL:ID_GRAFIC] +visible=0 +descriptor=Identificador Gràfic intern +MostrarUnitats=0 + +[TAULA_PRINCIPAL:ARCS_A_NOD] +MostrarUnitats=0 +descriptor=Nombre d'arcs al node + +[TAULA_PRINCIPAL:TIPUS_NODE] +MostrarUnitats=0 +descriptor=Tipus de node + +[QUALITY:LINEAGE:PROCESS1] +nOrganismes=1 +history=C:\MiraMon\MM64.exe +date=20230628 16235470+0200 + +[QUALITY:LINEAGE:PROCESS1:ORGANISME_1] +IndividualName=Abel Pau +PositionName=Tècnic SIG +OrganisationName=Students and educational institutions + +[QUALITY:LINEAGE] +processes=1 + +[GEOMETRIA_I_TOPOLOGIA] +NomCampArcsANode=ARCS_A_NOD +NomCampTipusNode=TIPUS_NODE diff --git a/autotest/ogr/data/miramon/Points/3dpoints/Some3dPoints.pnt b/autotest/ogr/data/miramon/Points/3dpoints/Some3dPoints.pnt new file mode 100644 index 000000000000..ed3737140bc0 Binary files /dev/null and b/autotest/ogr/data/miramon/Points/3dpoints/Some3dPoints.pnt differ diff --git a/autotest/ogr/data/miramon/Points/3dpoints/Some3dPointsT.dbf b/autotest/ogr/data/miramon/Points/3dpoints/Some3dPointsT.dbf new file mode 100644 index 000000000000..e55c35b2df32 Binary files /dev/null and b/autotest/ogr/data/miramon/Points/3dpoints/Some3dPointsT.dbf differ diff --git a/autotest/ogr/data/miramon/Points/3dpoints/Some3dPointsT.rel b/autotest/ogr/data/miramon/Points/3dpoints/Some3dPointsT.rel new file mode 100644 index 000000000000..94ff8a8dfe67 --- /dev/null +++ b/autotest/ogr/data/miramon/Points/3dpoints/Some3dPointsT.rel @@ -0,0 +1,112 @@ +[VERSIO] +VersMetaDades=5 +SubVersMetaDades=0 +Vers=4 +SubVers=3 + +[METADADES] +language=cat +MDIdiom=cat +dateStamp=20240318 15131947+0100 +characterSet=006 +nOrganismes=2 +FileIdentifier=LIDAR3d_totT_14533 + +[METADADES:ORGANISME_1] +role=009 +IndividualName=Abel Pau +PositionName=Tècnic SIG +OrganisationName=CREAF + +[METADADES:ORGANISME_2] +role=009 +IndividualName=Abel Pau +PositionName=Tècnic en SIG +OrganisationName=CREAF + +[IDENTIFICATION] +code=LIDAR3d_totT_32431 +codeSpace= +DatasetTitle=Selecció de -> Selecció de -> Fitxer extret de D:\[...]\LIDAR3d_tot.shp + +[SPATIAL_REFERENCE_SYSTEM:HORIZONTAL] +HorizontalSystemIdentifier=UTM-31N-ETRS89 + +[EXTENT] +MinX=440544.58 +MaxX=440551.66 +MinY=4635313.38 +MaxY=4635319.81 +toler_env=0 + +[OVERVIEW] +CreationDate=20240318 15131943+0100 + +[TAULA_PRINCIPAL] +IdGrafic=ID_GRAFIC +TipusRelacio=RELACIO_1_1_DICC + +[TAULA_PRINCIPAL:ID_GRAFIC] +descriptor=Identificador Gràfic intern +visible=0 +simbolitzable=0 +MostrarUnitats=0 + +[TAULA_PRINCIPAL:INTENS] +MostrarUnitats=0 + +[TAULA_PRINCIPAL:ID_CLAS] +MostrarUnitats=0 + +[TAULA_PRINCIPAL:ANGLE] +MostrarUnitats=0 + +[TAULA_PRINCIPAL:RETURN_NR] +MostrarUnitats=0 + +[TAULA_PRINCIPAL:N_T_RETURN] +MostrarUnitats=0 + +[TAULA_PRINCIPAL:PULSE_TIME] +MostrarUnitats=0 + +[QUALITY:LINEAGE:PROCESS1] +nOrganismes=1 +history=VecSelec_64.exe D:\GitHub-repository\gdal\autotest\ogr\data\miramon\Points\LidarRectangle\TMP0000.SEL D:\GitHub-repository\gdal\autotest\ogr\data\miramon\Points\LidarRectangle\Some3dPoints +purpose=Un fitxer de text indica quines són les entitats gràfiques i registres de la base de dades que cal desar en el fitxer de sortida. En aquesta ajuda ens referirem a aquest fitxer com a fitxer de seleccions. Es recomana l'extensió SEL, tot i que no és obligatòria. +date=20240318 15131959+0100 +NomFitxer=C:\MiraMon\VecSelec_64.exe + +[QUALITY:LINEAGE:PROCESS1:ORGANISME_1] +IndividualName=Abel Pau +PositionName=Tècnic en SIG +OrganisationName=CREAF + +[QUALITY:LINEAGE:PROCESS1:SOFTWARE_REFERENCE] +Titol= +Edition= +CollectiveTitle= +ISBN= +ISSN= + +[QUALITY:LINEAGE:PROCESS1:INOUT1] +identifier=Param1 +TypeValues=S +ResultUnits= +source=1 + +[QUALITY:LINEAGE:SOURCE1] +NomFitxer=TMP0000.SEL + +[QUALITY:LINEAGE:PROCESS1:INOUT2] +identifier=Param2 +sentit=1 +TypeValues=S +ResultUnits= +source=2 + +[QUALITY:LINEAGE:SOURCE2] +NomFitxer=Some3dPoints + +[QUALITY:LINEAGE] +processes=1 diff --git a/autotest/ogr/data/miramon/Points/EmptyPoints/Empty_PNT.pnt b/autotest/ogr/data/miramon/Points/EmptyPoints/Empty_PNT.pnt new file mode 100644 index 000000000000..d06314c2b7ad Binary files /dev/null and b/autotest/ogr/data/miramon/Points/EmptyPoints/Empty_PNT.pnt differ diff --git a/autotest/ogr/data/miramon/Points/EmptyPoints/Empty_PNTT.dbf b/autotest/ogr/data/miramon/Points/EmptyPoints/Empty_PNTT.dbf new file mode 100644 index 000000000000..82935df8b1a3 Binary files /dev/null and b/autotest/ogr/data/miramon/Points/EmptyPoints/Empty_PNTT.dbf differ diff --git a/autotest/ogr/data/miramon/Points/EmptyPoints/Empty_PNTT.rel b/autotest/ogr/data/miramon/Points/EmptyPoints/Empty_PNTT.rel new file mode 100644 index 000000000000..329c866f3352 --- /dev/null +++ b/autotest/ogr/data/miramon/Points/EmptyPoints/Empty_PNTT.rel @@ -0,0 +1,23 @@ +[VERSIO] +Vers=4 +SubVers=3 +VersMetaDades=5 +SubVersMetaDades=0 + +[TAULA_PRINCIPAL] +IdGrafic=ID_GRAFIC +TipusRelacio=RELACIO_1_N_DICC + +[TAULA_PRINCIPAL:ID_GRAFIC] +descriptor=Identificador Gràfic intern + + +[SPATIAL_REFERENCE_SYSTEM:HORIZONTAL] +HorizontalSystemIdentifier=UTM-31N-ETRS89 + +[EXTENT] +MinX=2.9E+301 +MaxX=2.9E+301 +MinY=2.9E+301 +MaxY=2.9E+301 + diff --git a/autotest/ogr/data/miramon/Points/SimplePoints/SimplePointsFile.pnt b/autotest/ogr/data/miramon/Points/SimplePoints/SimplePointsFile.pnt new file mode 100644 index 000000000000..f543cba80f3e Binary files /dev/null and b/autotest/ogr/data/miramon/Points/SimplePoints/SimplePointsFile.pnt differ diff --git a/autotest/ogr/data/miramon/Points/SimplePoints/SimplePointsFileT.dbf b/autotest/ogr/data/miramon/Points/SimplePoints/SimplePointsFileT.dbf new file mode 100644 index 000000000000..57527da74671 Binary files /dev/null and b/autotest/ogr/data/miramon/Points/SimplePoints/SimplePointsFileT.dbf differ diff --git a/autotest/ogr/data/miramon/Points/SimplePoints/SimplePointsFileT.rel b/autotest/ogr/data/miramon/Points/SimplePoints/SimplePointsFileT.rel new file mode 100644 index 000000000000..69a72d07ba46 --- /dev/null +++ b/autotest/ogr/data/miramon/Points/SimplePoints/SimplePointsFileT.rel @@ -0,0 +1,58 @@ +[VERSIO] +Vers=4 +SubVers=3 +VersMetaDades=5 +SubVersMetaDades=0 + +[TAULA_PRINCIPAL] +IdGrafic=ID_GRAFIC +TipusRelacio=RELACIO_1_N_DICC + +[TAULA_PRINCIPAL:ID_GRAFIC] +visible=0 +simbolitzable=0 +MostrarUnitats=0 +descriptor=Identificador Gràfic intern +descriptor_spa=Identificador Gráfico interno +descriptor_eng=Internal Graphic identifier + +[TAULA_PRINCIPAL:ATT1] +descriptor=Atributte1 + +[TAULA_PRINCIPAL:ATTRIBUTE_2] +descriptor=Atributte2 + +[METADADES] +language=cat,spa,eng +MDIdiom=cat,spa,eng +dateStamp=20230628 16344458+0200 +characterSet=006 +nOrganismes=1 +FileIdentifier=68ddf845-79e8-4791-bf7a-5459eb951a04_SimplePointsFile + +[METADADES:ORGANISME_1] +role=009 +IndividualName=Abel Pau +PositionName=Tècnic SIG +OrganisationName=Students and educational institutions + +[IDENTIFICATION] +code=68ddf845-79e8-4791-bf7a-5459eb951a04_SimplePointsFile +codeSpace= +DatasetTitle=Simple Points File + +[SPATIAL_REFERENCE_SYSTEM:HORIZONTAL] +HorizontalSystemDefinition=Local +HorizontalSystemIdentifier=plane +unitats=STB#T_pixels + +[EXTENT] +MinX=342.325404376834 +MaxX=594.503182156354 +MinY=715.680304471881 +MaxY=848.806850618409 +toler_env=0 + +[OVERVIEW] +CreationDate=20230628 16351606+0200 +ContentDate=20230629 12064184+0200 diff --git a/autotest/ogr/data/miramon/Polygons/3dPolygons/tin_3d.arc b/autotest/ogr/data/miramon/Polygons/3dPolygons/tin_3d.arc new file mode 100644 index 000000000000..00bd90e368b3 Binary files /dev/null and b/autotest/ogr/data/miramon/Polygons/3dPolygons/tin_3d.arc differ diff --git a/autotest/ogr/data/miramon/Polygons/3dPolygons/tin_3d.nod b/autotest/ogr/data/miramon/Polygons/3dPolygons/tin_3d.nod new file mode 100644 index 000000000000..6b26237eea78 Binary files /dev/null and b/autotest/ogr/data/miramon/Polygons/3dPolygons/tin_3d.nod differ diff --git a/autotest/ogr/data/miramon/Polygons/3dPolygons/tin_3d.pol b/autotest/ogr/data/miramon/Polygons/3dPolygons/tin_3d.pol new file mode 100644 index 000000000000..5e690dfd30d4 Binary files /dev/null and b/autotest/ogr/data/miramon/Polygons/3dPolygons/tin_3d.pol differ diff --git a/autotest/ogr/data/miramon/Polygons/3dPolygons/tin_3dA.dbf b/autotest/ogr/data/miramon/Polygons/3dPolygons/tin_3dA.dbf new file mode 100644 index 000000000000..36aebaac2b49 Binary files /dev/null and b/autotest/ogr/data/miramon/Polygons/3dPolygons/tin_3dA.dbf differ diff --git a/autotest/ogr/data/miramon/Polygons/3dPolygons/tin_3dA.rel b/autotest/ogr/data/miramon/Polygons/3dPolygons/tin_3dA.rel new file mode 100644 index 000000000000..4bdd6908fabc --- /dev/null +++ b/autotest/ogr/data/miramon/Polygons/3dPolygons/tin_3dA.rel @@ -0,0 +1,141 @@ +[VERSIO] +VersMetaDades=5 +SubVersMetaDades=0 +Vers=4 +SubVers=3 + +[METADADES] +language=cat +MDIdiom=cat +dateStamp=20240319 11074377+0100 +characterSet=006 +nOrganismes=2 +FileIdentifier=out_ID_B_tin_3d_27042 + +[METADADES:ORGANISME_1] +role=009 +IndividualName=Abel Pau +PositionName=Tècnic en SIG +OrganisationName=CREAF + +[METADADES:ORGANISME_2] +role=009 +OrganisationName=Students and educational institutions + +[IDENTIFICATION] +code=out_ID_B_tin_3d_27042 +codeSpace= +DatasetTitle=Triangulació de Delaunay - Selecció de -> Selecció de -> Retall de->D:\dades\20220909_Thiessen\AltEmporda\centroides_02.pnt + +[OVERVIEW:ASPECTES_TECNICS] +Ciclat1=tin_3d.pol +comment1=S'ha transformat el fitxer 'MARC0000.vec' +comment2=S'ha donat una estructura topològica a l'original + +[SPATIAL_REFERENCE_SYSTEM:HORIZONTAL] +HorizontalSystemIdentifier=UTM-31N-ETRS89 + +[EXTENT] +MinX=510886.760465633 +MaxX=511161.917984244 +MinY=4660885.499725 +MaxY=4661425.355 +toler_env=0 + +[OVERVIEW] +CreationDate=20240319 11074377+0100 + +[TAULA_PRINCIPAL] +IdGrafic=ID_GRAFIC +TipusRelacio=RELACIO_1_1 + +[TAULA_PRINCIPAL:ID_GRAFIC] +visible=0 +MostrarUnitats=0 +descriptor=Identificador Gràfic intern + +[TAULA_PRINCIPAL:N_VERTEXS] +visible=0 +MostrarUnitats=0 +descriptor=Nombre de vèrtexs + +[TAULA_PRINCIPAL:LONG_ARC] +descriptor=Longitud de l'arc + +[TAULA_PRINCIPAL:NODE_INI] +visible=0 +MostrarUnitats=0 +descriptor=Node inicial + +[TAULA_PRINCIPAL:NODE_FI] +visible=0 +MostrarUnitats=0 +descriptor=Node final + +[QUALITY:LINEAGE:PROCESS1] +nOrganismes=1 +history=Thiessen_64.exe D:\dades\20220909_Thiessen\AltEmporda\sel2\punts.pnt D:\dades\20220909_Thiessen\AltEmporda\sel2\out_ID_B.pol ID_B 0 /TIN=D:\dades\20220909_Thiessen\AltEmporda\sel2\out_ID_B_tin.pol +date=20220912 14134491+0200 + +[QUALITY:LINEAGE:PROCESS1:ORGANISME_1] +OrganisationName=Students and educational institutions + +[QUALITY:LINEAGE:PROCESS2] +nOrganismes=1 +history=Vec3D.exe 1 C:\Mapes\ColleccionsPreferides\Catalunya-ETRS89\Altimetria30m\MDE30m_ICC_Aster_mar0.img F:\dades\20220909_Thiessen\AltEmporda\+\sel2\out_ID_B_tin.arc F:\dades\20220909_Thiessen\AltEmporda\+\sel2\out_ID_B_tin_3d.arc 0 +purpose=Incorpora la 3a dimensió en capes vectorials +date=20231212 15421367+0100 + +[QUALITY:LINEAGE:PROCESS2:ORGANISME_1] +OrganisationName=Students and educational institutions + +[QUALITY:LINEAGE:PROCESS3] +nOrganismes=1 +history=VecSelec_64.exe /EMANCIPA D:\GitHub-repository\gdal\autotest\ogr\data\miramon\Polygons\3dPolygons\TMP0000.SEL D:\GitHub-repository\gdal\autotest\ogr\data\miramon\Polygons\3dPolygons\tin_3d +purpose=Un fitxer de text indica quines són les entitats gràfiques i registres de la base de dades que cal desar en el fitxer de sortida. En aquesta ajuda ens referirem a aquest fitxer com a fitxer de seleccions. Es recomana l'extensió SEL, tot i que no és obligatòria. +date=20240318 15230782+0100 +NomFitxer=C:\MiraMon\VecSelec_64.exe + +[QUALITY:LINEAGE:PROCESS3:ORGANISME_1] +IndividualName=Abel Pau +PositionName=Tècnic en SIG +OrganisationName=CREAF + +[QUALITY:LINEAGE:PROCESS3:SOFTWARE_REFERENCE] +Titol= +Edition= +CollectiveTitle= +ISBN= +ISSN= + +[QUALITY:LINEAGE:PROCESS3:INOUT1] +identifier=EMANCIPA +ResultUnits= + +[QUALITY:LINEAGE:PROCESS3:INOUT2] +identifier=Param1 +TypeValues=S +ResultUnits= +source=1 + +[QUALITY:LINEAGE:SOURCE1] +NomFitxer=TMP0000.SEL + +[QUALITY:LINEAGE:PROCESS3:INOUT3] +identifier=Param2 +sentit=1 +TypeValues=S +ResultUnits= +source=2 + +[QUALITY:LINEAGE:SOURCE2] +NomFitxer=tin_3d + +[QUALITY:LINEAGE] +processes=1,2,3 + +[GEOMETRIA_I_TOPOLOGIA] +NomCampNVertexs=N_VERTEXS +NomCampLongitudArc=LONG_ARC +NomCampNodeIni=NODE_INI +NomCampNodeFi=NODE_FI diff --git a/autotest/ogr/data/miramon/Polygons/3dPolygons/tin_3dN.dbf b/autotest/ogr/data/miramon/Polygons/3dPolygons/tin_3dN.dbf new file mode 100644 index 000000000000..d1f5c1539991 Binary files /dev/null and b/autotest/ogr/data/miramon/Polygons/3dPolygons/tin_3dN.dbf differ diff --git a/autotest/ogr/data/miramon/Polygons/3dPolygons/tin_3dN.rel b/autotest/ogr/data/miramon/Polygons/3dPolygons/tin_3dN.rel new file mode 100644 index 000000000000..42bdccbad750 --- /dev/null +++ b/autotest/ogr/data/miramon/Polygons/3dPolygons/tin_3dN.rel @@ -0,0 +1,108 @@ +[VERSIO] +VersMetaDades=5 +SubVersMetaDades=0 +Vers=4 +SubVers=3 + +[METADADES] +language=cat +MDIdiom=cat +dateStamp=20240319 11074377+0100 +characterSet=006 +nOrganismes=2 +FileIdentifier=MARC0000_10835 + +[METADADES:ORGANISME_1] +role=009 +IndividualName=Abel Pau +PositionName=Tècnic en SIG +OrganisationName=CREAF + +[METADADES:ORGANISME_2] +role=009 +OrganisationName=Students and educational institutions + +[IDENTIFICATION] +code=MARC0000_07743 +codeSpace= +DatasetTitle=Triangulació de Delaunay - Selecció de -> Selecció de -> Retall de->D:\dades\20220909_Thiessen\AltEmporda\centroides_02.pnt [Plantill] + +[OVERVIEW:ASPECTES_TECNICS] +comment1=Nodes del fitxer d'arcs 'D:\dades\20220909_Thiessen\AltEmporda\sel2\out_ID_B_tin.arc' + +[EXTENT] +MinX=510886.760465633 +MaxX=511161.917984244 +MinY=4660885.499725 +MaxY=4661425.355 +toler_env=0 + +[OVERVIEW] +CreationDate=20240319 11074377+0100 + +[TAULA_PRINCIPAL] +IdGrafic=ID_GRAFIC +TipusRelacio=RELACIO_1_N_DICC + +[TAULA_PRINCIPAL:ID_GRAFIC] +visible=0 +simbolitzable=0 +MostrarUnitats=0 +descriptor=Identificador Gràfic intern + +[TAULA_PRINCIPAL:ARCS_A_NOD] +MostrarUnitats=0 +descriptor=Nombre d'arcs al node + +[TAULA_PRINCIPAL:TIPUS_NODE] +MostrarUnitats=0 +descriptor=Tipus de node + +[QUALITY:LINEAGE:PROCESS1] +nOrganismes=1 +history=VecSelec_64.exe /EMANCIPA D:\GitHub-repository\gdal\autotest\ogr\data\miramon\Polygons\3dPolygons\TMP0000.SEL D:\GitHub-repository\gdal\autotest\ogr\data\miramon\Polygons\3dPolygons\tin_3d +purpose=Un fitxer de text indica quines són les entitats gràfiques i registres de la base de dades que cal desar en el fitxer de sortida. En aquesta ajuda ens referirem a aquest fitxer com a fitxer de seleccions. Es recomana l'extensió SEL, tot i que no és obligatòria. +date=20240318 15230787+0100 +NomFitxer=C:\MiraMon\VecSelec_64.exe + +[QUALITY:LINEAGE:PROCESS1:ORGANISME_1] +IndividualName=Abel Pau +PositionName=Tècnic en SIG +OrganisationName=CREAF + +[QUALITY:LINEAGE:PROCESS1:SOFTWARE_REFERENCE] +Titol= +Edition= +CollectiveTitle= +ISBN= +ISSN= + +[QUALITY:LINEAGE:PROCESS1:INOUT1] +identifier=EMANCIPA +ResultUnits= + +[QUALITY:LINEAGE:PROCESS1:INOUT2] +identifier=Param1 +TypeValues=S +ResultUnits= +source=1 + +[QUALITY:LINEAGE:SOURCE1] +NomFitxer=TMP0000.SEL + +[QUALITY:LINEAGE:PROCESS1:INOUT3] +identifier=Param2 +sentit=1 +TypeValues=S +ResultUnits= +source=2 + +[QUALITY:LINEAGE:SOURCE2] +NomFitxer=tin_3d + +[QUALITY:LINEAGE] +processes=1 + +[GEOMETRIA_I_TOPOLOGIA] +NomCampArcsANode=ARCS_A_NOD +NomCampTipusNode=TIPUS_NODE diff --git a/autotest/ogr/data/miramon/Polygons/3dPolygons/tin_3dP.dbf b/autotest/ogr/data/miramon/Polygons/3dPolygons/tin_3dP.dbf new file mode 100644 index 000000000000..d2a443329d44 Binary files /dev/null and b/autotest/ogr/data/miramon/Polygons/3dPolygons/tin_3dP.dbf differ diff --git a/autotest/ogr/data/miramon/Polygons/3dPolygons/tin_3dP.rel b/autotest/ogr/data/miramon/Polygons/3dPolygons/tin_3dP.rel new file mode 100644 index 000000000000..8dc3779d32db --- /dev/null +++ b/autotest/ogr/data/miramon/Polygons/3dPolygons/tin_3dP.rel @@ -0,0 +1,180 @@ +[VERSIO] +VersMetaDades=5 +SubVersMetaDades=0 +Vers=4 +SubVers=3 + +[METADADES] +language=cat +MDIdiom=cat +dateStamp=20240319 11074377+0100 +characterSet=006 +nOrganismes=2 +FileIdentifier=out_ID_B_tin_3d_14172 + +[METADADES:ORGANISME_1] +role=009 +IndividualName=Abel Pau +PositionName=Tècnic en SIG +OrganisationName=CREAF + +[METADADES:ORGANISME_2] +role=009 +OrganisationName=Students and educational institutions + +[IDENTIFICATION] +code=out_ID_B_tin_3d_27042 +codeSpace= +DatasetTitle=Selecció de -> Triangulació de Delaunay - Selecció de -> Selecció de -> Retall de->D:\dades\20220909_Thiessen\AltEmporda\centroides_02.pnt + +[OVERVIEW:ASPECTES_TECNICS] +ArcSource=tin_3d.arc +comment1=S'ha transformat el fitxer 'MARC0000.vec' +comment2=S'ha donat una estructura topològica a l'original +comment3=Ciclat totalment a partir del fitxer F:\dades\20220909_Thiessen\AltEmporda\+\sel2\+\out_ID_B_tin_3d.arc. + +[EXTENT] +MinX=510886.760465633 +MaxX=511161.917984244 +MinY=4660885.499725 +MaxY=4661425.355 +toler_env=0 + +[OVERVIEW] +CreationDate=20240319 11074377+0100 + +[TAULA_PRINCIPAL] +IdGrafic=ID_GRAFIC +TipusRelacio=RELACIO_1_1_DICC + +[TAULA_PRINCIPAL:ID_GRAFIC] +visible=0 +MostrarUnitats=0 +descriptor=Identificador Gràfic intern + +[TAULA_PRINCIPAL:N_VERTEXS] +visible=0 +MostrarUnitats=0 +descriptor=Nombre de vèrtexs + +[TAULA_PRINCIPAL:PERIMETRE] +descriptor=Perímetre del polígon (projecció) + +[TAULA_PRINCIPAL:PERIMETREE] +visible=0 +unitats=m +descriptor=Perímetre del polígon (el·lipsoide) + +[TAULA_PRINCIPAL:AREA] +descriptor=Àrea del polígon (projecció) + +[TAULA_PRINCIPAL:AREAE] +visible=0 +unitats=m² +descriptor=Àrea del polígon (el·lipsoide) + +[TAULA_PRINCIPAL:N_ARCS] +visible=0 +MostrarUnitats=0 +descriptor=Nombre d'arcs + +[TAULA_PRINCIPAL:N_POLIG] +visible=0 +MostrarUnitats=0 +descriptor=Nombre de polígons elementals + +[QUALITY:LINEAGE:PROCESS1] +nOrganismes=1 +history=VecSelec_64.exe /EMANCIPA D:\GitHub-repository\gdal\autotest\ogr\data\miramon\Polygons\3dPolygons\TMP0000.SEL D:\GitHub-repository\gdal\autotest\ogr\data\miramon\Polygons\3dPolygons\tin_3d +purpose=Un fitxer de text indica quines són les entitats gràfiques i registres de la base de dades que cal desar en el fitxer de sortida. En aquesta ajuda ens referirem a aquest fitxer com a fitxer de seleccions. Es recomana l'extensió SEL, tot i que no és obligatòria. +date=20240318 15230736+0100 +NomFitxer=C:\MiraMon\VecSelec_64.exe + +[QUALITY:LINEAGE:PROCESS1:ORGANISME_1] +IndividualName=Abel Pau +PositionName=Tècnic en SIG +OrganisationName=CREAF + +[QUALITY:LINEAGE:PROCESS1:SOFTWARE_REFERENCE] +Titol= +Edition= +CollectiveTitle= +ISBN= +ISSN= + +[QUALITY:LINEAGE:PROCESS1:INOUT1] +identifier=EMANCIPA +ResultUnits= + +[QUALITY:LINEAGE:PROCESS1:INOUT2] +identifier=Param1 +TypeValues=S +ResultUnits= +source=1 + +[QUALITY:LINEAGE:SOURCE1] +NomFitxer=TMP0000.SEL + +[QUALITY:LINEAGE:PROCESS1:INOUT3] +identifier=Param2 +sentit=1 +TypeValues=S +ResultUnits= +source=2 + +[QUALITY:LINEAGE:SOURCE2] +NomFitxer=tin_3d + +[QUALITY:LINEAGE:PROCESS2] +nOrganismes=1 +history=VecSelec_64.exe /EMANCIPA D:\GitHub-repository\gdal\autotest\ogr\data\miramon\Polygons\3dPolygons\TMP0000.SEL D:\GitHub-repository\gdal\autotest\ogr\data\miramon\Polygons\3dPolygons\tin_3d +purpose=Un fitxer de text indica quines són les entitats gràfiques i registres de la base de dades que cal desar en el fitxer de sortida. En aquesta ajuda ens referirem a aquest fitxer com a fitxer de seleccions. Es recomana l'extensió SEL, tot i que no és obligatòria. +date=20240318 15230777+0100 +NomFitxer=C:\MiraMon\VecSelec_64.exe + +[QUALITY:LINEAGE:PROCESS2:ORGANISME_1] +IndividualName=Abel Pau +PositionName=Tècnic en SIG +OrganisationName=CREAF + +[QUALITY:LINEAGE:PROCESS2:SOFTWARE_REFERENCE] +Titol= +Edition= +CollectiveTitle= +ISBN= +ISSN= + +[QUALITY:LINEAGE:PROCESS2:INOUT1] +identifier=EMANCIPA +ResultUnits= + +[QUALITY:LINEAGE:PROCESS2:INOUT2] +identifier=Param1 +TypeValues=S +ResultUnits= +source=3 + +[QUALITY:LINEAGE:SOURCE3] +NomFitxer=TMP0000.SEL + +[QUALITY:LINEAGE:PROCESS2:INOUT3] +identifier=Param2 +sentit=1 +TypeValues=S +ResultUnits= +source=4 + +[QUALITY:LINEAGE:SOURCE4] +NomFitxer=tin_3d + +[QUALITY:LINEAGE] +processes=1,2 + +[GEOMETRIA_I_TOPOLOGIA] +NomCampNVertexs=N_VERTEXS +NomCampPerimetre=PERIMETRE +NomCampPerimetreEllipsoidal=PERIMETREE +NomCampArea=AREA +NomCampAreaEllipsoidal=AREAE +NomCampNArcs=N_ARCS +NomCampNPoligons=N_POLIG diff --git a/autotest/ogr/data/miramon/Polygons/EmptyPolygons/Empty_POL.arc b/autotest/ogr/data/miramon/Polygons/EmptyPolygons/Empty_POL.arc new file mode 100644 index 000000000000..e089cf12d059 Binary files /dev/null and b/autotest/ogr/data/miramon/Polygons/EmptyPolygons/Empty_POL.arc differ diff --git a/autotest/ogr/data/miramon/Polygons/EmptyPolygons/Empty_POL.nod b/autotest/ogr/data/miramon/Polygons/EmptyPolygons/Empty_POL.nod new file mode 100644 index 000000000000..88197ddde3eb Binary files /dev/null and b/autotest/ogr/data/miramon/Polygons/EmptyPolygons/Empty_POL.nod differ diff --git a/autotest/ogr/data/miramon/Polygons/EmptyPolygons/Empty_POL.pol b/autotest/ogr/data/miramon/Polygons/EmptyPolygons/Empty_POL.pol new file mode 100644 index 000000000000..255d114a0e0c Binary files /dev/null and b/autotest/ogr/data/miramon/Polygons/EmptyPolygons/Empty_POL.pol differ diff --git a/autotest/ogr/data/miramon/Polygons/EmptyPolygons/Empty_POLA.dbf b/autotest/ogr/data/miramon/Polygons/EmptyPolygons/Empty_POLA.dbf new file mode 100644 index 000000000000..7481dceeefb0 Binary files /dev/null and b/autotest/ogr/data/miramon/Polygons/EmptyPolygons/Empty_POLA.dbf differ diff --git a/autotest/ogr/data/miramon/Polygons/EmptyPolygons/Empty_POLA.rel b/autotest/ogr/data/miramon/Polygons/EmptyPolygons/Empty_POLA.rel new file mode 100644 index 000000000000..b2454a7968ba --- /dev/null +++ b/autotest/ogr/data/miramon/Polygons/EmptyPolygons/Empty_POLA.rel @@ -0,0 +1,44 @@ +[VERSIO] +VersMetaDades=5 +SubVersMetaDades=0 +Vers=4 +SubVers=3 + +[SPATIAL_REFERENCE_SYSTEM:HORIZONTAL] +HorizontalSystemIdentifier=UTM-31N-ETRS89 + +[TAULA_PRINCIPAL] +IdGrafic=ID_GRAFIC +TipusRelacio=RELACIO_1_N_DICC + +[TAULA_PRINCIPAL:ID_GRAFIC] +visible=0 +descriptor=Identificador Gràfic intern +MostrarUnitats=0 + +[TAULA_PRINCIPAL:N_VERTEXS] +visible=0 +MostrarUnitats=0 +descriptor=Nombre de vèrtexs + +[TAULA_PRINCIPAL:LONG_ARC] +descriptor=Longitud de l'arc + +[TAULA_PRINCIPAL:NODE_INI] +visible=0 +MostrarUnitats=0 +descriptor=Node inicial + +[TAULA_PRINCIPAL:NODE_FI] +visible=0 +MostrarUnitats=0 +descriptor=Node final + +[OVERVIEW:ASPECTES_TECNICS] +Ciclat1=Empty_POL.pol + +[GEOMETRIA_I_TOPOLOGIA] +NomCampNVertexs=N_VERTEXS +NomCampLongitudArc=LONG_ARC +NomCampNodeIni=NODE_INI +NomCampNodeFi=NODE_FI diff --git a/autotest/ogr/data/miramon/Polygons/EmptyPolygons/Empty_POLN.dbf b/autotest/ogr/data/miramon/Polygons/EmptyPolygons/Empty_POLN.dbf new file mode 100644 index 000000000000..95689ae5c695 Binary files /dev/null and b/autotest/ogr/data/miramon/Polygons/EmptyPolygons/Empty_POLN.dbf differ diff --git a/autotest/ogr/data/miramon/Polygons/EmptyPolygons/Empty_POLN.rel b/autotest/ogr/data/miramon/Polygons/EmptyPolygons/Empty_POLN.rel new file mode 100644 index 000000000000..832186cce6c1 --- /dev/null +++ b/autotest/ogr/data/miramon/Polygons/EmptyPolygons/Empty_POLN.rel @@ -0,0 +1,26 @@ +[VERSIO] +VersMetaDades=5 +SubVersMetaDades=0 +Vers=4 +SubVers=3 + +[TAULA_PRINCIPAL] +IdGrafic=ID_GRAFIC +TipusRelacio=RELACIO_1_N_DICC + +[TAULA_PRINCIPAL:ID_GRAFIC] +visible=0 +descriptor=Identificador Gràfic intern +MostrarUnitats=0 + +[TAULA_PRINCIPAL:ARCS_A_NOD] +MostrarUnitats=0 +descriptor=Nombre d'arcs al node + +[TAULA_PRINCIPAL:TIPUS_NODE] +MostrarUnitats=0 +descriptor=Tipus de node + +[GEOMETRIA_I_TOPOLOGIA] +NomCampArcsANode=ARCS_A_NOD +NomCampTipusNode=TIPUS_NODE diff --git a/autotest/ogr/data/miramon/Polygons/EmptyPolygons/Empty_POLP.dbf b/autotest/ogr/data/miramon/Polygons/EmptyPolygons/Empty_POLP.dbf new file mode 100644 index 000000000000..a4ec5a35e356 Binary files /dev/null and b/autotest/ogr/data/miramon/Polygons/EmptyPolygons/Empty_POLP.dbf differ diff --git a/autotest/ogr/data/miramon/Polygons/EmptyPolygons/Empty_POLP.rel b/autotest/ogr/data/miramon/Polygons/EmptyPolygons/Empty_POLP.rel new file mode 100644 index 000000000000..cd5ab65e97a6 --- /dev/null +++ b/autotest/ogr/data/miramon/Polygons/EmptyPolygons/Empty_POLP.rel @@ -0,0 +1,52 @@ +[VERSIO] +VersMetaDades=5 +SubVersMetaDades=0 +Vers=4 +SubVers=3 + +[OVERVIEW:ASPECTES_TECNICS] +ArcSource=Empty_POL.arc + +[EXTENT] +toler_env=0 +MinX=2.9E+301 +MaxX=2.9E+301 +MinY=2.9E+301 +MaxY=2.9E+301 + +[TAULA_PRINCIPAL] +IdGrafic=ID_GRAFIC +TipusRelacio=RELACIO_1_N_DICC + +[TAULA_PRINCIPAL:ID_GRAFIC] +descriptor=Identificador Gràfic intern +visible=0 +TractamentVariable=Ordinal + +[TAULA_PRINCIPAL:N_VERTEXS] +descriptor=Nombre de vèrtexs +visible=0 +MostrarUnitats=0 + +[TAULA_PRINCIPAL:PERIMETRE] +descriptor=Perímetre del polígon + +[TAULA_PRINCIPAL:AREA] +descriptor=Àrea del polígon + +[TAULA_PRINCIPAL:N_ARCS] +descriptor=Nombre d'arcs +visible=0 +MostrarUnitats=0 + +[TAULA_PRINCIPAL:N_POLIG] +descriptor=Nombre de polígons elementals +visible=0 +MostrarUnitats=0 + +[GEOMETRIA_I_TOPOLOGIA] +NomCampNVertexs=N_VERTEXS +NomCampPerimetre=PERIMETRE +NomCampArea=AREA +NomCampNArcs=N_ARCS +NomCampNPoligons=N_POLIG diff --git a/autotest/ogr/data/miramon/Polygons/SimplePolygons/SimplePolFile.arc b/autotest/ogr/data/miramon/Polygons/SimplePolygons/SimplePolFile.arc new file mode 100644 index 000000000000..431d702a3a35 Binary files /dev/null and b/autotest/ogr/data/miramon/Polygons/SimplePolygons/SimplePolFile.arc differ diff --git a/autotest/ogr/data/miramon/Polygons/SimplePolygons/SimplePolFile.nod b/autotest/ogr/data/miramon/Polygons/SimplePolygons/SimplePolFile.nod new file mode 100644 index 000000000000..e5d310c3c0ce Binary files /dev/null and b/autotest/ogr/data/miramon/Polygons/SimplePolygons/SimplePolFile.nod differ diff --git a/autotest/ogr/data/miramon/Polygons/SimplePolygons/SimplePolFile.pol b/autotest/ogr/data/miramon/Polygons/SimplePolygons/SimplePolFile.pol new file mode 100644 index 000000000000..0a2e4736fe64 Binary files /dev/null and b/autotest/ogr/data/miramon/Polygons/SimplePolygons/SimplePolFile.pol differ diff --git a/autotest/ogr/data/miramon/Polygons/SimplePolygons/SimplePolFileA.dbf b/autotest/ogr/data/miramon/Polygons/SimplePolygons/SimplePolFileA.dbf new file mode 100644 index 000000000000..22d64985be23 Binary files /dev/null and b/autotest/ogr/data/miramon/Polygons/SimplePolygons/SimplePolFileA.dbf differ diff --git a/autotest/ogr/data/miramon/Polygons/SimplePolygons/SimplePolFileA.rel b/autotest/ogr/data/miramon/Polygons/SimplePolygons/SimplePolFileA.rel new file mode 100644 index 000000000000..355451718486 --- /dev/null +++ b/autotest/ogr/data/miramon/Polygons/SimplePolygons/SimplePolFileA.rel @@ -0,0 +1,89 @@ +[VERSIO] +VersMetaDades=5 +SubVersMetaDades=0 +Vers=4 +SubVers=3 + +[METADADES] +language=cat +MDIdiom=cat +dateStamp=20230628 16204654+0200 +characterSet=006 +nOrganismes=1 +FileIdentifier=0e09e6cf-0d31-499e-bcf0-bee4d3e9d87a_SimplePolFileA + +[METADADES:ORGANISME_1] +role=009 +IndividualName=Abel Pau +PositionName=Tècnic SIG +OrganisationName=Students and educational institutions + +[IDENTIFICATION] +code=0e09e6cf-0d31-499e-bcf0-bee4d3e9d87a_SimplePolFileA +codeSpace= +DatasetTitle=Simple Pol File [píxels] + +[SPATIAL_REFERENCE_SYSTEM:HORIZONTAL] +HorizontalSystemDefinition=Local +HorizontalSystemIdentifier=plane +unitats=STB#T_pixels +unitatsY=? + +[EXTENT] +toler_env=0 +MinX=335.3187440533326 +MaxX=1224.163653663228 +MinY=390.371075166458 +MaxY=856.814462416696 + +[OVERVIEW] +CreationDate=20230628 16204653+0200 + +[TAULA_PRINCIPAL] +IdGrafic=ID_GRAFIC +TipusRelacio=RELACIO_1_N_DICC + +[TAULA_PRINCIPAL:ID_GRAFIC] +visible=0 +descriptor=Identificador Gràfic intern +MostrarUnitats=0 + +[TAULA_PRINCIPAL:N_VERTEXS] +visible=0 +MostrarUnitats=0 +descriptor=Nombre de vèrtexs + +[TAULA_PRINCIPAL:LONG_ARC] +descriptor=Longitud de l'arc + +[TAULA_PRINCIPAL:NODE_INI] +visible=0 +MostrarUnitats=0 +descriptor=Node inicial + +[TAULA_PRINCIPAL:NODE_FI] +visible=0 +MostrarUnitats=0 +descriptor=Node final + +[OVERVIEW:ASPECTES_TECNICS] +Ciclat1=SimplePolFile.pol + +[QUALITY:LINEAGE:PROCESS1] +nOrganismes=1 +history=C:\MiraMon\MM64.exe +date=20230628 16204654+0200 + +[QUALITY:LINEAGE:PROCESS1:ORGANISME_1] +IndividualName=Abel Pau +PositionName=Tècnic SIG +OrganisationName=Students and educational institutions + +[QUALITY:LINEAGE] +processes=1 + +[GEOMETRIA_I_TOPOLOGIA] +NomCampNVertexs=N_VERTEXS +NomCampLongitudArc=LONG_ARC +NomCampNodeIni=NODE_INI +NomCampNodeFi=NODE_FI diff --git a/autotest/ogr/data/miramon/Polygons/SimplePolygons/SimplePolFileN.dbf b/autotest/ogr/data/miramon/Polygons/SimplePolygons/SimplePolFileN.dbf new file mode 100644 index 000000000000..19847ee7684c Binary files /dev/null and b/autotest/ogr/data/miramon/Polygons/SimplePolygons/SimplePolFileN.dbf differ diff --git a/autotest/ogr/data/miramon/Polygons/SimplePolygons/SimplePolFileN.rel b/autotest/ogr/data/miramon/Polygons/SimplePolygons/SimplePolFileN.rel new file mode 100644 index 000000000000..c0f476e1f0de --- /dev/null +++ b/autotest/ogr/data/miramon/Polygons/SimplePolygons/SimplePolFileN.rel @@ -0,0 +1,64 @@ +[VERSIO] +VersMetaDades=5 +SubVersMetaDades=0 +Vers=4 +SubVers=3 + +[METADADES] +language=cat +MDIdiom=cat +dateStamp=20230628 16204653+0200 +characterSet=006 +nOrganismes=1 +FileIdentifier=701e2102-a0ba-4cb2-aeb5-bdb329c79868_SimplePolFileN + +[METADADES:ORGANISME_1] +role=009 +IndividualName=Abel Pau +PositionName=Tècnic SIG +OrganisationName=Students and educational institutions + +[IDENTIFICATION] +code=701e2102-a0ba-4cb2-aeb5-bdb329c79868_SimplePolFileN +codeSpace= +DatasetTitle=Simple Pol File [píxels] + +[EXTENT] +toler_env=0 + +[OVERVIEW] +CreationDate=20230628 16204652+0200 + +[TAULA_PRINCIPAL] +IdGrafic=ID_GRAFIC +TipusRelacio=RELACIO_1_N_DICC + +[TAULA_PRINCIPAL:ID_GRAFIC] +visible=0 +descriptor=Identificador Gràfic intern +MostrarUnitats=0 + +[TAULA_PRINCIPAL:ARCS_A_NOD] +MostrarUnitats=0 +descriptor=Nombre d'arcs al node + +[TAULA_PRINCIPAL:TIPUS_NODE] +MostrarUnitats=0 +descriptor=Tipus de node + +[QUALITY:LINEAGE:PROCESS1] +nOrganismes=1 +history=C:\MiraMon\MM64.exe +date=20230628 16204653+0200 + +[QUALITY:LINEAGE:PROCESS1:ORGANISME_1] +IndividualName=Abel Pau +PositionName=Tècnic SIG +OrganisationName=Students and educational institutions + +[QUALITY:LINEAGE] +processes=1 + +[GEOMETRIA_I_TOPOLOGIA] +NomCampArcsANode=ARCS_A_NOD +NomCampTipusNode=TIPUS_NODE diff --git a/autotest/ogr/data/miramon/Polygons/SimplePolygons/SimplePolFileP.dbf b/autotest/ogr/data/miramon/Polygons/SimplePolygons/SimplePolFileP.dbf new file mode 100644 index 000000000000..5d00a81ae4a4 Binary files /dev/null and b/autotest/ogr/data/miramon/Polygons/SimplePolygons/SimplePolFileP.dbf differ diff --git a/autotest/ogr/data/miramon/Polygons/SimplePolygons/SimplePolFileP.rel b/autotest/ogr/data/miramon/Polygons/SimplePolygons/SimplePolFileP.rel new file mode 100644 index 000000000000..659952f5e451 --- /dev/null +++ b/autotest/ogr/data/miramon/Polygons/SimplePolygons/SimplePolFileP.rel @@ -0,0 +1,93 @@ +[VERSIO] +VersMetaDades=5 +SubVersMetaDades=0 +Vers=4 +SubVers=3 + +[METADADES] +language=cat +MDIdiom=cat +dateStamp=20230628 16204988+0200 +characterSet=006 +nOrganismes=1 +FileIdentifier=00691677-6d15-40f8-9d62-e8df34876e80_SimplePolFileP + +[METADADES:ORGANISME_1] +role=009 +IndividualName=Abel Pau +PositionName=Tècnic SIG +OrganisationName=Students and educational institutions + +[IDENTIFICATION] +code= +codeSpace= +DatasetTitle=Simple Pol File + +[OVERVIEW:ASPECTES_TECNICS] +ArcSource=SimplePolFile.arc + +[QUALITY:LINEAGE:PROCESS1] +nOrganismes=1 +history=C:\MiraMon\MM64.exe +date=20230628 16204988+0200 + +[QUALITY:LINEAGE:PROCESS1:ORGANISME_1] +IndividualName=Abel Pau +PositionName=Tècnic SIG +OrganisationName=Students and educational institutions + +[QUALITY:LINEAGE] +processes=1 + +[EXTENT] +toler_env=0 +MinX=335.318744053333 +MaxX=1224.16365366323 +MinY=390.371075166458 +MaxY=856.814462416696 + +[OVERVIEW] +CreationDate=20230628 16204986+0200 + +[TAULA_PRINCIPAL] +IdGrafic=ID_GRAFIC +TipusRelacio=RELACIO_1_N_DICC + +[TAULA_PRINCIPAL:ID_GRAFIC] +descriptor=Identificador Gràfic intern +visible=0 +TractamentVariable=Ordinal + +[TAULA_PRINCIPAL:N_VERTEXS] +descriptor=Nombre de vèrtexs +visible=0 +MostrarUnitats=0 + +[TAULA_PRINCIPAL:PERIMETRE] +descriptor=Perímetre del polígon + +[TAULA_PRINCIPAL:AREA] +descriptor=Àrea del polígon + +[TAULA_PRINCIPAL:N_ARCS] +descriptor=Nombre d'arcs +visible=0 +MostrarUnitats=0 + +[TAULA_PRINCIPAL:N_POLIG] +descriptor=Nombre de polígons elementals +visible=0 +MostrarUnitats=0 + +[GEOMETRIA_I_TOPOLOGIA] +NomCampNVertexs=N_VERTEXS +NomCampPerimetre=PERIMETRE +NomCampArea=AREA +NomCampNArcs=N_ARCS +NomCampNPoligons=N_POLIG + +[TAULA_PRINCIPAL:ATT1] +descriptor=atribute1 + +[TAULA_PRINCIPAL:ATT2] +descriptor=atribute2 diff --git a/autotest/ogr/ogr_miramon_vector.py b/autotest/ogr/ogr_miramon_vector.py new file mode 100644 index 000000000000..98a3cad117e8 --- /dev/null +++ b/autotest/ogr/ogr_miramon_vector.py @@ -0,0 +1,677 @@ +#!/usr/bin/env pytest +# -*- coding: utf-8 -*- +############################################################################### +# $Id$ +# +# Project: GDAL/OGR Test Suite +# Purpose: Test read functionality for OGR MiraMon vector driver. +# Author: Abel Pau +# +############################################################################### +# Copyright (c) 2024, Even Rouault +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. +############################################################################### + +# import os +# import pdb + +import gdaltest + +# import ogrtest +import pytest + +# from osgeo import gdal, ogr, osr +from osgeo import gdal, ogr + +pytestmark = pytest.mark.require_driver("MiraMonVector") + +############################################################################### +# basic point test + + +def check_simple_point(ds): + + lyr = ds.GetLayer(0) + assert lyr is not None, "Failed to get layer" + + assert lyr.GetFeatureCount() == 3 + assert lyr.GetGeomType() == ogr.wkbPoint + + f = lyr.GetNextFeature() + assert f is not None, "Failed to get feature" + assert f.GetFID() == 0 + assert ( + f.GetGeometryRef().ExportToWkt() == "POINT (513.488106565226 848.806850618409)" + ) + assert f.GetField("ID_GRAFIC") == 0 + assert f.GetFieldAsString("ATT1") == "A" + assert f.GetFieldAsString("ATTRIBUTE_2") == "B" + + f = lyr.GetNextFeature() + assert f is not None, "Failed to get feature" + assert ( + f.GetGeometryRef().ExportToWkt() == "POINT (342.325404376834 715.680304471881)" + ) + assert f.GetField("ID_GRAFIC") == 1 + assert f.GetFieldAsString("ATT1") == "C" + assert f.GetFieldAsString("ATTRIBUTE_2") == "D" + + f = lyr.GetNextFeature() + assert f is not None, "Failed to get feature" + assert ( + f.GetGeometryRef().ExportToWkt() == "POINT (594.503182156354 722.692543360232)" + ) + assert f.GetField("ID_GRAFIC") == 2 + assert f.GetFieldAsString("ATT1") == "" + assert f.GetFieldAsString("ATTRIBUTE_2") == "" + + +def test_ogr_miramon_read_simple_point(): + + ds = gdal.OpenEx("data/miramon/Points/SimplePoints/SimplePointsFile.pnt") + assert ds is not None, "Failed to get dataset" + + check_simple_point(ds) + + +def test_ogr_miramon_write_simple_pointV11(tmp_vsimem): + + out_filename = str(tmp_vsimem / "out.pnt") + gdal.VectorTranslate( + out_filename, + "data/miramon/Points/SimplePoints/SimplePointsFile.pnt", + format="MiraMonVector", + ) + ds = gdal.OpenEx(out_filename, gdal.OF_VECTOR) + check_simple_point(ds) + + +def test_ogr_miramon_write_simple_pointV20(tmp_vsimem): + + out_filename = str(tmp_vsimem / "out.pnt") + gdal.VectorTranslate( + out_filename, + "data/miramon/Points/SimplePoints/SimplePointsFile.pnt", + format="MiraMonVector", + options="-lco Version=V2.0", + ) + + ds = gdal.OpenEx(out_filename, gdal.OF_VECTOR) + check_simple_point(ds) + + +############################################################################### +# basic linestring test + + +def check_simple_arc(ds): + + lyr = ds.GetLayer(0) + assert lyr is not None, "Failed to get layer" + + assert lyr.GetFeatureCount() == 4 + assert lyr.GetGeomType() == ogr.wkbLineString + + f = lyr.GetNextFeature() + assert f is not None, "Failed to get feature" + assert f.GetFID() == 0 + assert ( + f.GetGeometryRef().ExportToWkt() + == "LINESTRING (351.333967649907 610.58039961936,474.450999048575 824.784015223546,758.721217887776 838.797335870549,1042.99143672698 610.58039961936,1369.30161750719 562.534728829636)" + ) + assert f.GetField("ID_GRAFIC") == 0 + assert f.GetField("N_VERTEXS") == 5 + assert f.GetField("LONG_ARC") == pytest.approx(1226.052754666, abs=1e-5) + assert f.GetField("NODE_INI") == 0 + assert f.GetField("NODE_FI") == 1 + assert f.GetFieldAsString("ATT1") == "A" + assert f.GetFieldAsString("ATT2") == "B" + + f = lyr.GetNextFeature() + assert f is not None, "Failed to get feature" + assert f.GetFID() == 1 + assert ( + f.GetGeometryRef().ExportToWkt() + == "LINESTRING (794.755470980069 442.420551855326,613.583254043818 399.379638439531,642.61084681261 212.201712654565,861.819219790726 201.191246431919,1041.99048525219 460.437678401472,598.568981922029 591.562321598428,1109.05423406285 931.88582302564)" + ) + assert f.GetField("ID_GRAFIC") == 1 + assert f.GetField("N_VERTEXS") == 7 + assert f.GetField("LONG_ARC") == pytest.approx(1986.750568, abs=1e-5) + assert f.GetField("NODE_INI") == 2 + assert f.GetField("NODE_FI") == 3 + assert f.GetFieldAsString("ATT1") == "C" + assert f.GetFieldAsString("ATT2") == "D" + + f = lyr.GetNextFeature() + assert f is not None, "Failed to get feature" + assert f.GetFID() == 2 + assert ( + f.GetGeometryRef().ExportToWkt() + == "LINESTRING (887.843958135159 858.816365366268,989.941008563323 767.729781160749)" + ) + assert f.GetField("ID_GRAFIC") == 2 + assert f.GetField("N_VERTEXS") == 2 + assert f.GetField("LONG_ARC") == pytest.approx(136.823147, abs=1e-5) + assert f.GetField("NODE_INI") == 4 + assert f.GetField("NODE_FI") == 5 + assert f.GetFieldAsString("ATT1") == "C" + assert f.GetFieldAsString("ATT2") == "D" + + f = lyr.GetNextFeature() + assert f is not None, "Failed to get feature" + assert f.GetFID() == 3 + assert ( + f.GetGeometryRef().ExportToWkt() + == "LINESTRING (537.510941960088 719.684110371025,496.471931493865 633.602283539436,432.411037107567 572.544243577495,415.394862036206 631.600380589864,492.468125594722 642.610846812509,564.536631779308 630.599429115078)" + ) + assert f.GetField("ID_GRAFIC") == 3 + assert f.GetField("N_VERTEXS") == 6 + assert f.GetField("LONG_ARC") == pytest.approx(396.238966, abs=1e-5) + assert f.GetField("NODE_INI") == 6 + assert f.GetField("NODE_FI") == 7 + assert f.GetFieldAsString("ATT1") == "E" + assert f.GetFieldAsString("ATT2") == "F" + + +def test_ogr_miramon_read_simple_arc(): + + ds = gdal.OpenEx("data/miramon/Arcs/SimpleArcs/SimpleArcFile.arc") + assert ds is not None, "Failed to get dataset" + check_simple_arc(ds) + + +def test_ogr_miramon_write_simple_arcV11(tmp_vsimem): + + out_filename = str(tmp_vsimem / "out.arc") + gdal.VectorTranslate( + out_filename, + "data/miramon/Arcs/SimpleArcs/SimpleArcFile.arc", + format="MiraMonVector", + ) + ds = gdal.OpenEx(out_filename, gdal.OF_VECTOR) + check_simple_arc(ds) + del ds + + +def test_ogr_miramon_write_simple_arcV20(tmp_vsimem): + + out_filename = str(tmp_vsimem / "out.arc") + gdal.VectorTranslate( + out_filename, + "data/miramon/Arcs/SimpleArcs/SimpleArcFile.arc", + format="MiraMonVector", + options="-lco Version=V2.0", + ) + ds = gdal.OpenEx(out_filename, gdal.OF_VECTOR) + check_simple_arc(ds) + del ds + + +############################################################################### +# basic polygon test + + +def check_simple_polygon(ds): + + lyr = ds.GetLayer(0) + + assert lyr is not None, "Failed to get layer" + + assert lyr.GetFeatureCount() == 3 + assert lyr.GetGeomType() == ogr.wkbPolygon + + # going to the first polygon + f = lyr.GetNextFeature() + assert f is not None, "Failed to get feature" + assert f.GetFID() == 0 + assert ( + f.GetGeometryRef().ExportToWkt() + == "POLYGON ((335.318744053333 769.731684110321,552.525214081877 856.814462416696,775.737392959137 707.672692673594,648.616555661325 493.469077069408,386.367269267414 498.473834443337,335.318744053333 769.731684110321))" + ) + assert f.GetField("ID_GRAFIC") == 1 + assert f.GetField("N_VERTEXS") == 6 + assert f.GetField("PERIMETRE") == pytest.approx(1289.866489495, abs=1e-5) + assert f.GetField("AREA") == pytest.approx(112471.221989, abs=1e-5) + assert f.GetField("N_ARCS") == 1 + assert f.GetField("N_POLIG") == 1 + assert f.GetFieldAsString("ATT1") == "A" + assert f.GetFieldAsString("ATT2") == "B" + + f = lyr.GetNextFeature() + assert f is not None, "Failed to get feature" + assert f.GetFID() == 1 + assert ( + f.GetGeometryRef().ExportToWkt() + == "POLYGON ((1068.01522359662 849.807802093194,1160.10275927693 795.756422454755,1224.16365366323 682.648905803946,1156.09895337779 525.499524262557,962.915318744103 489.465271170264,830.789724072362 617.587059942862,924.879162702239 740.704091341529,1068.01522359662 849.807802093194))" + ) + assert f.GetField("ID_GRAFIC") == 2 + assert f.GetField("N_VERTEXS") == 8 + assert f.GetField("PERIMETRE") == pytest.approx(1123.514024, abs=1e-5) + assert f.GetField("AREA") == pytest.approx(88563.792204, abs=1e-5) + assert f.GetField("N_ARCS") == 1 + assert f.GetField("N_POLIG") == 1 + assert f.GetFieldAsString("ATT1") == "C" + assert f.GetFieldAsString("ATT2") == "D" + + f = lyr.GetNextFeature() + assert f is not None, "Failed to get feature" + assert f.GetFID() == 2 + assert ( + f.GetGeometryRef().ExportToWkt() + == "POLYGON ((636.605137963894 390.371075166458,580.551855375883 575.547098001853,723.687916270269 594.565176022785,796.757373929641 475.451950523261,744.707897240773 396.376784015173,636.605137963894 390.371075166458))" + ) + assert f.GetField("ID_GRAFIC") == 3 + assert f.GetField("N_VERTEXS") == 6 + assert f.GetField("PERIMETRE") == pytest.approx(680.544697, abs=1e-5) + assert f.GetField("AREA") == pytest.approx(30550.052343, abs=1e-5) + assert f.GetField("N_ARCS") == 1 + assert f.GetField("N_POLIG") == 1 + assert f.GetFieldAsString("ATT1") == "C" + assert f.GetFieldAsString("ATT2") == "D" + + +def test_ogr_miramon_read_simple_polygon(): + + ds = gdal.OpenEx( + "data/miramon/Polygons/SimplePolygons/SimplePolFile.pol", gdal.OF_VECTOR + ) + assert ds is not None, "Failed to get dataset" + check_simple_polygon(ds) + + +def test_ogr_miramon_write_simple_polygonV11(tmp_vsimem): + + out_filename = str(tmp_vsimem / "out.pol") + gdal.VectorTranslate( + out_filename, + "data/miramon/Polygons/SimplePolygons/SimplePolFile.pol", + format="MiraMonVector", + ) + ds = gdal.OpenEx(out_filename, gdal.OF_VECTOR) + check_simple_polygon(ds) + + +def test_ogr_miramon_write_simple_polygonV20(tmp_vsimem): + + out_filename = str(tmp_vsimem / "out.pol") + gdal.VectorTranslate( + out_filename, + "data/miramon/Polygons/SimplePolygons/SimplePolFile.pol", + format="MiraMonVector", + options="-lco Version=V2.0", + ) + ds = gdal.OpenEx(out_filename, gdal.OF_VECTOR) + check_simple_polygon(ds) + + +############################################################################### +# testing empty layers + + +def test_ogr_miramon_empty_point_layers(): + + ds = gdal.OpenEx("data/miramon/Points/EmptyPoints/Empty_PNT.pnt", gdal.OF_VECTOR) + assert ds is not None, "Failed to get dataset" + + lyr = ds.GetLayer(0) + + assert lyr is not None, "Failed to get layer" + + assert lyr.GetFeatureCount() == 0 + + f = lyr.GetNextFeature() + assert f is None, "Failed to get empty feature" + + ds = None + + +def test_ogr_miramon_empty_arc_layers(): + + ds = gdal.OpenEx("data/miramon/Arcs/EmptyArcs/Empty_ARC.arc", gdal.OF_VECTOR) + assert ds is not None, "Failed to get dataset" + + lyr = ds.GetLayer(0) + + assert lyr is not None, "Failed to get layer" + + assert lyr.GetFeatureCount() == 0 + + f = lyr.GetNextFeature() + assert f is None, "Failed to get empty feature" + + ds = None + + +def test_ogr_miramon_empty_pol_layers(): + + ds = gdal.OpenEx( + "data/miramon/Polygons/EmptyPolygons/Empty_POL.pol", gdal.OF_VECTOR + ) + assert ds is not None, "Failed to get dataset" + + lyr = ds.GetLayer(0) + + assert lyr is not None, "Failed to get layer" + + # The layer has no features + assert lyr.GetFeatureCount() == 0 + + f = lyr.GetNextFeature() + assert f is None, "Failed to get empty feature" + + ds = None + + +############################################################################### +# testing 3d part + + +def check_3d_point(ds): + + lyr = ds.GetLayer(0) + + assert lyr is not None, "Failed to get layer" + + assert lyr.GetFeatureCount() == 31 + assert lyr.GetGeomType() == ogr.wkbPoint25D + + f = lyr.GetNextFeature() + assert f is not None, "Failed to get feature" + assert f.GetFID() == 0 + + assert f.GetGeometryRef().ExportToWkt() == "POINT (440551.66 4635315.3 619.96)" + + g = f.GetGeometryRef() + assert g is not None, "Failed to get geometry" + assert g.GetCoordinateDimension() == 3 + assert g.GetZ() == 619.96 + + f = lyr.GetFeature(30) + assert f is not None, "Failed to get feature" + g = f.GetGeometryRef() + assert g is not None, "Failed to get geometry" + assert g.GetZ() == 619.77 + + +def test_ogr_miramon_read_3d_point(tmp_vsimem): + + ds = gdal.OpenEx("data/miramon/Points/3dpoints/Some3dPoints.pnt", gdal.OF_VECTOR) + assert ds is not None, "Failed to get dataset" + check_3d_point(ds) + + +def test_ogr_miramon_write_3d_point(tmp_vsimem): + + out_filename = str(tmp_vsimem / "out.pnt") + gdal.VectorTranslate( + out_filename, + "data/miramon/Points/3dpoints/Some3dPoints.pnt", + format="MiraMonVector", + ) + ds = gdal.OpenEx(out_filename, gdal.OF_VECTOR) + check_3d_point(ds) + + +def check_3d_arc(ds): + + lyr = ds.GetLayer(0) + + assert lyr is not None, "Failed to get layer" + + assert lyr.GetFeatureCount() == 6 + assert lyr.GetGeomType() == ogr.wkbLineString25D + + f = lyr.GetFeature(0) + assert f is not None, "Failed to get feature" + g = f.GetGeometryRef() + assert g is not None, "Failed to get geometry" + assert g.GetCoordinateDimension() == 3 + assert g.GetPointCount() == 4 + p = g.GetPoint(0) + assert p[2] == 595.1063842773438 + p = g.GetPoint(1) + assert p[2] == 326.656005859375 + p = g.GetPoint(2) + assert p[2] == 389.99432373046875 + p = g.GetPoint(3) + assert p[2] == 716.6224975585938 + + f = lyr.GetFeature(5) + assert f is not None, "Failed to get feature" + g = f.GetGeometryRef() + assert g is not None, "Failed to get geometry" + assert g.GetCoordinateDimension() == 3 + assert g.GetPointCount() == 2 + p = g.GetPoint(0) + assert p[2] == 233.82064819335938 + p = g.GetPoint(1) + assert p[2] == 794.5372314453125 + + ds = None + + +def test_ogr_miramon_read_3d_arc(tmp_vsimem): + + ds = gdal.OpenEx("data/miramon/Arcs/3dArcs/linies_3d_WGS84.arc", gdal.OF_VECTOR) + assert ds is not None, "Failed to get dataset" + check_3d_arc(ds) + + +def test_ogr_miramon_write_3d_arc(tmp_vsimem): + + out_filename = str(tmp_vsimem / "out.arc") + gdal.VectorTranslate( + out_filename, + "data/miramon/Arcs/3dArcs/linies_3d_WGS84.arc", + format="MiraMonVector", + ) + ds = gdal.OpenEx(out_filename, gdal.OF_VECTOR) + check_3d_arc(ds) + del ds + + +def check_3d_pol(ds): + + lyr = ds.GetLayer(0) + + assert lyr is not None, "Failed to get layer" + + assert lyr.GetFeatureCount() == 5 + assert lyr.GetGeomType() == ogr.wkbPolygon25D + + f = lyr.GetFeature(0) + assert f is not None, "Failed to get feature" + g = f.GetGeometryRef() + assert g is not None, "Failed to get geometry" + assert g.GetCoordinateDimension() == 3 + r = g.GetGeometryRef(0) + assert r is not None, "Failed to get geometry" + assert r.GetPointCount() == 4 + p = r.GetPoint(0) + assert p[2] == 11.223576545715332 + p = r.GetPoint(1) + assert p[2] == 9.221868515014648 + p = r.GetPoint(2) + assert p[2] == 21.929399490356445 + p = r.GetPoint(3) + assert p[2] == 11.223576545715332 + + f = lyr.GetFeature(4) + assert f is not None, "Failed to get feature" + g = f.GetGeometryRef() + assert g is not None, "Failed to get geometry" + assert g.GetCoordinateDimension() == 3 + r = g.GetGeometryRef(0) + assert r is not None, "Failed to get geometry" + assert r.GetPointCount() == 4 + p = r.GetPoint(0) + assert p[2] == 18.207277297973633 + p = r.GetPoint(1) + assert p[2] == 21.929399490356445 + p = r.GetPoint(2) + assert p[2] == 5.746463775634766 + p = r.GetPoint(3) + assert p[2] == 18.207277297973633 + + +def test_ogr_miramon_read_3d_pol(): + + ds = gdal.OpenEx("data/miramon/Polygons/3dPolygons/tin_3d.pol", gdal.OF_VECTOR) + assert ds is not None, "Failed to get dataset" + check_3d_pol(ds) + + +def test_ogr_miramon_write_3d_pol(tmp_vsimem): + + out_filename = str(tmp_vsimem / "out.pol") + gdal.VectorTranslate( + out_filename, + "data/miramon/Polygons/3dPolygons/tin_3d.pol", + format="MiraMonVector", + ) + ds = gdal.OpenEx(out_filename, gdal.OF_VECTOR) + check_3d_pol(ds) + del ds + + +############################################################################### +# ogrsf test in some files + + +@pytest.mark.parametrize( + "filename", + [ + "Points/3dpoints/Some3dPoints.pnt", + "Points/SimplePoints/SimplePointsFile.pnt", + "Points/EmptyPoints/Empty_PNT.pnt", + "Arcs/SimpleArcs/SimpleArcFile.arc", + "Arcs/EmptyArcs/Empty_ARC.arc", + "Arcs/3dArcs/linies_3d_WGS84.arc", + "Polygons/SimplePolygons/SimplePolFile.pol", + "Polygons/EmptyPolygons/Empty_POL.pol", + "Polygons/3dPolygons/tin_3d.pol", + ], +) +def test_ogr_miramon_test_ogrsf(filename): + + import test_cli_utilities + + if test_cli_utilities.get_test_ogrsf_path() is None: + pytest.skip("test_ogrsf not available") + + ret = gdaltest.runexternal( + test_cli_utilities.get_test_ogrsf_path() + " -ro data/miramon/" + filename + ) + + assert "INFO" in ret + assert "ERROR" not in ret + + +############################################################################### +# -lco tests: CreationLanguage + + +@pytest.mark.parametrize( + "Language, expected_description", + [ + ("CAT", "Identificador Gràfic intern"), + ("SPA", "Identificador Gráfico interno"), + ("ENG", "Internal Graphic identifier"), + ], +) +def test_ogr_miramon_CreationLanguage(tmp_vsimem, Language, expected_description): + + out_filename = str(tmp_vsimem / "out.pnt") + gdal.VectorTranslate( + out_filename, + "data/miramon/Points/SimplePoints/SimplePointsFile.pnt", + format="MiraMonVector", + options="-lco CreationLanguage=" + Language, + ) + + ds = gdal.OpenEx(out_filename, gdal.OF_VECTOR) + lyr = ds.GetLayer(0) + assert lyr is not None, "Failed to get layer" + + layer_def = lyr.GetLayerDefn() + field_index = layer_def.GetFieldIndex("ID_GRAFIC") + assert field_index >= 0 + + field_def = layer_def.GetFieldDefn(field_index) + field_description = field_def.GetAlternativeNameRef() + assert field_description == expected_description + + +############################################################################### +# -lco tests: CreationLanguage + + +@pytest.mark.parametrize( + "Language,expected_description", + [ + ("CAT", "Identificador Gràfic intern"), + ("SPA", "Identificador Gráfico interno"), + ("ENG", "Internal Graphic identifier"), + ], +) +def test_ogr_miramon_OpenLanguagePoint(Language, expected_description): + + ds = gdal.OpenEx( + "data/miramon/Points/SimplePoints/SimplePointsFile.pnt", + gdal.OF_VECTOR, + open_options=["OpenLanguage=" + Language], + ) + lyr = ds.GetLayer(0) + assert lyr is not None, "Failed to get layer" + + layer_def = lyr.GetLayerDefn() + field_index = layer_def.GetFieldIndex("ID_GRAFIC") + assert field_index >= 0 + + field_def = layer_def.GetFieldDefn(field_index) + field_description = field_def.GetAlternativeNameRef() + assert field_description == expected_description + + +@pytest.mark.parametrize( + "Language,expected_description", + [ + ("CAT", "Node inicial"), + ("SPA", "Nodo inicial"), + ("ENG", "Initial node"), + ], +) +def test_ogr_miramon_OpenLanguageArc(Language, expected_description): + + ds = gdal.OpenEx( + "data/miramon/Arcs/SimpleArcs/SimpleArcFile.arc", + gdal.OF_VECTOR, + open_options=["OpenLanguage=" + Language], + ) + lyr = ds.GetLayer(0) + assert lyr is not None, "Failed to get layer" + + layer_def = lyr.GetLayerDefn() + field_index = layer_def.GetFieldIndex("NODE_INI") + assert field_index >= 0 + + field_def = layer_def.GetFieldDefn(field_index) + field_description = field_def.GetAlternativeNameRef() + assert field_description == expected_description diff --git a/doc/source/drivers/vector/index.rst b/doc/source/drivers/vector/index.rst index 02db55e2d72a..818b44db488f 100644 --- a/doc/source/drivers/vector/index.rst +++ b/doc/source/drivers/vector/index.rst @@ -67,6 +67,7 @@ Vector drivers lvbag mapml memory + miramon mitab mongodbv3 mssqlspatial diff --git a/doc/source/drivers/vector/miramon.rst b/doc/source/drivers/vector/miramon.rst new file mode 100644 index 000000000000..48f1427d184b --- /dev/null +++ b/doc/source/drivers/vector/miramon.rst @@ -0,0 +1,374 @@ +.. _vector.miramon: + +MiraMon Vector +==================== + +.. shortname:: MiraMonVector + +.. built_in_by_default:: + +This driver is capable of translating (reading and writing) structured vectors +of point, arc (*linestrings*), and polygon types from MiraMon vector format. + +In MiraMon the concepts of OGRMultiPoints and OGRMultiLineStrings are not supported, +but the driver translates a multipoint into N points and a multistring into N arcs. +When reading a MiraMon file of type *.pol*, the corresponding +layer will be reported as of type wkbPolygon, but depending on the +number of parts of each geometry, the actual type of the geometry for +each feature can be either OGRPolygon or OGRMultiPolygon. + +The reading driver verifies if multipart polygons adhere to the +specification (that is to say, the vertices of outer rings should be +oriented clockwise on the X/Y plane, and those of inner rings +counterclockwise). Otherwise, the driver corrects the orientation +(in the original format this specification is not the case as polygon +files are based on topological arc files, where the order of the vertices +may be relevant). + +Measures (M coordinate) are not supported. +Symbolization is neither read nor generated by this driver. + +A `look-up-table of MiraMon `__ and +`EPSG `__ Spatial Reference Systems allows matching +identifiers in both systems. + +If a layer contains an old *.rel* format file (used some decades ago), +a warning message will appear explaining how to convert it into a modern *.rel 4* file. + +Driver capabilities +------------------- + +.. supports_create:: + +.. supports_georeferencing:: + +.. supports_virtualio:: + +Overview of MiraMon format +-------------------------- + +The MiraMon format is a binary format for vector layer data, linked to +one or more database tables, with or without topology and with rich metadata. +More information about the structured MiraMon vector format is available `on the public +specification `__. + +It is important to keep in mind that a MiraMon vector layer is composed by several files as follows: + +To operate with a point layer, you must provide the name with the extension .pnt +(the T.dbf and T.rel files must accompany the .pnt). + +To operate with a linestring layer, you must provide the name with the extension .arc +(the A.dbf and A.rel, .nod, N.dbf, and N.rel files must accompany the .arc). + +To operate with a polygon layer, you must provide the name with the extension .pol +(the P.dbf, P.rel, A.dbf and A.rel, .nod, N.dbf, and N.rel files must accompany the .pol). + +By providing only the main file name, the driver will automatically use the other sidecar files to obtain the +necessary information. In the creation of MiraMon layers, you only need to provide the name +of the main file (with or without extension), and the driver will create the rest of the files. + +The following outlines the information contained within each sidecar file: + +Preliminary note: *FileName* is, in the following explanations, the first part of the name +of the layer file. + +- **Point layers**: These layers contain *point* type features which are described by a single + coordinate (x,y) or (x, y, z). Each layer is composed by 3 files: + + - *FileName.pnt* file: Contains the geographic database with the coordinates that define the + point vector features. + + - *FileNameT.dbf* file (note the 'T' before the '.'): Contains the main table of the database + in dBASE (DBF) format, or in `extended DBF format `__, + if necessary. It contains the information (usually alphanumeric, but also file or web links, etc) + of every feature. The Feature Identifier (FID) field is a field called *ID_GRAFIC* and relates + every geographical feature to one or more records in the main table. + + - *FileNameT.rel* file (note the 'T' before the '.'): Contains the layer metadata, + the relational structure of the database (links between the main table and other + tables [thesauruses, etc] if needed, and the cardinality of the link) and the default + symbolization description. In the GDAL environment + only some aspects are documented: the spatial reference system, the language of the + metadata (English), the extension and a description of the fields. + +- **Arc layers**: These layers contain *linestring* type features which are lines + described by a series of segments, each one defined by coordinates (x, y) or (x, y, z). + Both extreme vertices of each *linestring* are called nodes. Each layer is composed by 6 files: + + - *FileName.arc* file: Contains the geographic database with the coordinates that define the + linestring (arc) vector features. + + - *FileNameA.dbf* file (note the 'A' before the '.'): Contains the main table of the database + in dBASE (DBF) format, or in `extended DBF format `__, + if necessary. It contains the information (usually alphanumeric, but also file or web links, etc) + of every feature. The Feature Identifier (FID) field is a field called *ID_GRAFIC* and relates + every geographical feature to one or more records in the main table. + + - *FileNameA.rel* file (note the 'A' before the '.'): Contains the layer metadata, + the relational structure of the database (links between the main table and other + tables [thesauruses, etc] if needed, and the cardinality of the link) and the default + symbolization description. In the GDAL environment + only some aspects are documented: the spatial reference system, the language of the + metadata (English), the extension and a description of the fields. + + - *FileName.nod* file: Contains the geographic database with information about the + linestring needed to define each node. It is necessary in the MiraMon vector format but not read by + the GDAL MiraMon vector driver because nodes contain topological information that is not + transferred to other formats. + + - *FileNameN.dbf* file (note the 'N' before the '.'): Contains the main table of the database + in dBASE (DBF) format, or in extended DBF if necessary. This table contains information about + the relationships between arcs and nodes, and other attributes of the nodes, if needed. + It is necessary in the MiraMon vector format but not read by the GDAL MiraMon vector driver because + nodes contain topological information that is not transferred to other formats. + + - *FileNameN.rel* file (note the 'N' before the '.'): Contains the layer metadata, + the relational structure of the database (links between the main table and other + tables [thesauruses, etc] if needed, and the cardinality of the link) and the default + symbolization description. It is necessary in the MiraMon vector format but not read by + the GDAL MiraMon vector driver because nodes contain topological information that is not + transferred to other formats. + +- **Polygon layers**: These layers contain *polygon* or *multipolygon* type features. + In MiraMon vector format a polygon is a closed shape described by one or more arcs. + A polygon can have holes inside it. A polygon can also be linked to other polygons; + in this case, it is termed a group (*multipolygon*). + Each layer is composed by 9 files: + + - *FileName.pol* file: Contains the geographic database with information about the linestring + vector features needed to define the polygon (or multipolygon) vector features. + + - *FileNameP.dbf* file (note the 'P' before the '.'): Contains the main table of the database + in dBASE (DBF) format, or in `extended DBF format `__, + if necessary. It contains the information (usually alphanumeric, but also file or web links, etc) + of every feature. The Feature Identifier (FID) field is a field called *ID_GRAFIC* and relates + every geographical feature to one or more records in the main table. + + - *FileNameP.rel* file (note the 'P' before the '.'): Contains the layer metadata, + the relational structure of the database (links between the main table and other + tables [thesauruses, etc] if needed, and the cardinality of the link) and the default + symbolization description. In the GDAL environment + only some aspects are documented: the spatial reference system, the language of the + metadata (English), the extension and a description of the fields. + + - *FileName.arc* file: Contains the geographic database with the coordinates that define the + arc vector features. The polygons within the polygon file reference the arcs in this file by their index. + + - *FileNameA.dbf* file (note the 'A' before the '.'): Contains the main table of the database + in dBASE (DBF) format, or in extended DBF if necessary. This table contains information about + the relationship between arcs and polygons, not the main features information. It is necessary in + MiraMon but not read directly by the GDAL MiraMon vector driver because + it is redundant to the information on the linestring part. + + - *FileNameA.rel* file (note the 'A' before the '.'): Contains additional data about the data, + the relations of the database and the symbolization description. It is necessary in + MiraMon but not read directly by the GDAL MiraMon vector driver. + + - *FileName.nod* file: Contains the geographic database with information about the + linestring needed to define each node. It is necessary in the MiraMon vector format but not read by + the GDAL MiraMon vector driver because nodes contain topological information that is not + transferred to other formats. + + - *FileNameN.dbf* file (note the 'N' before the '.'): Contains the main table of the database + in dBASE (DBF) format, or in extended DBF if necessary. This table contains information about + the relationships between arcs and nodes, and other attributes of the nodes, if needed. + It is necessary in the MiraMon vector format but not read by the GDAL MiraMon vector driver because + nodes contain topological information that is not transferred to other formats. + + - *FileNameN.rel* file (note the 'N' before the '.'): Contains additional data about the data, + the relations of the database and the symbolization description. It is necessary in + MiraMon but not read directly by the GDAL MiraMon vector driver. + +Encoding +-------- + +When reading MiraMon files the code page setting in the header of the .dbf file +is read and used to translate string fields to UTF-8 (regardless of whether they +are in ANSI, OEM or UTF-8). + +When writing MiraMon files the codepage of *.dbf* files can be ANSI or UTF8 +depending on the layer creation option DBFEncoding. + +Creation Issues +--------------- + +MiraMon can only store one kind of geometry per layer +(points, arcs or polygons). Mixing different kinds of layers +(including raster and geoservices as WMS or WMTS) is possible through MiraMon maps (.mmm). +During creation the driver generates the necessary files to +accommodate each of the three possible types of geometries. +For instance, if a layer or a dataset contains points and arcs, +a set of point files and a set of arc files will be created. + +Consequently, during creation the MiraMon vector driver output can be a +folder or a set of files with the appropriate extension (*.pnt*, etc): + +- If the output is a **folder**, it will contain all the translated layers with the original name in the origin dataset. + + - In this case a *.mmm* file will be created referencing all layers in the origin dataset to make an + easy open of the dataset using the MiraMon software. + - In this case, please specify the MiraMon file output format name using the -f option (**-f MiraMonVector**). + +- If the output is a **file** with extension all the translated layers in the origin dataset will be created with the specified name. + Use this option only when you know that there is only one layer with one feature type in the origin dataset. + +The attributes of the MiraMon feature are stored in an associated *.dbf*. +If a classical DBF IV table could not be used (too many fields or records, +large text fields, etc) a file type called extended DBF is used. +This is an improvement of dBASE IV DBF files. The specification of this format can be found in `this file +`__. + +Note that extended *.dbf* files cannot be opened with Excel or +other typical programs. If the complete MiraMon Professional software +is not installed on the computer, the free and standalone +MiraD application can be downloaded from +`this page `__ to open them. + +Connection string +----------------- + +The MiraMon driver accepts three types of sources of data: + +When translating from a MiraMon vector format, the MiraMon vector driver input needs a file with one of the +described extensions: + +- *.pnt* for *points*. +- *.arc* for *linestrings*. +- *.pol* for *polygons* (or *multipolygons*). + +The extension *.nod* is not valid for translation. Take in consideration all auxiliary files described above. + +Field sizes +----------- + +The driver will automatically extend string and integer fields to +dynamically accommodate the length of the data to be inserted. + +Size Issues +----------- + +Geometry: The MiraMon vector format explicitly uses 32-bit offsets in the 1.1 version +and 64-bit offsets in the 2.0 version. It is better to produce 1.1 version files if 2.0 +version is not really necessary than always use 2.0 version. Version 1.x files are smaller. + +Attributes: The dbf format does not have any offsets in it, so it can be +arbitrarily large. + +Open options +------------ + +The following open options are available. + +- .. oo:: Height + :choices: First, Lowest, Highest + + Sets which of the possible heights for each vertex is read: + the *first*, the *lowest* or the *highest* one. It only applies to + MiraMon multi-height layers, where the same X,Y vertex can have more than one Z. + +- .. oo:: MultiRecordIndex + :choices: 1, 2, ..., Last, JSON + + In case of fields of type List, if the output driver cannot support them, + user can select which one wants to keep: *MultiRecordIndex=1* for first, *MultiRecordIndex=2* for second, etc + and *MultiRecordIndex=last* for the last element of the list. + *MultiRecordIndex=JSON* option converts the list in a single value in JSON format. + If not specified, all elements of the list will be translated by default as a OGR list field type. + +- .. oo:: OpenLanguage + :choices: ENG, CAT, SPA + :default: ENG + + If the layer to be opened is multilingual (in fact, the *.rel* file), this + parameter sets the language to be read. + + +Dataset creation options +------------------------ + +None + +Layer creation options +---------------------- + +- .. lco:: Version + :choices: V1.1, V2.0, last_version + :default: V1.1 + + Version of the file. + Version 1.1 is limited to an unsigned 32-bit integer for FID, for internal + offsets and for the number of entities the layer can handle. + It is the default option. + Version 2.0 is the 64-bit version. It is practically unlimited + (unsigned 64-bit integer for FID and internal offsets). + last_version selects to the last existing version ever. + +- .. lco:: DBFEncoding + :choices: UTF8, ANSI + :default: ANSI + + Encoding of the *.dbf* files. + The MiraMon vector driver can write *.dbf* files in UTF-8 or ANSI charsets. + + As at the moment of this release, UTF-8 tables are not editable in the + `MiraD application `__, so it + is recommended to use ANSI instead, if there are no coding problems. + +- .. oo:: CreationLanguage + :choices: ENG, CAT, SPA + :default: ENG + + Sets the language used in the metadata file (*.rel*) for the descriptors of + the *.dbf* fields. + +Examples +-------- + +- A translation from an *Example_1.dxf* file with one layer but some different geometric types + in the layer, will result 'file1.dxf' into a new MiraMon set of layers in the 'output_folder'. + + :: + + ogr2ogr output_folder Example_1.dxf -f MiraMonVector -lco Version=V1.1 + + +- A translation from a *Example_2.dxf* file with one polygon type layer 'file1.dxf' into a new MiraMon layer + 'territories.pol' (with UTF-8 encoding at the *.dbf* files) is performed like this: + + :: + + ogr2ogr territories.pol Example_2.dxf -lco DBFEncoding=UTF8 (no needed to include **-f MiraMonVector** because the output layer is not a directory) + + +- A translation from a MiraMon layer of arcs, 'rivers.arc', into a new *.gml* file (taking only the first element of + the multirecords in the attributes table) is performed like this: + + :: + + ogr2ogr rivers.gml rivers.arc -oo MultiRecordIndex=1 + +- A translation from a MiraMon layer 'tracks.arc' into a new *.gml* file taking the first height of + every point is performed like this: + + :: + + ogr2ogr tracks.gml tracks.arc -oo Height=First + +- A translation from a MiraMon layer 'tracks.arc' into a new *.gml* file taking the last height of + every point and documenting the attribute descriptors in Catalan (if the layer is multilingual + and it has this language available) is performed like this: + + :: + + ogr2ogr tracks.gml tracks.arc -oo Height=First -oo Language=CAT + + +See Also +-------- + +- `MiraMon's vector format specifications `__ +- `MiraMon Extended DBF format `__ +- `MiraMon vector layer concepts `__. +- `MiraMon page `__ +- `MiraMon help guide `__ +- `Grumets research group, the people behind MiraMon `__ diff --git a/frmts/drivers.ini b/frmts/drivers.ini index 01cce7154ea0..116fdab02b9d 100644 --- a/frmts/drivers.ini +++ b/frmts/drivers.ini @@ -262,6 +262,7 @@ Arrow GTFS PMTiles JSONFG +MiraMonVector # Put TIGER and AVCBIN at end since they need poOpenInfo->GetSiblingFiles() Tiger diff --git a/fuzzers/CMakeLists.txt b/fuzzers/CMakeLists.txt index 47a0c5bf144c..5cf1b6dc73bf 100644 --- a/fuzzers/CMakeLists.txt +++ b/fuzzers/CMakeLists.txt @@ -90,6 +90,7 @@ build_ogr_specialized_fuzzer(avcbin RegisterOGRAVCBin "/vsimem/test.tar" "/vsita build_ogr_specialized_fuzzer(gml RegisterOGRGML "/vsimem/test.tar" "/vsitar//vsimem/test.tar/test.gml") build_fuzzer(NAME cad_fuzzer SOURCES ogr_fuzzer.cpp DEFINITIONS -DREGISTER_FUNC=RegisterOGRCAD) +build_fuzzer(NAME ogr_miramon_fuzzer SOURCES ogr_fuzzer.cpp DEFINITIONS -DREGISTER_FUNC=RegisterOGRMiraMon -DMEM_FILENAME="/vsimem/test.tar" -DFOR_OGR_MIRAMON) function (build_gdal_specialized_fuzzer _format _registerFunc _memfile _gdalfile) build_fuzzer( diff --git a/fuzzers/build_google_oss_fuzzers.sh b/fuzzers/build_google_oss_fuzzers.sh index 6095517a0120..ad0b2c2a863c 100755 --- a/fuzzers/build_google_oss_fuzzers.sh +++ b/fuzzers/build_google_oss_fuzzers.sh @@ -93,6 +93,7 @@ build_ogr_specialized_fuzzer avcbin RegisterOGRAVCBin "/vsimem/test.tar" "/vsita build_ogr_specialized_fuzzer gml RegisterOGRGML "/vsimem/test.tar" "/vsitar//vsimem/test.tar/test.gml" build_ogr_specialized_fuzzer gmlas RegisterOGRGMLAS "/vsimem/test.tar" "GMLAS:/vsitar//vsimem/test.tar/test.gml" build_ogr_specialized_fuzzer fgb RegisterOGRFlatGeobuf "/vsimem/test.fgb" "/vsimem/test.fgb" +build_fuzzer ogr_miramon_fuzzer $(dirname $0)/ogr_fuzzer.cpp -DREGISTER_FUNC=RegisterOGRMiraMon -DMEM_FILENAME="\"/vsimem/test.tar\"" -DFOR_OGR_MIRAMON build_fuzzer cad_fuzzer $(dirname $0)/ogr_fuzzer.cpp -DREGISTER_FUNC=RegisterOGRCAD formats="GTiff HFA" diff --git a/fuzzers/build_seed_corpus.sh b/fuzzers/build_seed_corpus.sh index f158e5bf8fe5..291fc011250d 100755 --- a/fuzzers/build_seed_corpus.sh +++ b/fuzzers/build_seed_corpus.sh @@ -592,6 +592,26 @@ rm -f $OUT/lvbag_fuzzer_seed_corpus.zip zip -r $OUT/lvbag_fuzzer_seed_corpus.zip ./*.xml >/dev/null cd $OLDPWD +echo "Building ogr_miramon_fuzzer_seed_corpus.zip" +rm -f $OUT/ogr_miramon_fuzzer_seed_corpus.zip +CUR_DIR=$PWD +cd $(dirname $0)/../autotest/ogr/data/miramon +for subdir in *; do + (cd $subdir + for subdir2 in *; do + (cd $subdir2 + printf "FUZZER_FRIENDLY_ARCHIVE\\n" > $CUR_DIR/ogr_miramon_${subdir}_${subdir2}.tar + for file in *; do + printf "***NEWFILE***:%s\\n" "$file" >> $CUR_DIR/ogr_miramon_${subdir}_${subdir2}.tar + cat $file >> $CUR_DIR/ogr_miramon_${subdir}_${subdir2}.tar + done + ) + done + ) +done +cd $CUR_DIR +zip -r $OUT/ogr_miramon_fuzzer_seed_corpus.zip ogr_miramon_*.tar >/dev/null +rm ogr_miramon_*.tar echo "Copying data to $OUT" cp $(dirname $0)/../data/* $OUT diff --git a/fuzzers/ogr_fuzzer.cpp b/fuzzers/ogr_fuzzer.cpp index 75286078a614..805473ce236e 100644 --- a/fuzzers/ogr_fuzzer.cpp +++ b/fuzzers/ogr_fuzzer.cpp @@ -107,6 +107,32 @@ int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len) CPLPushErrorHandler(CPLQuietErrorHandler); #ifdef USE_FILESYSTEM OGRDataSourceH hDS = OGROpen(szTempFilename, FALSE, nullptr); +#elif defined(FOR_OGR_MIRAMON) + std::string osVsitarPrefix("/vsitar/"); + char **papszFiles = + VSIReadDir(std::string(osVsitarPrefix).append(MEM_FILENAME).c_str()); + std::string osFileInTar; + for (int i = 0; papszFiles && papszFiles[i]; ++i) + { + if (EQUAL(CPLGetExtension(papszFiles[i]), "pol") || + EQUAL(CPLGetExtension(papszFiles[i]), "arc") || + EQUAL(CPLGetExtension(papszFiles[i]), "pnt")) + { + osFileInTar = papszFiles[i]; + break; + } + } + CSLDestroy(papszFiles); + OGRDataSourceH hDS = nullptr; + if (!osFileInTar.empty()) + { + hDS = OGROpen(std::string(osVsitarPrefix) + .append(MEM_FILENAME) + .append("/") + .append(osFileInTar) + .c_str(), + FALSE, nullptr); + } #else OGRDataSourceH hDS = OGROpen(GDAL_FILENAME, FALSE, nullptr); #endif diff --git a/ogr/ogrsf_frmts/CMakeLists.txt b/ogr/ogrsf_frmts/CMakeLists.txt index ec61b85a1529..f11285935dcb 100644 --- a/ogr/ogrsf_frmts/CMakeLists.txt +++ b/ogr/ogrsf_frmts/CMakeLists.txt @@ -47,6 +47,9 @@ ogr_optional_driver(vdv "VDV-451/VDV-452/INTREST Data Format") ogr_optional_driver(flatgeobuf FlatGeobuf) ogr_optional_driver(mapml MapML) ogr_optional_driver(jsonfg JSONFG) +if( NOT WORDS_BIGENDIAN ) + ogr_optional_driver(miramon "MiraMonVector") +endif() # ###################################################################################################################### # diff --git a/ogr/ogrsf_frmts/generic/ogrregisterall.cpp b/ogr/ogrsf_frmts/generic/ogrregisterall.cpp index be4de0ae847f..8b364b141964 100644 --- a/ogr/ogrsf_frmts/generic/ogrregisterall.cpp +++ b/ogr/ogrsf_frmts/generic/ogrregisterall.cpp @@ -263,6 +263,9 @@ void OGRRegisterAllInternal() #ifdef JSONFG_ENABLED RegisterOGRJSONFG(); #endif +#ifdef MIRAMON_ENABLED + RegisterOGRMiraMon(); +#endif // NOTE: you need to generally insert your own driver before that line. diff --git a/ogr/ogrsf_frmts/miramon/CMakeLists.txt b/ogr/ogrsf_frmts/miramon/CMakeLists.txt new file mode 100644 index 000000000000..8eccd4e46b07 --- /dev/null +++ b/ogr/ogrsf_frmts/miramon/CMakeLists.txt @@ -0,0 +1,14 @@ +add_gdal_driver( + TARGET ogr_MiraMon + SOURCES ogrmiramondatasource.cpp ogrmiramondriver.cpp ogrmiramonlayer.cpp mm_wrlayr.c mm_gdal_functions.c mm_rdlayr.c + PLUGIN_CAPABLE) +gdal_standard_includes(ogr_MiraMon) +target_include_directories(ogr_MiraMon PRIVATE $) + +set(GDAL_DATA_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/data/MM_m_idofic.csv +) +set_property( + TARGET ${GDAL_LIB_TARGET_NAME} + APPEND + PROPERTY RESOURCE "${GDAL_DATA_FILES}") diff --git a/ogr/ogrsf_frmts/miramon/data/MM_m_idofic.csv b/ogr/ogrsf_frmts/miramon/data/MM_m_idofic.csv new file mode 100644 index 000000000000..c19fd2581642 --- /dev/null +++ b/ogr/ogrsf_frmts/miramon/data/MM_m_idofic.csv @@ -0,0 +1,233 @@ +PSIDGEODES;ID_GEODES;NOTA_CAT;NOTA_SPA;NOTA_ENG +ESRI:102022;Albers_Equal_Area-Africa-WGS84;;; +ESRI:102025;Albers_Equal_Area-Asia_North-WGS84;;; +EPSG:5070;Albers_Equal_Area-N_America-NAD83;https://epsg.io/5070;https://epsg.io/5070;https://epsg.io/5070 +Azimuthal_Equidistant;Azimuthal_Equidistant-0-90-WGS84;;; +EPSG:4088;Cilindrical_Equidistant-WGS84;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +Cylindrical_Equal_Area;Cylindrical_Equal_Area-15-0-WGS84;;; +EPSG:22171;Gauss-Kruger_Faja1-PosGAR98;;; +EPSG:22172;Gauss-Kruger_Faja2-PosGAR98;;; +EPSG:22173;Gauss-Kruger_Faja3-PosGAR98;;; +EPSG:22174;Gauss-Kruger_Faja4-PosGAR98;;; +EPSG:22175;Gauss-Kruger_Faja5-PosGAR98;;; +EPSG:22176;Gauss-Kruger_Faja6-PosGAR98;;; +EPSG:22177;Gauss-Kruger_Faja7-PosGAR98;;; +EPSG:3763;Gauss-Kruger_Portugal-ETRS89;;; +PT-TM06/ETRS89;Gauss-Kruger_Portugal-ETRS89;;; +EPSG:20791;Gauss-Kruger_Portugal-Lisboa1937;;; +EPSG:2932;Gauss-Kruger_Qatar-QND;;; +EPSG:3116;Gauss-Kruger_Zona2-MAGNA;;; +SR-ORG:9111;Geostationary-WGS84;;; +Goode_Homolosine;Goode_Homolosine-WGS84;;; +ESRI:102017;LambertAzimEqualA-0-90-WGS84-Ellipsoide;https://epsg.io/102017;https://epsg.io/102017;https://epsg.io/102017 +EPSG:9821;LambertAzimEqualA-0-90-WGS84-Esfera;https://epsg.io/9821-method;https://epsg.io/9821-method;https://epsg.io/9821-method +Lambert_Azimuthal_Equal_Area;LambertAzimEqualA-0-90-WGS84-Esfera;;; +Lambert_Azimuthal_Equal_Area-0-90-WGS84;LambertAzimEqualA-0-90-WGS84-Esfera;;; +EPSG:3035;Lambert_Azimuthal_Equal_Area-1052-ETRS89;;; +urn:ogc:def:crs:EPSG::3035;Lambert_Azimuthal_Equal_Area-1052-ETRS89;;; +ETRS-LAEA;Lambert_Azimuthal_Equal_Area-1052-ETRS89;;; +SR-ORG:7297;Lambert_Azimuthal_Equal_Area-9-48-ETRS89;;; +ETRS-LCC;Lambert_Conformal_Conic-Europa-ETRS89;;; +EPSG:3034;Lambert_Conformal_Conic-Europa-ETRS89;;; +EPSG:2154;Lambert_Conformal_Conic-França-ETRS89;;; +EPSG:2062;Lambert_Conformal_Conic-Madrid1870;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:26191;Lambert_Conformal_Conic-Maroc_N-Merchich;;; +EPSG:27561;Lambert_Conformal_Conic-ZoneI-NTF;;; +EPSG:27562;Lambert_Conformal_Conic-ZoneII-NTF;;; +EPSG:27563;Lambert_Conformal_Conic-ZoneIII-NTF;;; +Lambert_Conformal_Conic;Lambert_Conformal_Conic-ZoneIII-NTF;;; +EPSG:27573;Lambert_Conformal_Conic-ZoneIII_ext-NTF;;; +EPSG:27572;Lambert_Conformal_Conic-ZoneII_ext-NTF;;; +AUTO2:LCC,1,14.5,38,35,41;Lambert_Conformal_Conic_ICC_Mediterrani;;; +AUTO2:MERCATOR,1,0,0.0;Mercator-Equator-ED50-UB/ICC;;; +Mercator;Mercator-Equator-ED50-UB/ICC;;; +Mercator-ED50;Mercator-Equator-ED50-UB/ICC;;; +Mercator-ED50-UB/ICC;Mercator-Equator-ED50-UB/ICC;;; +Mercator-Ecuador-ED50-UB/ICC;Mercator-Equator-ED50-UB/ICC;;; +EPSG:3395;Mercator-Equator-WGS84;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +AUTO2:MERCATOR,1,0,40.60;Mercator-IHM-485-60k-ED50-UB/ICC;;; +AUTO2:MERCATOR,1,0,41.42;Mercator-IHM-489-50k-ED50-UB/ICC;;; +AUTO2:MERCATOR_WGS84,1,0,41.42;Mercator-IHM-489-50k-WGS84;;; +EPSG:3785;Mercator-Popular-Visualisation-Sphere;;; +EPSG:3857;Mercator-Popular-Visualisation-Sphere;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +urn:ogc:def:crs:EPSG::3857;Mercator-Popular-Visualisation-Sphere;;; +EPSG:900913;Mercator-Popular-Visualisation-Sphere;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +ESRI:102100;Mercator-Popular-Visualisation-Sphere;;; +EPSG:21782;ObliqueMercator-Rosenmund1903;;; +SR-ORG:6842;Sinusoidal-V5-MODIS;https://spatialreference.org/ref/sr-org/modis-sinusoidal/;https://spatialreference.org/ref/sr-org/modis-sinusoidal/;https://spatialreference.org/ref/sr-org/modis-sinusoidal/ +SR-ORG:6974;Sinusoidal-V5-MODIS;https://spatialreference.org/ref/sr-org/modis-sinusoidal-3/;https://spatialreference.org/ref/sr-org/modis-sinusoidal-3/;https://spatialreference.org/ref/sr-org/modis-sinusoidal-3/ +SR-ORG:6965;Sinusoidal-V5-MODIS;https://spatialreference.org/ref/sr-org/modis-sinusoidal-2/;https://spatialreference.org/ref/sr-org/modis-sinusoidal-2/;https://spatialreference.org/ref/sr-org/modis-sinusoidal-2/ +Sinusoidal;Sinusoidal-WGS84;;; +EPSG:3909;TransverseMercator-BalkansMGI1901;;; +EPSG:2393;TransverseMercator-Finland-KKJ;;; +EPSG:29903;TransverseMercator-Ireland1965;;; +EPSG:2039;TransverseMercator-Israel1989;;; +EPSG:3003;TransverseMercator-Monte_Mario-Italy_Z1;;; +EPSG:3021;TransverseMercator-Sweden-RT90;;; +EPSG:26710;UTM-10N-NAD27-CW;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:32610;UTM-10N-WGS84;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:26901;UTM-1N-NAD83;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:26902;UTM-2N-NAD83;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:26903;UTM-3N-NAD83;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:26904;UTM-4N-NAD83;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:26905;UTM-5N-NAD83;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:26906;UTM-6N-NAD83;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:26906;UTM-7N-NAD83;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:26908;UTM-8N-NAD83;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:26909;UTM-9N-NAD83;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:26910;UTM-10N-NAD83;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:26911;UTM-11N-NAD83;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:26912;UTM-12N-NAD83;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:26913;UTM-13N-NAD83;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:26914;UTM-14N-NAD83;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:26915;UTM-15N-NAD83;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:26916;UTM-16N-NAD83;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:26917;UTM-17N-NAD83;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:26918;UTM-18N-NAD83;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:26919;UTM-19N-NAD83;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:26920;UTM-20N-NAD83;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:26921;UTM-21N-NAD83;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:26922;UTM-22N-NAD83;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:26923;UTM-23N-NAD83;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:26711;UTM-11N-NAD27-CW;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:4486;UTM-13N-ITRF92;;; +EPSG:26713;UTM-13N-NAD27-MX;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:4487;UTM-14N-ITRF92;;; +EPSG:26714;UTM-14N-NAD27-MX;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:4488;UTM-15N-ITRF92;;; +EPSG:26715;UTM-15N-NAD27-MX;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:26915;UTM-15N-NAD83;;; +EPSG:26716;UTM-16N-NAD27-BC;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:32616;UTM-16N-WGS84;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:24877;UTM-17S-PSA56-P;;; +EPSG:29187;UTM-17S-SAD69-PE;;; +EPSG:32717;UTM-17S-WGS84;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:32618;UTM-18N-WGS84;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:29188;UTM-18S-SAD69-CH;;; +EPSG:32718;UTM-18S-WGS84;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:29169;UTM-19N-SAD69-BR;;; +EPSG:32619;UTM-19N-WGS84;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:24879;UTM-19S-PSAD56-BC;;; +EPSG:24879-1201;UTM-19S-PSAD56-BC;Transformació per defecte segons https://epsg.io/24879;Transformación por defecto según https://epsg.io/24879;Default transformation according to https://epsg.io/24879 +EPSG:24879-1203;UTM-19S-PSAD56-CN;;; +EPSG:24879-1209;UTM-19S-PSAD56-V;;; +EPSG:29189;UTM-19S-SAD69-CH;;; +EPSG:32719;UTM-19S-WGS84;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:29170;UTM-20N-SAD69-BR;;; +EPSG:32620;UTM-20N-WGS84;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:29190;UTM-20S-SAD69-BR;;; +EPSG:32720;UTM-20S-WGS84;Sense paràmetres TOWGS84 a https://epsg.io/;; +EPSG:29171;UTM-21N-SAD69-BR;;; +EPSG:32621;UTM-21N-WGS84;Sense paràmetres TOWGS84 a https://epsg.io/;; +EPSG:29191;UTM-21S-SAD69-BR;;; +EPSG:32721;UTM-21S-WGS84;Sense paràmetres TOWGS84 a https://epsg.io/;; +EPSG:29172;UTM-22N-SAD69-BR;;; +EPSG:32622;UTM-22N-WGS84;Sense paràmetres TOWGS84 a https://epsg.io/;; +EPSG:29192;UTM-22S-SAD69-BR;;; +EPSG:32722;UTM-22S-WGS84;Sense paràmetres TOWGS84 a https://epsg.io/;; +EPSG:29193;UTM-23S-SAD69-BR;;; +EPSG:29194;UTM-24S-SAD69-BR;;; +EPSG:29195;UTM-25S-SAD69-BR;;; +ETRS-TM26;UTM-26N-ETRS89;;; +EPSG:3038;UTM-26N-ETRS89;;; +EPSG:3039;UTM-27N-ETRS89;;; +ETRS-TM27;UTM-27N-ETRS89;;; +EPSG:32627;UTM-27N-WGS84;Ordre d'eixos preferit: est-nord (XY). Sense paràmetres TOWGS84 a https://epsg.io/;Orden de ejes preferido: est-norte (XY). Sin parámetros TOWGS84 en https://epsg.io/;Preferred axis order: east-north (XY). No TOWGS84 parameters at https://epsg.io/ +EPSG:3040;UTM-28N-ETRS89;Ordre d'eixos preferit: nord-est (YX);Orden de ejes preferido: norte-est (YX). Sin parámetros TOWGS84 en https://epsg.io/;Preferred axis order: north-east (YX). No TOWGS84 parameters at https://epsg.io/ +ETRS-TM28;UTM-28N-ETRS89;;; +EPSG:4083;UTM-28N-REGCAN95;;; +EPSG:32628;UTM-28N-WGS84;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:23029;UTM-29N-S/IGN;;; +EPSG:23029-0000;UTM-29N-ED50-ABDF;;; +EPSG:23029-1145;UTM-29N-ED50-PS;;; +EPSG:25829;UTM-29N-ETRS89;Ordre d'eixos preferit: est-nord (XY);Orden de ejes preferido: est-norte (XY);Preferred axis order: east-north (XY) +EPSG:3041;UTM-29N-ETRS89;Ordre d'eixos preferit: nord-est (YX);Orden de ejes preferido: norte-est (YX);Preferred axis order: north-east (YX) +urn:ogc:def:crs:EPSG::25829;UTM-29N-ETRS89;;; +ETRS-TM29;UTM-29N-ETRS89;;; +EPSG:23029-1633;UTM-29N-S/IGN;;; +EPSG:32629;UTM-29N-WGS84;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +urn:ogc:def:crs:EPSG::23029;UTM-29N-S/IGN;;; +EPSG:25830;UTM-30N-ETRS89;Ordre d'eixos preferit: est-nord (XY);Orden de ejes preferido: est-norte (XY);Preferred axis order: east-north (XY) +EPSG:3042;UTM-30N-ETRS89;Ordre d'eixos preferit: nord-est (YX);Orden de ejes preferido: norte-est (YX);Preferred axis order: north-east (YX) +urn:ogc:def:crs:EPSG::25830;UTM-30N-ETRS89;;; +ETRS-TM30;UTM-30N-ETRS89;;; +EPSG:23030;UTM-30N-S/IGN;;; +EPSG:23030-0000;UTM-30N-ABDF;Transformació per defecte segons https://epsg.io/23030-to-4326;Transformación por defecto según https://epsg.io/23030-to-4326;Default transformation according to https://epsg.io/23030-to-4326 +EPSG:23030-15933;UTM-30N-IP;;; +EPSG:23030-1631;UTM-30N-Balearic;;; +EPSG:23030-1635;UTM-30N-NW_IP;;; +EPSG:23030-1145;UTM-30N-PS;;; +EPSG:23030-1633;UTM-30N-S/IGN;;; +urn:ogc:def:crs:EPSG::23030;UTM-30N-S/IGN;;; +UTM-30N;UTM-30N-S/IGN;;; +EPSG:32630;UTM-30N-WGS84;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:25831;UTM-31N-ETRS89;Ordre d'eixos preferit: est-nord (XY);Orden de ejes preferido: est-norte (XY);Preferred axis order: east-north (XY) +EPSG:3043;UTM-31N-ETRS89;Ordre d'eixos preferit: nord-est (YX);Orden de ejes preferido: norte-est (YX);Preferred axis order: north-east (YX) +urn:ogc:def:crs:EPSG::25831;UTM-31N-ETRS89;;; +ETRS-TM31;UTM-31N-ETRS89;;; +UTM-31N;UTM-31N-UB/ICC;;; +UTM-31N-ED50;UTM-31N-UB/ICC;;; +EPSG:23031;UTM-31N-UB/ICC;Excepcionalment no es fa correspondre a UTM-31N-ABDF (=https://epsg.io/23031) sinó a UTM-31N-UB/ICC per compatibilitat descendent;Excepcionalmente no se hace corresponder a UTM-31N-ABDF (=https://epsg.io/23031) sino a UTM-31N-UB/ICC por compatibilidad descendente;Exceptionally it does not correspond to UTM-31N-ABDF (=https://epsg.io/23031) but to UTM-31N-UB/ICC for backwards compatibility +EPSG:23031-0000;UTM-31N-ABDF;Transformació per defecte segons https://epsg.io/23031-to-4326;Transformación por defecto según https://epsg.io/23031-to-4326;Default transformation according to https://epsg.io/23031-to-4326 +urn:ogc:def:crs:EPSG::23031;UTM-31N-UB/ICC;;; +EPSG:32631;UTM-31N-WGS84;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:25832;UTM-32N-ETRS89;;; +ETRS-TM32;UTM-32N-ETRS89;;; +EPSG:32632;UTM-32N-WGS84;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:25833;UTM-33N-ETRS89;;; +ETRS-TM33;UTM-33N-ETRS89;;; +EPSG:32633;UTM-33N-WGS84;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:22033;UTM-33S-Camacupa1980;;; +EPSG:32733;UTM-33S-WGS84;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:25834;UTM-34N-ETRS89;;; +ETRS-TM34;UTM-34N-ETRS89;;; +EPSG:2100;UTM-34N-GGRS87;;; +EPSG:32634;UTM-34N-WGS84;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:25835;UTM-35N-ETRS89;;; +ETRS-TM35;UTM-35N-ETRS89;;; +EPSG:32635;UTM-35N-WGS84;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +ETRS-TM36;UTM-36N-ETRS89;;; +EPSG:25836;UTM-36N-ETRS89;;; +EPSG:32636;UTM-36N-WGS84;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:2736;UTM-36S-Tete-MZ;;; +EPSG:32736;UTM-36S-WGS84;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +ETRS-TM37;UTM-37N-ETRS89;;; +EPSG:25837;UTM-37N-ETRS89;;; +EPSG:32637;UTM-37N-WGS84;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +ETRS-TM38;UTM-38N-ETRS89;;; +EPSG:25838;UTM-38N-ETRS89;;; +ETRS-TM39;UTM-39N-ETRS89;;; +EPSG:32642;UTM-42N-WGS84;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:32643;UTM-43N-WGS84;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:32644;UTM-44N-WGS84;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:32647;UTM-47N-WGS84;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:32659;UTM-59N-WGS84;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:32759;UTM-59S-WGS84;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:32606;UTM-6N-WGS84;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:4218;lat/long-Bogota;;; +EPSG:4149;lat/long-CH1903;;; +EPSG:4230-1145;lat/long-ED50-PS;;; +EPSG:4230-1633;lat/long-ED50-S/IGN;;; +EPSG:4230-0000;lat/long-ED50-ABDF;Transformació per defecte segons https://epsg.io/4230-to-4326;Transformación por defecto según https://epsg.io/4230-to-4326;Default transformation according to https://epsg.io/4230-to-4326 +EPSG:4230;lat/long-ED50-UB/ICC;;; +lat/long-ED50;lat/long-ED50-UB/ICC;;; +EPSG:4258;lat/long-ETRS89;;; +EPSG:4686;lat/long-MAGNA;;; +EPSG:4903;lat/long-Madrid1870;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:4261;lat/long-Merchich;;; +EPSG:4267;lat/long-NAD27-BC;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +EPSG:4269;lat/long-NAD83-AA;;; +EPSG:4275;lat/long-NTF;;; +EPSG:4190;lat/long-PosGAR98;;; +EPSG:4081;lat/long-REGCAN95;;; +EPSG:5527;lat/long-SAD69-CH;;; +EPSG:4124;lat/long-Sweden-RT90;;; +EPSG:4127;lat/long-Tete-MZ;;; +EPSG:4326;lat/long-WGS84;Sense paràmetres TOWGS84 a https://epsg.io/;Sin parámetros TOWGS84 en https://epsg.io/;No TOWGS84 parameters at https://epsg.io/ +urn:ogc:def:crs:EPSG::4326;lat/long-WGS84;;; +CRS:84;lat/long-WGS84;;; +Equirectangular;lat/long-WGS84;;; +lat/long;lat/long-WGS84;;; +urn:ogc:def:crs:OGC:1.3:CRS84;lat/long-WGS84;;; +EPSG:9377;Transverse-Mercator_Colombia_ONacional;;; +MAGNA-SIRGAS / Origen-Nacional;Transverse-Mercator_Colombia_ONacional;https://origen.igac.gov.co/herramientas.html;https://origen.igac.gov.co/herramientas.html;https://origen.igac.gov.co/herramientas.html diff --git a/ogr/ogrsf_frmts/miramon/mm_constants.h b/ogr/ogrsf_frmts/miramon/mm_constants.h new file mode 100644 index 000000000000..dbef6343ccd4 --- /dev/null +++ b/ogr/ogrsf_frmts/miramon/mm_constants.h @@ -0,0 +1,173 @@ +#ifndef __MM_CONSTANTS_H +#define __MM_CONSTANTS_H +/* -------------------------------------------------------------------- */ +/* Constants used in GDAL and in MiraMon */ +/* -------------------------------------------------------------------- */ +#ifdef GDAL_COMPILATION +CPL_C_START // Necessary for compiling C in GDAL project +#else +#ifndef UINT32_MAX +#define UINT32_MAX _UI32_MAX +#endif +#endif // GDAL_COMPILATION + +#define MM_OFFSET_BYTESxCAMP_CAMP_CLASSIC 16 +#define MM_OFFSET_BYTESxCAMP_CAMP_ESPECIAL 21 +#define MM_MAX_LON_RESERVAT_1_CAMP_BD_XP 4 +#define MM_OFFSET_RESERVAT2_BYTESxCAMP_CAMP_ESPECIAL 3 +#define MM_OFFSET_RESERVAT2_OFFSET_NOM_ESTES 7 +#define MM_OFFSET_RESERVED2_EXTENDED_NAME_SIZE 11 +#define MM_MAX_LON_RESERVAT_2_CAMP_BD_XP 13 + +#define MM_ES_DBF_ESTESA(dbf_version) \ + (((dbf_version) == MM_MARCA_VERSIO_1_DBF_ESTESA) ? TRUE : FALSE) + +#define MM_UNDEFINED_STATISTICAL_VALUE (2.9E+301) +#define MM_CPL_PATH_BUF_SIZE 2048 + +// BIT 1 +#define MM_BIT_1_ON 0x02 // Generated using MiraMon +// BIT 3 +#define MM_BIT_3_ON 0x08 // Multipolygon +// BIT 4 +#define MM_BIT_4_ON 0x10 // 3D +// BIT 5 +#define MM_BIT_5_ON \ + 0x20 // Explicital polygons (every polygon has only one arc) + +#define MM_CREATED_USING_MIRAMON MM_BIT_1_ON +#define MM_LAYER_MULTIPOLYGON MM_BIT_3_ON +#define MM_LAYER_3D_INFO MM_BIT_4_ON + +#define MM_BOOLEAN char +#define MM_HANDLE void * + +#define MM_MESSAGE_LENGTH 512 +#define MM_MAX_BYTES_FIELD_DESC 360 +#define MM_MAX_BYTES_IN_A_FIELD_EXT (UINT32_MAX - 1) +#define MM_MAX_LON_FIELD_NAME_DBF 129 +#define MM_MAX_LON_CLASSICAL_FIELD_NAME_DBF 11 +#define MM_MAX_LON_UNITATS 66 +#define MM_MAX_LON_UNITATS_CAMP MM_MAX_LON_UNITATS + +// Determines if an arc is external, the last one in a ring or +// if it has to be inverted to be consistent with other arcs +// in the ring. +#define MM_POL_EXTERIOR_SIDE 0x01 +#define MM_POL_END_RING 0x02 +#define MM_POL_REVERSE_ARC 0x04 + +// Z Part +#define MM_SELECT_FIRST_COORDZ 0 +#define MM_SELECT_HIGHEST_COORDZ 1 +#define MM_SELECT_LOWEST_COORDZ 2 + +#define MM_STRING_HIGHEST_ALTITUDE 0x0001 +#define MM_STRING_LOWEST_ALTITUDE 0x0002 + +#define /*double*/ MM_NODATA_COORD_Z (-1.0E+300) + +// General static variables +#define MM_MAX_LEN_LAYER_NAME 255 +#define MM_MAX_LEN_LAYER_IDENTIFIER 255 + +#define MM_TYPICAL_NODE 0 +#define MM_LINE_NODE 1 +#define MM_RING_NODE 2 +#define MM_FINAL_NODE 3 + +#define MM_MAX_ID_SNY 41 + +#ifndef GDAL_COMPILATION + typedef unsigned int uint32_t; +typedef int int32_t; +#endif + +// Extended DBF +// Type of the number of records of an extended DBF +#define MM_MAX_N_CAMPS_DBF_CLASSICA 255 +#define MM_MAX_AMPLADA_CAMP_C_DBF_CLASSICA 254 + +#define MM_MARCA_VERSIO_1_DBF_ESTESA 0x90 +#define MM_MARCA_DBASE4 0x03 +#define MM_MAX_LON_RESERVAT_1_BASE_DADES_XP 2 +#define MM_MAX_LON_DBF_ON_A_LAN_BASE_DADES_XP 12 +#define MM_MAX_LON_RESERVAT_2_BASE_DADES_XP 2 + +#define MM_MAX_LON_DESCRIPCIO_CAMP_DBF MM_CPL_PATH_BUF_SIZE + 100 +#define MM_NUM_IDIOMES_MD_MULTIDIOMA 4 + +#define MM_CAMP_NO_MOSTRABLE 0 +#define MM_CAMP_MOSTRABLE 1 +#define MM_CAMP_MOSTRABLE_QUAN_TE_CONTINGUT 4 +#define MM_CAMP_QUE_MOSTRA_TESAURE 2 +#define MM_CAMP_QUE_MOSTRA_TAULA_BDXP_O_BDODBC 3 + +#define MM_CAMP_INDETERMINAT 0 +#define MM_CATEGORICAL_FIELD 1 +#define MM_CAMP_ORDINAL 2 +#define MM_QUANTITATIVE_CONTINUOUS_FIELD 3 + +#define MM_CAMP_NO_SIMBOLITZABLE 0 +#define MM_CAMP_SIMBOLITZABLE 1 + +#define MM_NO_ES_CAMP_GEOTOPO 0 +#define MM_CAMP_ES_ID_GRAFIC 1 +#define MM_CAMP_ES_MAPA_X 2 +#define MM_CAMP_ES_MAPA_Y 3 +#define MM_CAMP_ES_MAPA_Z 17 +#define MM_CAMP_ES_N_VERTEXS 4 +#define MM_CAMP_ES_LONG_ARC 5 +#define MM_CAMP_ES_LONG_ARCE 6 +#define MM_CAMP_ES_NODE_INI 7 +#define MM_CAMP_ES_NODE_FI 8 +#define MM_CAMP_ES_ARCS_A_NOD 9 +#define MM_CAMP_ES_TIPUS_NODE 10 +#define MM_CAMP_ES_PERIMETRE 11 +#define MM_CAMP_ES_PERIMETREE 12 +#define MM_CAMP_ES_PERIMETRE_3D 18 +#define MM_CAMP_ES_AREA 13 +#define MM_CAMP_ES_AREAE 14 +#define MM_CAMP_ES_AREA_3D 19 +#define MM_CAMP_ES_N_ARCS 15 +#define MM_CAMP_ES_N_POLIG 16 +#define MM_CAMP_ES_PENDENT 20 +#define MM_CAMP_ES_ORIENTACIO 21 + +#define MM_JOC_CARAC_ANSI_MM 1252 +#define MM_JOC_CARAC_ANSI_DBASE 0x58 +#define MM_JOC_CARAC_OEM850_MM 850 +#define MM_JOC_CARAC_OEM850_DBASE 0x14 +#define MM_JOC_CARAC_UTF8_DBF 0xFF +#define MM_JOC_CARAC_UTF8_MM 8 + +typedef unsigned char MM_BYTE; + +#define MM_PRIMER_OFFSET_a_OFFSET_1a_FITXA 8 +#define MM_SEGON_OFFSET_a_OFFSET_1a_FITXA 30 + +#define MM_FIRST_OFFSET_to_N_RECORDS 4 +#define MM_SECOND_OFFSET_to_N_RECORDS 16 + +#define MM_FIELD_NAME_TOO_LONG 0x01 +#define MM_FIELD_NAME_CHARACTER_INVALID 0x02 +#define MM_FIELD_NAME_FIRST_CHARACTER_ 0x04 + +#define MM_MAX_AMPLADA_CAMP_N_DBF 21 +#define MM_MAX_AMPLADA_CAMP_C_DBF 254 +#define MM_MAX_AMPLADA_CAMP_D_DBF 10 + +#define MM_PRIVATE_POINT_DB_FIELDS 1 +#define MM_PRIVATE_ARC_DB_FIELDS 5 +#define MM_PRIVATE_POLYGON_DB_FIELDS 6 + +#define MM_NOU_N_DECIMALS_NO_APLICA 0 +#define MM_APLICAR_NOU_N_DECIMALS 1 +#define MM_NOMES_DOCUMENTAR_NOU_N_DECIMALS 2 +#define MM_PREGUNTA_SI_APLICAR_NOU_N_DECIM 3 +#define MM_CHARACTERS_DOUBLE 40 + +#ifdef GDAL_COMPILATION +CPL_C_END // Necessary for compiling in GDAL project +#endif +#endif //__MM_CONSTANTS_H diff --git a/ogr/ogrsf_frmts/miramon/mm_gdal_constants.h b/ogr/ogrsf_frmts/miramon/mm_gdal_constants.h new file mode 100644 index 000000000000..f42878f7d088 --- /dev/null +++ b/ogr/ogrsf_frmts/miramon/mm_gdal_constants.h @@ -0,0 +1,97 @@ +#ifndef __MM_GDAL_CONSTANTS_H +#define __MM_GDAL_CONSTANTS_H +/* -------------------------------------------------------------------- */ +/* Constants used in GDAL and in MiraMon */ +/* -------------------------------------------------------------------- */ +#ifndef GDAL_COMPILATION +#ifdef _WIN64 +#include "gdal\release-1911-x64\cpl_port.h" // For GUInt64 +#else +#include "gdal\release-1911-32\cpl_port.h" // For GUInt64 +#endif +#else +#include "cpl_port.h" // For GUInt64 +CPL_C_START // Necessary for compiling C in GDAL project +#endif // GDAL_COMPILATION + +#if defined(_WIN32) && !defined(strcasecmp) +#define strcasecmp stricmp +#endif + +#define MAX_LOCAL_MESSAGE 5000 + +#define sprintf_UINT64 "%llu" + +// Type of the Feature ID: determines the maximum number of features in a layer. +typedef GUInt64 MM_INTERNAL_FID; +// Offset to the coordinates of the Features. +typedef GUInt64 MM_FILE_OFFSET; + +// Type of the coordinates of a Point, Arc or Polygons points. +typedef double MM_COORD_TYPE; + +// Points + +// StringLines (or Arcs) +typedef GUInt64 MM_N_VERTICES_TYPE; // size_t in MiraMon + +// Polygons (or polypolygons) +typedef GUInt64 MM_POLYGON_ARCS_COUNT; +typedef GUInt64 MM_POLYGON_RINGS_COUNT; + +// Z Part +typedef int MM_SELEC_COORDZ_TYPE; + +// Extended DBF +// Type of the number of fields of an extended DBF +typedef GUInt32 + MM_EXT_DBF_N_FIELDS; //(TIPUS_NUMERADOR_CAMP in MiraMon internal code) +#define MM_MAX_EXT_DBF_N_FIELDS_TYPE UINT32_MAX + +// In MiraMon code: MM_TIPUS_BYTES_PER_CAMP_DBF +typedef GUInt32 MM_BYTES_PER_FIELD_TYPE_DBF; + +// In MiraMon code: MM_TIPUS_BYTES_ACUMULATS_DBF +typedef GUInt32 MM_ACCUMULATED_BYTES_TYPE_DBF; + +// Type of the number of records of an extended DBF +typedef GUInt32 MM_EXT_DBF_N_MULTIPLE_RECORDS; +typedef GUInt64 MM_EXT_DBF_N_RECORDS; +typedef GInt64 MM_EXT_DBF_SIGNED_N_RECORDS; +#define scanf_MM_EXT_DBF_SIGNED_N_RECORDS "%lld" +typedef GInt32 MM_FIRST_RECORD_OFFSET_TYPE; + +typedef GInt32 MM_N_HEIGHT_TYPE; + +#define MM_ARC_HEIGHT_FOR_EACH_VERTEX \ + 1 // In MiraMon code: MM_ARC_ALCADA_PER_CADA_VERTEX +#define MM_ARC_CONSTANT_HEIGHT -1 // In MiraMon code: MM_ARC_ALCADA_CONSTANT +// In MiraMon code: MM_ARC_TIPUS_ALCADA +#define MM_ARC_HEIGHT_TYPE(n) \ + (((n) < 0) ? MM_ARC_CONSTANT_HEIGHT : MM_ARC_HEIGHT_FOR_EACH_VERTEX) +// In MiraMon code: MM_ARC_N_ALCADES +#define MM_ARC_N_HEIGHTS(n) (((n) < 0) ? -(n) : (n)) +// In MiraMon code: MM_ARC_N_TOTAL_ALCADES_DISC +#define MM_ARC_TOTAL_N_HEIGHTS_DISK(n, n_vrt) \ + (((n) < 0) ? -(n) : (n) * (MM_N_HEIGHT_TYPE)(n_vrt)) + +#define MM_EscriuOffsetNomEstesBD_XP(bd_xp, i_camp, offset_nom_camp) \ + memcpy((bd_xp)->pField[(i_camp)].reserved_2 + \ + MM_OFFSET_RESERVAT2_OFFSET_NOM_ESTES, \ + &(offset_nom_camp), 4) + +enum MM_TipusNomCamp +{ + NM_CLASSICAL_DBF_AND_VALID_NAME = 0, + MM_DBF_NAME_LOWERCASE_AND_VALID, + MM_VALID_EXTENDED_DBF_NAME, + MM_DBF_NAME_NO_VALID +}; + +#define MM_DonaBytesNomEstesCamp(camp) \ + ((MM_BYTE)((camp)->reserved_2[MM_OFFSET_RESERVED2_EXTENDED_NAME_SIZE])) + +#ifdef GDAL_COMPILATION +CPL_C_END // Necessary for compiling in GDAL project +#endif +#endif //__MM_GDAL_CONSTANTS_H diff --git a/ogr/ogrsf_frmts/miramon/mm_gdal_driver_structs.h b/ogr/ogrsf_frmts/miramon/mm_gdal_driver_structs.h new file mode 100644 index 000000000000..5ae905329dd4 --- /dev/null +++ b/ogr/ogrsf_frmts/miramon/mm_gdal_driver_structs.h @@ -0,0 +1,826 @@ +#ifndef __MM_GDAL_DRIVER_STRUCTS_H +#define __MM_GDAL_DRIVER_STRUCTS_H +/* -------------------------------------------------------------------- */ +/* Necessary functions to read/write a MiraMon Vector File */ +/* -------------------------------------------------------------------- */ + +#ifdef GDAL_COMPILATION +#include "mm_gdal_constants.h" +#include "mm_gdal_structures.h" + +CPL_C_START // Necessary for compiling in GDAL project +#else +#include // For FILE +#include "mm_constants.h" +#include "mm_gdal\mm_gdal_structures.h" +#endif + +// For MetaData +#define SECTION_VERSIO "VERSIO" +#define KEY_Vers "Vers" +#define KEY_SubVers "SubVers" +#define MM_VERS 4 +#define MM_SUBVERS 3 +#define KEY_VersMetaDades "VersMetaDades" +#define KEY_SubVersMetaDades "SubVersMetaDades" +#define MM_VERS_METADADES 5 +#define MM_SUBVERS_METADADES 0 +#define SECTION_METADADES "METADADES" +#define KEY_FileIdentifier "FileIdentifier" +#define SECTION_IDENTIFICATION "IDENTIFICATION" +#define KEY_code "code" +#define KEY_codeSpace "codeSpace" +#define KEY_DatasetTitle "DatasetTitle" +#define SECTION_OVERVIEW "OVERVIEW" +#define SECTION_OVVW_ASPECTES_TECNICS "OVERVIEW:ASPECTES_TECNICS" +#define KEY_ArcSource "ArcSource" +#define SECTION_EXTENT "EXTENT" +#define KEY_toler_env "toler_env" +#define KEY_MinX "MinX" +#define KEY_MaxX "MaxX" +#define KEY_MinY "MinY" +#define KEY_MaxY "MaxY" +#define KEY_CreationDate "CreationDate" +#define SECTION_SPATIAL_REFERENCE_SYSTEM "SPATIAL_REFERENCE_SYSTEM" +#define SECTION_HORIZONTAL "HORIZONTAL" +#define KEY_HorizontalSystemIdentifier "HorizontalSystemIdentifier" +#define SECTION_TAULA_PRINCIPAL "TAULA_PRINCIPAL" +#define KEY_IdGrafic "IdGrafic" +#define KEY_TipusRelacio "TipusRelacio" +#define KEY_descriptor "descriptor" +#define KEY_HorizontalSystemDefinition "HorizontalSystemDefinition" +#define KEY_unitats "unitats" +#define KEY_unitatsY "unitatsY" +#define KEY_language "language" +#define KEY_Value_eng "eng" +#define KEY_MDIdiom "MDIdiom" +#define KEY_characterSet "characterSet" +#define KEY_Value_characterSet "006" + +// MiraMon feature field names +#define szMMNomCampIdGraficDefecte "ID_GRAFIC" +#define szMMNomCampPerimetreDefecte "PERIMETRE" +#define szMMNomCampAreaDefecte "AREA" +#define szMMNomCampLongitudArcDefecte "LONG_ARC" +#define szMMNomCampNodeIniDefecte "NODE_INI" +#define szMMNomCampNodeFiDefecte "NODE_FI" +#define szMMNomCampArcsANodeDefecte "ARCS_A_NOD" +#define szMMNomCampTipusNodeDefecte "TIPUS_NODE" +#define szMMNomCampNVertexsDefecte "N_VERTEXS" +#define szMMNomCampNArcsDefecte "N_ARCS" +#define szMMNomCampNPoligonsDefecte "N_POLIG" + +#define MAX_RELIABLE_SF_DOUBLE \ + 15 // Maximum nr. of reliable significant figures in any double. + +// Initial width of MiraMon fields +#define MM_MIN_WIDTH_ID_GRAFIC 3 +#define MM_MIN_WIDTH_N_VERTEXS 5 +#define MM_MIN_WIDTH_INITIAL_NODE MM_MIN_WIDTH_ID_GRAFIC + 1 +#define MM_MIN_WIDTH_FINAL_NODE MM_MIN_WIDTH_ID_GRAFIC + 1 +#define MM_MIN_WIDTH_ARCS_TO_NODE 1 +#define MM_MIN_WIDTH_LONG 14 // For LONG_ARC and PERIMETRE +#define MM_MIN_WIDTH_AREA 19 // For LONG_ARC and PERIMETRE + +#define MM_MIN_WIDTH_N_ARCS 2 +#define MM_MIN_WIDTH_N_POLIG 2 + +// Types of layers in MiraMon +#define MM_LayerType_Unknown 0 // Unknown type, or DBF alone +#define MM_LayerType_Point 1 // Layer of Points +#define MM_LayerType_Point3d 2 // Layer of 3D Points +#define MM_LayerType_Arc 3 // Layer of Arcs +#define MM_LayerType_Arc3d 4 // Layer of 3D Arcs +#define MM_LayerType_Pol 5 // Layer of Polygons +#define MM_LayerType_Pol3d 6 // Layer of 3D Polygons +#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 +#define MM_FIRST_NUMBER_OF_VERTICES 10000 +#define MM_INCR_NUMBER_OF_VERTICES 1000 + +#define MM_1MB 1048576 // 1 MB of buffer + +// Version asked for user +#define MM_UNKNOWN_VERSION 0 +#define MM_LAST_VERSION 1 +#define MM_32BITS_VERSION 2 +#define MM_64BITS_VERSION 3 + +// AddFeature returns +#define MM_CONTINUE_WRITING_FEATURES 0 +#define MM_FATAL_ERROR_WRITING_FEATURES 1 +#define MM_STOP_WRITING_FEATURES 2 + +// Size of the FID (and OFFSETS) in the current version +#define MM_SIZE_OF_FID_4BYTES_VERSION 4 +#define MM_SIZE_OF_FID_8BYTES_VERSION 8 + +/* Different values that first member of every PAL section element can take*/ +#define MM_EXTERIOR_ARC_SIDE 0x01 +#define MM_END_ARC_IN_RING 0x02 +#define MM_ROTATE_ARC 0x04 + +#define ARC_VRT_INICI 0 +#define ARC_VRT_FI 1 + +#define STATISTICAL_UNDEF_VALUE (2.9E+301) + +#define MAXIMUM_OBJECT_INDEX_IN_2GB_VECTORS UINT32_MAX //_UI32_MAX +#define MAXIMUM_OFFSET_IN_2GB_VECTORS UINT32_MAX //_UI32_MAX + +// Number of rings a polygon could have (it is just an initial approximation) +#define MM_MEAN_NUMBER_OF_RINGS 10 + +// Number of coordinates a feature could have (it is just an initial approximation) +#define MM_MEAN_NUMBER_OF_NCOORDS 100 +#define MM_MEAN_NUMBER_OF_COORDS 1000 + +// Initial and increment number of records and fields. +#define MM_INIT_NUMBER_OF_RECORDS 1 +#define MM_INC_NUMBER_OF_RECORDS 5 +#define MM_INIT_NUMBER_OF_FIELDS 20 +#define MM_INC_NUMBER_OF_FIELDS 10 + + enum FieldType { + /*! Numeric Field */ + MM_Numeric = 0, + /*! Character Fi eld */ + MM_Character = 1, + /*! Data Field */ MM_Data = + 2, + /*! Logic Field */ MM_Logic = + 3 + }; + +// Size of disk parts of the MiraMon vector format +// Common header +#define MM_HEADER_SIZE_32_BITS 48 +#define MM_HEADER_SIZE_64_BITS 64 + +// Points +#define MM_SIZE_OF_TL 16 + +// Nodes +#define MM_SIZE_OF_NH_32BITS 8 +#define MM_SIZE_OF_NH_64BITS 12 +#define MM_SIZE_OF_NL_32BITS 4 +#define MM_SIZE_OF_NL_64BITS 8 + +// Arcs +#define MM_SIZE_OF_AH_32BITS 56 +#define MM_SIZE_OF_AH_64BITS 72 +#define MM_SIZE_OF_AL 16 + +// Polygons +#define MM_SIZE_OF_PS_32BITS 8 +#define MM_SIZE_OF_PS_64BITS 16 +#define MM_SIZE_OF_PH_32BITS 64 +#define MM_SIZE_OF_PH_64BITS 80 +#define MM_SIZE_OF_PAL_32BITS 5 +#define MM_SIZE_OF_PAL_64BITS 9 + +// 3D part +#define MM_SIZE_OF_ZH 32 +#define MM_SIZE_OF_ZD_32_BITS 24 +#define MM_SIZE_OF_ZD_64_BITS 32 + +// Coordinates +#define MM_SIZE_OF_COORDINATE 16 + +// Recode in DBF's +#define MM_RECODE_UTF8 0 +#define MM_RECODE_ANSI 1 + +// Language in REL files: +// It is the language of the MiraMon generated descriptors. +// Metadata will not be translated but these descriptors are +// generated from scratch and it is good to use a custom language. +#define MM_DEF_LANGUAGE 0 +#define MM_ENG_LANGUAGE 1 // English language +#define MM_CAT_LANGUAGE 2 // Catalan language +#define MM_SPA_LANGUAGE 3 // Spanish language + +/* -------------------------------------------------------------------- */ +/* Structures */ +/* -------------------------------------------------------------------- */ +// Auxiliary structures +struct MMBoundingBox +{ + double dfMinX; + double dfMaxX; + double dfMinY; + double dfMaxY; +}; + +struct MM_POINT_2D +{ + double dfX; + double dfY; +}; + +struct ARC_VRT_STRUCTURE +{ + struct MM_POINT_2D vertice; + MM_BOOLEAN bIniFi; // boolean: 0=initial, 1=final + MM_INTERNAL_FID nIArc; // Internal arc index + MM_INTERNAL_FID nINod; // Internal node index, empty at the beginning */ +}; + +struct MM_VARIABLES_LLEGEIX_POLS +{ + size_t Nomb_Max_Coord; + size_t Bloc_Max_Coord; + size_t Nomb_Max_Coord_Z; + size_t Nomb_Max_avnp; + size_t Nomb_Max_Elem; + size_t Nomb_Max_vora_de_qui; +}; + +struct MM_FLUSH_INFO +{ + size_t nMyDiskSize; + GUInt64 NTimesFlushed; + + // Pointer to an OPEN file where to flush. + FILE_TYPE *pF; + // Offset in the disk where to flush + MM_FILE_OFFSET OffsetWhereToFlush; + + GUInt64 TotalSavedBytes; // Internal use + + // Block where to be saved + size_t SizeOfBlockToBeSaved; + void *pBlockToBeSaved; + + // Block where to save the pBlockToBeSaved or read from + void *pBlockWhereToSaveOrRead; + // Number of full bytes: flushed every time it is needed + GUInt64 nNumBytes; + // Number of bytes allocated: flushed every time it is needed + GUInt64 nBlockSize; + + // Internal Use + MM_FILE_OFFSET CurrentOffset; +}; + +// MIRAMON METADATA +struct MiraMonVectorMetaData +{ + char *szLayerTitle; + char *aLayerName; + char *aArcFile; // Polygon's arc name + int ePlainLT; // Plain layer type (no 3D specified): MM_LayerType_Point, + // MM_LayerType_Arc, MM_LayerType_Node, MM_LayerType_Pol + char *pSRS; // EPSG code of the spatial reference system. + char *pXUnit; // X units if pszSRS is empty. + char *pYUnit; // Y units if pszSRS is empty. If Y units is empty, + // X unit will be assigned as Y unit by default. + + struct MMBoundingBox hBB; // Bounding box of the entire layer + + // Pointer to a Layer DataBase, used to create MiraMon DBF (extended) file. + struct MiraMonDataBase *pLayerDB; + + // Language in REL files: + // It is the language of the MiraMon generated descriptors. + // Metadata will not be translated but these descriptors are + // generated from scratch and it is good to use a custom language. + char nMMLanguage; +}; + +// MIRAMON DATA BASE +#define MM_GRAPHICAL_ID_INIT_SIZE 5 +#define MM_N_VERTEXS_INIT_SIZE 12 +#define MM_LONG_ARC_INIT_SIZE 12 +#define MM_LONG_ARC_DECIMALS_SIZE 6 +#define MM_NODE_INI_INIT_SIZE 5 +#define MM_NODE_FI_INIT_SIZE 5 + +#define MM_PERIMETRE_INIT_SIZE 13 +#define MM_PERIMETRE_DECIMALS_SIZE 6 +#define MM_AREA_INIT_SIZE 14 +#define MM_AREA_DECIMALS_SIZE 6 + +#define MM_N_ARCS_INIT_SIZE 3 +#define MM_N_ARCS_DECIMALS_SIZE 3 + +#define MM_ARCS_A_NOD_INIT_SIZE 1 + +struct MiraMonFieldValue +{ + MM_BOOLEAN bIsValid; // If 1 the value is filled. If 0, there is no value. +#define MM_INIT_STRING_FIELD_VALUE 50000 // Never less than 10 + MM_EXT_DBF_N_FIELDS nNumDinValue; // Size of the reserved string value + char *pDinValue; // Used if MM_MAX_STRING_FIELD_VALUE is not enough + double dValue; // For double and 32 bit integer numeric values + GInt64 iValue; // For 64 bit integer values. + //MM_BOOLEAN kbValue; // For binary values. +}; + +struct MiraMonRecord +{ + MM_EXT_DBF_N_FIELDS nMaxField; // Number of reserved fields + MM_EXT_DBF_N_FIELDS nNumField; // Number of fields + struct MiraMonFieldValue *pField; // Value of the fields. +}; + +struct MiraMonDataBaseField +{ + char pszFieldName[MM_MAX_LON_FIELD_NAME_DBF + 1]; + char pszFieldDescription[MM_MAX_BYTES_FIELD_DESC + 1]; + enum FieldType eFieldType; // See enum FieldType + GUInt32 nFieldSize; // MM_MAX_BYTES_IN_A_FIELD_EXT as maximum + GUInt32 nNumberOfDecimals; // MM_MAX_BYTES_IN_A_FIELD_EXT as maximum + MM_BOOLEAN bIs64BitInteger; // For 64 bits integer fields +}; + +struct MiraMonDataBase +{ + MM_EXT_DBF_N_FIELDS nNFields; + struct MiraMonDataBaseField *pFields; +}; + +struct MMAdmDatabase +{ + // MiraMon table (extended DBF) + // Name of the extended DBF file + char pszExtDBFLayerName[MM_CPL_PATH_BUF_SIZE]; + // Pointer to the extended DBF file + FILE_TYPE *pFExtDBF; + // Pointer to a MiraMon table (auxiliary) + struct MM_DATA_BASE_XP *pMMBDXP; + // How to write all it to disk + struct MM_FLUSH_INFO FlushRecList; + char *pRecList; // Records list // (II mode) + + // Temporary space where to mount the DBF record. + // Reused every time a feature is created + GUInt64 nNumRecordOnCourse; + char *szRecordOnCourse; +}; + +struct MM_ID_GRAFIC_MULTIPLE_RECORD +{ + MM_FILE_OFFSET offset; + MM_EXT_DBF_N_MULTIPLE_RECORDS + nMR; // Determines the number of the list (multiple record) +}; + +// MIRAMON GEOMETRY + +// Top Header section +struct MM_TH +{ + char aLayerVersion[2]; + char aLayerSubVersion; + + char aFileType[3]; // (PNT, ARC, NOD, POL) + + unsigned short int bIs3d; + unsigned short int bIsMultipolygon; // Only apply to polygons + + unsigned char Flag; // 1 byte: defined at DefTopMM.H + struct MMBoundingBox hBB; + MM_INTERNAL_FID nElemCount; // 4/8 bytes depending on the version + // 8/4 reserved bytes depending on the version +}; + +// Z Header (32 bytes) +struct MM_ZH +{ + size_t nMyDiskSize; + // 16 bytes reserved + double dfBBminz; // 8 bytes Minimum Z + double dfBBmaxz; // 8 bytes Maximum Z +}; + +// Z Description +struct MM_ZD +{ + double dfBBminz; // 8 bytes Minimum Z + double dfBBmaxz; // 8 bytes Maximum Z + GInt32 nZCount; // 4 bytes (signed) + // 4 bytes reserved (only in version 2.0) + MM_FILE_OFFSET nOffsetZ; // 4 or 8 bytes depending on the version +}; + +struct MM_ZSection +{ + // Offset where the section begins in disk. It is a precalculated value + // using nElemCount from LayerInfo. TH+n*CL + MM_FILE_OFFSET ZSectionOffset; + struct MM_ZH ZHeader; // (I mode) + + // Number of pZDescription allocated + // nMaxZDescription = nElemCount from LayerInfo + MM_FILE_OFFSET ZDOffset; + size_t nZDDiskSize; + GUInt64 nMaxZDescription; + struct MM_ZD *pZDescription; //(I mode) + + struct MM_FLUSH_INFO FlushZL; + char *pZL; // (II mode) +}; + +// Header of Arcs +struct MM_AH +{ + struct MMBoundingBox dfBB; + MM_N_VERTICES_TYPE nElemCount; // 4/8 bytes depending on the version + MM_FILE_OFFSET nOffset; // 4/8 bytes depending on the version + MM_INTERNAL_FID nFirstIdNode; // 4/8 bytes depending on the version + MM_INTERNAL_FID nLastIdNode; // 4/8 bytes depending on the version + double dfLength; +}; + +// Header of Nodes +struct MM_NH +{ + short int nArcsCount; + char cNodeType; + // 1 reserved byte + MM_FILE_OFFSET nOffset; // 4/8 bytes depending on the version +}; + +// Header of Polygons +struct MM_PH +{ + // Common Arc & Polyons section + struct MMBoundingBox dfBB; + MM_POLYGON_ARCS_COUNT nArcsCount; // 4/8 bytes depending on the version + MM_POLYGON_RINGS_COUNT + nExternalRingsCount; // 4/8 bytes depending on the version + MM_POLYGON_RINGS_COUNT nRingsCount; // 4/8 bytes depending on the version + MM_FILE_OFFSET nOffset; // 4/8 bytes depending on the version + double dfPerimeter; + double dfArea; + //struct GEOMETRIC_I_TOPOLOGIC_POL GeoTopoPol; +}; + +struct MM_PAL_MEM +{ + unsigned char VFG; + MM_INTERNAL_FID nIArc; // 4/8 bytes depending on the version +}; + +/* Every MiraMon file is composed as is specified in documentation. + Here are the structures to every file where we can find two ways + of keeping the information in memory (to be, finally, flushed to the disk) + * (I mode) Pointers to structs that keep information that changes every + time a feature is added. They will be written at the end to the disk. + * (II mode) Memory blocks that are used as buffer blocks to store + information that is going to be flushed (as are) to the disk + periodically instead of writing them to the disk every time a Feature + is added (not efficient). The place where they are going to be flushed + depends on one variable: the number of elements of the layer. +*/ + +// MiraMon Point Layer: TH, List of CL (coordinates), ZH, ZD, ZL +struct MiraMonPointLayer +{ + // Name of the layer with extension + char pszLayerName[MM_CPL_PATH_BUF_SIZE]; + FILE_TYPE *pF; + + // Coordinates x,y of the points + struct MM_FLUSH_INFO FlushTL; + char *pTL; // (II mode) + char pszTLName[MM_CPL_PATH_BUF_SIZE]; // Temporary file where to flush + FILE_TYPE *pFTL; // Pointer to temporary file where to flush + + // Z section + // Temporary file where the Z coordinates are stored + // if necessary + char psz3DLayerName[MM_CPL_PATH_BUF_SIZE]; + FILE_TYPE *pF3d; + struct MM_ZSection pZSection; + + // MiraMon table (extended DBF) + struct MMAdmDatabase MMAdmDB; + + // Metadata name + char pszREL_LayerName[MM_CPL_PATH_BUF_SIZE]; +}; + +struct MiraMonNodeLayer +{ + char + pszLayerName[MM_CPL_PATH_BUF_SIZE]; // Name of the layer with extension + FILE_TYPE *pF; + + // Header of every node + GUInt32 nSizeNodeHeader; + MM_INTERNAL_FID nMaxNodeHeader; // Number of pNodeHeader allocated + struct MM_NH *pNodeHeader; // (I mode) + + // NL: arcs confuent to node + struct MM_FLUSH_INFO FlushNL; // (II mode) + char *pNL; // + char pszNLName[MM_CPL_PATH_BUF_SIZE]; // Temporary file where to flush + FILE_TYPE *pFNL; // Pointer to temporary file where to flush + + struct MMAdmDatabase MMAdmDB; + + // Metadata name + char pszREL_LayerName[MM_CPL_PATH_BUF_SIZE]; +}; + +struct MiraMonArcLayer +{ + char + pszLayerName[MM_CPL_PATH_BUF_SIZE]; // Name of the layer with extension + FILE_TYPE *pF; + + // Temporal file where the Z coordinates are stored + // if necessary + char psz3DLayerName[MM_CPL_PATH_BUF_SIZE]; + FILE_TYPE *pF3d; + + // Header of every arc + GUInt32 nSizeArcHeader; + MM_INTERNAL_FID nMaxArcHeader; // Number of allocated pArcHeader + struct MM_AH *pArcHeader; // (I mode) + + // AL Section + struct MM_FLUSH_INFO FlushAL; + unsigned short int nALElementSize; // 16 bytes: 2 doubles (coordinates) + char *pAL; // Arc List // (II mode) + char pszALName[MM_CPL_PATH_BUF_SIZE]; // Temporary file where to flush + FILE_TYPE *pFAL; // Pointer to temporary file where to flush + + // Z section + struct MM_ZSection pZSection; + + // Node layer associated to the arc layer + struct MM_TH TopNodeHeader; + struct MiraMonNodeLayer MMNode; + + // Private data + GUInt64 nMaxArcVrt; // Number of allocated + struct ARC_VRT_STRUCTURE *pArcVrt; + MM_FILE_OFFSET nOffsetArc; // It is an auxiliary offset + + struct MMAdmDatabase MMAdmDB; + + // Metadata name + char pszREL_LayerName[MM_CPL_PATH_BUF_SIZE]; +}; + +struct MiraMonPolygonLayer +{ + char + pszLayerName[MM_CPL_PATH_BUF_SIZE]; // Name of the layer with extension + FILE_TYPE *pF; + + // PS part + struct MM_FLUSH_INFO FlushPS; + unsigned short int nPSElementSize; + char *pPS; // Polygon side (II mode) + char pszPSName[MM_CPL_PATH_BUF_SIZE]; // Temporary file where to flush + FILE_TYPE *pFPS; // Pointer to temporary file where to flush + + // Header of every polygon + MM_INTERNAL_FID nMaxPolHeader; // Number of pPolHeader allocated + unsigned short int nPHElementSize; + struct MM_PH *pPolHeader; // (I mode) + + // PAL + struct MM_FLUSH_INFO FlushPAL; + unsigned short int nPALElementSize; + char *pPAL; // Polygon Arc List // (II mode) + char pszPALName[MM_CPL_PATH_BUF_SIZE]; // Temporary file where to flush + FILE_TYPE *pFPAL; // Pointer to temporary file where to flush + + // Arc layer associated to the arc layer + struct MM_TH TopArcHeader; + struct MiraMonArcLayer MMArc; + + struct MMAdmDatabase MMAdmDB; + + // Metadata name + char pszREL_LayerName[MM_CPL_PATH_BUF_SIZE]; +}; + +/* +#define MM_VECTOR_LAYER_LAST_VERSION 1 +#define CheckMMVectorLayerVersion(a, r) \ + { \ + if ((a)->Version != MM_VECTOR_LAYER_LAST_VERSION) \ + return (r); \ + } +*/ + +// Information that allows to reuse memory stuff when +// features are being read +struct MiraMonFeature +{ + // Number of parts + MM_POLYGON_RINGS_COUNT nNRings; // =1 for lines and points + MM_POLYGON_RINGS_COUNT nIRing; // The ring is being processed + + // Number of reserved elements in *pNCoord (a vector with number of vertices in each ring) + MM_N_VERTICES_TYPE nMaxpNCoordRing; + MM_N_VERTICES_TYPE *pNCoordRing; // [0]=1 for lines and points + + // Number of reserved elements in *pCoord + MM_N_VERTICES_TYPE nMaxpCoord; + // Number of used elements in *pCoord (only for reading features) + MM_N_VERTICES_TYPE nNumpCoord; + // Coordinate index that is being processed + MM_N_VERTICES_TYPE nICoord; + // List of the coordinates of the feature + struct MM_POINT_2D *pCoord; + + // Number of reserved elements in *flag_VFG + MM_INTERNAL_FID nMaxVFG; + char *flag_VFG; // In case of multipolygons, for each ring: + // if flag_VFG[i]|MM_EXTERIOR_ARC_SIDE: outer ring if set + // if flag_VFG[i]|MM_END_ARC_IN_RING: always set (every ring has only + // one arc) + // if flag_VFG[i]|MM_ROTATE_ARC: coordinates are in the inverse order + // of the read ones + + // List of the Z-coordinates (as many as pCoord) + // Number of reserved elements in *pZCoord + MM_N_VERTICES_TYPE nMaxpZCoord; + // Number of used elements in *pZCoord + MM_N_VERTICES_TYPE nNumpZCoord; + MM_COORD_TYPE *pZCoord; + + // Records of the feature + MM_EXT_DBF_N_MULTIPLE_RECORDS nNumMRecords; + // Number of reserved elements in *pRecords + MM_EXT_DBF_N_MULTIPLE_RECORDS nMaxMRecords; + struct MiraMonRecord *pRecords; + + // Number of features just processed (for writing) + MM_INTERNAL_FID nReadFeatures; +}; + +// There is the possibility of creating a map with all layers +// to visualize it with only one click +struct MiraMonVectMapInfo +{ + char pszMapName[MM_CPL_PATH_BUF_SIZE]; + FILE_TYPE *fMMMap; + int nNumberOfLayers; +}; + +// MIRAMON OBJECT: Contains everything +struct MiraMonVectLayerInfo +{ + // Version of the structure + //GUInt32 Version; + + // Version of the layer + // MM_32BITS_LAYER_VERSION: less than 2 Gbyte files + // MM_64BITS_LAYER_VERSION: more than 2 Gbyte files + char LayerVersion; + + // Layer name + char *pszSrcLayerName; + + // Layer title in metadata + char *szLayerTitle; + + // Pointer to the main REL name (do not free it) + char *pszMainREL_LayerName; + +// To know if we are writing or reading +#define MM_READING_MODE 0 // Reading MiraMon layer +#define MM_WRITING_MODE 1 // Writing MiraMon layer + MM_BOOLEAN ReadOrWrite; + + char pszFlags[10]; // To Open the file + unsigned short int bIsPolygon; + unsigned short int bIsArc; // Also 1 in a polygon layer + unsigned short int bIsNode; // Not used in GDAL + unsigned short int bIsPoint; + unsigned short int bIsDBF; // When there is no geometry + + // In writing mode when one of the features is 3D, the MM layer will be 3D, + // but if none of the features are 3D, then the layer will not be 3D. + unsigned short int bIsReal3d; + + // Final number of elements of the layer. + MM_INTERNAL_FID nFinalElemCount; // Real element count after conversion + + // Header of the layer + size_t nHeaderDiskSize; + struct MM_TH TopHeader; + + int eLT; // Type of layer: Point, line or polygon (3D or not) + int bIsBeenInit; // 1 if layer has already been initialized + + // Point layer + struct MiraMonPointLayer MMPoint; + + // Arc layer + struct MiraMonArcLayer MMArc; + + // Polygon layer + struct MiraMonPolygonLayer MMPolygon; + + // Offset used to write features. + MM_FILE_OFFSET OffsetCheck; + + // EPSG code of the spatial reference system. + char *pSRS; + int nSRS_EPSG; // Ref. system if has EPSG code. + + // In GDAL->MiraMon sense: + // Transformed table from input layer to a MiraMon table. + // This table has to be merged with private MiraMon fields to obtain + // a MiraMon extended DBF + struct MiraMonDataBase *pLayerDB; + + // In MiraMon->GDAL sense: + // MiraMon extended DBF header + // In GDAL->MiraMon, used when there is no geometry + struct MM_DATA_BASE_XP *pMMBDXP; + + // In GDAL->MiraMon, used when there is no geometry + struct MMAdmDatabase MMAdmDBWriting; + + // Offset of every FID in the table + MM_BOOLEAN + isListField; // It determines if fields are list or simple (multirecord). + MM_EXT_DBF_N_RECORDS + nMaxN; // Max number of elements in a field features list + struct MM_ID_GRAFIC_MULTIPLE_RECORD *pMultRecordIndex; +// In case of multirecord, if user wants only one Record 'iMultiRecord' +// specifies which one: 0, 1, 2,... or "Last". There is also the "JSON" option +// that writes a serialized JSON array like (``[1,2]``). +#define MM_MULTIRECORD_LAST -1 +#define MM_MULTIRECORD_NO_MULTIRECORD -2 +#define MM_MULTIRECORD_JSON -3 + int iMultiRecord; + + // Charset of DBF files (same for all) when writing it. + // MM_JOC_CARAC_UTF8_DBF + // MM_JOC_CARAC_ANSI_DBASE; + MM_BYTE nCharSet; + + // Language in REL files: + // It is the language of the MiraMon generated descriptors. + // Metadata will not be translated but these descriptors are + // generated from scratch and it is good to use a custom language. + char nMMLanguage; + + // This is used only to write temporary stuff + char szNFieldAux[MM_MAX_AMPLADA_CAMP_N_DBF]; + // Dynamic string that is used as temporary buffer + // with variable size as needed. Its value is + // highly temporary. Copy in a safe place to save its value. + GUInt64 nNumStringToOperate; + char *szStringToOperate; + + // Temporary elements when reading features from MiraMon files + struct MiraMonFeature ReadFeature; + + MM_SELEC_COORDZ_TYPE nSelectCoordz; // MM_SELECT_FIRST_COORDZ + // MM_SELECT_HIGHEST_COORDZ + // MM_SELECT_LOWEST_COORDZ + + // For polygon layers this is an efficient space to read + // the PAL section + MM_POLYGON_ARCS_COUNT nMaxArcs; + MM_POLYGON_ARCS_COUNT nNumArcs; + struct MM_PAL_MEM *pArcs; + + struct MM_FLUSH_INFO FlushPAL; + + struct MiraMonVectMapInfo *MMMap; // Do not free +}; + +enum DataType +{ + MMDTByte, + MMDTInteger, + MMDTuInteger, + MMDTLong, + MMDTReal, + MMDTDouble, + MMDT4bits +}; + +enum TreatmentVariable +{ + MMTVQuantitativeContinuous, + MMTVOrdinal, + MMTVCategorical +}; + +#ifdef GDAL_COMPILATION +CPL_C_END // Necessary for compiling in GDAL project +#endif +#endif //__MM_GDAL_DRIVER_STRUCTS_H diff --git a/ogr/ogrsf_frmts/miramon/mm_gdal_functions.c b/ogr/ogrsf_frmts/miramon/mm_gdal_functions.c new file mode 100644 index 000000000000..4ce989685b9c --- /dev/null +++ b/ogr/ogrsf_frmts/miramon/mm_gdal_functions.c @@ -0,0 +1,2906 @@ +/****************************************************************************** + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: C MiraMon code adapted to be used in GDAL + * Author: Abel Pau, a.pau@creaf.uab.cat, based on the MiraMon codes, + * mainly written by Xavier Pons, Joan Maso (correctly written + * "Mas0xF3"), Abel Pau, Nuria Julia (N0xFAria Juli0xE0), + * Xavier Calaf, Lluis (Llu0xEDs) Pesquer and Alaitz Zabala, from + * CREAF and Universitat Autonoma (Aut0xF2noma) de Barcelona. + * For a complete list of contributors: + * https://www.miramon.cat/eng/QuiSom.htm + ****************************************************************************** + * Copyright (c) 2024, Xavier Pons + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifdef GDAL_COMPILATION +#include "ogr_api.h" // For CPL_C_START +#include "mm_gdal_functions.h" // For CPLStrlcpy() +#include "mm_wrlayr.h" // For calloc_function()... +#else +#include "CmptCmp.h" +#include "mm_gdal\mm_gdal_functions.h" // For CPLStrlcpy() +#include "mm_gdal\mm_wrlayr.h" // For calloc_function()... +#endif // GDAL_COMPILATION + +#ifdef GDAL_COMPILATION +CPL_C_START // Necessary for compiling in GDAL project +#include "cpl_string.h" // For CPL_ENC_UTF8 +#else +#ifdef _WIN64 +#include "gdal\release-1911-x64\cpl_string.h" // For CPL_ENC_UTF8 +#else +#include "gdal\release-1911-32\cpl_string.h" // For CPL_ENC_UTF8szNumberOfVerticesEsp +#endif +#endif + + char szInternalGraphicIdentifierEng[MM_MAX_IDENTIFIER_SIZE]; +char szInternalGraphicIdentifierCat[MM_MAX_IDENTIFIER_SIZE]; +char szInternalGraphicIdentifierSpa[MM_MAX_IDENTIFIER_SIZE]; + +char szNumberOfVerticesEng[MM_MAX_IDENTIFIER_SIZE]; +char szNumberOfVerticesCat[MM_MAX_IDENTIFIER_SIZE]; +char szNumberOfVerticesSpa[MM_MAX_IDENTIFIER_SIZE]; + +char szLengthOfAarcEng[MM_MAX_IDENTIFIER_SIZE]; +char szLengthOfAarcCat[MM_MAX_IDENTIFIER_SIZE]; +char szLengthOfAarcSpa[MM_MAX_IDENTIFIER_SIZE]; + +char szInitialNodeEng[MM_MAX_IDENTIFIER_SIZE]; +char szInitialNodeCat[MM_MAX_IDENTIFIER_SIZE]; +char szInitialNodeSpa[MM_MAX_IDENTIFIER_SIZE]; + +char szFinalNodeEng[MM_MAX_IDENTIFIER_SIZE]; +char szFinalNodeCat[MM_MAX_IDENTIFIER_SIZE]; +char szFinalNodeSpa[MM_MAX_IDENTIFIER_SIZE]; + +char szNumberOfArcsToNodeEng[MM_MAX_IDENTIFIER_SIZE]; +char szNumberOfArcsToNodeCat[MM_MAX_IDENTIFIER_SIZE]; +char szNumberOfArcsToNodeSpa[MM_MAX_IDENTIFIER_SIZE]; + +char szNodeTypeEng[MM_MAX_IDENTIFIER_SIZE]; +char szNodeTypeCat[MM_MAX_IDENTIFIER_SIZE]; +char szNodeTypeSpa[MM_MAX_IDENTIFIER_SIZE]; + +char szPerimeterOfThePolygonEng[MM_MAX_IDENTIFIER_SIZE]; +char szPerimeterOfThePolygonCat[MM_MAX_IDENTIFIER_SIZE]; +char szPerimeterOfThePolygonSpa[MM_MAX_IDENTIFIER_SIZE]; + +char szAreaOfThePolygonEng[MM_MAX_IDENTIFIER_SIZE]; +char szAreaOfThePolygonCat[MM_MAX_IDENTIFIER_SIZE]; +char szAreaOfThePolygonSpa[MM_MAX_IDENTIFIER_SIZE]; + +char szNumberOfArcsEng[MM_MAX_IDENTIFIER_SIZE]; +char szNumberOfArcsCat[MM_MAX_IDENTIFIER_SIZE]; +char szNumberOfArcsSpa[MM_MAX_IDENTIFIER_SIZE]; + +char szNumberOfElementaryPolygonsEng[MM_MAX_IDENTIFIER_SIZE]; +char szNumberOfElementaryPolygonsCat[MM_MAX_IDENTIFIER_SIZE]; +char szNumberOfElementaryPolygonsSpa[MM_MAX_IDENTIFIER_SIZE]; + +void MM_FillFieldDescriptorByLanguage(void) +{ + CPLStrlcpy(szInternalGraphicIdentifierEng, "Internal Graphic identifier", + MM_MAX_IDENTIFIER_SIZE); + CPLStrlcpy(szInternalGraphicIdentifierCat, "Identificador Grafic intern", + MM_MAX_IDENTIFIER_SIZE); + *(unsigned char *)&szInternalGraphicIdentifierCat[16] = MM_a_WITH_GRAVE; + CPLStrlcpy(szInternalGraphicIdentifierSpa, "Identificador Grafico interno", + MM_MAX_IDENTIFIER_SIZE); + *(unsigned char *)&szInternalGraphicIdentifierSpa[16] = MM_a_WITH_ACUTE; + + CPLStrlcpy(szNumberOfVerticesEng, "Number of vertices", + MM_MAX_IDENTIFIER_SIZE); + CPLStrlcpy(szNumberOfVerticesCat, "Nombre de vertexs", + MM_MAX_IDENTIFIER_SIZE); + CPLStrlcpy(szNumberOfVerticesSpa, "Numero de vertices", + MM_MAX_IDENTIFIER_SIZE); + *(unsigned char *)&szNumberOfVerticesSpa[1] = MM_u_WITH_ACUTE; + *(unsigned char *)&szNumberOfVerticesSpa[11] = MM_e_WITH_ACUTE; + + CPLStrlcpy(szLengthOfAarcEng, "Length of arc", MM_MAX_IDENTIFIER_SIZE); + CPLStrlcpy(szLengthOfAarcCat, "Longitud de l'arc", MM_MAX_IDENTIFIER_SIZE); + CPLStrlcpy(szLengthOfAarcSpa, "Longitud del arco", MM_MAX_IDENTIFIER_SIZE); + + CPLStrlcpy(szInitialNodeEng, "Initial node", MM_MAX_IDENTIFIER_SIZE); + CPLStrlcpy(szInitialNodeCat, "Node inicial", MM_MAX_IDENTIFIER_SIZE); + CPLStrlcpy(szInitialNodeSpa, "Nodo inicial", MM_MAX_IDENTIFIER_SIZE); + + CPLStrlcpy(szFinalNodeEng, "Final node", MM_MAX_IDENTIFIER_SIZE); + CPLStrlcpy(szFinalNodeCat, "Node final", MM_MAX_IDENTIFIER_SIZE); + CPLStrlcpy(szFinalNodeSpa, "Nodo final", MM_MAX_IDENTIFIER_SIZE); + + CPLStrlcpy(szNumberOfArcsToNodeEng, "Number of arcs to node", + MM_MAX_IDENTIFIER_SIZE); + CPLStrlcpy(szNumberOfArcsToNodeCat, "Nombre d'arcs al node", + MM_MAX_IDENTIFIER_SIZE); + CPLStrlcpy(szNumberOfArcsToNodeSpa, "Numero de arcos al nodo", + MM_MAX_IDENTIFIER_SIZE); + *(unsigned char *)&szNumberOfArcsToNodeSpa[1] = MM_u_WITH_ACUTE; + + CPLStrlcpy(szNodeTypeEng, "Node type", MM_MAX_IDENTIFIER_SIZE); + CPLStrlcpy(szNodeTypeCat, "Tipus de node", MM_MAX_IDENTIFIER_SIZE); + CPLStrlcpy(szNodeTypeSpa, "Tipo de nodo", MM_MAX_IDENTIFIER_SIZE); + + CPLStrlcpy(szPerimeterOfThePolygonEng, "Perimeter of the polygon", + MM_MAX_IDENTIFIER_SIZE); + CPLStrlcpy(szPerimeterOfThePolygonCat, "Perimetre del poligon", + MM_MAX_IDENTIFIER_SIZE); + CPLStrlcpy(szPerimeterOfThePolygonSpa, "Perimetro del poligono", + MM_MAX_IDENTIFIER_SIZE); + + *(unsigned char *)&szPerimeterOfThePolygonCat[3] = MM_i_WITH_ACUTE; + *(unsigned char *)&szPerimeterOfThePolygonSpa[3] = MM_i_WITH_ACUTE; + *(unsigned char *)&szPerimeterOfThePolygonCat[17] = MM_i_WITH_ACUTE; + *(unsigned char *)&szPerimeterOfThePolygonSpa[17] = MM_i_WITH_ACUTE; + + CPLStrlcpy(szAreaOfThePolygonEng, "Area of the polygon", + MM_MAX_IDENTIFIER_SIZE); + CPLStrlcpy(szAreaOfThePolygonCat, "Area del poligon", + MM_MAX_IDENTIFIER_SIZE); + CPLStrlcpy(szAreaOfThePolygonSpa, "Area del poligono", + MM_MAX_IDENTIFIER_SIZE); + + *(unsigned char *)&szAreaOfThePolygonCat[0] = MM_A_WITH_GRAVE; + *(unsigned char *)&szAreaOfThePolygonSpa[0] = MM_A_WITH_ACUTE; + *(unsigned char *)&szAreaOfThePolygonCat[12] = MM_i_WITH_ACUTE; + *(unsigned char *)&szAreaOfThePolygonSpa[12] = MM_i_WITH_ACUTE; + + CPLStrlcpy(szNumberOfArcsEng, "Number of arcs", MM_MAX_IDENTIFIER_SIZE); + CPLStrlcpy(szNumberOfArcsCat, "Nombre d'arcs", MM_MAX_IDENTIFIER_SIZE); + CPLStrlcpy(szNumberOfArcsSpa, "Numero de arcos", MM_MAX_IDENTIFIER_SIZE); + + *(unsigned char *)&szNumberOfArcsSpa[1] = MM_u_WITH_ACUTE; + + CPLStrlcpy(szNumberOfElementaryPolygonsEng, "Number of elementary polygons", + MM_MAX_IDENTIFIER_SIZE); + CPLStrlcpy(szNumberOfElementaryPolygonsCat, "Nombre de poligons elementals", + MM_MAX_IDENTIFIER_SIZE); + CPLStrlcpy(szNumberOfElementaryPolygonsSpa, + "Numero de poligonos elementales", MM_MAX_IDENTIFIER_SIZE); + + *(unsigned char *)&szNumberOfElementaryPolygonsSpa[1] = MM_u_WITH_ACUTE; + *(unsigned char *)&szNumberOfElementaryPolygonsCat[13] = MM_i_WITH_ACUTE; + *(unsigned char *)&szNumberOfElementaryPolygonsSpa[13] = MM_i_WITH_ACUTE; +} + +const char *MM_pszLogFilename = nullptr; + +// Logging +const char *MMLog(const char *pszMsg, int nLineNumber) +{ + FILE *f; + + if (MM_pszLogFilename == nullptr) + return pszMsg; + f = fopen(MM_pszLogFilename, "at"); + if (f == nullptr) + return pszMsg; + fprintf(f, "%d: %s\n", nLineNumber, pszMsg); /*ok*/ + fclose(f); + return pszMsg; +} + +static const char MM_EmptyString[] = {""}; +#define MM_SetEndOfString (*MM_EmptyString) +static const char MM_BlankString[] = {" "}; + +void fclose_and_nullify(FILE_TYPE **pFunc) +{ + if (!pFunc || !(*pFunc)) + return; + fclose_function(*pFunc); + *pFunc = nullptr; +} + +// CREATING AN EXTENDED MIRAMON DBF +void MM_InitializeField(struct MM_FIELD *pField) +{ + memset(pField, '\0', sizeof(*pField)); + pField->FieldType = 'C'; + pField->GeoTopoTypeField = MM_NO_ES_CAMP_GEOTOPO; +} + +#define MM_ACCEPTABLE_NUMBER_OF_FIELDS 20000 + +struct MM_FIELD *MM_CreateAllFields(MM_EXT_DBF_N_FIELDS nFields) +{ + struct MM_FIELD *camp; + MM_EXT_DBF_N_FIELDS i; + + // MiraMon could accept a number of fields 13.4 million + // but GDAL prefers to limit that to 20.000 to avoid + // too large memory allocation attempts with corrupted datasets + if (nFields > MM_ACCEPTABLE_NUMBER_OF_FIELDS) + { + MMCPLError(CE_Failure, CPLE_OutOfMemory, + "More than 20000 fields not accepted"); + return nullptr; + } + +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + if (nFields >= UINT32_MAX / sizeof(*camp)) + return nullptr; +#else + if (nFields >= (1000U * 1000 * 1000) / sizeof(*camp)) + return nullptr; +#endif + + if ((camp = calloc_function(nFields * sizeof(*camp))) == nullptr) + return nullptr; + + for (i = 0; i < nFields; i++) + MM_InitializeField(camp + i); + return camp; +} + +static struct MM_DATA_BASE_XP *MM_CreateEmptyHeader(MM_EXT_DBF_N_FIELDS nFields) +{ + struct MM_DATA_BASE_XP *data_base_XP; + + if ((data_base_XP = (struct MM_DATA_BASE_XP *)calloc_function( + sizeof(struct MM_DATA_BASE_XP))) == nullptr) + return nullptr; + + if (nFields == 0) + { + ; + } + else + { + data_base_XP->pField = (struct MM_FIELD *)MM_CreateAllFields(nFields); + if (!data_base_XP->pField) + { + free_function(data_base_XP); + return nullptr; + } + } + data_base_XP->nFields = nFields; + return data_base_XP; +} + +struct MM_DATA_BASE_XP *MM_CreateDBFHeader(MM_EXT_DBF_N_FIELDS n_camps, + MM_BYTE charset) +{ + struct MM_DATA_BASE_XP *bd_xp; + struct MM_FIELD *camp; + MM_EXT_DBF_N_FIELDS i; + + if (nullptr == (bd_xp = MM_CreateEmptyHeader(n_camps))) + return nullptr; + + bd_xp->CharSet = charset; + + strcpy(bd_xp->ReadingMode, "a+b"); + + bd_xp->IdGraficField = n_camps; + bd_xp->IdEntityField = MM_MAX_EXT_DBF_N_FIELDS_TYPE; + bd_xp->dbf_version = (MM_BYTE)((n_camps > MM_MAX_N_CAMPS_DBF_CLASSICA) + ? MM_MARCA_VERSIO_1_DBF_ESTESA + : MM_MARCA_DBASE4); + + for (i = 0, camp = bd_xp->pField; i < n_camps; i++, camp++) + { + MM_InitializeField(camp); + if (i < 99999) + snprintf(camp->FieldName, sizeof(camp->FieldName), "CAMP%05u", + (unsigned)(i + 1)); + else + snprintf(camp->FieldName, sizeof(camp->FieldName), "CM%u", + (unsigned)(i + 1)); + camp->FieldType = 'C'; + camp->DecimalsIfFloat = 0; + camp->BytesPerField = 50; + } + return bd_xp; +} + +MM_BYTE MM_DBFFieldTypeToVariableProcessing(MM_BYTE tipus_camp_DBF) +{ + switch (tipus_camp_DBF) + { + case 'N': + return MM_QUANTITATIVE_CONTINUOUS_FIELD; + case 'D': + case 'C': + case 'L': + return MM_CATEGORICAL_FIELD; + } + return MM_CATEGORICAL_FIELD; +} + +static MM_BYTE MM_GetDefaultDesiredDBFFieldWidth(const struct MM_FIELD *camp) +{ + size_t a, b, c, d, e; + + b = strlen(camp->FieldName); + c = strlen(camp->FieldDescription[0]); + + if (camp->FieldType == 'D') + { + d = (b > c ? b : c); + a = (size_t)camp->BytesPerField + 2; + return (MM_BYTE)(a > d ? a : d); + } + a = camp->BytesPerField; + d = (unsigned int)(b > c ? b : c); + e = (a > d ? a : d); + return (MM_BYTE)(e < 80 ? e : 80); +} + +static MM_BOOLEAN MM_is_field_name_lowercase(const char *cadena) +{ + const char *p; + + for (p = cadena; *p; p++) + { + if ((*p >= 'a' && *p <= 'z')) + return TRUE; + } + return FALSE; +} + +static MM_BOOLEAN +MM_Is_classical_DBF_field_name_or_lowercase(const char *cadena) +{ + const char *p; + + for (p = cadena; *p; p++) + { + if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || + (*p >= '0' && *p <= '9') || *p == '_') + ; + else + return FALSE; + } + if (cadena[0] == '_') + return FALSE; + return TRUE; +} + +static MM_BOOLEAN +MM_Is_character_valid_for_extended_DBF_field_name(int valor, + int *valor_substitut) +{ + if (valor_substitut) + { + switch (valor) + { + case 32: + *valor_substitut = '_'; + return FALSE; + case 91: + *valor_substitut = '('; + return FALSE; + case 93: + *valor_substitut = ')'; + return FALSE; + case 96: + *valor_substitut = '\''; + return FALSE; + case 127: + *valor_substitut = '_'; + return FALSE; + case 168: + *valor_substitut = '-'; + return FALSE; + } + } + else + { + if (valor < 32 || valor == 91 || valor == 93 || valor == 96 || + valor == 127 || valor == 168) + return FALSE; + } + return TRUE; +} + +static int MM_ISExtendedNameBD_XP(const char *nom_camp) +{ + size_t mida, j; + + mida = strlen(nom_camp); + if (mida >= MM_MAX_LON_FIELD_NAME_DBF) + return MM_DBF_NAME_NO_VALID; + + for (j = 0; j < mida; j++) + { + if (!MM_Is_character_valid_for_extended_DBF_field_name( + (unsigned char)nom_camp[j], nullptr)) + return MM_DBF_NAME_NO_VALID; + } + + if (mida >= MM_MAX_LON_CLASSICAL_FIELD_NAME_DBF) + return MM_VALID_EXTENDED_DBF_NAME; + + if (!MM_Is_classical_DBF_field_name_or_lowercase(nom_camp)) + return MM_VALID_EXTENDED_DBF_NAME; + + if (MM_is_field_name_lowercase(nom_camp)) + return MM_DBF_NAME_LOWERCASE_AND_VALID; + + return NM_CLASSICAL_DBF_AND_VALID_NAME; +} + +static MM_BYTE MM_CalculateBytesExtendedFieldName(struct MM_FIELD *camp) +{ + camp->reserved_2[MM_OFFSET_RESERVED2_EXTENDED_NAME_SIZE] = + (MM_BYTE)strlen(camp->FieldName); + return MM_DonaBytesNomEstesCamp(camp); +} + +static MM_ACCUMULATED_BYTES_TYPE_DBF +MM_CalculateBytesExtendedFieldNames(const struct MM_DATA_BASE_XP *bd_xp) +{ + MM_ACCUMULATED_BYTES_TYPE_DBF bytes_acumulats = 0; + MM_EXT_DBF_N_FIELDS i_camp; + + for (i_camp = 0; i_camp < bd_xp->nFields; i_camp++) + { + if (MM_VALID_EXTENDED_DBF_NAME == + MM_ISExtendedNameBD_XP(bd_xp->pField[i_camp].FieldName)) + bytes_acumulats += + MM_CalculateBytesExtendedFieldName(bd_xp->pField + i_camp); + } + + return bytes_acumulats; +} + +static MM_FIRST_RECORD_OFFSET_TYPE +MM_CalculateBytesFirstRecordOffset(struct MM_DATA_BASE_XP *bd_xp) +{ + if (bd_xp) + return (32 + 32 * bd_xp->nFields + 1 + + MM_CalculateBytesExtendedFieldNames(bd_xp)); + return 0; +} + +static void MM_CheckDBFHeader(struct MM_DATA_BASE_XP *bd_xp) +{ + struct MM_FIELD *camp; + MM_EXT_DBF_N_FIELDS i; + MM_BOOLEAN cal_DBF_estesa = FALSE; + + bd_xp->BytesPerRecord = 1; + for (i = 0, camp = bd_xp->pField; i < bd_xp->nFields; i++, camp++) + { + camp->AccumulatedBytes = bd_xp->BytesPerRecord; + bd_xp->BytesPerRecord += camp->BytesPerField; + if (camp->DesiredWidth == 0) + camp->DesiredWidth = camp->OriginalDesiredWidth = + MM_GetDefaultDesiredDBFFieldWidth(camp); //camp->BytesPerField; + if (camp->FieldType == 'C' && + camp->BytesPerField > MM_MAX_AMPLADA_CAMP_C_DBF_CLASSICA) + cal_DBF_estesa = TRUE; + if (MM_VALID_EXTENDED_DBF_NAME == + MM_ISExtendedNameBD_XP(camp->FieldName)) + cal_DBF_estesa = TRUE; + } + + bd_xp->FirstRecordOffset = MM_CalculateBytesFirstRecordOffset(bd_xp); + + if (cal_DBF_estesa || bd_xp->nFields > MM_MAX_N_CAMPS_DBF_CLASSICA || + bd_xp->nRecords > UINT32_MAX) + bd_xp->dbf_version = (MM_BYTE)MM_MARCA_VERSIO_1_DBF_ESTESA; + else + bd_xp->dbf_version = MM_MARCA_DBASE4; +} + +static void +MM_InitializeOffsetExtendedFieldNameFields(struct MM_DATA_BASE_XP *bd_xp, + MM_EXT_DBF_N_FIELDS i_camp) +{ + memset((char *)(&bd_xp->pField[i_camp].reserved_2) + + MM_OFFSET_RESERVAT2_OFFSET_NOM_ESTES, + 0, 4); +} + +static void +MM_InitializeBytesExtendedFieldNameFields(struct MM_DATA_BASE_XP *bd_xp, + MM_EXT_DBF_N_FIELDS i_camp) +{ + memset((char *)(&bd_xp->pField[i_camp].reserved_2) + + MM_OFFSET_RESERVED2_EXTENDED_NAME_SIZE, + 0, 1); +} + +static short int MM_return_common_valid_DBF_field_name_string(char *cadena) +{ + char *p; + short int error_retornat = 0; + + if (!cadena) + return 0; + //strupr(cadena); + for (p = cadena; *p; p++) + { + (*p) = (char)toupper((unsigned char)*p); + if ((*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9') || *p == '_') + ; + else + { + *p = '_'; + error_retornat |= MM_FIELD_NAME_CHARACTER_INVALID; + } + } + if (cadena[0] == '_') + { + // To avoid having field names starting by '_' this case is + // substituted by a 0 (not a '\0'). + cadena[0] = '0'; + error_retornat |= MM_FIELD_NAME_FIRST_CHARACTER_; + } + return error_retornat; +} + +static short int MM_ReturnValidClassicDBFFieldName(char *cadena) +{ + size_t long_nom_camp; + short int error_retornat = 0; + + long_nom_camp = strlen(cadena); + if ((long_nom_camp < 1) || + (long_nom_camp >= MM_MAX_LON_CLASSICAL_FIELD_NAME_DBF)) + { + cadena[MM_MAX_LON_FIELD_NAME_DBF - 1] = '\0'; + error_retornat |= MM_FIELD_NAME_TOO_LONG; + } + error_retornat |= MM_return_common_valid_DBF_field_name_string(cadena); + return error_retornat; +} + +static MM_BOOLEAN +MM_CheckClassicFieldNameEqual(const struct MM_DATA_BASE_XP *data_base_XP, + const char *classical_name) +{ + MM_EXT_DBF_N_FIELDS i; + + for (i = 0; i < data_base_XP->nFields; i++) + { + if ((strcasecmp(data_base_XP->pField[i].ClassicalDBFFieldName, + classical_name)) == 0 || + (strcasecmp(data_base_XP->pField[i].FieldName, classical_name)) == + 0) + return TRUE; + } + return FALSE; +} + +static char *MM_GiveNewStringWithCharacterInFront(const char *text, + char character) +{ + char *ptr; + size_t i; + + if (!text) + return nullptr; + + i = strlen(text); + if ((ptr = calloc_function(i + 2)) == nullptr) + return nullptr; + + *ptr = character; + memcpy(ptr + 1, text, i + 1); + return ptr; +} + +static char *MM_SetSubIndexFieldNam(const char *nom_camp, + MM_EXT_DBF_N_FIELDS index, + size_t ampladamax) +{ + char *NomCamp_SubIndex; + char *_subindex; + char subindex[19 + 1]; + size_t sizet_subindex; + size_t sizet_nomcamp; + + NomCamp_SubIndex = calloc_function(ampladamax); + if (!NomCamp_SubIndex) + return nullptr; + + CPLStrlcpy(NomCamp_SubIndex, nom_camp, ampladamax); + NomCamp_SubIndex[ampladamax - 1] = '\0'; + + snprintf(subindex, sizeof(subindex), sprintf_UINT64, (GUInt64)index); + + _subindex = MM_GiveNewStringWithCharacterInFront(subindex, '_'); + if (!_subindex) + { + free_function(NomCamp_SubIndex); + return nullptr; + } + + sizet_subindex = strlen(_subindex); + sizet_nomcamp = strlen(NomCamp_SubIndex); + + if (sizet_nomcamp + sizet_subindex > ampladamax - 1) + memcpy(NomCamp_SubIndex + ((ampladamax - 1) - sizet_subindex), + _subindex, strlen(_subindex)); + else + NomCamp_SubIndex = strcat(NomCamp_SubIndex, _subindex); + + free_function(_subindex); + + return NomCamp_SubIndex; +} + +MM_FIRST_RECORD_OFFSET_TYPE +MM_GiveOffsetExtendedFieldName(const struct MM_FIELD *camp) +{ + MM_FIRST_RECORD_OFFSET_TYPE offset_nom_camp; + + memcpy(&offset_nom_camp, + (char *)(&camp->reserved_2) + MM_OFFSET_RESERVAT2_OFFSET_NOM_ESTES, + 4); + return offset_nom_camp; +} + +int MM_WriteNRecordsMMBD_XPFile(struct MMAdmDatabase *MMAdmDB) +{ + if (!MMAdmDB->pMMBDXP || !MMAdmDB->pFExtDBF) + return 0; + + // Updating number of features in features table + fseek_function(MMAdmDB->pFExtDBF, MM_FIRST_OFFSET_to_N_RECORDS, SEEK_SET); + + if (MMAdmDB->pMMBDXP->nRecords > UINT32_MAX) + { + MMAdmDB->pMMBDXP->dbf_version = MM_MARCA_VERSIO_1_DBF_ESTESA; + } + else + { + MMAdmDB->pMMBDXP->dbf_version = MM_MARCA_DBASE4; + } + + { + GUInt32 nRecords32LowBits = + (GUInt32)(MMAdmDB->pMMBDXP->nRecords & UINT32_MAX); + if (fwrite_function(&nRecords32LowBits, 4, 1, MMAdmDB->pFExtDBF) != 1) + return 1; + } + + fseek_function(MMAdmDB->pFExtDBF, MM_SECOND_OFFSET_to_N_RECORDS, SEEK_SET); + if (MMAdmDB->pMMBDXP->dbf_version == MM_MARCA_VERSIO_1_DBF_ESTESA) + { + /* from 16 to 19, position MM_SECOND_OFFSET_to_N_RECORDS */ + GUInt32 nRecords32HighBits = + (GUInt32)(MMAdmDB->pMMBDXP->nRecords >> 32); + if (fwrite_function(&nRecords32HighBits, 4, 1, MMAdmDB->pFExtDBF) != 1) + return 1; + + /* from 20 to 27 */ + if (fwrite_function(&(MMAdmDB->pMMBDXP->dbf_on_a_LAN), 8, 1, + MMAdmDB->pFExtDBF) != 1) + return 1; + } + else + { + if (fwrite_function(&(MMAdmDB->pMMBDXP->dbf_on_a_LAN), 12, 1, + MMAdmDB->pFExtDBF) != 1) + return 1; + } + + return 0; +} + +static MM_BOOLEAN MM_UpdateEntireHeader(struct MM_DATA_BASE_XP *data_base_XP) +{ + MM_BYTE variable_byte; + MM_EXT_DBF_N_FIELDS i, j = 0; + char zero[11] = {0}; + const MM_BYTE byte_zero = 0; + char ModeLectura_previ[4] = ""; + MM_FIRST_RECORD_OFFSET_TYPE bytes_acumulats; + MM_BYTE name_size; + int estat; + char nom_camp[MM_MAX_LON_FIELD_NAME_DBF]; + size_t retorn_fwrite; + MM_BOOLEAN table_should_be_closed = FALSE; + + if (data_base_XP->pfDataBase == nullptr) + { + strcpy(ModeLectura_previ, data_base_XP->ReadingMode); + strcpy(data_base_XP->ReadingMode, "wb"); + + if ((data_base_XP->pfDataBase = + fopen_function(data_base_XP->szFileName, + data_base_XP->ReadingMode)) == nullptr) + { + return FALSE; + } + + table_should_be_closed = TRUE; + } + + if ((data_base_XP->nFields) > MM_MAX_N_CAMPS_DBF_CLASSICA) + data_base_XP->dbf_version = MM_MARCA_VERSIO_1_DBF_ESTESA; + else if ((data_base_XP->nRecords) > UINT32_MAX) + data_base_XP->dbf_version = MM_MARCA_VERSIO_1_DBF_ESTESA; + else + { + if (data_base_XP->dbf_version == MM_MARCA_VERSIO_1_DBF_ESTESA) + data_base_XP->dbf_version = MM_MARCA_DBASE4; + for (i = 0; i < data_base_XP->nFields; i++) + { + if (data_base_XP->pField[i].FieldType == 'C' && + data_base_XP->pField[i].BytesPerField > + MM_MAX_AMPLADA_CAMP_C_DBF_CLASSICA) + { + data_base_XP->dbf_version = MM_MARCA_VERSIO_1_DBF_ESTESA; + break; + } + if (MM_VALID_EXTENDED_DBF_NAME == + MM_ISExtendedNameBD_XP(data_base_XP->pField[i].FieldName)) + { + data_base_XP->dbf_version = MM_MARCA_VERSIO_1_DBF_ESTESA; + break; + } + } + } + + // Writing header + fseek_function(data_base_XP->pfDataBase, 0, SEEK_SET); + + /* Byte 0 */ + if (fwrite_function(&(data_base_XP->dbf_version), 1, 1, + data_base_XP->pfDataBase) != 1) + { + return FALSE; + } + + /* MM_BYTE from 1 to 3 */ + variable_byte = (MM_BYTE)(data_base_XP->year - 1900); + if (fwrite_function(&variable_byte, 1, 1, data_base_XP->pfDataBase) != 1) + return FALSE; + if (fwrite_function(&(data_base_XP->month), 1, 1, + data_base_XP->pfDataBase) != 1) + return FALSE; + if (fwrite_function(&(data_base_XP->day), 1, 1, data_base_XP->pfDataBase) != + 1) + return FALSE; + + /* from 4 a 7, position MM_FIRST_OFFSET_to_N_RECORDS */ + { + GUInt32 nRecords32LowBits = + (GUInt32)(data_base_XP->nRecords & UINT32_MAX); + if (fwrite_function(&nRecords32LowBits, 4, 1, + data_base_XP->pfDataBase) != 1) + return FALSE; + } + + /* from 8 a 9, position MM_PRIMER_OFFSET_a_OFFSET_1a_FITXA */ + if (fwrite_function(&(data_base_XP->FirstRecordOffset), 2, 1, + data_base_XP->pfDataBase) != 1) + return FALSE; + /* from 10 to 11, & from 12 to 13 */ + if (MM_ES_DBF_ESTESA(data_base_XP->dbf_version)) + { + if (fwrite_function(&(data_base_XP->BytesPerRecord), + sizeof(MM_ACCUMULATED_BYTES_TYPE_DBF), 1, + data_base_XP->pfDataBase) != 1) + return FALSE; + } + else + { + /* from 10 to 11 */ + if (fwrite_function(&(data_base_XP->BytesPerRecord), 2, 1, + data_base_XP->pfDataBase) != 1) + return FALSE; + /* from 12 to 13 */ + if (fwrite_function(&(data_base_XP->reserved_1), 2, 1, + data_base_XP->pfDataBase) != 1) + return FALSE; + } + /* byte 14 */ + if (fwrite_function(&(data_base_XP->transaction_flag), 1, 1, + data_base_XP->pfDataBase) != 1) + return FALSE; + /* byte 15 */ + if (fwrite_function(&(data_base_XP->encryption_flag), 1, 1, + data_base_XP->pfDataBase) != 1) + return FALSE; + + /* from 16 to 27 */ + if (data_base_XP->nRecords > UINT32_MAX) + { + /* from 16 to 19, position MM_SECOND_OFFSET_to_N_RECORDS */ + GUInt32 nRecords32HighBits = (GUInt32)(data_base_XP->nRecords >> 32); + if (fwrite_function(&nRecords32HighBits, 4, 1, + data_base_XP->pfDataBase) != 1) + return FALSE; + + /* from 20 to 27 */ + if (fwrite_function(&(data_base_XP->dbf_on_a_LAN), 8, 1, + data_base_XP->pfDataBase) != 1) + return FALSE; + } + else + { + /* from 16 to 27 */ + if (fwrite_function(&(data_base_XP->dbf_on_a_LAN), 12, 1, + data_base_XP->pfDataBase) != 1) + return FALSE; + } + /* byte 28 */ + if (fwrite_function(&(data_base_XP->MDX_flag), 1, 1, + data_base_XP->pfDataBase) != 1) + return FALSE; + + /* Byte 29 */ + if (fwrite_function(&(data_base_XP->CharSet), 1, 1, + data_base_XP->pfDataBase) != 1) + return FALSE; + + /* Bytes from 30 to 31, in position MM_SEGON_OFFSET_a_OFFSET_1a_FITXA */ + if (MM_ES_DBF_ESTESA(data_base_XP->dbf_version)) + { + if (fwrite_function(((char *)&(data_base_XP->FirstRecordOffset)) + 2, 2, + 1, data_base_XP->pfDataBase) != 1) + return FALSE; + } + else + { + if (fwrite_function(&(data_base_XP->reserved_2), 2, 1, + data_base_XP->pfDataBase) != 1) + return FALSE; + } + + /* At 32th byte fields description begins */ + /* Every description is 32 bytes long */ + bytes_acumulats = 32 + 32 * (data_base_XP->nFields) + 1; + + for (i = 0; i < data_base_XP->nFields; i++) + { + /* Bytes from 0 to 10 -> Field name, \0 finished */ + estat = MM_ISExtendedNameBD_XP(data_base_XP->pField[i].FieldName); + if (estat == NM_CLASSICAL_DBF_AND_VALID_NAME || + estat == MM_DBF_NAME_LOWERCASE_AND_VALID) + { + j = (short)strlen(data_base_XP->pField[i].FieldName); + + retorn_fwrite = fwrite_function(&data_base_XP->pField[i].FieldName, + 1, j, data_base_XP->pfDataBase); + if (retorn_fwrite != (size_t)j) + { + return FALSE; + } + MM_InitializeOffsetExtendedFieldNameFields(data_base_XP, i); + MM_InitializeBytesExtendedFieldNameFields(data_base_XP, i); + } + else if (estat == MM_VALID_EXTENDED_DBF_NAME) + { + if (*(data_base_XP->pField[i].ClassicalDBFFieldName) == '\0') + { + char nom_temp[MM_MAX_LON_FIELD_NAME_DBF]; + + CPLStrlcpy(nom_temp, data_base_XP->pField[i].FieldName, + MM_MAX_LON_FIELD_NAME_DBF); + MM_ReturnValidClassicDBFFieldName(nom_temp); + nom_temp[MM_MAX_LON_CLASSICAL_FIELD_NAME_DBF - 1] = '\0'; + if ((MM_CheckClassicFieldNameEqual(data_base_XP, nom_temp)) == + TRUE) + { + char *c; + + c = MM_SetSubIndexFieldNam( + nom_temp, i, MM_MAX_LON_CLASSICAL_FIELD_NAME_DBF); + + if (c) + { + j = 0; + while (MM_CheckClassicFieldNameEqual(data_base_XP, c) == + TRUE && + j < data_base_XP->nFields) + { + free_function(c); + c = MM_SetSubIndexFieldNam( + nom_temp, ++j, + MM_MAX_LON_CLASSICAL_FIELD_NAME_DBF); + } + if (c) + { + CPLStrlcpy( + data_base_XP->pField[i].ClassicalDBFFieldName, + c, + sizeof(data_base_XP->pField[i] + .ClassicalDBFFieldName)); + free_function(c); + } + } + } + else + CPLStrlcpy( + data_base_XP->pField[i].ClassicalDBFFieldName, nom_temp, + sizeof(data_base_XP->pField[i].ClassicalDBFFieldName)); + } + + // This is a 11-byte fixed size field consisting of the filename + // and it's been padding calculated some next lines. + j = (short)strlen(data_base_XP->pField[i].ClassicalDBFFieldName); + + retorn_fwrite = + fwrite_function(&data_base_XP->pField[i].ClassicalDBFFieldName, + 1, j, data_base_XP->pfDataBase); + if (retorn_fwrite != (size_t)j) + { + return FALSE; + } + + name_size = + MM_CalculateBytesExtendedFieldName(data_base_XP->pField + i); + MM_EscriuOffsetNomEstesBD_XP(data_base_XP, i, bytes_acumulats); + bytes_acumulats += name_size; + } + else + { + return FALSE; + } + + if (fwrite_function(zero, 1, 11 - j, data_base_XP->pfDataBase) != + 11 - (size_t)j) + { + return FALSE; + } + /* Byte 11, Field type */ + if (fwrite_function(&data_base_XP->pField[i].FieldType, 1, 1, + data_base_XP->pfDataBase) != 1) + { + return FALSE; + } + /* Bytes 12 to 15 --> Reserved */ + if (fwrite_function(&data_base_XP->pField[i].reserved_1, 4, 1, + data_base_XP->pfDataBase) != 1) + { + return FALSE; + } + /* Byte 16, or OFFSET_BYTESxCAMP_CAMP_CLASSIC --> BytesPerField */ + if (MM_ES_DBF_ESTESA(data_base_XP->dbf_version) && + data_base_XP->pField[i].FieldType == 'C') + { + if (fwrite_function((void *)&byte_zero, 1, 1, + data_base_XP->pfDataBase) != 1) + { + return FALSE; + } + } + else + { + if (fwrite_function(&data_base_XP->pField[i].BytesPerField, 1, 1, + data_base_XP->pfDataBase) != 1) + { + return FALSE; + } + } + /* 17th byte 17 --> In fields of type 'N' and 'F' indicates decimal places.*/ + if (data_base_XP->pField[i].FieldType == 'N' || + data_base_XP->pField[i].FieldType == 'F') + { + if (fwrite_function(&data_base_XP->pField[i].DecimalsIfFloat, 1, 1, + data_base_XP->pfDataBase) != 1) + { + return FALSE; + } + } + else + { + if (fwrite_function(zero, 1, 1, data_base_XP->pfDataBase) != 1) + { + return FALSE; + } + } + if (MM_ES_DBF_ESTESA(data_base_XP->dbf_version) && + data_base_XP->pField[i].FieldType == 'C') + { + /* Bytes from 18 to 20 --> Reserved */ + if (fwrite_function(&data_base_XP->pField[i].reserved_2, + 20 - 18 + 1, 1, data_base_XP->pfDataBase) != 1) + { + return FALSE; + } + /* Bytes from 21 to 24 --> OFFSET_BYTESxCAMP_CAMP_ESPECIAL, special fields, like C + in extended DBF */ + if (fwrite_function(&data_base_XP->pField[i].BytesPerField, + sizeof(MM_BYTES_PER_FIELD_TYPE_DBF), 1, + data_base_XP->pfDataBase) != 1) + { + return FALSE; + } + + /* Bytes from 25 to 30 --> Reserved */ + if (fwrite_function(&data_base_XP->pField[i].reserved_2[25 - 18], + 30 - 25 + 1, 1, data_base_XP->pfDataBase) != 1) + { + return FALSE; + } + } + else + { + /* Bytes de 21 a 24 --> OFFSET_BYTESxCAMP_CAMP_ESPECIAL, special fields, like C */ + memset(data_base_XP->pField[i].reserved_2 + + MM_OFFSET_RESERVAT2_BYTESxCAMP_CAMP_ESPECIAL, + '\0', 4); + /* Bytes from 18 to 30 --> Reserved */ + if (fwrite_function(&data_base_XP->pField[i].reserved_2, 13, 1, + data_base_XP->pfDataBase) != 1) + { + return FALSE; + } + } + /* Byte 31 --> MDX flag. */ + if (fwrite_function(&data_base_XP->pField[i].MDX_field_flag, 1, 1, + data_base_XP->pfDataBase) != 1) + { + return FALSE; + } + } + + variable_byte = 13; + if (fwrite_function(&variable_byte, 1, 1, data_base_XP->pfDataBase) != 1) + return FALSE; + + if (data_base_XP->FirstRecordOffset != bytes_acumulats) + return FALSE; + + // Extended fields + for (i = 0; i < data_base_XP->nFields; i++) + { + if (MM_VALID_EXTENDED_DBF_NAME == + MM_ISExtendedNameBD_XP(data_base_XP->pField[i].FieldName)) + { + bytes_acumulats = + MM_GiveOffsetExtendedFieldName(data_base_XP->pField + i); + name_size = MM_DonaBytesNomEstesCamp(data_base_XP->pField + i); + + fseek_function(data_base_XP->pfDataBase, bytes_acumulats, SEEK_SET); + + strcpy(nom_camp, data_base_XP->pField[i].FieldName); + //CanviaJocCaracPerEscriureDBF(nom_camp, JocCaracDBFaMM(data_base_XP->CharSet, ParMM.JocCaracDBFPerDefecte)); + + retorn_fwrite = fwrite_function(nom_camp, 1, name_size, + data_base_XP->pfDataBase); + + if (retorn_fwrite != (size_t)name_size) + return FALSE; + } + } + + if (table_should_be_closed) + { + fclose_and_nullify(&data_base_XP->pfDataBase); + } + + return TRUE; +} /* End of MM_UpdateEntireHeader() */ + +MM_BOOLEAN MM_CreateDBFFile(struct MM_DATA_BASE_XP *bd_xp, + const char *NomFitxer) +{ + if (!NomFitxer || MMIsEmptyString(NomFitxer) || !bd_xp) + return FALSE; + + MM_CheckDBFHeader(bd_xp); + CPLStrlcpy(bd_xp->szFileName, NomFitxer, sizeof(bd_xp->szFileName)); + return MM_UpdateEntireHeader(bd_xp); +} + +void MM_ReleaseMainFields(struct MM_DATA_BASE_XP *data_base_XP) +{ + MM_EXT_DBF_N_FIELDS i; + size_t j; + char **cadena; + + if (data_base_XP->pField) + { + for (i = 0; i < data_base_XP->nFields; i++) + { + for (j = 0; j < MM_NUM_IDIOMES_MD_MULTIDIOMA; j++) + { + cadena = data_base_XP->pField[i].Separator; + if (cadena[j]) + { + free_function(cadena[j]); + cadena[j] = nullptr; + } + } + } + free_function(data_base_XP->pField); + data_base_XP->pField = nullptr; + data_base_XP->nFields = 0; + } + return; +} + +// READING THE HEADER OF AN EXTENDED DBF +// Free with MM_ReleaseDBFHeader() +int MM_ReadExtendedDBFHeaderFromFile(const char *szFileName, + struct MM_DATA_BASE_XP *pMMBDXP, + const char *pszRelFile) +{ + MM_BYTE variable_byte; + FILE_TYPE *pf; + unsigned short int two_bytes; + MM_EXT_DBF_N_FIELDS nIField; + MM_FIRST_RECORD_OFFSET_TYPE offset_primera_fitxa; + MM_FIRST_RECORD_OFFSET_TYPE offset_fals = 0; + MM_BOOLEAN incoherent_record_size = FALSE; + MM_BYTE un_byte; + MM_BYTES_PER_FIELD_TYPE_DBF bytes_per_camp; + MM_BYTE tretze_bytes[13]; + MM_FIRST_RECORD_OFFSET_TYPE offset_possible; + MM_BYTE some_problems_when_reading = 0; + MM_FILE_OFFSET offset_reintent = 0; // For retrying + char cpg_file[MM_CPL_PATH_BUF_SIZE]; + char *pszDesc; + char section[MM_MAX_LON_FIELD_NAME_DBF + 25]; // TAULA_PRINCIPAL:field_name + GUInt32 nRecords32LowBits; + char *pszString; + + if (!szFileName) + return 1; + + CPLStrlcpy(pMMBDXP->szFileName, szFileName, sizeof(pMMBDXP->szFileName)); + strcpy(pMMBDXP->ReadingMode, "rb"); + + if ((pMMBDXP->pfDataBase = fopen_function(pMMBDXP->szFileName, + pMMBDXP->ReadingMode)) == nullptr) + return 1; + + pf = pMMBDXP->pfDataBase; + + fseek_function(pf, 0, SEEK_SET); + /* ====== Header reading (32 bytes) =================== */ + offset_primera_fitxa = 0; + + if (1 != fread_function(&(pMMBDXP->dbf_version), 1, 1, pf) || + 1 != fread_function(&variable_byte, 1, 1, pf) || + 1 != fread_function(&(pMMBDXP->month), 1, 1, pf) || + 1 != fread_function(&(pMMBDXP->day), 1, 1, pf)) + { + fclose_and_nullify(&pMMBDXP->pfDataBase); + return 1; + } + + if (1 != fread_function(&nRecords32LowBits, 4, 1, pf)) + { + fclose_and_nullify(&pMMBDXP->pfDataBase); + return 1; + } + + if (1 != fread_function(&offset_primera_fitxa, 2, 1, pf)) + { + fclose_and_nullify(&pMMBDXP->pfDataBase); + return 1; + } + + pMMBDXP->year = (short)(1900 + variable_byte); +reintenta_lectura_per_si_error_CreaCampBD_XP: + + if (some_problems_when_reading > 0) + { + if (!MM_ES_DBF_ESTESA(pMMBDXP->dbf_version)) + { + offset_fals = + offset_primera_fitxa & (MM_FIRST_RECORD_OFFSET_TYPE)(~31); + } + } + else + offset_reintent = ftell_function(pf); + + if (1 != fread_function(&two_bytes, 2, 1, pf) || + 1 != fread_function(&(pMMBDXP->reserved_1), 2, 1, pf) || + 1 != fread_function(&(pMMBDXP->transaction_flag), 1, 1, pf) || + 1 != fread_function(&(pMMBDXP->encryption_flag), 1, 1, pf) || + 1 != fread_function(&(pMMBDXP->dbf_on_a_LAN), 12, 1, pf)) + { + free_function(pMMBDXP->pField); + pMMBDXP->pField = nullptr; + pMMBDXP->nFields = 0; + fclose_and_nullify(&pMMBDXP->pfDataBase); + return 1; + } + + if (MM_ES_DBF_ESTESA(pMMBDXP->dbf_version)) + { + GUInt32 nRecords32HighBits; + + // Getting 4 bytes of the 8 bytes variable + memcpy(&nRecords32HighBits, &pMMBDXP->dbf_on_a_LAN, 4); + + // Getting other 4 bytes of the 8 bytes variable + // The cast to GUInt64 of the high 32 bits is important to + // make sure the left bit shift is done correctly + pMMBDXP->nRecords = + ((GUInt64)nRecords32HighBits << 32) | nRecords32LowBits; + } + else + pMMBDXP->nRecords = nRecords32LowBits; + + if (1 != fread_function(&(pMMBDXP->MDX_flag), 1, 1, pf) || + 1 != fread_function(&(pMMBDXP->CharSet), 1, 1, pf) || + 1 != fread_function(&(pMMBDXP->reserved_2), 2, 1, pf)) + { + free_function(pMMBDXP->pField); + pMMBDXP->pField = nullptr; + pMMBDXP->nFields = 0; + fclose_and_nullify(&pMMBDXP->pfDataBase); + return 1; + } + + // Checking for a cpg file + if (pMMBDXP->CharSet == 0) + { + FILE_TYPE *f_cpg; + char charset_cpg[11]; + + strcpy(cpg_file, pMMBDXP->szFileName); + CPLStrlcpy(cpg_file, reset_extension(cpg_file, "cpg"), + sizeof(cpg_file)); + f_cpg = fopen_function(cpg_file, "r"); + if (f_cpg) + { + char *p; + size_t read_bytes; + fseek_function(f_cpg, 0L, SEEK_SET); + if (11 > (read_bytes = fread_function(charset_cpg, 1, 10, f_cpg))) + { + charset_cpg[read_bytes] = '\0'; + p = strstr(charset_cpg, "UTF-8"); + if (p) + pMMBDXP->CharSet = MM_JOC_CARAC_UTF8_DBF; + p = strstr(charset_cpg, "UTF8"); + if (p) + pMMBDXP->CharSet = MM_JOC_CARAC_UTF8_DBF; + p = strstr(charset_cpg, "ISO-8859-1"); + if (p) + pMMBDXP->CharSet = MM_JOC_CARAC_ANSI_DBASE; + } + fclose_function(f_cpg); + } + } + if (MM_ES_DBF_ESTESA(pMMBDXP->dbf_version)) + { + unsigned short FirstRecordOffsetLow16Bits; + unsigned short FirstRecordOffsetHigh16Bits; + + memcpy(&FirstRecordOffsetLow16Bits, &offset_primera_fitxa, 2); + memcpy(&FirstRecordOffsetHigh16Bits, &pMMBDXP->reserved_2, 2); + + pMMBDXP->FirstRecordOffset = + ((GUInt32)FirstRecordOffsetHigh16Bits << 16) | + FirstRecordOffsetLow16Bits; + + if (some_problems_when_reading > 0) + offset_fals = pMMBDXP->FirstRecordOffset; + + memcpy(&FirstRecordOffsetLow16Bits, &two_bytes, 2); + memcpy(&FirstRecordOffsetHigh16Bits, &pMMBDXP->reserved_1, 2); + + pMMBDXP->BytesPerRecord = ((GUInt32)FirstRecordOffsetHigh16Bits << 16) | + FirstRecordOffsetLow16Bits; + } + else + { + pMMBDXP->FirstRecordOffset = offset_primera_fitxa; + pMMBDXP->BytesPerRecord = two_bytes; + } + + /* ====== Record structure ========================= */ + + if (some_problems_when_reading > 0) + { + if ((offset_fals - 1) - 32 < 0) + pMMBDXP->nFields = 0; + else + pMMBDXP->nFields = + (MM_EXT_DBF_N_FIELDS)(((offset_fals - 1) - 32) / 32); + } + else + { + // There's a chance that bytes_acumulats could overflow if it's GUInt32. + // For that reason it's better to promote to GUInt64. + GUInt64 bytes_acumulats = 1; + + pMMBDXP->nFields = 0; + + fseek_function(pf, 0, SEEK_END); + if (32 - 1 < ftell_function(pf)) + { + fseek_function(pf, 32, SEEK_SET); + do + { + bytes_per_camp = 0; + fseek_function( + pf, + 32 + (MM_FILE_OFFSET)pMMBDXP->nFields * 32 + + (MM_MAX_LON_CLASSICAL_FIELD_NAME_DBF + 1 + 4), + SEEK_SET); + if (1 != fread_function(&bytes_per_camp, 1, 1, pf) || + 1 != fread_function(&un_byte, 1, 1, pf) || + 1 != fread_function(&tretze_bytes, + 3 + sizeof(bytes_per_camp), 1, pf)) + { + free_function(pMMBDXP->pField); + pMMBDXP->pField = nullptr; + pMMBDXP->nFields = 0; + fclose_and_nullify(&pMMBDXP->pfDataBase); + return 1; + } + if (bytes_per_camp == 0) + memcpy(&bytes_per_camp, (char *)(&tretze_bytes) + 3, + sizeof(bytes_per_camp)); + + bytes_acumulats += bytes_per_camp; + pMMBDXP->nFields++; + } while (bytes_acumulats < pMMBDXP->BytesPerRecord); + } + } + + if (pMMBDXP->nFields != 0) + { + free_function(pMMBDXP->pField); + pMMBDXP->pField = MM_CreateAllFields(pMMBDXP->nFields); + if (!pMMBDXP->pField) + { + pMMBDXP->nFields = 0; + fclose_and_nullify(&pMMBDXP->pfDataBase); + return 1; + } + } + else + { + free_function(pMMBDXP->pField); + pMMBDXP->pField = nullptr; + } + + fseek_function(pf, 32, SEEK_SET); + for (nIField = 0; nIField < pMMBDXP->nFields; nIField++) + { + if (1 != fread_function(pMMBDXP->pField[nIField].FieldName, + MM_MAX_LON_CLASSICAL_FIELD_NAME_DBF, 1, pf) || + 1 != fread_function(&(pMMBDXP->pField[nIField].FieldType), 1, 1, + pf) || + 1 != fread_function(&(pMMBDXP->pField[nIField].reserved_1), 4, 1, + pf) || + 1 != fread_function(&(pMMBDXP->pField[nIField].BytesPerField), 1, 1, + pf) || + 1 != fread_function(&(pMMBDXP->pField[nIField].DecimalsIfFloat), 1, + 1, pf) || + 1 != fread_function(&(pMMBDXP->pField[nIField].reserved_2), 13, 1, + pf) || + 1 != fread_function(&(pMMBDXP->pField[nIField].MDX_field_flag), 1, + 1, pf)) + { + free_function(pMMBDXP->pField); + pMMBDXP->pField = nullptr; + pMMBDXP->nFields = 0; + fclose_function(pf); + pMMBDXP->pfDataBase = nullptr; + return 1; + } + + if (pMMBDXP->pField[nIField].FieldType == 'F') + pMMBDXP->pField[nIField].FieldType = 'N'; + + pMMBDXP->pField[nIField] + .FieldName[MM_MAX_LON_CLASSICAL_FIELD_NAME_DBF - 1] = '\0'; + if (EQUAL(pMMBDXP->pField[nIField].FieldName, + szMMNomCampIdGraficDefecte)) + pMMBDXP->IdGraficField = nIField; + + if (pMMBDXP->pField[nIField].BytesPerField == 0) + { + if (!MM_ES_DBF_ESTESA(pMMBDXP->dbf_version)) + { + free_function(pMMBDXP->pField); + pMMBDXP->pField = nullptr; + pMMBDXP->nFields = 0; + fclose_function(pf); + pMMBDXP->pfDataBase = nullptr; + return 1; + } + if (pMMBDXP->pField[nIField].FieldType != 'C') + { + free_function(pMMBDXP->pField); + pMMBDXP->pField = nullptr; + pMMBDXP->nFields = 0; + fclose_function(pf); + pMMBDXP->pfDataBase = nullptr; + return 1; + } + + memcpy(&pMMBDXP->pField[nIField].BytesPerField, + (char *)(&pMMBDXP->pField[nIField].reserved_2) + 3, + sizeof(MM_BYTES_PER_FIELD_TYPE_DBF)); + } + + if (nIField) + { + // To avoid overflow + if (pMMBDXP->pField[nIField - 1].AccumulatedBytes > + UINT32_MAX - pMMBDXP->pField[nIField - 1].BytesPerField) + { + free_function(pMMBDXP->pField); + pMMBDXP->pField = nullptr; + pMMBDXP->nFields = 0; + fclose_function(pf); + pMMBDXP->pfDataBase = nullptr; + return 1; + } + + pMMBDXP->pField[nIField].AccumulatedBytes = + (pMMBDXP->pField[nIField - 1].AccumulatedBytes + + pMMBDXP->pField[nIField - 1].BytesPerField); + } + else + { + pMMBDXP->pField[nIField].AccumulatedBytes = 1; + } + + if (pszRelFile) + { + // Usually, in multilingual MiraMon metadata files, the main + // language is the default one and has no "_cat", "_spa", or + // "_eng" suffix after the keyword. So, to retrieve all + // languages in a multilingual file, first, we'll identify + // the one with no suffix "_cat", "_spa", or "_eng", and then the + // others. If one of them lacks a value, it gets the default value. + snprintf(section, sizeof(section), "TAULA_PRINCIPAL:%s", + pMMBDXP->pField[nIField].FieldName); + + // MM_DEF_LANGUAGE + pszDesc = MMReturnValueFromSectionINIFile(pszRelFile, section, + "descriptor"); + if (pszDesc) + { + CPLStrlcpy( + pMMBDXP->pField[nIField].FieldDescription[MM_DEF_LANGUAGE], + pszDesc, MM_MAX_LON_DESCRIPCIO_CAMP_DBF); + + free_function(pszDesc); + } + else + *pMMBDXP->pField[nIField].FieldDescription[MM_DEF_LANGUAGE] = + '\0'; + + // MM_ENG_LANGUAGE + pszDesc = MMReturnValueFromSectionINIFile(pszRelFile, section, + "descriptor_eng"); + if (pszDesc) + { + CPLStrlcpy( + pMMBDXP->pField[nIField].FieldDescription[MM_ENG_LANGUAGE], + pszDesc, MM_MAX_LON_DESCRIPCIO_CAMP_DBF); + + if (*pMMBDXP->pField[nIField] + .FieldDescription[MM_DEF_LANGUAGE] == '\0') + { + CPLStrlcpy(pMMBDXP->pField[nIField] + .FieldDescription[MM_DEF_LANGUAGE], + pszDesc, MM_MAX_LON_DESCRIPCIO_CAMP_DBF); + } + free_function(pszDesc); + } + else + { + // If there is no value descriptor_eng it's because it's the + // default one. So, it's taken from there. + CPLStrlcpy( + pMMBDXP->pField[nIField].FieldDescription[MM_ENG_LANGUAGE], + pMMBDXP->pField[nIField].FieldDescription[MM_DEF_LANGUAGE], + MM_MAX_LON_DESCRIPCIO_CAMP_DBF); + } + + // MM_CAT_LANGUAGE + pszDesc = MMReturnValueFromSectionINIFile(pszRelFile, section, + "descriptor_cat"); + if (pszDesc) + { + CPLStrlcpy( + pMMBDXP->pField[nIField].FieldDescription[MM_CAT_LANGUAGE], + pszDesc, MM_MAX_LON_DESCRIPCIO_CAMP_DBF); + + if (*pMMBDXP->pField[nIField] + .FieldDescription[MM_DEF_LANGUAGE] == '\0') + { + CPLStrlcpy(pMMBDXP->pField[nIField] + .FieldDescription[MM_DEF_LANGUAGE], + pszDesc, MM_MAX_LON_DESCRIPCIO_CAMP_DBF); + } + + free_function(pszDesc); + } + else + { + // If there is no value descriptor_cat it's because it's the + // default one. So, it's taken from there. + CPLStrlcpy( + pMMBDXP->pField[nIField].FieldDescription[MM_CAT_LANGUAGE], + pMMBDXP->pField[nIField].FieldDescription[MM_DEF_LANGUAGE], + MM_MAX_LON_DESCRIPCIO_CAMP_DBF); + } + + // MM_SPA_LANGUAGE + pszDesc = MMReturnValueFromSectionINIFile(pszRelFile, section, + "descriptor_spa"); + if (pszDesc) + { + CPLStrlcpy( + pMMBDXP->pField[nIField].FieldDescription[MM_SPA_LANGUAGE], + pszDesc, MM_MAX_LON_DESCRIPCIO_CAMP_DBF); + + if (*pMMBDXP->pField[nIField] + .FieldDescription[MM_DEF_LANGUAGE] == '\0') + { + CPLStrlcpy(pMMBDXP->pField[nIField] + .FieldDescription[MM_DEF_LANGUAGE], + pszDesc, MM_MAX_LON_DESCRIPCIO_CAMP_DBF); + } + + free_function(pszDesc); + } + else + { + // If there is no value descriptor_spa it's because it's the + // default one. So, it's taken from there. + CPLStrlcpy( + pMMBDXP->pField[nIField].FieldDescription[MM_SPA_LANGUAGE], + pMMBDXP->pField[nIField].FieldDescription[MM_DEF_LANGUAGE], + MM_MAX_LON_DESCRIPCIO_CAMP_DBF); + } + } + } + + if (!pMMBDXP->nFields) + { + if (pMMBDXP->BytesPerRecord) + incoherent_record_size = TRUE; + } + else + { + // To avoid overflow + if (pMMBDXP->pField[pMMBDXP->nFields - 1].AccumulatedBytes > + UINT32_MAX - pMMBDXP->pField[pMMBDXP->nFields - 1].BytesPerField) + { + free_function(pMMBDXP->pField); + pMMBDXP->pField = nullptr; + pMMBDXP->nFields = 0; + fclose_function(pf); + pMMBDXP->pfDataBase = nullptr; + return 1; + } + if (pMMBDXP->pField[pMMBDXP->nFields - 1].BytesPerField + + pMMBDXP->pField[pMMBDXP->nFields - 1].AccumulatedBytes > + pMMBDXP->BytesPerRecord) + incoherent_record_size = TRUE; + } + if (incoherent_record_size) + { + if (some_problems_when_reading == 0) + { + incoherent_record_size = FALSE; + fseek_function(pf, offset_reintent, SEEK_SET); + some_problems_when_reading++; + /* Reset IdGraficField as it might no longer be valid */ + pMMBDXP->IdGraficField = 0; + goto reintenta_lectura_per_si_error_CreaCampBD_XP; + } + else + { + free_function(pMMBDXP->pField); + pMMBDXP->pField = nullptr; + pMMBDXP->nFields = 0; + fclose_function(pf); + pMMBDXP->pfDataBase = nullptr; + return 1; + } + } + + offset_possible = 32 + 32 * (pMMBDXP->nFields) + 1; + + if (!incoherent_record_size && + offset_possible != pMMBDXP->FirstRecordOffset) + { // Extended names + MM_FIRST_RECORD_OFFSET_TYPE offset_nom_camp; + int mida_nom; + + for (nIField = 0; nIField < pMMBDXP->nFields; nIField++) + { + offset_nom_camp = + MM_GiveOffsetExtendedFieldName(pMMBDXP->pField + nIField); + mida_nom = MM_DonaBytesNomEstesCamp(pMMBDXP->pField + nIField); + if (mida_nom > 0 && mida_nom < MM_MAX_LON_FIELD_NAME_DBF && + offset_nom_camp >= offset_possible && + offset_nom_camp < pMMBDXP->FirstRecordOffset) + { + CPLStrlcpy(pMMBDXP->pField[nIField].ClassicalDBFFieldName, + pMMBDXP->pField[nIField].FieldName, + MM_MAX_LON_CLASSICAL_FIELD_NAME_DBF); + fseek_function(pf, offset_nom_camp, SEEK_SET); + if (1 != fread_function(pMMBDXP->pField[nIField].FieldName, + mida_nom, 1, pf)) + { + free_function(pMMBDXP->pField); + pMMBDXP->pField = nullptr; + pMMBDXP->nFields = 0; + fclose_function(pf); + pMMBDXP->pfDataBase = nullptr; + return 1; + } + pMMBDXP->pField[nIField].FieldName[mida_nom] = '\0'; + + // All field names to UTF-8 + if (pMMBDXP->CharSet == MM_JOC_CARAC_ANSI_DBASE) + { + pszString = + CPLRecode_function(pMMBDXP->pField[nIField].FieldName, + CPL_ENC_ISO8859_1, CPL_ENC_UTF8); + CPLStrlcpy(pMMBDXP->pField[nIField].FieldName, pszString, + MM_MAX_LON_FIELD_NAME_DBF); + CPLFree_function(pszString); + } + else if (pMMBDXP->CharSet == MM_JOC_CARAC_OEM850_DBASE) + { + MM_oemansi(pMMBDXP->pField[nIField].FieldName); + pszString = + CPLRecode_function(pMMBDXP->pField[nIField].FieldName, + CPL_ENC_ISO8859_1, CPL_ENC_UTF8); + CPLStrlcpy(pMMBDXP->pField[nIField].FieldName, pszString, + MM_MAX_LON_FIELD_NAME_DBF - 1); + CPLFree_function(pszString); + } + } + } + } + + pMMBDXP->IdEntityField = MM_MAX_EXT_DBF_N_FIELDS_TYPE; + return 0; +} // End of MM_ReadExtendedDBFHeaderFromFile() + +void MM_ReleaseDBFHeader(struct MM_DATA_BASE_XP *data_base_XP) +{ + if (data_base_XP) + { + MM_ReleaseMainFields(data_base_XP); + free_function(data_base_XP); + } + return; +} + +int MM_ModifyFieldNameAndDescriptorIfPresentBD_XP( + struct MM_FIELD *camp, struct MM_DATA_BASE_XP *bd_xp, + MM_BOOLEAN no_modifica_descriptor, size_t mida_nom) +{ + MM_EXT_DBF_N_FIELDS i_camp; + unsigned n_digits_i = 0, i; + int retorn = 0; + + if (mida_nom == 0) + mida_nom = MM_MAX_LON_FIELD_NAME_DBF; + + for (i_camp = 0; i_camp < bd_xp->nFields; i_camp++) + { + if (bd_xp->pField + i_camp == camp) + continue; + if (!strcasecmp(bd_xp->pField[i_camp].FieldName, camp->FieldName)) + break; + } + if (i_camp < bd_xp->nFields) + { + retorn = 1; + if (strlen(camp->FieldName) > mida_nom - 2) + camp->FieldName[mida_nom - 2] = '\0'; + strcat(camp->FieldName, "0"); + for (i = 2; i < (size_t)10; i++) + { + snprintf(camp->FieldName + strlen(camp->FieldName) - 1, + sizeof(camp->FieldName) - strlen(camp->FieldName) + 1, + "%u", i); + for (i_camp = 0; i_camp < bd_xp->nFields; i_camp++) + { + if (bd_xp->pField + i_camp == camp) + continue; + if (!strcasecmp(bd_xp->pField[i_camp].FieldName, + camp->FieldName)) + break; + } + if (i_camp == bd_xp->nFields) + { + n_digits_i = 1; + break; + } + } + if (i == 10) + { + camp->FieldName[strlen(camp->FieldName) - 1] = '\0'; + if (strlen(camp->FieldName) > mida_nom - 3) + camp->FieldName[mida_nom - 3] = '\0'; + strcat(camp->FieldName, "00"); + for (i = 10; i < (size_t)100; i++) + { + snprintf(camp->FieldName + strlen(camp->FieldName) - 2, + sizeof(camp->FieldName) - strlen(camp->FieldName) + 2, + "%u", i); + for (i_camp = 0; i_camp < bd_xp->nFields; i_camp++) + { + if (bd_xp->pField + i_camp == camp) + continue; + if (!strcasecmp(bd_xp->pField[i_camp].FieldName, + camp->FieldName)) + break; + } + if (i_camp == bd_xp->nFields) + { + n_digits_i = 2; + break; + } + } + if (i == 100) + { + camp->FieldName[strlen(camp->FieldName) - 2] = '\0'; + if (strlen(camp->FieldName) > mida_nom - 4) + camp->FieldName[mida_nom - 4] = '\0'; + strcat(camp->FieldName, "000"); + for (i = 100; i < (size_t)256 + 2; i++) + { + snprintf(camp->FieldName + strlen(camp->FieldName) - 3, + sizeof(camp->FieldName) - strlen(camp->FieldName) + + 3, + "%u", i); + for (i_camp = 0; i_camp < bd_xp->nFields; i_camp++) + { + if (bd_xp->pField + i_camp == camp) + continue; + if (!strcasecmp(bd_xp->pField[i_camp].FieldName, + camp->FieldName)) + break; + } + if (i_camp == bd_xp->nFields) + { + n_digits_i = 3; + break; + } + } + if (i == 256) + return 2; + } + } + } + else + { + i = 1; + } + + if ((*(camp->FieldDescription[0]) == '\0') || no_modifica_descriptor) + return retorn; + + for (i_camp = 0; i_camp < bd_xp->nFields; i_camp++) + { + if (bd_xp->pField + i_camp == camp) + continue; + if (!strcasecmp(bd_xp->pField[i_camp].FieldDescription[0], + camp->FieldDescription[0])) + break; + } + if (i_camp == bd_xp->nFields) + return retorn; + + if (retorn == 1) + { + if (strlen(camp->FieldDescription[0]) > + MM_MAX_LON_DESCRIPCIO_CAMP_DBF - 4 - n_digits_i) + camp->FieldDescription[0][mida_nom - 4 - n_digits_i] = '\0'; + + snprintf(camp->FieldDescription[0] + strlen(camp->FieldDescription[0]), + sizeof(camp->FieldDescription[0]) - + strlen(camp->FieldDescription[0]), + " (%u)", i); + for (i_camp = 0; i_camp < bd_xp->nFields; i_camp++) + { + if (bd_xp->pField + i_camp == camp) + continue; + if (!strcasecmp(bd_xp->pField[i_camp].FieldDescription[0], + camp->FieldDescription[0])) + break; + } + if (i_camp == bd_xp->nFields) + return retorn; + } + + retorn = 1; + if (strlen(camp->FieldDescription[0]) > + MM_MAX_LON_DESCRIPCIO_CAMP_DBF - 4 - n_digits_i) + camp->FieldDescription[0][mida_nom - 4 - n_digits_i] = '\0'; + camp->FieldDescription[0][strlen(camp->FieldDescription[0]) - 4 - + n_digits_i + 1] = '\0'; + if (strlen(camp->FieldDescription[0]) > MM_MAX_LON_DESCRIPCIO_CAMP_DBF - 7) + camp->FieldDescription[0][mida_nom - 7] = '\0'; + for (i++; i < (size_t)256; i++) + { + //if (camp->FieldDescription[0] + strlen(camp->FieldDescription[0])) + snprintf(camp->FieldDescription[0] + strlen(camp->FieldDescription[0]), + sizeof(camp->FieldDescription[0]) - + strlen(camp->FieldDescription[0]), + " (%u)", i); + for (i_camp = 0; i_camp < bd_xp->nFields; i_camp++) + { + if (bd_xp->pField + i_camp == camp) + continue; + if (!strcasecmp(bd_xp->pField[i_camp].FieldName, camp->FieldName)) + break; + } + if (i_camp == bd_xp->nFields) + return retorn; + } + return 2; +} // End of MM_ModifyFieldNameAndDescriptorIfPresentBD_XP() + +static int MM_DuplicateMultilingualString( + char *(cadena_final[MM_NUM_IDIOMES_MD_MULTIDIOMA]), + const char *const(cadena_inicial[MM_NUM_IDIOMES_MD_MULTIDIOMA])) +{ + size_t i; + + for (i = 0; i < MM_NUM_IDIOMES_MD_MULTIDIOMA; i++) + { + if (cadena_inicial[i]) + { + if (nullptr == (cadena_final[i] = strdup(cadena_inicial[i]))) + return 1; + } + else + cadena_final[i] = nullptr; + } + return 0; +} + +int MM_DuplicateFieldDBXP(struct MM_FIELD *camp_final, + const struct MM_FIELD *camp_inicial) +{ + *camp_final = *camp_inicial; + + if (0 != MM_DuplicateMultilingualString( + camp_final->Separator, + (const char *const(*))camp_inicial->Separator)) + return 1; + + return 0; +} + +#ifndef GDAL_COMPILATION +size_t CPLStrlcpy(char *pszDest, const char *pszSrc, size_t nDestSize) +{ + if (nDestSize == 0) + return strlen(pszSrc); + + char *pszDestIter = pszDest; + const char *pszSrcIter = pszSrc; + + --nDestSize; + while (nDestSize != 0 && *pszSrcIter != '\0') + { + *pszDestIter = *pszSrcIter; + ++pszDestIter; + ++pszSrcIter; + --nDestSize; + } + *pszDestIter = '\0'; + return pszSrcIter - pszSrc + strlen(pszSrcIter); +} +#endif + +// If n_bytes==SIZE_MAX, the parameter is ignored ant, then, +// it's assumed that szcadena is NUL terminated +char *MM_oemansi_n(char *szcadena, size_t n_bytes) +{ + size_t u_i; + unsigned char *punter_bait; + unsigned char t_oemansi[128] = { + 199, 252, 233, 226, 228, 224, 229, 231, 234, 235, 232, 239, 238, + 236, 196, 197, 201, 230, 198, 244, 246, 242, 251, 249, 255, 214, + 220, 248, 163, 216, 215, 131, 225, 237, 243, 250, 241, 209, 170, + 186, 191, 174, 172, 189, 188, 161, 171, 187, 164, 164, 164, 166, + 166, 193, 194, 192, 169, 166, 166, 164, 164, 162, 165, 164, 164, + 164, 164, 164, 164, 164, 227, 195, 164, 164, 164, 164, 166, 164, + 164, 164, 240, 208, 202, 203, 200, 180, 205, 206, 207, 164, 164, + 164, 164, 166, 204, 164, 211, 223, 212, 210, 245, 213, 181, 254, + 222, 218, 219, 217, 253, 221, 175, 180, 173, 177, 164, 190, 182, + 167, 247, 184, 176, 168, 183, 185, 179, 178, 164, 183}; + if (n_bytes == SIZE_MAX) + { + for (punter_bait = (unsigned char *)szcadena; *punter_bait; + punter_bait++) + { + if (*punter_bait > 127) + *punter_bait = t_oemansi[*punter_bait - 128]; + } + } + else + { + for (u_i = 0, punter_bait = (unsigned char *)szcadena; u_i < n_bytes; + punter_bait++, u_i++) + { + if (*punter_bait > 127) + *punter_bait = t_oemansi[*punter_bait - 128]; + } + } + return szcadena; +} + +char *MM_oemansi(char *szcadena) +{ + return MM_oemansi_n(szcadena, SIZE_MAX); +} + +static MM_BOOLEAN MM_FillFieldDB_XP( + struct MM_FIELD *camp, const char *FieldName, + const char *FieldDescriptionEng, const char *FieldDescriptionCat, + const char *FieldDescriptionSpa, char FieldType, + MM_BYTES_PER_FIELD_TYPE_DBF BytesPerField, MM_BYTE DecimalsIfFloat) +{ + char nom_temp[MM_MAX_LON_FIELD_NAME_DBF]; + int retorn_valida_nom_camp; + + if (FieldName) + { + retorn_valida_nom_camp = MM_ISExtendedNameBD_XP(FieldName); + if (retorn_valida_nom_camp == MM_DBF_NAME_NO_VALID) + return FALSE; + CPLStrlcpy(camp->FieldName, FieldName, MM_MAX_LON_FIELD_NAME_DBF); + + if (retorn_valida_nom_camp == MM_VALID_EXTENDED_DBF_NAME) + { + MM_CalculateBytesExtendedFieldName(camp); + CPLStrlcpy(nom_temp, FieldName, MM_MAX_LON_FIELD_NAME_DBF); + MM_ReturnValidClassicDBFFieldName(nom_temp); + nom_temp[MM_MAX_LON_CLASSICAL_FIELD_NAME_DBF] = '\0'; + CPLStrlcpy(camp->ClassicalDBFFieldName, nom_temp, + MM_MAX_LON_CLASSICAL_FIELD_NAME_DBF); + } + } + + if (FieldDescriptionEng) + CPLStrlcpy(camp->FieldDescription[MM_DEF_LANGUAGE], FieldDescriptionEng, + sizeof(camp->FieldDescription[MM_DEF_LANGUAGE])); + else + strcpy(camp->FieldDescription[MM_DEF_LANGUAGE], "\0"); + + if (FieldDescriptionEng) + CPLStrlcpy(camp->FieldDescription[MM_ENG_LANGUAGE], FieldDescriptionEng, + sizeof(camp->FieldDescription[MM_ENG_LANGUAGE])); + else + strcpy(camp->FieldDescription[MM_ENG_LANGUAGE], "\0"); + + if (FieldDescriptionCat) + CPLStrlcpy(camp->FieldDescription[MM_CAT_LANGUAGE], FieldDescriptionCat, + sizeof(camp->FieldDescription[MM_CAT_LANGUAGE])); + else + strcpy(camp->FieldDescription[MM_CAT_LANGUAGE], "\0"); + + if (FieldDescriptionSpa) + CPLStrlcpy(camp->FieldDescription[MM_SPA_LANGUAGE], FieldDescriptionSpa, + sizeof(camp->FieldDescription[MM_SPA_LANGUAGE])); + else + strcpy(camp->FieldDescription[MM_SPA_LANGUAGE], "\0"); + + camp->FieldType = FieldType; + camp->DecimalsIfFloat = DecimalsIfFloat; + camp->BytesPerField = BytesPerField; + return TRUE; +} + +size_t MM_DefineFirstPolygonFieldsDB_XP(struct MM_DATA_BASE_XP *bd_xp) +{ + MM_EXT_DBF_N_FIELDS i_camp = 0; + + MM_FillFieldDB_XP( + bd_xp->pField + i_camp, szMMNomCampIdGraficDefecte, + szInternalGraphicIdentifierEng, szInternalGraphicIdentifierCat, + szInternalGraphicIdentifierSpa, 'N', MM_MIN_WIDTH_ID_GRAFIC, 0); + bd_xp->IdGraficField = 0; + (bd_xp->pField + i_camp)->GeoTopoTypeField = (MM_BYTE)MM_CAMP_ES_ID_GRAFIC; + i_camp++; + + MM_FillFieldDB_XP(bd_xp->pField + i_camp, szMMNomCampNVertexsDefecte, + szNumberOfVerticesEng, szNumberOfVerticesCat, + szNumberOfVerticesSpa, 'N', MM_MIN_WIDTH_N_VERTEXS, 0); + (bd_xp->pField + i_camp)->GeoTopoTypeField = (MM_BYTE)MM_CAMP_ES_N_VERTEXS; + i_camp++; + + MM_FillFieldDB_XP(bd_xp->pField + i_camp, szMMNomCampPerimetreDefecte, + szPerimeterOfThePolygonEng, szPerimeterOfThePolygonCat, + szPerimeterOfThePolygonSpa, 'N', MM_MIN_WIDTH_LONG, 9); + (bd_xp->pField + i_camp)->GeoTopoTypeField = (MM_BYTE)MM_CAMP_ES_PERIMETRE; + i_camp++; + + MM_FillFieldDB_XP(bd_xp->pField + i_camp, szMMNomCampAreaDefecte, + szAreaOfThePolygonEng, szAreaOfThePolygonCat, + szAreaOfThePolygonSpa, 'N', MM_MIN_WIDTH_AREA, 12); + (bd_xp->pField + i_camp)->GeoTopoTypeField = (MM_BYTE)MM_CAMP_ES_AREA; + i_camp++; + + MM_FillFieldDB_XP(bd_xp->pField + i_camp, szMMNomCampNArcsDefecte, + szNumberOfArcsEng, szNumberOfArcsCat, szNumberOfArcsSpa, + 'N', MM_MIN_WIDTH_N_ARCS, 0); + (bd_xp->pField + i_camp)->GeoTopoTypeField = (MM_BYTE)MM_CAMP_ES_N_ARCS; + i_camp++; + + MM_FillFieldDB_XP( + bd_xp->pField + i_camp, szMMNomCampNPoligonsDefecte, + szNumberOfElementaryPolygonsEng, szNumberOfElementaryPolygonsCat, + szNumberOfElementaryPolygonsSpa, 'N', MM_MIN_WIDTH_N_POLIG, 0); + (bd_xp->pField + i_camp)->GeoTopoTypeField = (MM_BYTE)MM_CAMP_ES_N_POLIG; + i_camp++; + + return i_camp; +} + +size_t MM_DefineFirstArcFieldsDB_XP(struct MM_DATA_BASE_XP *bd_xp) +{ + MM_EXT_DBF_N_FIELDS i_camp; + + i_camp = 0; + MM_FillFieldDB_XP( + bd_xp->pField + i_camp, szMMNomCampIdGraficDefecte, + szInternalGraphicIdentifierEng, szInternalGraphicIdentifierCat, + szInternalGraphicIdentifierSpa, 'N', MM_MIN_WIDTH_ID_GRAFIC, 0); + bd_xp->IdGraficField = 0; + (bd_xp->pField + i_camp)->GeoTopoTypeField = (MM_BYTE)MM_CAMP_ES_ID_GRAFIC; + i_camp++; + + MM_FillFieldDB_XP(bd_xp->pField + i_camp, szMMNomCampNVertexsDefecte, + szNumberOfVerticesEng, szNumberOfVerticesCat, + szNumberOfVerticesSpa, 'N', MM_MIN_WIDTH_N_VERTEXS, 0); + (bd_xp->pField + i_camp)->GeoTopoTypeField = (MM_BYTE)MM_CAMP_ES_N_VERTEXS; + i_camp++; + + MM_FillFieldDB_XP(bd_xp->pField + i_camp, szMMNomCampLongitudArcDefecte, + szLengthOfAarcEng, szLengthOfAarcCat, szLengthOfAarcSpa, + 'N', MM_MIN_WIDTH_LONG, 9); + (bd_xp->pField + i_camp)->GeoTopoTypeField = (MM_BYTE)MM_CAMP_ES_LONG_ARC; + i_camp++; + + MM_FillFieldDB_XP(bd_xp->pField + i_camp, szMMNomCampNodeIniDefecte, + szInitialNodeEng, szInitialNodeCat, szInitialNodeSpa, 'N', + MM_MIN_WIDTH_INITIAL_NODE, 0); + (bd_xp->pField + i_camp)->GeoTopoTypeField = (MM_BYTE)MM_CAMP_ES_NODE_INI; + i_camp++; + + MM_FillFieldDB_XP(bd_xp->pField + i_camp, szMMNomCampNodeFiDefecte, + szFinalNodeEng, szFinalNodeCat, szFinalNodeSpa, 'N', + MM_MIN_WIDTH_FINAL_NODE, 0); + (bd_xp->pField + i_camp)->GeoTopoTypeField = (MM_BYTE)MM_CAMP_ES_NODE_FI; + i_camp++; + + return i_camp; +} + +size_t MM_DefineFirstNodeFieldsDB_XP(struct MM_DATA_BASE_XP *bd_xp) +{ + MM_EXT_DBF_N_FIELDS i_camp; + + i_camp = 0; + + MM_FillFieldDB_XP( + bd_xp->pField + i_camp, szMMNomCampIdGraficDefecte, + szInternalGraphicIdentifierEng, szInternalGraphicIdentifierCat, + szInternalGraphicIdentifierSpa, 'N', MM_MIN_WIDTH_ID_GRAFIC, 0); + bd_xp->IdGraficField = 0; + (bd_xp->pField + i_camp)->GeoTopoTypeField = (MM_BYTE)MM_CAMP_ES_ID_GRAFIC; + i_camp++; + + MM_FillFieldDB_XP(bd_xp->pField + i_camp, szMMNomCampArcsANodeDefecte, + szNumberOfArcsToNodeEng, szNumberOfArcsToNodeCat, + szNumberOfArcsToNodeSpa, 'N', MM_MIN_WIDTH_ARCS_TO_NODE, + 0); + (bd_xp->pField + i_camp)->GeoTopoTypeField = (MM_BYTE)MM_CAMP_ES_ARCS_A_NOD; + i_camp++; + + MM_FillFieldDB_XP(bd_xp->pField + i_camp, szMMNomCampTipusNodeDefecte, + szNodeTypeEng, szNodeTypeCat, szNodeTypeSpa, 'N', 1, 0); + (bd_xp->pField + i_camp)->GeoTopoTypeField = (MM_BYTE)MM_CAMP_ES_TIPUS_NODE; + i_camp++; + + return i_camp; +} + +size_t MM_DefineFirstPointFieldsDB_XP(struct MM_DATA_BASE_XP *bd_xp) +{ + size_t i_camp = 0; + + MM_FillFieldDB_XP( + bd_xp->pField + i_camp, szMMNomCampIdGraficDefecte, + szInternalGraphicIdentifierEng, szInternalGraphicIdentifierCat, + szInternalGraphicIdentifierSpa, 'N', MM_MIN_WIDTH_ID_GRAFIC, 0); + bd_xp->IdGraficField = 0; + (bd_xp->pField + i_camp)->GeoTopoTypeField = (MM_BYTE)MM_CAMP_ES_ID_GRAFIC; + i_camp++; + + return i_camp; +} + +static int MM_SprintfDoubleWidth(char *cadena, size_t cadena_size, int amplada, + int n_decimals, double valor_double, + MM_BOOLEAN *Error_sprintf_n_decimals) +{ +#define VALOR_LIMIT_IMPRIMIR_EN_FORMAT_E 1E+17 +#define VALOR_MASSA_PETIT_PER_IMPRIMIR_f 1E-17 + char cadena_treball[MM_CHARACTERS_DOUBLE + 1]; + int retorn_printf; + + if (MM_IsNANDouble(valor_double)) + { + if (amplada < 3) + { + *cadena = *MM_EmptyString; + return EOF; + } + return snprintf(cadena, cadena_size, "NAN"); + } + if (MM_IsDoubleInfinite(valor_double)) + { + if (amplada < 3) + { + *cadena = *MM_EmptyString; + return EOF; + } + return snprintf(cadena, cadena_size, "INF"); + } + + *Error_sprintf_n_decimals = FALSE; + if (valor_double == 0) + { + retorn_printf = snprintf(cadena_treball, sizeof(cadena_treball), + "%*.*f", amplada, n_decimals, valor_double); + if (retorn_printf >= (int)sizeof(cadena_treball)) + { + *cadena = *MM_EmptyString; + return retorn_printf; + } + + if (retorn_printf > amplada) + { + int escurcament = retorn_printf - amplada; + if (escurcament > n_decimals) + { + *cadena = *MM_EmptyString; + return EOF; + } + *Error_sprintf_n_decimals = TRUE; + n_decimals = n_decimals - escurcament; + retorn_printf = snprintf(cadena, cadena_size, "%*.*f", amplada, + n_decimals, valor_double); + } + else + CPLStrlcpy(cadena, cadena_treball, cadena_size); + + return retorn_printf; + } + + if (valor_double > VALOR_LIMIT_IMPRIMIR_EN_FORMAT_E || + valor_double < -VALOR_LIMIT_IMPRIMIR_EN_FORMAT_E || + (valor_double < VALOR_MASSA_PETIT_PER_IMPRIMIR_f && + valor_double > -VALOR_MASSA_PETIT_PER_IMPRIMIR_f)) + { + retorn_printf = snprintf(cadena_treball, sizeof(cadena_treball), + "%*.*E", amplada, n_decimals, valor_double); + + if (retorn_printf >= (int)sizeof(cadena_treball)) + { + *cadena = *MM_EmptyString; + return retorn_printf; + } + if (retorn_printf > amplada) + { + int escurcament = retorn_printf - amplada; + if (escurcament > n_decimals) + { + *cadena = *MM_EmptyString; + return EOF; + } + *Error_sprintf_n_decimals = TRUE; + n_decimals = n_decimals - escurcament; + retorn_printf = snprintf(cadena, cadena_size, "%*.*E", amplada, + n_decimals, valor_double); + } + else + CPLStrlcpy(cadena, cadena_treball, cadena_size); + + return retorn_printf; + } + + retorn_printf = snprintf(cadena_treball, sizeof(cadena_treball), "%*.*f", + amplada, n_decimals, valor_double); + + if (retorn_printf >= (int)sizeof(cadena_treball)) + { + *cadena = *MM_EmptyString; + return retorn_printf; + } + + if (retorn_printf > amplada) + { + int escurcament = retorn_printf - amplada; + if (escurcament > n_decimals) + { + *cadena = *MM_EmptyString; + return EOF; + } + *Error_sprintf_n_decimals = TRUE; + n_decimals = n_decimals - escurcament; + retorn_printf = snprintf(cadena, cadena_size, "%*.*f", amplada, + n_decimals, valor_double); + } + else + CPLStrlcpy(cadena, cadena_treball, cadena_size); + + return retorn_printf; + +#undef VALOR_LIMIT_IMPRIMIR_EN_FORMAT_E +#undef VALOR_MASSA_PETIT_PER_IMPRIMIR_f +} // End of MM_SprintfDoubleWidth() + +static MM_BOOLEAN MM_EmptyString_function(const char *cadena) +{ + const char *ptr = cadena; + + for (; *ptr; ptr++) + { + if (*ptr != ' ' && *ptr != '\t') + { + return FALSE; + } + } + + return TRUE; +} + +int MM_SecureCopyStringFieldValue(char **pszStringDst, const char *pszStringSrc, + MM_EXT_DBF_N_FIELDS *nStringCurrentLength) +{ + + if (!pszStringSrc) + { + if (1 >= *nStringCurrentLength) + { + void *new_ptr = realloc_function(*pszStringDst, 2); + if (!new_ptr) + return 1; + *pszStringDst = new_ptr; + *nStringCurrentLength = (MM_EXT_DBF_N_FIELDS)2; + } + strcpy(*pszStringDst, "\0"); + return 0; + } + + if (strlen(pszStringSrc) >= *nStringCurrentLength) + { + void *new_ptr = + realloc_function(*pszStringDst, strlen(pszStringSrc) + 1); + if (!new_ptr) + return 1; + (*pszStringDst) = new_ptr; + *nStringCurrentLength = (MM_EXT_DBF_N_FIELDS)(strlen(pszStringSrc) + 1); + } + strcpy(*pszStringDst, pszStringSrc); + return 0; +} + +// This function assumes that all the file is saved in disk and closed. +int MM_ChangeDBFWidthField(struct MM_DATA_BASE_XP *data_base_XP, + MM_EXT_DBF_N_FIELDS nIField, + MM_BYTES_PER_FIELD_TYPE_DBF nNewWidth, + MM_BYTE nNewPrecision, + MM_BYTE que_fer_amb_reformatat_decimals) +{ + char *record, *whites = nullptr; + MM_BYTES_PER_FIELD_TYPE_DBF l_glop1, l_glop2, i_glop2; + MM_EXT_DBF_N_RECORDS nfitx, i_reg; + int canvi_amplada; // change width + GInt32 j; + MM_EXT_DBF_N_FIELDS i_camp; + size_t retorn_fwrite; + int retorn_TruncaFitxer; + + MM_BOOLEAN error_sprintf_n_decimals = FALSE; + + canvi_amplada = nNewWidth - data_base_XP->pField[nIField].BytesPerField; + + if (data_base_XP->nRecords != 0) + { + l_glop1 = data_base_XP->pField[nIField].AccumulatedBytes; + i_glop2 = l_glop1 + data_base_XP->pField[nIField].BytesPerField; + if (nIField == data_base_XP->nFields - 1) + l_glop2 = 0; + else + l_glop2 = data_base_XP->BytesPerRecord - + data_base_XP->pField[nIField + 1].AccumulatedBytes; + + if ((record = calloc_function((size_t)data_base_XP->BytesPerRecord)) == + nullptr) + return 1; + + record[data_base_XP->BytesPerRecord - 1] = MM_SetEndOfString; + + if ((whites = (char *)calloc_function((size_t)nNewWidth)) == nullptr) + { + free_function(record); + return 1; + } + memset(whites, ' ', nNewWidth); + + nfitx = data_base_XP->nRecords; + i_reg = (canvi_amplada < 0 ? 0 : nfitx - 1); + while (TRUE) + { + if (0 != fseek_function(data_base_XP->pfDataBase, + data_base_XP->FirstRecordOffset + + (MM_FILE_OFFSET)i_reg * + data_base_XP->BytesPerRecord, + SEEK_SET)) + { + free_function(whites); + free_function(record); + return 1; + } + + if (1 != fread_function(record, data_base_XP->BytesPerRecord, 1, + data_base_XP->pfDataBase)) + { + free_function(whites); + free_function(record); + return 1; + } + + if (0 != + fseek_function( + data_base_XP->pfDataBase, + (MM_FILE_OFFSET)data_base_XP->FirstRecordOffset + + i_reg * ((MM_FILE_OFFSET)data_base_XP->BytesPerRecord + + canvi_amplada), + SEEK_SET)) + { + free_function(whites); + free_function(record); + return 1; + } + + if (1 != + fwrite_function(record, l_glop1, 1, data_base_XP->pfDataBase)) + { + free_function(whites); + free_function(record); + return 1; + } + + switch (data_base_XP->pField[nIField].FieldType) + { + case 'C': + case 'L': + memcpy(whites, record + l_glop1, + (canvi_amplada < 0 + ? nNewWidth + : data_base_XP->pField[nIField].BytesPerField)); + retorn_fwrite = fwrite_function(whites, nNewWidth, 1, + data_base_XP->pfDataBase); + + if (1 != retorn_fwrite) + { + free_function(whites); + free_function(record); + return 1; + } + break; + case 'N': + if (nNewPrecision == + data_base_XP->pField[nIField].DecimalsIfFloat || + que_fer_amb_reformatat_decimals == + MM_NOU_N_DECIMALS_NO_APLICA) + que_fer_amb_reformatat_decimals = + MM_NOMES_DOCUMENTAR_NOU_N_DECIMALS; + else if (que_fer_amb_reformatat_decimals == + MM_PREGUNTA_SI_APLICAR_NOU_N_DECIM) + que_fer_amb_reformatat_decimals = + MM_NOMES_DOCUMENTAR_NOU_N_DECIMALS; + + if (que_fer_amb_reformatat_decimals == + MM_NOMES_DOCUMENTAR_NOU_N_DECIMALS) + { + if (canvi_amplada >= 0) + { + if (1 != + fwrite_function(whites, canvi_amplada, 1, + data_base_XP->pfDataBase) || + 1 != fwrite_function( + record + l_glop1, + data_base_XP->pField[nIField] + .BytesPerField, + 1, data_base_XP->pfDataBase)) + { + free_function(whites); + free_function(record); + return 1; + } + } + else if (canvi_amplada < 0) + { + j = (GInt32)(l_glop1 + + (data_base_XP->pField[nIField] + .BytesPerField - + 1)); + while (TRUE) + { + j--; + + if (j < (GInt32)l_glop1 || record[j] == ' ') + { + j++; + break; + } + } + + if ((data_base_XP->pField[nIField].BytesPerField + + l_glop1 - j) < nNewWidth) + j -= (GInt32)(nNewWidth - + (data_base_XP->pField[nIField] + .BytesPerField + + l_glop1 - j)); + + retorn_fwrite = + fwrite_function(record + j, nNewWidth, 1, + data_base_XP->pfDataBase); + if (1 != retorn_fwrite) + { + free_function(whites); + free_function(record); + return 1; + } + } + } + else // MM_APLICAR_NOU_N_DECIMALS + { + double valor; + char *sz_valor; + size_t sz_valor_size = + max_function( + nNewWidth, + data_base_XP->pField[nIField].BytesPerField) + + 1; + + if ((sz_valor = calloc_function(sz_valor_size)) == + nullptr) // Sumo 1 per poder posar-hi el \0 + { + free_function(whites); + free_function(record); + return 1; + } + memcpy(sz_valor, record + l_glop1, + data_base_XP->pField[nIField].BytesPerField); + sz_valor[data_base_XP->pField[nIField].BytesPerField] = + 0; + + if (!MM_EmptyString_function(sz_valor)) + { + if (sscanf(sz_valor, "%lf", &valor) != 1) + memset( + sz_valor, *MM_BlankString, + max_function(nNewWidth, + data_base_XP->pField[nIField] + .BytesPerField)); + else + { + MM_SprintfDoubleWidth( + sz_valor, sz_valor_size, nNewWidth, + nNewPrecision, valor, + &error_sprintf_n_decimals); + } + + retorn_fwrite = + fwrite_function(sz_valor, nNewWidth, 1, + data_base_XP->pfDataBase); + if (1 != retorn_fwrite) + { + free_function(whites); + free_function(record); + free_function(sz_valor); + return 1; + } + } + else + { + memset(sz_valor, *MM_BlankString, nNewWidth); + retorn_fwrite = + fwrite_function(sz_valor, nNewWidth, 1, + data_base_XP->pfDataBase); + if (1 != retorn_fwrite) + { + free_function(whites); + free_function(record); + free_function(sz_valor); + return 1; + } + } + free_function(sz_valor); + } + break; + default: + free_function(whites); + free_function(record); + return 1; + } + if (l_glop2) + { + retorn_fwrite = fwrite_function(record + i_glop2, l_glop2, 1, + data_base_XP->pfDataBase); + if (1 != retorn_fwrite) + { + free_function(whites); + free_function(record); + return 1; + } + } + + if (canvi_amplada < 0) + { + if (i_reg + 1 == nfitx) + break; + i_reg++; + } + else + { + if (i_reg == 0) + break; + i_reg--; + } + } + + free_function(whites); + free_function(record); + + retorn_TruncaFitxer = TruncateFile_function( + data_base_XP->pfDataBase, + (MM_FILE_OFFSET)data_base_XP->FirstRecordOffset + + (MM_FILE_OFFSET)data_base_XP->nRecords * + ((MM_FILE_OFFSET)data_base_XP->BytesPerRecord + + canvi_amplada)); + if (canvi_amplada < 0 && retorn_TruncaFitxer) + return 1; + } /* Fi de registres de != 0*/ + + if (canvi_amplada != 0) + { + data_base_XP->pField[nIField].BytesPerField = nNewWidth; + data_base_XP->BytesPerRecord += canvi_amplada; + for (i_camp = (MM_EXT_DBF_N_FIELDS)(nIField + 1); + i_camp < data_base_XP->nFields; i_camp++) + data_base_XP->pField[i_camp].AccumulatedBytes += canvi_amplada; + } + data_base_XP->pField[nIField].DecimalsIfFloat = nNewPrecision; + + //DonaData(&(data_base_XP->day), &(data_base_XP->month), &(data_base_XP->year)); + + if ((MM_UpdateEntireHeader(data_base_XP)) == FALSE) + return 1; + + return 0; +} /* End of MMChangeCFieldWidthDBF() */ + +static void MM_AdoptHeight(double *desti, const double *proposta, uint32_t flag) +{ + if (*proposta == MM_NODATA_COORD_Z) + return; + + if (flag & MM_STRING_HIGHEST_ALTITUDE) + { + if (*desti == MM_NODATA_COORD_Z || *desti < *proposta) + *desti = *proposta; + } + else if (flag & MM_STRING_LOWEST_ALTITUDE) + { + if (*desti == MM_NODATA_COORD_Z || *desti > *proposta) + *desti = *proposta; + } + else + { + // First coordinate of this vertice + if (*desti == MM_NODATA_COORD_Z) + *desti = *proposta; + } +} + +int MM_GetArcHeights(double *coord_z, FILE_TYPE *pF, MM_N_VERTICES_TYPE n_vrt, + struct MM_ZD *pZDescription, uint32_t flag) +{ + MM_N_HEIGHT_TYPE i; + MM_N_VERTICES_TYPE i_vrt; + double *pcoord_z; + MM_N_HEIGHT_TYPE n_alcada, n_h_total; + int tipus; + double *alcada = nullptr, *palcada, *palcada_i; +#define MM_N_ALCADA_LOCAL 50 // Nr of local heights + double local_CinquantaAlcades[MM_N_ALCADA_LOCAL]; + + for (i_vrt = 0; i_vrt < n_vrt; i_vrt++) + coord_z[i_vrt] = MM_NODATA_COORD_Z; + + if (pZDescription->nZCount == INT_MIN) + return 0; + tipus = MM_ARC_HEIGHT_TYPE(pZDescription->nZCount); + n_alcada = MM_ARC_N_HEIGHTS(pZDescription->nZCount); + if (n_vrt == 0 || n_alcada == 0) + return 0; + + if (tipus == MM_ARC_HEIGHT_FOR_EACH_VERTEX) + { + if (n_vrt > (unsigned)(INT_MAX / n_alcada)) + { + MMCPLError(CE_Failure, CPLE_OutOfMemory, "Integer overflow"); + return 1; + } + n_h_total = (MM_N_HEIGHT_TYPE)n_vrt * n_alcada; + } + else + n_h_total = n_alcada; + + if (n_h_total <= MM_N_ALCADA_LOCAL) + palcada = local_CinquantaAlcades; + else + { + if (MMCheckSize_t(n_h_total, sizeof(double))) + return 1; + if (nullptr == (palcada = alcada = calloc_function((size_t)n_h_total * + sizeof(double)))) + return 1; + } + + if (fseek_function(pF, pZDescription->nOffsetZ, SEEK_SET)) + { + if (alcada) + free_function(alcada); + return 1; + } + if (n_h_total != (MM_N_HEIGHT_TYPE)fread_function(palcada, sizeof(double), + n_h_total, pF)) + { + if (alcada) + free_function(alcada); + return 1; + } + + if (tipus == MM_ARC_HEIGHT_FOR_EACH_VERTEX) + { + palcada_i = palcada; + for (i = 0; i < n_alcada; i++) + { + for (i_vrt = 0, pcoord_z = coord_z; i_vrt < n_vrt; + i_vrt++, pcoord_z++, palcada_i++) + MM_AdoptHeight(pcoord_z, palcada_i, flag); + } + } + else + { + palcada_i = palcada; + pcoord_z = coord_z; + for (i = 0; i < n_alcada; i++, palcada_i++) + MM_AdoptHeight(pcoord_z, palcada_i, flag); + + if (*pcoord_z != MM_NODATA_COORD_Z) + { + /*Copio el mateix valor a totes les alcades.*/ + for (i_vrt = 1, pcoord_z++; i_vrt < (size_t)n_vrt; + i_vrt++, pcoord_z++) + *pcoord_z = *coord_z; + } + } + if (alcada) + free_function(alcada); + return 0; +} // End of MM_GetArcHeights() + +static char *MM_l_RemoveWhitespacesFromEndOfString(char *punter, + size_t l_cadena) +{ + size_t longitud_cadena = l_cadena; + while (longitud_cadena > 0) + { + longitud_cadena--; + if (punter[longitud_cadena] != ' ' && punter[longitud_cadena] != '\t') + { + break; + } + punter[longitud_cadena] = '\0'; + } + return punter; +} + +char *MM_RemoveInitial_and_FinalQuotationMarks(char *cadena) +{ + char *ptr1, *ptr2; + char cometa = '"'; + + if (*cadena == cometa) + { + ptr1 = cadena; + ptr2 = ptr1 + 1; + if (*ptr2) + { + while (*ptr2) + { + *ptr1 = *ptr2; + ptr1++; + ptr2++; + } + if (*ptr1 == cometa) + *(ptr1 - 1) = 0; + else + *ptr1 = 0; + } + } + return cadena; +} /* End of MM_RemoveInitial_and_FinalQuotationMarks() */ + +char *MM_RemoveLeadingWhitespaceOfString(char *cadena) +{ + char *ptr; + char *ptr2; + + if (cadena == nullptr) + return cadena; + + for (ptr = cadena; *ptr && (*ptr == ' ' || *ptr == '\t'); ptr++) + continue; + + if (ptr != cadena) + { + ptr2 = cadena; + while (*ptr) + { + *ptr2 = *ptr; + ptr2++; + ptr++; + } + *ptr2 = 0; + } + return cadena; +} + +char *MM_RemoveWhitespacesFromEndOfString(char *str) +{ + if (str == nullptr) + return str; + return MM_l_RemoveWhitespacesFromEndOfString(str, strlen(str)); +} + +struct MM_ID_GRAFIC_MULTIPLE_RECORD *MMCreateExtendedDBFIndex( + FILE_TYPE *f, MM_EXT_DBF_N_RECORDS nNumberOfRecords, + MM_FIRST_RECORD_OFFSET_TYPE offset_1era, + MM_ACCUMULATED_BYTES_TYPE_DBF bytes_per_fitxa, + MM_ACCUMULATED_BYTES_TYPE_DBF bytes_acumulats_id_grafic, + MM_BYTES_PER_FIELD_TYPE_DBF bytes_id_grafic, MM_BOOLEAN *isListField, + MM_EXT_DBF_N_RECORDS *nMaxN) +{ + struct MM_ID_GRAFIC_MULTIPLE_RECORD *id; + MM_EXT_DBF_N_RECORDS i_dbf; + MM_EXT_DBF_SIGNED_N_RECORDS i, id_grafic; + char *fitxa; + MM_BYTES_PER_FIELD_TYPE_DBF bytes_final_id_principi_id1 = + bytes_per_fitxa - bytes_id_grafic; + + *isListField = FALSE; + *nMaxN = 0; + if (!nNumberOfRecords) + return nullptr; // No elements to read + + if (MMCheckSize_t(nNumberOfRecords, sizeof(*id))) + return nullptr; + if (nullptr == (id = (struct MM_ID_GRAFIC_MULTIPLE_RECORD *)calloc_function( + (size_t)nNumberOfRecords * sizeof(*id)))) + return nullptr; + + if (bytes_id_grafic == UINT32_MAX) + { + free_function(id); + MMCPLError(CE_Failure, CPLE_OutOfMemory, + "Overflow in bytes_id_graphic"); + return nullptr; + } + + if (nullptr == + (fitxa = (char *)calloc_function((size_t)bytes_id_grafic + 1))) + { + free_function(id); + return nullptr; + } + fitxa[bytes_id_grafic] = '\0'; + + fseek_function(f, + (MM_FILE_OFFSET)offset_1era + + (MM_FILE_OFFSET)bytes_acumulats_id_grafic, + SEEK_SET); + + i_dbf = 0; + do + { + if (i_dbf == nNumberOfRecords || + fread_function(fitxa, 1, bytes_id_grafic, f) != + (size_t)bytes_id_grafic) + { + free_function(id); + free_function(fitxa); + return nullptr; + } + i_dbf++; + } while (1 != + sscanf(fitxa, scanf_MM_EXT_DBF_SIGNED_N_RECORDS, &id_grafic) || + id_grafic < 0); + i = 0; + + while (TRUE) + { + if (i > id_grafic) + { + free_function(id); + free_function(fitxa); + return nullptr; + } + i = id_grafic; + if (i >= (MM_EXT_DBF_SIGNED_N_RECORDS)nNumberOfRecords) + { + free_function(fitxa); + return id; + } + id[(size_t)i].offset = (MM_FILE_OFFSET)offset_1era + + (MM_FILE_OFFSET)(i_dbf - 1) * bytes_per_fitxa; + do + { + id[(size_t)i].nMR++; + if (!(*isListField) && id[(size_t)i].nMR > 1) + *isListField = TRUE; + if (*nMaxN < id[(size_t)i].nMR) + *nMaxN = id[(size_t)i].nMR; + + if (i_dbf == nNumberOfRecords) + { + free_function(fitxa); + return id; + } + fseek_function(f, bytes_final_id_principi_id1, SEEK_CUR); + if (fread_function(fitxa, 1, bytes_id_grafic, f) != + (size_t)bytes_id_grafic) + { + free_function(id); + free_function(fitxa); + return nullptr; + } + if (1 != sscanf(fitxa, scanf_MM_EXT_DBF_SIGNED_N_RECORDS, + &id_grafic) || + id_grafic >= (MM_EXT_DBF_SIGNED_N_RECORDS)nNumberOfRecords) + { + free_function(fitxa); + return id; + } + i_dbf++; + } while (id_grafic == i); + } +} // End of MMCreateExtendedDBFIndex() + +#ifdef GDAL_COMPILATION +CPL_C_END // Necessary for compiling in GDAL project +#endif diff --git a/ogr/ogrsf_frmts/miramon/mm_gdal_functions.h b/ogr/ogrsf_frmts/miramon/mm_gdal_functions.h new file mode 100644 index 000000000000..a0feda45dbda --- /dev/null +++ b/ogr/ogrsf_frmts/miramon/mm_gdal_functions.h @@ -0,0 +1,164 @@ +#ifndef __MM_GDAL_FUNCTIONS_H +#define __MM_GDAL_FUNCTIONS_H +/* -------------------------------------------------------------------- */ +/* Constants used in GDAL and in MiraMon */ +/* -------------------------------------------------------------------- */ + +#ifdef GDAL_COMPILATION +#include "mm_gdal_constants.h" // MM_BYTE +#include "mm_gdal_structures.h" // struct BASE_DADES_XP +#include "mm_gdal_driver_structs.h" // struct MMAdmDatabase +CPL_C_START // Necessary for compiling in GDAL project +#else +#include "mm_constants.h" // MM_BYTE +#include "mm_gdal\mm_gdal_structures.h" // struct BASE_DADES_XP +#include "mm_gdal\mm_gdal_driver_structs.h" // struct MMAdmDatabase +#endif + +#define nullptr NULL + + // Log. It should be temporal + extern const char *MM_pszLogFilename; + +#define LOG_STR(str) (MMLog((str), __LINE__)) +#define LOG_ACTION(action) ((void)MMLog(#action, __LINE__), (action)) + +const char *MMLog(const char *pszMsg, int nLineNumber); + +void fclose_and_nullify(FILE_TYPE **pFunc); + +// MiraMon feature table descriptors +#define MM_MAX_IDENTIFIER_SIZE 50 +#define MM_a_WITH_GRAVE 224 +#define MM_a_WITH_ACUTE 225 +#define MM_e_WITH_GRAVE 232 +#define MM_e_WITH_ACUTE 233 +#define MM_i_WITH_ACUTE 237 +#define MM_o_WITH_GRAVE 242 +#define MM_o_WITH_ACUTE 243 +#define MM_u_WITH_ACUTE 250 + +#define MM_A_WITH_GRAVE 192 +#define MM_A_WITH_ACUTE 193 +#define MM_E_WITH_GRAVE 200 +#define MM_E_WITH_ACUTE 201 +#define MM_I_WITH_ACUTE 205 +#define MM_O_WITH_GRAVE 210 +#define MM_O_WITH_ACUTE 211 +#define MM_U_WITH_ACUTE 218 + +// In case of diaeresis use "_WITH_DIAERESIS" +// In case of cedilla use "_WITH_CEDILLA" +// In case of tilde use "_WITH_TILDE" +// In case of middle dot use "_MIDDLE_DOT" + +void MM_FillFieldDescriptorByLanguage(void); + +extern char szInternalGraphicIdentifierEng[]; +extern char szInternalGraphicIdentifierCat[]; +extern char szInternalGraphicIdentifierSpa[]; + +extern char szNumberOfVerticesEng[]; +extern char szNumberOfVerticesCat[]; +extern char szNumberOfVerticesSpa[]; + +extern char szLengthOfAarcEng[]; +extern char szLengthOfAarcCat[]; +extern char szLengthOfAarcSpa[]; + +extern char szInitialNodeEng[]; +extern char szInitialNodeCat[]; +extern char szInitialNodeSpa[]; + +extern char szFinalNodeEng[]; +extern char szFinalNodeCat[]; +extern char szFinalNodeSpa[]; + +extern char szNumberOfArcsToNodeEng[]; +extern char szNumberOfArcsToNodeCat[]; +extern char szNumberOfArcsToNodeSpa[]; + +extern char szNodeTypeEng[]; +extern char szNodeTypeCat[]; +extern char szNodeTypeSpa[]; + +extern char szPerimeterOfThePolygonEng[]; +extern char szPerimeterOfThePolygonCat[]; +extern char szPerimeterOfThePolygonSpa[]; + +extern char szAreaOfThePolygonEng[]; +extern char szAreaOfThePolygonCat[]; +extern char szAreaOfThePolygonSpa[]; + +extern char szNumberOfArcsEng[]; +extern char szNumberOfArcsCat[]; +extern char szNumberOfArcsSpa[]; + +extern char szNumberOfElementaryPolygonsEng[]; +extern char szNumberOfElementaryPolygonsCat[]; +extern char szNumberOfElementaryPolygonsSpa[]; + +#ifndef GDAL_COMPILATION +char *CPLStrlcpy(char *dest, const char *src, size_t maxlen); +#endif +char *MM_oemansi(char *szcadena); +char *MM_oemansi_n(char *szcadena, size_t n_bytes); +void MM_InitializeField(struct MM_FIELD *camp); +struct MM_FIELD *MM_CreateAllFields(MM_EXT_DBF_N_FIELDS ncamps); +MM_FIRST_RECORD_OFFSET_TYPE +MM_GiveOffsetExtendedFieldName(const struct MM_FIELD *camp); +struct MM_DATA_BASE_XP *MM_CreateDBFHeader(MM_EXT_DBF_N_FIELDS n_camps, + MM_BYTE nCharSet); +MM_BYTE MM_DBFFieldTypeToVariableProcessing(MM_BYTE tipus_camp_DBF); +void MM_ReleaseMainFields(struct MM_DATA_BASE_XP *data_base_XP); +void MM_ReleaseDBFHeader(struct MM_DATA_BASE_XP *data_base_XP); +MM_BOOLEAN MM_CreateDBFFile(struct MM_DATA_BASE_XP *bd_xp, + const char *NomFitxer); +int MM_DuplicateFieldDBXP(struct MM_FIELD *camp_final, + const struct MM_FIELD *camp_inicial); +int MM_WriteNRecordsMMBD_XPFile(struct MMAdmDatabase *MMAdmDB); + +size_t MM_DefineFirstPolygonFieldsDB_XP(struct MM_DATA_BASE_XP *bd_xp); +size_t MM_DefineFirstArcFieldsDB_XP(struct MM_DATA_BASE_XP *bd_xp); +size_t MM_DefineFirstNodeFieldsDB_XP(struct MM_DATA_BASE_XP *bd_xp); +size_t MM_DefineFirstPointFieldsDB_XP(struct MM_DATA_BASE_XP *bd_xp); +int MM_ModifyFieldNameAndDescriptorIfPresentBD_XP( + struct MM_FIELD *camp, struct MM_DATA_BASE_XP *bd_xp, + MM_BOOLEAN no_modifica_descriptor, size_t mida_nom); + +int MMWriteValueToRecordDBXP(struct MiraMonVectLayerInfo *hMiraMonLayer, + char *registre, const struct MM_FIELD *camp, + const void *valor, MM_BOOLEAN is_64); +int MM_SecureCopyStringFieldValue(char **pszStringDst, const char *pszStringSrc, + MM_EXT_DBF_N_FIELDS *nStringCurrentLength); +int MM_ChangeDBFWidthField(struct MM_DATA_BASE_XP *data_base_XP, + MM_EXT_DBF_N_FIELDS quincamp, + MM_BYTES_PER_FIELD_TYPE_DBF novaamplada, + MM_BYTE nou_decimals, + MM_BYTE que_fer_amb_reformatat_decimals); + +int MM_GetArcHeights(double *coord_z, FILE_TYPE *pF, MM_N_VERTICES_TYPE n_vrt, + struct MM_ZD *pZDescription, uint32_t flag); + +// Strings +char *MM_RemoveInitial_and_FinalQuotationMarks(char *cadena); +char *MM_RemoveWhitespacesFromEndOfString(char *str); +char *MM_RemoveLeadingWhitespaceOfString(char *cadena); + +// DBF +struct MM_ID_GRAFIC_MULTIPLE_RECORD *MMCreateExtendedDBFIndex( + FILE_TYPE *f, MM_EXT_DBF_N_RECORDS n_dbf, + MM_FIRST_RECORD_OFFSET_TYPE offset_1era, + MM_ACCUMULATED_BYTES_TYPE_DBF bytes_per_fitxa, + MM_ACCUMULATED_BYTES_TYPE_DBF bytes_acumulats_id_grafic, + MM_BYTES_PER_FIELD_TYPE_DBF bytes_id_grafic, MM_BOOLEAN *isListField, + MM_EXT_DBF_N_RECORDS *nMaxN); + +int MM_ReadExtendedDBFHeaderFromFile(const char *szFileName, + struct MM_DATA_BASE_XP *pMMBDXP, + const char *pszRelFile); + +#ifdef GDAL_COMPILATION +CPL_C_END // Necessary for compiling in GDAL project +#endif +#endif //__MM_GDAL_FUNCTIONS_H diff --git a/ogr/ogrsf_frmts/miramon/mm_gdal_structures.h b/ogr/ogrsf_frmts/miramon/mm_gdal_structures.h new file mode 100644 index 000000000000..58133dca3ff1 --- /dev/null +++ b/ogr/ogrsf_frmts/miramon/mm_gdal_structures.h @@ -0,0 +1,116 @@ +#ifndef __MM_GDAL_STRUCTURES_H +#define __MM_GDAL_STRUCTURES_H +/* -------------------------------------------------------------------- */ +/* Constants used in GDAL and in MiraMon */ +/* -------------------------------------------------------------------- */ +#ifdef GDAL_COMPILATION +#include "cpl_conv.h" // For FILE_TYPE +#include "mm_gdal_constants.h" +#else +#include "F64_str.h" // For FILE_64 +#include "mm_gdal\mm_gdal_constants.h" +#endif + +#include "mm_constants.h" + +#ifdef GDAL_COMPILATION +CPL_C_START // Necessary for compiling in GDAL project +#endif + +#ifdef GDAL_COMPILATION +#define FILE_TYPE VSILFILE +#else +#define FILE_TYPE FILE_64 +#endif + + /* Internal field of an extended DBF. It is a copy of a MiraMon internal +structure but translated to be understood by anyone who wants to +review the code of the driver. +*/ + + struct MM_FIELD // In MiraMon code: MM_CAMP +{ + // Name of the field + char FieldName[MM_MAX_LON_FIELD_NAME_DBF]; // In MiraMon code: NomCamp + + // Name of the field in dBASEIII + char ClassicalDBFFieldName + [MM_MAX_LON_CLASSICAL_FIELD_NAME_DBF]; // In MiraMon code: + // NomCampDBFClassica + + // Type of the field C, N, D, L, M, F, G and B + char FieldType; // In MiraMon code: TipusDeCamp + MM_BOOLEAN Is64; // Is an signed 64 bit integer + + // Number of decimal places if it is a float + MM_BYTE DecimalsIfFloat; // In MiraMon code: DecimalsSiEsFloat + + // Number of bytes of a field + MM_BYTES_PER_FIELD_TYPE_DBF + BytesPerField; // In MiraMon code: MM_TIPUS_BYTES_PER_CAMP_DBF BytesPerCamp + + // Accumulated bytes before a field starts + MM_ACCUMULATED_BYTES_TYPE_DBF + AccumulatedBytes; // In MiraMon code: + // MM_TIPUS_BYTES_ACUMULATS_DBF BytesAcumulats + + // Not used in GDAL + char + *Separator[MM_NUM_IDIOMES_MD_MULTIDIOMA]; // In MiraMon code: separador + + // Description of the field (alternative name) + char FieldDescription + [MM_NUM_IDIOMES_MD_MULTIDIOMA] + [MM_MAX_LON_DESCRIPCIO_CAMP_DBF]; // In MiraMon code: DescripcioCamp + + MM_BYTE DesiredWidth; // In MiraMon code: AmpleDesitjat + MM_BYTE OriginalDesiredWidth; // In MiraMon code: AmpleDesitjatOriginal + + MM_BYTE reserved_1 + [MM_MAX_LON_RESERVAT_1_CAMP_BD_XP]; // In MiraMon code: reservat_1 + + MM_BYTE reserved_2 + [MM_MAX_LON_RESERVAT_2_CAMP_BD_XP]; // In MiraMon code: reservat_2 + MM_BYTE MDX_field_flag; // In MiraMon code: MDX_camp_flag + MM_BYTE GeoTopoTypeField; // In MiraMon code: TipusCampGeoTopo +}; + +struct MM_DATA_BASE_XP // MiraMon table Structure +{ + // Extended DBF file name + char szFileName[MM_CPL_PATH_BUF_SIZE]; // In MiraMon code: szNomFitxer + + FILE_TYPE *pfDataBase; // In MiraMon code: pfBaseDades + + // Charset of the DBF + MM_BYTE CharSet; + + char ReadingMode[4]; // In MiraMon code: ModeLectura + MM_EXT_DBF_N_RECORDS nRecords; // In MiraMon code: n_fitxes + MM_ACCUMULATED_BYTES_TYPE_DBF + BytesPerRecord; // In MiraMon code: BytesPerFitxa + MM_EXT_DBF_N_FIELDS nFields; // In MiraMon code: ncamps + struct MM_FIELD *pField; // In MiraMon code: Camp + MM_FIRST_RECORD_OFFSET_TYPE + FirstRecordOffset; // In MiraMon code: OffsetPrimeraFitxa + MM_EXT_DBF_N_FIELDS IdGraficField; // In MiraMon code: CampIdGrafic + MM_EXT_DBF_N_FIELDS IdEntityField; // In MiraMon code: CampIdEntitat + short int year; // In MiraMon code: any + MM_BYTE month; // In MiraMon code: mes + MM_BYTE day; // In MiraMon code: dia + + MM_BYTE dbf_version; // In MiraMon code: versio_dbf + + MM_BYTE reserved_1 // Used in extended DBF format to recompose BytesPerRecord + [MM_MAX_LON_RESERVAT_1_BASE_DADES_XP]; // In MiraMon code: reservat_1 + MM_BYTE transaction_flag; + MM_BYTE encryption_flag; + MM_BYTE dbf_on_a_LAN[MM_MAX_LON_DBF_ON_A_LAN_BASE_DADES_XP]; + MM_BYTE MDX_flag; + MM_BYTE reserved_2 // Used in extended DBF format to recompose BytesPerRecord + [MM_MAX_LON_RESERVAT_2_BASE_DADES_XP]; // In MiraMon code: reservat_2 +}; +#ifdef GDAL_COMPILATION +CPL_C_END // Necessary for compiling in GDAL project +#endif +#endif //__MM_GDAL_STRUCTURES_H diff --git a/ogr/ogrsf_frmts/miramon/mm_rdlayr.c b/ogr/ogrsf_frmts/miramon/mm_rdlayr.c new file mode 100644 index 000000000000..4a8a2ec09126 --- /dev/null +++ b/ogr/ogrsf_frmts/miramon/mm_rdlayr.c @@ -0,0 +1,707 @@ +/****************************************************************************** + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: C API to read a MiraMon layer + * Author: Abel Pau, a.pau@creaf.uab.cat, based on the MiraMon codes, + * mainly written by Xavier Pons, Joan Maso (correctly written + * "Mas0xF3"), Abel Pau, Nuria Julia (N0xFAria Juli0xE0), + * Xavier Calaf, Lluis (Llu0xEDs) Pesquer and Alaitz Zabala, from + * CREAF and Universitat Autonoma (Aut0xF2noma) de Barcelona. + * For a complete list of contributors: + * https://www.miramon.cat/eng/QuiSom.htm + ****************************************************************************** + * Copyright (c) 2024, Xavier Pons + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifdef GDAL_COMPILATION +#include "ogr_api.h" // For CPL_C_START +#include "mm_wrlayr.h" +#include "mm_wrlayr.h" // For MMReadHeader() +#include "mm_gdal_functions.h" +#include "mm_gdal_constants.h" +#else +#include "CmptCmp.h" // Compatibility between compilers +#include "mm_gdal\mm_wrlayr.h" // For MMReadHeader() +#include "mm_gdal\mm_gdal_functions.h" // For int MM_GetArcHeights() +#include "mm_constants.h" +#endif + +#include "mm_rdlayr.h" + +#ifdef GDAL_COMPILATION +CPL_C_START // Necessary for compiling in GDAL project +#endif + + /* -------------------------------------------------------------------- */ + /* Reading MiraMon format file functions */ + /* -------------------------------------------------------------------- */ + + // Initializes a MiraMon vector layer for reading + int + MMInitLayerToRead(struct MiraMonVectLayerInfo *hMiraMonLayer, + FILE_TYPE *m_fp, const char *pszFilename) +{ + char szResult[MM_MAX_ID_SNY + 10]; + char *pszSRS; + + memset(hMiraMonLayer, 0, sizeof(*hMiraMonLayer)); + if (MMReadHeader(m_fp, &hMiraMonLayer->TopHeader)) + { + MMCPLError(CE_Failure, CPLE_NoWriteAccess, + "Error reading header of file %s", pszFilename); + return 1; + } + hMiraMonLayer->ReadOrWrite = MM_READING_MODE; + strcpy(hMiraMonLayer->pszFlags, "rb"); + + hMiraMonLayer->pszSrcLayerName = strdup_function(pszFilename); + + hMiraMonLayer->LayerVersion = + (char)MMGetVectorVersion(&hMiraMonLayer->TopHeader); + if (hMiraMonLayer->LayerVersion == MM_UNKNOWN_VERSION) + { + MMCPLError(CE_Failure, CPLE_NotSupported, + "MiraMon version file unknown."); + return 1; + } + if (hMiraMonLayer->LayerVersion == MM_LAST_VERSION) + hMiraMonLayer->nHeaderDiskSize = MM_HEADER_SIZE_64_BITS; + else if (hMiraMonLayer->LayerVersion == MM_32BITS_VERSION) + hMiraMonLayer->nHeaderDiskSize = MM_HEADER_SIZE_32_BITS; + else + hMiraMonLayer->nHeaderDiskSize = MM_HEADER_SIZE_64_BITS; + + if (hMiraMonLayer->TopHeader.aFileType[0] == 'P' && + hMiraMonLayer->TopHeader.aFileType[1] == 'N' && + hMiraMonLayer->TopHeader.aFileType[2] == 'T') + { + if (hMiraMonLayer->TopHeader.Flag & MM_LAYER_3D_INFO) + { + hMiraMonLayer->TopHeader.bIs3d = 1; + hMiraMonLayer->eLT = MM_LayerType_Point3d; + } + else + hMiraMonLayer->eLT = MM_LayerType_Point; + + hMiraMonLayer->bIsPoint = TRUE; + } + else if (hMiraMonLayer->TopHeader.aFileType[0] == 'A' && + hMiraMonLayer->TopHeader.aFileType[1] == 'R' && + hMiraMonLayer->TopHeader.aFileType[2] == 'C') + { + if (hMiraMonLayer->TopHeader.Flag & MM_LAYER_3D_INFO) + { + hMiraMonLayer->TopHeader.bIs3d = 1; + hMiraMonLayer->eLT = MM_LayerType_Arc3d; + } + else + hMiraMonLayer->eLT = MM_LayerType_Arc; + + hMiraMonLayer->bIsArc = TRUE; + } + else if (hMiraMonLayer->TopHeader.aFileType[0] == 'P' && + hMiraMonLayer->TopHeader.aFileType[1] == 'O' && + hMiraMonLayer->TopHeader.aFileType[2] == 'L') + { + // 3D + if (hMiraMonLayer->TopHeader.Flag & MM_LAYER_3D_INFO) + { + hMiraMonLayer->TopHeader.bIs3d = 1; + hMiraMonLayer->eLT = MM_LayerType_Pol3d; + } + else + hMiraMonLayer->eLT = MM_LayerType_Pol; + + hMiraMonLayer->bIsPolygon = TRUE; + + if (hMiraMonLayer->TopHeader.Flag & MM_LAYER_MULTIPOLYGON) + hMiraMonLayer->TopHeader.bIsMultipolygon = 1; + } + + //hMiraMonLayer->Version = MM_VECTOR_LAYER_LAST_VERSION; + + if (MMInitLayerByType(hMiraMonLayer)) + return 1; + hMiraMonLayer->bIsBeenInit = 1; + + // Get the basic metadata + pszSRS = MMReturnValueFromSectionINIFile( + hMiraMonLayer->pszMainREL_LayerName, + "SPATIAL_REFERENCE_SYSTEM:HORIZONTAL", "HorizontalSystemIdentifier"); + if (pszSRS) + hMiraMonLayer->pSRS = pszSRS; + else + hMiraMonLayer->pSRS = nullptr; + + if (!hMiraMonLayer->pSRS && hMiraMonLayer->bIsPolygon) + { + pszSRS = MMReturnValueFromSectionINIFile( + hMiraMonLayer->MMPolygon.MMArc.pszREL_LayerName, + "SPATIAL_REFERENCE_SYSTEM:HORIZONTAL", + "HorizontalSystemIdentifier"); + + hMiraMonLayer->pSRS = pszSRS; + } + + if (!ReturnEPSGCodeSRSFromMMIDSRS(hMiraMonLayer->pSRS, szResult)) + { + if (MMIsEmptyString(szResult)) + hMiraMonLayer->nSRS_EPSG = 0; + else + hMiraMonLayer->nSRS_EPSG = atoi(szResult); + } + else + hMiraMonLayer->nSRS_EPSG = 0; + + if (hMiraMonLayer->nSRS_EPSG == 0) + { + if (hMiraMonLayer->pSRS && strcmp(hMiraMonLayer->pSRS, "plane")) + { + MMCPLWarning(CE_Warning, CPLE_NotSupported, + "The MiraMon layer SRS has no equivalent " + "in EPSG code"); + } + } + + // If more nNumStringToOperate is needed, it'll be increased. + hMiraMonLayer->nNumStringToOperate = 0; + if (MMResizeStringToOperateIfNeeded(hMiraMonLayer, 5000)) + return 1; + + return 0; +} + +// Reads stringline coordinates and puts them in a buffer +static int +MMAddStringLineCoordinates(struct MiraMonVectLayerInfo *hMiraMonLayer, + MM_INTERNAL_FID i_elem, uint32_t flag_z, + MM_N_VERTICES_TYPE nStartVertice, + MM_BOOLEAN bAvoidFirst, unsigned char VFG) +{ + FILE_TYPE *pF; + struct MM_AH *pArcHeader; + struct MiraMonArcLayer *pMMArc; + struct MM_ZD *pZDescription = nullptr; + + if (hMiraMonLayer->bIsPolygon) + pMMArc = &hMiraMonLayer->MMPolygon.MMArc; + else + pMMArc = &hMiraMonLayer->MMArc; + + pF = pMMArc->pF; + pArcHeader = pMMArc->pArcHeader; + if (hMiraMonLayer->TopHeader.bIs3d) + pZDescription = pMMArc->pZSection.pZDescription; + + fseek_function(pF, pArcHeader[i_elem].nOffset, SEEK_SET); + + if (hMiraMonLayer->bIsPolygon && (VFG & MM_POL_REVERSE_ARC)) // && + //nStartVertice > 0) + { + MM_N_VERTICES_TYPE nIVertice; + + // Reading arcs vertices in an inverse order + if (MMResizeMM_POINT2DPointer( + &hMiraMonLayer->ReadFeature.pCoord, + &hMiraMonLayer->ReadFeature.nMaxpCoord, + nStartVertice + pArcHeader[i_elem].nElemCount * + 2, // ask for twice memory to reverse + 0, 0)) + return 1; + + // Get the vertices far away from their place to be inverted later + if (pArcHeader[i_elem].nElemCount != + fread_function(hMiraMonLayer->ReadFeature.pCoord + nStartVertice + + pArcHeader[i_elem].nElemCount, + sizeof(*hMiraMonLayer->ReadFeature.pCoord), + (size_t)pArcHeader[i_elem].nElemCount, pF)) + { + return 1; + } + + if (hMiraMonLayer->TopHeader.bIs3d) + { + if (MMResizeDoublePointer( + &hMiraMonLayer->ReadFeature.pZCoord, + &hMiraMonLayer->ReadFeature.nMaxpZCoord, + nStartVertice + pArcHeader[i_elem].nElemCount * 2, 0, 0)) + return 1; + + // +nStartVertice + MM_GetArcHeights(hMiraMonLayer->ReadFeature.pZCoord + + nStartVertice + pArcHeader[i_elem].nElemCount, + pF, pArcHeader[i_elem].nElemCount, + pZDescription + i_elem, flag_z); + + // If there is a value for Z-nodata in GDAL this lines can be uncommented + // and MM_GDAL_NODATA_COORD_Z can be defined + /*if(!DOUBLES_DIFERENTS_DJ(punts_z[k], MM_NODATA_COORD_Z)) + { + MM_N_VERTICES_TYPE nIVertice; + for(nIVertice=0; nIVerticeReadFeature.pZCoord[nIVertice]=MM_GDAL_NODATA_COORD_Z; + } + */ + } + + // Reverse the vertices while putting on their place + for (nIVertice = 0; nIVertice < pArcHeader[i_elem].nElemCount; + nIVertice++) + { + memcpy(hMiraMonLayer->ReadFeature.pCoord + nStartVertice - + ((nStartVertice > 0 && bAvoidFirst) ? 1 : 0) + nIVertice, + hMiraMonLayer->ReadFeature.pCoord + nStartVertice + + 2 * pArcHeader[i_elem].nElemCount - nIVertice - 1, + sizeof(*hMiraMonLayer->ReadFeature.pCoord)); + + if (hMiraMonLayer->TopHeader.bIs3d) + { + memcpy(hMiraMonLayer->ReadFeature.pZCoord + nStartVertice - + ((nStartVertice > 0 && bAvoidFirst) ? 1 : 0) + + nIVertice, + hMiraMonLayer->ReadFeature.pZCoord + nStartVertice + + 2 * pArcHeader[i_elem].nElemCount - nIVertice - 1, + sizeof(*hMiraMonLayer->ReadFeature.pZCoord)); + } + } + } + else + { + // Reading arcs vertices + if (MMResizeMM_POINT2DPointer( + &hMiraMonLayer->ReadFeature.pCoord, + &hMiraMonLayer->ReadFeature.nMaxpCoord, + nStartVertice + pArcHeader[i_elem].nElemCount, 0, 0)) + return 1; + + if (pArcHeader[i_elem].nElemCount != + fread_function(hMiraMonLayer->ReadFeature.pCoord + nStartVertice - + (bAvoidFirst ? 1 : 0), + sizeof(*hMiraMonLayer->ReadFeature.pCoord), + (size_t)pArcHeader[i_elem].nElemCount, pF)) + { + return 1; + } + + if (hMiraMonLayer->TopHeader.bIs3d) + { + if (MMResizeDoublePointer( + &hMiraMonLayer->ReadFeature.pZCoord, + &hMiraMonLayer->ReadFeature.nMaxpZCoord, + nStartVertice + pArcHeader[i_elem].nElemCount, 0, 0)) + return 1; + + // +nStartVertice + MM_GetArcHeights(hMiraMonLayer->ReadFeature.pZCoord + + nStartVertice - (bAvoidFirst ? 1 : 0), + pF, pArcHeader[i_elem].nElemCount, + pZDescription + i_elem, flag_z); + + // If there is a value for Z-nodata in GDAL this lines can be uncommented + // and MM_GDAL_NODATA_COORD_Z can be defined + /*if(!DOUBLES_DIFERENTS_DJ(punts_z[k], MM_NODATA_COORD_Z)) + { + MM_N_VERTICES_TYPE nIVertice; + for(nIVertice=0; nIVerticeReadFeature.pZCoord[nIVertice]=MM_GDAL_NODATA_COORD_Z; + } + */ + } + } + hMiraMonLayer->ReadFeature.nNumpCoord = + pArcHeader[i_elem].nElemCount - (bAvoidFirst ? 1 : 0); + + return 0; +} + +// Reads Polygon coordinates and puts them in a buffer +static int +MMGetMultiPolygonCoordinates(struct MiraMonVectLayerInfo *hMiraMonLayer, + MM_INTERNAL_FID i_pol, uint32_t flag_z) +{ + struct MM_PH *pPolHeader; + struct MM_AH *pArcHeader; + char *pBuffer; + MM_POLYGON_ARCS_COUNT nIndex; + MM_BOOLEAN bAvoidFirst; + MM_N_VERTICES_TYPE nNAcumulVertices = 0; + + // Checking if the index of the polygon is in the correct range. + if (i_pol >= hMiraMonLayer->TopHeader.nElemCount) + return 1; + + MMResetFeatureGeometry(&hMiraMonLayer->ReadFeature); + MMResetFeatureRecord(&hMiraMonLayer->ReadFeature); + pPolHeader = hMiraMonLayer->MMPolygon.pPolHeader + i_pol; + + // It's accepted not having arcs in the universal polygon + if (!pPolHeader->nArcsCount) + { + if (i_pol == 0) + return 0; + else + return 1; + } + + if (MMResizeMiraMonPolygonArcs(&hMiraMonLayer->pArcs, + &hMiraMonLayer->nMaxArcs, + pPolHeader->nArcsCount, 0, 0)) + return 1; + + if (MMInitFlush(&hMiraMonLayer->FlushPAL, hMiraMonLayer->MMPolygon.pF, + hMiraMonLayer->MMPolygon.nPALElementSize * + pPolHeader->nArcsCount, + &pBuffer, pPolHeader->nOffset, 0)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + hMiraMonLayer->FlushPAL.pBlockWhereToSaveOrRead = (void *)pBuffer; + if (MMReadFlush(&hMiraMonLayer->FlushPAL)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + hMiraMonLayer->ReadFeature.nNRings = 0; + hMiraMonLayer->ReadFeature.nNumpCoord = 0; + if (MMResize_MM_N_VERTICES_TYPE_Pointer( + &hMiraMonLayer->ReadFeature.pNCoordRing, + &hMiraMonLayer->ReadFeature.nMaxpNCoordRing, + (MM_N_VERTICES_TYPE)hMiraMonLayer->ReadFeature.nNRings + 1, 10, 10)) + { + free_function(pBuffer); + return 1; + } + + if (MMResizeVFGPointer(&hMiraMonLayer->ReadFeature.flag_VFG, + &hMiraMonLayer->ReadFeature.nMaxVFG, + (MM_INTERNAL_FID)pPolHeader->nArcsCount, 0, + 0)) // Perhaps more memory than needed + { + free_function(pBuffer); + return 1; + } + + // Preparing memory for all coordinates + hMiraMonLayer->ReadFeature.pNCoordRing[hMiraMonLayer->ReadFeature.nNRings] = + 0; + for (nIndex = 0; nIndex < pPolHeader->nArcsCount; nIndex++) + { + hMiraMonLayer->FlushPAL.SizeOfBlockToBeSaved = + sizeof((hMiraMonLayer->pArcs + nIndex)->VFG); + hMiraMonLayer->FlushPAL.pBlockToBeSaved = + (void *)&(hMiraMonLayer->pArcs + nIndex)->VFG; + if (MMReadBlockFromBuffer(&hMiraMonLayer->FlushPAL)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + // Arc index + if (MMReadGUInt64DependingOnVersion( + hMiraMonLayer, &hMiraMonLayer->FlushPAL, + &((hMiraMonLayer->pArcs + nIndex)->nIArc))) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + if (hMiraMonLayer->MMPolygon.MMArc.pArcHeader == nullptr) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + // Checking if the index of the arc is in the correct range. + if ((hMiraMonLayer->pArcs + nIndex)->nIArc >= + hMiraMonLayer->MMPolygon.TopArcHeader.nElemCount) + { + free_function(pBuffer); + return 1; + } + + pArcHeader = hMiraMonLayer->MMPolygon.MMArc.pArcHeader + + (hMiraMonLayer->pArcs + nIndex)->nIArc; + hMiraMonLayer->ReadFeature + .pNCoordRing[hMiraMonLayer->ReadFeature.nNRings] += + pArcHeader->nElemCount; + } + if (MMResizeMM_POINT2DPointer( + &hMiraMonLayer->ReadFeature.pCoord, + &hMiraMonLayer->ReadFeature.nMaxpCoord, + hMiraMonLayer->ReadFeature + .pNCoordRing[hMiraMonLayer->ReadFeature.nNRings], + 0, 0)) + { + free_function(pBuffer); + return 1; + } + + hMiraMonLayer->FlushPAL.CurrentOffset = 0; + + // Real work + hMiraMonLayer->ReadFeature.pNCoordRing[hMiraMonLayer->ReadFeature.nNRings] = + 0; + for (nIndex = 0; nIndex < pPolHeader->nArcsCount; nIndex++) + { + hMiraMonLayer->FlushPAL.SizeOfBlockToBeSaved = + sizeof((hMiraMonLayer->pArcs + nIndex)->VFG); + hMiraMonLayer->FlushPAL.pBlockToBeSaved = + (void *)&(hMiraMonLayer->pArcs + nIndex)->VFG; + if (MMReadBlockFromBuffer(&hMiraMonLayer->FlushPAL)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + // Arc index + if (MMReadGUInt64DependingOnVersion( + hMiraMonLayer, &hMiraMonLayer->FlushPAL, + &((hMiraMonLayer->pArcs + nIndex)->nIArc))) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + bAvoidFirst = FALSE; + if (hMiraMonLayer->ReadFeature + .pNCoordRing[hMiraMonLayer->ReadFeature.nNRings] != 0) + bAvoidFirst = TRUE; + + // Add coordinates to hMiraMonLayer->ReadFeature.pCoord + if (MMAddStringLineCoordinates(hMiraMonLayer, + (hMiraMonLayer->pArcs + nIndex)->nIArc, + flag_z, nNAcumulVertices, bAvoidFirst, + (hMiraMonLayer->pArcs + nIndex)->VFG)) + { + free_function(pBuffer); + return 1; + } + + if (MMResize_MM_N_VERTICES_TYPE_Pointer( + &hMiraMonLayer->ReadFeature.pNCoordRing, + &hMiraMonLayer->ReadFeature.nMaxpNCoordRing, + (MM_N_VERTICES_TYPE)hMiraMonLayer->ReadFeature.nNRings + 1, 10, + 10)) + { + free_function(pBuffer); + return 1; + } + + hMiraMonLayer->ReadFeature + .pNCoordRing[hMiraMonLayer->ReadFeature.nNRings] += + hMiraMonLayer->ReadFeature.nNumpCoord; + nNAcumulVertices += hMiraMonLayer->ReadFeature.nNumpCoord; + if ((hMiraMonLayer->pArcs + nIndex)->VFG & MM_POL_END_RING) + { + hMiraMonLayer->ReadFeature + .flag_VFG[hMiraMonLayer->ReadFeature.nNRings] = + (hMiraMonLayer->pArcs + nIndex)->VFG; + hMiraMonLayer->ReadFeature.nNRings++; + hMiraMonLayer->ReadFeature + .pNCoordRing[hMiraMonLayer->ReadFeature.nNRings] = 0; + } + } + hMiraMonLayer->nNumArcs = pPolHeader->nArcsCount; + if (pBuffer) + free_function(pBuffer); + + return 0; +} + +// Reads the geographical part of a MiraMon layer feature +int MMGetGeoFeatureFromVector(struct MiraMonVectLayerInfo *hMiraMonLayer, + MM_INTERNAL_FID i_elem) +{ + FILE_TYPE *pF; + struct MM_ZD *pZDescription; + uint32_t flag_z; + int num; + double cz; + + if (hMiraMonLayer->nSelectCoordz == MM_SELECT_HIGHEST_COORDZ) + flag_z = MM_STRING_HIGHEST_ALTITUDE; + else if (hMiraMonLayer->nSelectCoordz == MM_SELECT_LOWEST_COORDZ) + flag_z = MM_STRING_LOWEST_ALTITUDE; + else + flag_z = 0L; + + if (hMiraMonLayer->bIsPoint) + { + pF = hMiraMonLayer->MMPoint.pF; + + // Getting to the i-th element offset + fseek_function(pF, + hMiraMonLayer->nHeaderDiskSize + + sizeof(MM_COORD_TYPE) * 2 * i_elem, + SEEK_SET); + + // Reading the point + if (MMResizeMM_POINT2DPointer(&hMiraMonLayer->ReadFeature.pCoord, + &hMiraMonLayer->ReadFeature.nMaxpCoord, + hMiraMonLayer->ReadFeature.nNumpCoord, 1, + 1)) + return 1; + + if (1 != fread_function(hMiraMonLayer->ReadFeature.pCoord, + sizeof(MM_COORD_TYPE) * 2, 1, pF)) + { + return 1; + } + + hMiraMonLayer->ReadFeature.nNRings = 1; + + if (MMResize_MM_N_VERTICES_TYPE_Pointer( + &hMiraMonLayer->ReadFeature.pNCoordRing, + &hMiraMonLayer->ReadFeature.nMaxpNCoordRing, 1, 0, 1)) + return 1; + + hMiraMonLayer->ReadFeature.pNCoordRing[0] = 1; + + if (hMiraMonLayer->TopHeader.bIs3d) + { + pZDescription = + hMiraMonLayer->MMPoint.pZSection.pZDescription + i_elem; + if (pZDescription->nZCount == INT_MIN) + return 1; + num = MM_ARC_TOTAL_N_HEIGHTS_DISK(pZDescription->nZCount, 1); + + if (MMResizeDoublePointer(&hMiraMonLayer->ReadFeature.pZCoord, + &hMiraMonLayer->ReadFeature.nMaxpZCoord, + 1, 1, 1)) + return 1; + + if (num == 0) + hMiraMonLayer->ReadFeature.pZCoord[0] = MM_NODATA_COORD_Z; + else + { + if (flag_z == MM_STRING_HIGHEST_ALTITUDE) // Max z + cz = pZDescription->dfBBmaxz; + else if (flag_z == MM_STRING_LOWEST_ALTITUDE) // Min z + cz = pZDescription->dfBBminz; + else + { + // Reading the first z coordinate + fseek_function(pF, pZDescription->nOffsetZ, SEEK_SET); + if ((size_t)1 != + fread_function( + &cz, sizeof(*hMiraMonLayer->ReadFeature.pZCoord), 1, + pF)) + { + return 1; + } + } + // If there is a value for Z-nodata in GDAL this lines can be uncommented + // and MM_GDAL_NODATA_COORD_Z can be defined + /*if(!DOUBLES_DIFERENTS_DJ(cz, MM_NODATA_COORD_Z)) + hMiraMonLayer->ReadFeature.pZCoord[0]=MM_GDAL_NODATA_COORD_Z; + else */ + hMiraMonLayer->ReadFeature.pZCoord[0] = cz; + } + } + + return 0; + } + + // Stringlines + if (hMiraMonLayer->bIsArc && !hMiraMonLayer->bIsPolygon) + { + if (MMAddStringLineCoordinates(hMiraMonLayer, i_elem, flag_z, 0, FALSE, + 0)) + return 1; + + if (MMResize_MM_N_VERTICES_TYPE_Pointer( + &hMiraMonLayer->ReadFeature.pNCoordRing, + &hMiraMonLayer->ReadFeature.nMaxpNCoordRing, 1, 0, 1)) + return 1; + + hMiraMonLayer->ReadFeature.pNCoordRing[0] = + hMiraMonLayer->ReadFeature.nNumpCoord; + + return 0; + } + + // Polygons or multipolygons + if (MMGetMultiPolygonCoordinates(hMiraMonLayer, i_elem, flag_z)) + return 1; + + return 0; +} + +// Reads the header of a MiraMon DBF +// Please read the format at this link: +// https://www.miramon.cat/new_note/usa/notes/DBF_estesa.pdf +int MM_ReadExtendedDBFHeader(struct MiraMonVectLayerInfo *hMiraMonLayer) +{ + const char *pszRelFile = nullptr; + struct MM_DATA_BASE_XP *pMMBDXP; + const char *szDBFFileName = nullptr; + + // If read don't read again. It happens when Polygon reads + // the database and then in initArc() it's read again. + if (hMiraMonLayer->pMMBDXP) + return 0; + + pMMBDXP = hMiraMonLayer->pMMBDXP = calloc_function(sizeof(*pMMBDXP)); + + if (hMiraMonLayer->bIsPoint) + { + hMiraMonLayer->MMPoint.MMAdmDB.pMMBDXP = pMMBDXP; + szDBFFileName = hMiraMonLayer->MMPoint.MMAdmDB.pszExtDBFLayerName; + pszRelFile = hMiraMonLayer->MMPoint.pszREL_LayerName; + } + else if (hMiraMonLayer->bIsArc && !hMiraMonLayer->bIsPolygon) + { + hMiraMonLayer->MMArc.MMAdmDB.pMMBDXP = pMMBDXP; + szDBFFileName = hMiraMonLayer->MMArc.MMAdmDB.pszExtDBFLayerName; + pszRelFile = hMiraMonLayer->MMArc.pszREL_LayerName; + } + else if (hMiraMonLayer->bIsPolygon) + { + hMiraMonLayer->MMPolygon.MMAdmDB.pMMBDXP = pMMBDXP; + szDBFFileName = hMiraMonLayer->MMPolygon.MMAdmDB.pszExtDBFLayerName; + pszRelFile = hMiraMonLayer->MMPolygon.pszREL_LayerName; + } + + if (MM_ReadExtendedDBFHeaderFromFile(szDBFFileName, pMMBDXP, pszRelFile)) + { + MMCPLError(CE_Failure, CPLE_NotSupported, + "Error reading the format in the DBF file %s.", + szDBFFileName); + return 1; + } + + fclose_and_nullify(&pMMBDXP->pfDataBase); + return 0; +} + +#ifdef GDAL_COMPILATION +CPL_C_END // Necessary for compiling in GDAL project +#endif diff --git a/ogr/ogrsf_frmts/miramon/mm_rdlayr.h b/ogr/ogrsf_frmts/miramon/mm_rdlayr.h new file mode 100644 index 000000000000..905e8ea52ec2 --- /dev/null +++ b/ogr/ogrsf_frmts/miramon/mm_rdlayr.h @@ -0,0 +1,22 @@ +#ifndef __MMRDLAYR_H +#define __MMRDLAYR_H + +#ifndef GDAL_COMPILATION +#include "mm_gdal\mm_gdal_driver_structs.h" +#else +//#include "ogr_api.h" // For CPL_C_START +#include "mm_gdal_driver_structs.h" +CPL_C_START // Necessary for compiling in GDAL project +#endif + +int MMInitLayerToRead(struct MiraMonVectLayerInfo *hMiraMonLayer, + FILE_TYPE *m_fp, const char *pszFilename); + +int MMGetGeoFeatureFromVector(struct MiraMonVectLayerInfo *hMiraMonLayer, + MM_INTERNAL_FID i_elem); +int MM_ReadExtendedDBFHeader(struct MiraMonVectLayerInfo *hMiraMonLayer); + +#ifdef GDAL_COMPILATION +CPL_C_END // Necessary for compiling in GDAL project +#endif +#endif //__MMRDLAYR_H diff --git a/ogr/ogrsf_frmts/miramon/mm_wrlayr.c b/ogr/ogrsf_frmts/miramon/mm_wrlayr.c new file mode 100644 index 000000000000..adf5c5d295f7 --- /dev/null +++ b/ogr/ogrsf_frmts/miramon/mm_wrlayr.c @@ -0,0 +1,7456 @@ +/****************************************************************************** + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: C API to create a MiraMon layer + * Author: Abel Pau, a.pau@creaf.uab.cat, based on the MiraMon codes, + * mainly written by Xavier Pons, Joan Maso (correctly written + * "Mas0xF3"), Abel Pau, Nuria Julia (N0xFAria Juli0xE0), + * Xavier Calaf, Lluis (Llu0xEDs) Pesquer and Alaitz Zabala, from + * CREAF and Universitat Autonoma (Aut0xF2noma) de Barcelona. + * For a complete list of contributors: + * https://www.miramon.cat/eng/QuiSom.htm + ****************************************************************************** + * Copyright (c) 2024, Xavier Pons + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifdef GDAL_COMPILATION +#include "mm_wrlayr.h" +#include "mm_gdal_functions.h" +#include "mm_gdal_constants.h" +#include "mm_rdlayr.h" // For MM_ReadExtendedDBFHeader() +#include "gdal.h" // For GDALDatasetH +#include "ogr_srs_api.h" // For OSRGetAuthorityCode +#include "cpl_string.h" // For CPL_ENC_UTF8 +#else +#include "CmptCmp.h" // Compatibility between compilers +#include "PrjMMVGl.h" // For a DirectoriPrograma +#include "mm_gdal\mm_wrlayr.h" // For fseek_function() +#include "mm_gdal\mm_gdal_functions.h" // For CPLStrlcpy() +#include "mm_gdal\mm_rdlayr.h" // For MM_ReadExtendedDBFHeader() +#include "msg.h" // For ErrorMsg() +#ifdef _WIN64 +#include "gdal\release-1911-x64\cpl_string.h" // Per a CPL_ENC_UTF8 +#else +#include "gdal\release-1911-32\cpl_string.h" // Per a CPL_ENC_UTF8 +#endif +#endif + +#ifdef GDAL_COMPILATION +CPL_C_START // Necessary for compiling in GDAL project +#endif // GDAL_COMPILATION + + /* -------------------------------------------------------------------- */ + /* Header Functions */ + /* -------------------------------------------------------------------- */ + int + MMAppendBlockToBuffer(struct MM_FLUSH_INFO *FlushInfo); +void MMInitBoundingBox(struct MMBoundingBox *dfBB); +int MMWriteAHArcSection(struct MiraMonVectLayerInfo *hMiraMonLayer, + MM_FILE_OFFSET DiskOffset); +int MMWriteNHNodeSection(struct MiraMonVectLayerInfo *hMiraMonLayer, + MM_FILE_OFFSET DiskOffset); +int MMWritePHPolygonSection(struct MiraMonVectLayerInfo *hMiraMonLayer, + MM_FILE_OFFSET DiskOffset); +int MMAppendIntegerDependingOnVersion( + struct MiraMonVectLayerInfo *hMiraMonLayer, struct MM_FLUSH_INFO *FlushInfo, + uint32_t *nUL32, GUInt64 nUI64); +int MMMoveFromFileToFile(FILE_TYPE *pSrcFile, FILE_TYPE *pDestFile, + MM_FILE_OFFSET *nOffset); +int MMResizeZSectionDescrPointer(struct MM_ZD **pZDescription, GUInt64 *nMax, + GUInt64 nNum, GUInt64 nIncr, + GUInt64 nProposedMax); +int MMResizeArcHeaderPointer(struct MM_AH **pArcHeader, GUInt64 *nMax, + GUInt64 nNum, GUInt64 nIncr, GUInt64 nProposedMax); +int MMResizeNodeHeaderPointer(struct MM_NH **pNodeHeader, GUInt64 *nMax, + GUInt64 nNum, GUInt64 nIncr, + GUInt64 nProposedMax); +int MMResizePolHeaderPointer(struct MM_PH **pPolHeader, GUInt64 *nMax, + GUInt64 nNum, GUInt64 nIncr, GUInt64 nProposedMax); +void MMUpdateBoundingBoxXY(struct MMBoundingBox *dfBB, + struct MM_POINT_2D *pCoord); +void MMUpdateBoundingBox(struct MMBoundingBox *dfBBToBeAct, + struct MMBoundingBox *dfBBWithData); +int MMCheckVersionFor3DOffset(struct MiraMonVectLayerInfo *hMiraMonLayer, + MM_FILE_OFFSET nOffset, + MM_INTERNAL_FID nElemCount); +int MMCheckVersionOffset(struct MiraMonVectLayerInfo *hMiraMonLayer, + MM_FILE_OFFSET OffsetToCheck); +int MMCheckVersionForFID(struct MiraMonVectLayerInfo *hMiraMonLayer, + MM_INTERNAL_FID FID); + +// Extended DBF functions +int MMCreateMMDB(struct MiraMonVectLayerInfo *hMiraMonLayer); +int MMAddDBFRecordToMMDB(struct MiraMonVectLayerInfo *hMiraMonLayer, + struct MiraMonFeature *hMMFeature); +int MMAddPointRecordToMMDB(struct MiraMonVectLayerInfo *hMiraMonLayer, + struct MiraMonFeature *hMMFeature, + MM_INTERNAL_FID nElemCount); +int MMAddArcRecordToMMDB(struct MiraMonVectLayerInfo *hMiraMonLayer, + struct MiraMonFeature *hMMFeature, + MM_INTERNAL_FID nElemCount, struct MM_AH *pArcHeader); +int MMAddNodeRecordToMMDB(struct MiraMonVectLayerInfo *hMiraMonLayer, + MM_INTERNAL_FID nElemCount, + struct MM_NH *pNodeHeader); +int MMAddPolygonRecordToMMDB(struct MiraMonVectLayerInfo *hMiraMonLayer, + struct MiraMonFeature *hMMFeature, + MM_INTERNAL_FID nElemCount, + MM_N_VERTICES_TYPE nVerticesCount, + struct MM_PH *pPolHeader); +int MMCloseMMBD_XP(struct MiraMonVectLayerInfo *hMiraMonLayer); +void MMDestroyMMDB(struct MiraMonVectLayerInfo *hMiraMonLayer); + +/* -------------------------------------------------------------------- */ +/* Managing errors and warnings */ +/* -------------------------------------------------------------------- */ + +#ifndef GDAL_COMPILATION +void MMCPLError(int code, const char *fmt, ...) +{ + char szBigEnoughBuffer[1024]; + + va_list args; + va_start(args, fmt); + vsnprintf(szBigEnoughBuffer, sizeof(szBigEnoughBuffer), fmt, args); + ErrorMsg(szBigEnoughBuffer); + va_end(args); +} + +void MMCPLWarning(int code, const char *fmt, ...) +{ + char szBigEnoughBuffer[1024]; + + va_list args; + va_start(args, fmt); + vsnprintf(szBigEnoughBuffer, sizeof(szBigEnoughBuffer), fmt, args); + InfoMsg(szBigEnoughBuffer); + va_end(args); +} + +void MMCPLDebug(int code, const char *fmt, ...) +{ + char szBigEnoughBuffer[1024]; + + va_list args; + va_start(args, fmt); + vsnprintf(szBigEnoughBuffer, sizeof(szBigEnoughBuffer), fmt, args); + printf(szBigEnoughBuffer); /*ok*/ + va_end(args); +} + +int snprintf(char *str, size_t size, const char *format, ...) +{ + int result; + va_list args; + + va_start(args, format); + result = vsnprintf(str, size, format, args); + va_end(args); + + return result; +} +#endif + +// Checks for potential arithmetic overflow when performing multiplication +// operations between two GUInt64 values and converting the result to size_t. +// Important for 32 vs. 64 bit compiling compatibility. +int MMCheckSize_t(GUInt64 nCount, GUInt64 nSize) +{ + if ((size_t)nCount != nCount) + return 1; + + if ((size_t)nSize != nSize) + return 1; + +#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + if (nCount != 0 && nSize > SIZE_MAX / nCount) +#else + if (nCount != 0 && nSize > (1000 * 1000 * 1000U) / nCount) +#endif + { + MMCPLError(CE_Failure, CPLE_OutOfMemory, "Overflow in MMCheckSize_t()"); + return 1; + } + return 0; +} + +/* -------------------------------------------------------------------- */ +/* Layer Functions: Version */ +/* -------------------------------------------------------------------- */ +int MMGetVectorVersion(struct MM_TH *pTopHeader) +{ + if ((pTopHeader->aLayerVersion[0] == ' ' || + pTopHeader->aLayerVersion[0] == '0') && + pTopHeader->aLayerVersion[1] == '1' && + pTopHeader->aLayerSubVersion == '1') + return MM_32BITS_VERSION; + + if ((pTopHeader->aLayerVersion[0] == ' ' || + pTopHeader->aLayerVersion[0] == '0') && + pTopHeader->aLayerVersion[1] == '2' && + pTopHeader->aLayerSubVersion == '0') + return MM_64BITS_VERSION; + + return MM_UNKNOWN_VERSION; +} + +static void MMSet1_1Version(struct MM_TH *pTopHeader) +{ + pTopHeader->aLayerVersion[0] = ' '; + pTopHeader->aLayerVersion[1] = '1'; + pTopHeader->aLayerSubVersion = '1'; +} + +static void MMSet2_0Version(struct MM_TH *pTopHeader) +{ + pTopHeader->aLayerVersion[0] = ' '; + pTopHeader->aLayerVersion[1] = '2'; + pTopHeader->aLayerSubVersion = '0'; +} + +/* -------------------------------------------------------------------- */ +/* Layer Functions: Header */ +/* -------------------------------------------------------------------- */ +int MMReadHeader(FILE_TYPE *pF, struct MM_TH *pMMHeader) +{ + char dot; + uint32_t NCount; + int32_t reservat4 = 0L; + + pMMHeader->Flag = 0x0; + if (fseek_function(pF, 0, SEEK_SET)) + return 1; + if (fread_function(pMMHeader->aFileType, 1, 3, pF) != 3) + return 1; + if (fread_function(pMMHeader->aLayerVersion, 1, 2, pF) != 2) + return 1; + if (fread_function(&dot, 1, 1, pF) != 1) + return 1; + if (fread_function(&pMMHeader->aLayerSubVersion, 1, 1, pF) != 1) + return 1; + if (fread_function(&pMMHeader->Flag, sizeof(pMMHeader->Flag), 1, pF) != 1) + return 1; + if (fread_function(&pMMHeader->hBB.dfMinX, sizeof(pMMHeader->hBB.dfMinX), 1, + pF) != 1) + return 1; + if (fread_function(&pMMHeader->hBB.dfMaxX, sizeof(pMMHeader->hBB.dfMaxX), 1, + pF) != 1) + return 1; + if (fread_function(&pMMHeader->hBB.dfMinY, sizeof(pMMHeader->hBB.dfMinY), 1, + pF) != 1) + return 1; + if (fread_function(&pMMHeader->hBB.dfMaxY, sizeof(pMMHeader->hBB.dfMaxY), 1, + pF) != 1) + return 1; + if (pMMHeader->aLayerVersion[0] == ' ' && + pMMHeader->aLayerVersion[1] == '1') + { + if (fread_function(&NCount, sizeof(NCount), 1, pF) != 1) + return 1; + + pMMHeader->nElemCount = (MM_INTERNAL_FID)NCount; + + if (fread_function(&reservat4, 4, 1, pF) != 1) + return 1; + } + else if (pMMHeader->aLayerVersion[0] == ' ' && + pMMHeader->aLayerVersion[1] == '2') + { + if (fread_function(&(pMMHeader->nElemCount), + sizeof(pMMHeader->nElemCount), 1, pF) != 1) + return 1; + + if (fread_function(&reservat4, 4, 1, pF) != 1) + return 1; + if (fread_function(&reservat4, 4, 1, pF) != 1) + return 1; + } + + if (pMMHeader->Flag & MM_LAYER_3D_INFO) + pMMHeader->bIs3d = 1; + + if (pMMHeader->Flag & MM_LAYER_MULTIPOLYGON) + pMMHeader->bIsMultipolygon = 1; + + return 0; +} + +static int MMWriteHeader(FILE_TYPE *pF, struct MM_TH *pMMHeader) +{ + char dot = '.'; + uint32_t NCount; + int32_t reservat4 = 0L; + MM_INTERNAL_FID nNumber1 = 1, nNumber0 = 0; + + if (!pF) + return 0; + + pMMHeader->Flag = MM_CREATED_USING_MIRAMON; // Created from MiraMon + if (pMMHeader->bIs3d) + pMMHeader->Flag |= MM_LAYER_3D_INFO; // 3D + + if (pMMHeader->bIsMultipolygon) + pMMHeader->Flag |= MM_LAYER_MULTIPOLYGON; // Multipolygon. + + if (pMMHeader->aFileType[0] == 'P' && pMMHeader->aFileType[1] == 'O' && + pMMHeader->aFileType[2] == 'L') + pMMHeader->Flag |= MM_BIT_5_ON; // Explicital polygons + + if (fseek_function(pF, 0, SEEK_SET)) + return 1; + if (fwrite_function(pMMHeader->aFileType, 1, 3, pF) != 3) + return 1; + if (fwrite_function(pMMHeader->aLayerVersion, 1, 2, pF) != 2) + return 1; + if (fwrite_function(&dot, 1, 1, pF) != 1) + return 1; + if (fwrite_function(&pMMHeader->aLayerSubVersion, 1, 1, pF) != 1) + return 1; + if (fwrite_function(&pMMHeader->Flag, sizeof(pMMHeader->Flag), 1, pF) != 1) + return 1; + if (fwrite_function(&pMMHeader->hBB.dfMinX, sizeof(pMMHeader->hBB.dfMinX), + 1, pF) != 1) + return 1; + if (fwrite_function(&pMMHeader->hBB.dfMaxX, sizeof(pMMHeader->hBB.dfMaxX), + 1, pF) != 1) + return 1; + if (fwrite_function(&pMMHeader->hBB.dfMinY, sizeof(pMMHeader->hBB.dfMinY), + 1, pF) != 1) + return 1; + if (fwrite_function(&pMMHeader->hBB.dfMaxY, sizeof(pMMHeader->hBB.dfMaxY), + 1, pF) != 1) + return 1; + if (pMMHeader->aLayerVersion[0] == ' ' && + pMMHeader->aLayerVersion[1] == '1') + { + NCount = (uint32_t)pMMHeader->nElemCount; + if (fwrite_function(&NCount, sizeof(NCount), 1, pF) != 1) + return 1; + + if (fwrite_function(&reservat4, 4, 1, pF) != 1) + return 1; + } + else if (pMMHeader->aLayerVersion[0] == ' ' && + pMMHeader->aLayerVersion[1] == '2') + { + if (fwrite_function(&(pMMHeader->nElemCount), + sizeof(pMMHeader->nElemCount), 1, pF) != 1) + return 1; + + // Next part of the file (don't apply for the moment) + if (fwrite_function(&nNumber1, sizeof(nNumber1), 1, pF) != 1) + return 1; + if (fwrite_function(&nNumber0, sizeof(nNumber0), 1, pF) != 1) + return 1; + + // Reserved bytes + if (fwrite_function(&reservat4, 4, 1, pF) != 1) + return 1; + if (fwrite_function(&reservat4, 4, 1, pF) != 1) + return 1; + } + return 0; +} + +void MMInitHeader(struct MM_TH *pMMHeader, int layerType, int nVersion) +{ + memset(pMMHeader, 0, sizeof(*pMMHeader)); + switch (nVersion) + { + case MM_32BITS_VERSION: + pMMHeader->aLayerVersion[0] = '0'; + pMMHeader->aLayerVersion[1] = '1'; + pMMHeader->aLayerSubVersion = '1'; + break; + case MM_64BITS_VERSION: + case MM_LAST_VERSION: + default: + pMMHeader->aLayerVersion[0] = '0'; + pMMHeader->aLayerVersion[1] = '2'; + pMMHeader->aLayerSubVersion = '0'; + break; + } + switch (layerType) + { + case MM_LayerType_Point: + pMMHeader->aFileType[0] = 'P'; + pMMHeader->aFileType[1] = 'N'; + pMMHeader->aFileType[2] = 'T'; + break; + case MM_LayerType_Point3d: + pMMHeader->aFileType[0] = 'P'; + pMMHeader->aFileType[1] = 'N'; + pMMHeader->aFileType[2] = 'T'; + pMMHeader->bIs3d = 1; + break; + case MM_LayerType_Arc: + pMMHeader->aFileType[0] = 'A'; + pMMHeader->aFileType[1] = 'R'; + pMMHeader->aFileType[2] = 'C'; + break; + case MM_LayerType_Arc3d: + pMMHeader->aFileType[0] = 'A'; + pMMHeader->aFileType[1] = 'R'; + pMMHeader->aFileType[2] = 'C'; + pMMHeader->bIs3d = 1; + break; + case MM_LayerType_Pol: + pMMHeader->aFileType[0] = 'P'; + pMMHeader->aFileType[1] = 'O'; + pMMHeader->aFileType[2] = 'L'; + break; + case MM_LayerType_Pol3d: + pMMHeader->aFileType[0] = 'P'; + pMMHeader->aFileType[1] = 'O'; + pMMHeader->aFileType[2] = 'L'; + pMMHeader->bIs3d = 1; + break; + default: + break; + } + pMMHeader->nElemCount = 0; + pMMHeader->hBB.dfMinX = MM_UNDEFINED_STATISTICAL_VALUE; + pMMHeader->hBB.dfMaxX = -MM_UNDEFINED_STATISTICAL_VALUE; + pMMHeader->hBB.dfMinY = MM_UNDEFINED_STATISTICAL_VALUE; + pMMHeader->hBB.dfMaxY = -MM_UNDEFINED_STATISTICAL_VALUE; + + pMMHeader->Flag = MM_CREATED_USING_MIRAMON; // Created from MiraMon + if (pMMHeader->bIs3d) + pMMHeader->Flag |= MM_LAYER_3D_INFO; // 3D + + if (pMMHeader->bIsMultipolygon) + pMMHeader->Flag |= MM_LAYER_MULTIPOLYGON; // Multipolygon. + + if (pMMHeader->aFileType[0] == 'P' && pMMHeader->aFileType[1] == 'O' && + pMMHeader->aFileType[2] == 'L') + pMMHeader->Flag |= MM_BIT_5_ON; // Explicital polygons +} + +int MMWriteEmptyHeader(FILE_TYPE *pF, int layerType, int nVersion) +{ + struct MM_TH pMMHeader; + + memset(&pMMHeader, 0, sizeof(pMMHeader)); + switch (nVersion) + { + case MM_32BITS_VERSION: + pMMHeader.aLayerVersion[0] = '0'; + pMMHeader.aLayerVersion[1] = '1'; + pMMHeader.aLayerSubVersion = '1'; + break; + case MM_64BITS_VERSION: + case MM_LAST_VERSION: + default: + pMMHeader.aLayerVersion[0] = '0'; + pMMHeader.aLayerVersion[1] = '2'; + pMMHeader.aLayerSubVersion = '0'; + break; + } + switch (layerType) + { + case MM_LayerType_Point: + pMMHeader.aFileType[0] = 'P'; + pMMHeader.aFileType[1] = 'N'; + pMMHeader.aFileType[2] = 'T'; + break; + case MM_LayerType_Point3d: + pMMHeader.aFileType[0] = 'P'; + pMMHeader.aFileType[1] = 'N'; + pMMHeader.aFileType[2] = 'T'; + pMMHeader.bIs3d = 1; + break; + case MM_LayerType_Arc: + pMMHeader.aFileType[0] = 'A'; + pMMHeader.aFileType[1] = 'R'; + pMMHeader.aFileType[2] = 'C'; + break; + case MM_LayerType_Arc3d: + pMMHeader.aFileType[0] = 'A'; + pMMHeader.aFileType[1] = 'R'; + pMMHeader.aFileType[2] = 'C'; + pMMHeader.bIs3d = 1; + break; + case MM_LayerType_Pol: + pMMHeader.aFileType[0] = 'P'; + pMMHeader.aFileType[1] = 'O'; + pMMHeader.aFileType[2] = 'L'; + break; + case MM_LayerType_Pol3d: + pMMHeader.aFileType[0] = 'P'; + pMMHeader.aFileType[1] = 'O'; + pMMHeader.aFileType[2] = 'L'; + pMMHeader.bIs3d = 1; + break; + default: + break; + } + pMMHeader.nElemCount = 0; + pMMHeader.hBB.dfMinX = MM_UNDEFINED_STATISTICAL_VALUE; + pMMHeader.hBB.dfMaxX = -MM_UNDEFINED_STATISTICAL_VALUE; + pMMHeader.hBB.dfMinY = MM_UNDEFINED_STATISTICAL_VALUE; + pMMHeader.hBB.dfMaxY = -MM_UNDEFINED_STATISTICAL_VALUE; + + return MMWriteHeader(pF, &pMMHeader); +} + +/* -------------------------------------------------------------------- */ +/* Layer Functions: Z section */ +/* -------------------------------------------------------------------- */ +int MMReadZSection(struct MiraMonVectLayerInfo *hMiraMonLayer, FILE_TYPE *pF, + struct MM_ZSection *pZSection) +{ + int32_t reservat4 = 0L; + + if (!hMiraMonLayer) + return 1; + + if (hMiraMonLayer->bIsPoint) + { + if (MMCheckSize_t(hMiraMonLayer->TopHeader.nElemCount, MM_SIZE_OF_TL)) + return 1; + if (hMiraMonLayer->TopHeader.nElemCount * MM_SIZE_OF_TL > + UINT64_MAX - hMiraMonLayer->nHeaderDiskSize) + return 1; + pZSection->ZSectionOffset = + hMiraMonLayer->nHeaderDiskSize + + hMiraMonLayer->TopHeader.nElemCount * MM_SIZE_OF_TL; + } + else if (hMiraMonLayer->bIsArc && !(hMiraMonLayer->bIsPolygon) && + hMiraMonLayer->TopHeader.nElemCount > 0) + { + const struct MM_AH *pArcHeader = + &(hMiraMonLayer->MMArc + .pArcHeader[hMiraMonLayer->TopHeader.nElemCount - 1]); + if (MMCheckSize_t(pArcHeader->nElemCount, MM_SIZE_OF_COORDINATE)) + return 1; + if (pArcHeader->nElemCount * MM_SIZE_OF_COORDINATE > + UINT64_MAX - pArcHeader->nOffset) + return 1; + // Z section begins just after last coordinate of the last arc + pZSection->ZSectionOffset = + pArcHeader->nOffset + + pArcHeader->nElemCount * MM_SIZE_OF_COORDINATE; + } + else if (hMiraMonLayer->bIsPolygon && + hMiraMonLayer->MMPolygon.TopArcHeader.nElemCount > 0) + { + const struct MM_AH *pArcHeader = + &(hMiraMonLayer->MMPolygon.MMArc + .pArcHeader[hMiraMonLayer->MMPolygon.TopArcHeader.nElemCount - + 1]); + if (MMCheckSize_t(pArcHeader->nElemCount, MM_SIZE_OF_COORDINATE)) + return 1; + if (pArcHeader->nElemCount * MM_SIZE_OF_COORDINATE > + UINT64_MAX - pArcHeader->nOffset) + return 1; + // Z section begins just after last coordinate of the last arc + pZSection->ZSectionOffset = + pArcHeader->nOffset + + pArcHeader->nElemCount * MM_SIZE_OF_COORDINATE; + } + else + return 1; + + if (pF) + { + if (fseek_function(pF, pZSection->ZSectionOffset, SEEK_SET)) + return 1; + + if (fread_function(&reservat4, 4, 1, pF) != 1) + return 1; + pZSection->ZSectionOffset += 4; + if (fread_function(&reservat4, 4, 1, pF) != 1) + return 1; + pZSection->ZSectionOffset += 4; + if (fread_function(&reservat4, 4, 1, pF) != 1) + return 1; + pZSection->ZSectionOffset += 4; + if (fread_function(&reservat4, 4, 1, pF) != 1) + return 1; + pZSection->ZSectionOffset += 4; + + if (fread_function(&pZSection->ZHeader.dfBBminz, + sizeof(pZSection->ZHeader.dfBBminz), 1, pF) != 1) + return 1; + pZSection->ZSectionOffset += sizeof(pZSection->ZHeader.dfBBminz); + + if (fread_function(&pZSection->ZHeader.dfBBmaxz, + sizeof(pZSection->ZHeader.dfBBmaxz), 1, pF) != 1) + return 1; + pZSection->ZSectionOffset += sizeof(pZSection->ZHeader.dfBBmaxz); + } + return 0; +} + +static int MMWriteZSection(FILE_TYPE *pF, struct MM_ZSection *pZSection) +{ + int32_t reservat4 = 0L; + + if (fseek_function(pF, pZSection->ZSectionOffset, SEEK_SET)) + return 1; + + if (fwrite_function(&reservat4, 4, 1, pF) != 1) + return 1; + if (fwrite_function(&reservat4, 4, 1, pF) != 1) + return 1; + if (fwrite_function(&reservat4, 4, 1, pF) != 1) + return 1; + if (fwrite_function(&reservat4, 4, 1, pF) != 1) + return 1; + + pZSection->ZSectionOffset += 16; + + if (fwrite_function(&pZSection->ZHeader.dfBBminz, + sizeof(pZSection->ZHeader.dfBBminz), 1, pF) != 1) + return 1; + pZSection->ZSectionOffset += sizeof(pZSection->ZHeader.dfBBminz); + if (fwrite_function(&pZSection->ZHeader.dfBBmaxz, + sizeof(pZSection->ZHeader.dfBBmaxz), 1, pF) != 1) + return 1; + pZSection->ZSectionOffset += sizeof(pZSection->ZHeader.dfBBmaxz); + return 0; +} + +int MMReadZDescriptionHeaders(struct MiraMonVectLayerInfo *hMiraMonLayer, + FILE_TYPE *pF, MM_INTERNAL_FID nElements, + struct MM_ZSection *pZSection) +{ + struct MM_FLUSH_INFO FlushTMP; + char *pBuffer = nullptr; + MM_INTERNAL_FID nIndex = 0; + MM_FILE_OFFSET nBlockSize; + struct MM_ZD *pZDescription; + + if (!hMiraMonLayer) + return 1; + + if (!pZSection) + return 1; + + if (!nElements) + return 0; // No elements to read + + pZDescription = pZSection->pZDescription; + + nBlockSize = nElements * pZSection->nZDDiskSize; + + if (MMInitFlush(&FlushTMP, pF, nBlockSize, &pBuffer, + pZSection->ZSectionOffset, 0)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + FlushTMP.pBlockWhereToSaveOrRead = (void *)pBuffer; + if (MMReadFlush(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + for (nIndex = 0; nIndex < nElements; nIndex++) + { + FlushTMP.SizeOfBlockToBeSaved = + sizeof((pZDescription + nIndex)->dfBBminz); + FlushTMP.pBlockToBeSaved = (void *)&(pZDescription + nIndex)->dfBBminz; + if (MMReadBlockFromBuffer(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + FlushTMP.SizeOfBlockToBeSaved = + sizeof((pZDescription + nIndex)->dfBBmaxz); + FlushTMP.pBlockToBeSaved = (void *)&(pZDescription + nIndex)->dfBBmaxz; + if (MMReadBlockFromBuffer(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + FlushTMP.SizeOfBlockToBeSaved = + sizeof((pZDescription + nIndex)->nZCount); + FlushTMP.pBlockToBeSaved = (void *)&(pZDescription + nIndex)->nZCount; + if (MMReadBlockFromBuffer(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + if (hMiraMonLayer->LayerVersion == MM_64BITS_VERSION) + { + FlushTMP.SizeOfBlockToBeSaved = 4; + FlushTMP.pBlockToBeSaved = (void *)nullptr; + if (MMReadBlockFromBuffer(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + } + + if (MMReadOffsetDependingOnVersion(hMiraMonLayer, &FlushTMP, + &(pZDescription + nIndex)->nOffsetZ)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + } + if (pBuffer) + free_function(pBuffer); + + return 0; +} + +static int +MMWriteZDescriptionHeaders(struct MiraMonVectLayerInfo *hMiraMonLayer, + FILE_TYPE *pF, MM_INTERNAL_FID nElements, + struct MM_ZSection *pZSection) +{ + struct MM_FLUSH_INFO FlushTMP; + char *pBuffer = nullptr; + uint32_t nUL32; + MM_INTERNAL_FID nIndex = 0; + MM_FILE_OFFSET nOffsetDiff; + struct MM_ZD *pZDescription; + + if (!hMiraMonLayer) + return 1; + + if (!pF) + return 1; + + if (!pZSection) + return 1; + + pZDescription = pZSection->pZDescription; + + nOffsetDiff = + pZSection->ZSectionOffset + nElements * pZSection->nZDDiskSize; + + if (MMInitFlush(&FlushTMP, pF, MM_1MB, &pBuffer, pZSection->ZSectionOffset, + 0)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + FlushTMP.pBlockWhereToSaveOrRead = (void *)pBuffer; + for (nIndex = 0; nIndex < nElements; nIndex++) + { + FlushTMP.SizeOfBlockToBeSaved = + sizeof((pZDescription + nIndex)->dfBBminz); + FlushTMP.pBlockToBeSaved = (void *)&(pZDescription + nIndex)->dfBBminz; + if (MMAppendBlockToBuffer(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + FlushTMP.SizeOfBlockToBeSaved = + sizeof((pZDescription + nIndex)->dfBBmaxz); + FlushTMP.pBlockToBeSaved = (void *)&(pZDescription + nIndex)->dfBBmaxz; + if (MMAppendBlockToBuffer(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + FlushTMP.SizeOfBlockToBeSaved = + sizeof((pZDescription + nIndex)->nZCount); + FlushTMP.pBlockToBeSaved = (void *)&(pZDescription + nIndex)->nZCount; + if (MMAppendBlockToBuffer(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + if (hMiraMonLayer->LayerVersion == MM_64BITS_VERSION) + { + FlushTMP.SizeOfBlockToBeSaved = 4; + FlushTMP.pBlockToBeSaved = (void *)nullptr; + if (MMAppendBlockToBuffer(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + } + + if (MMAppendIntegerDependingOnVersion( + hMiraMonLayer, &FlushTMP, &nUL32, + (pZDescription + nIndex)->nOffsetZ + nOffsetDiff)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + } + FlushTMP.SizeOfBlockToBeSaved = 0; + if (MMAppendBlockToBuffer(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + pZSection->ZSectionOffset += FlushTMP.TotalSavedBytes; + + if (pBuffer) + free_function(pBuffer); + + return 0; +} + +static void MMDestroyZSectionDescription(struct MM_ZSection *pZSection) +{ + if (pZSection->pZL) + { + free_function(pZSection->pZL); + pZSection->pZL = nullptr; + } + + if (pZSection->pZDescription) + { + free_function(pZSection->pZDescription); + pZSection->pZDescription = nullptr; + } +} + +static int MMInitZSectionDescription(struct MM_ZSection *pZSection) +{ + if (MMCheckSize_t(pZSection->nMaxZDescription, + sizeof(*pZSection->pZDescription))) + return 1; + + if (!pZSection->nMaxZDescription) + { + pZSection->pZDescription = nullptr; + return 0; // No elements to read (or write) + } + + pZSection->pZDescription = + (struct MM_ZD *)calloc_function((size_t)pZSection->nMaxZDescription * + sizeof(*pZSection->pZDescription)); + if (!pZSection->pZDescription) + return 1; + return 0; +} + +static int MMInitZSectionLayer(struct MiraMonVectLayerInfo *hMiraMonLayer, + FILE_TYPE *pF3d, struct MM_ZSection *pZSection) +{ + if (!hMiraMonLayer) + return 1; + + // Zsection + if (!hMiraMonLayer->TopHeader.bIs3d) + { + pZSection->pZDescription = nullptr; + return 0; + } + + if (hMiraMonLayer->ReadOrWrite == MM_WRITING_MODE) + { + pZSection->ZHeader.dfBBminz = STATISTICAL_UNDEF_VALUE; + pZSection->ZHeader.dfBBmaxz = -STATISTICAL_UNDEF_VALUE; + } + + // ZH + pZSection->ZHeader.nMyDiskSize = 32; + pZSection->ZSectionOffset = 0; + + // ZD + if (hMiraMonLayer->ReadOrWrite == MM_WRITING_MODE) + { + pZSection->nMaxZDescription = + MM_FIRST_NUMBER_OF_VERTICES * sizeof(double); + if (MMInitZSectionDescription(pZSection)) + return 1; + } + else + { + if (hMiraMonLayer->bIsPolygon) + { + if (MMCheckSize_t(hMiraMonLayer->MMPolygon.TopArcHeader.nElemCount, + sizeof(double))) + return 1; + + pZSection->nMaxZDescription = + hMiraMonLayer->MMPolygon.TopArcHeader.nElemCount * + sizeof(double); + } + else + { + if (MMCheckSize_t(hMiraMonLayer->TopHeader.nElemCount, + sizeof(double))) + return 1; + + pZSection->nMaxZDescription = + hMiraMonLayer->TopHeader.nElemCount * sizeof(double); + } + if (MMInitZSectionDescription(pZSection)) + return 1; + } + + if (hMiraMonLayer->LayerVersion == MM_32BITS_VERSION) + pZSection->nZDDiskSize = MM_SIZE_OF_ZD_32_BITS; + else + pZSection->nZDDiskSize = MM_SIZE_OF_ZD_64_BITS; + + pZSection->ZDOffset = 0; + + // ZL + if (hMiraMonLayer->ReadOrWrite == MM_WRITING_MODE) + { + if (MMInitFlush(&pZSection->FlushZL, pF3d, MM_1MB, &pZSection->pZL, 0, + sizeof(double))) + return 1; + } + return 0; +} + +/* -------------------------------------------------------------------- */ +/* Layer Functions: Extensions */ +/* -------------------------------------------------------------------- */ + +/* Find the last occurrence of pszFinalPart in pszName + and changes it by pszNewPart. + + Examples of desired behavior + AA.pnt -> AAT.rel + AA.nod -> N.~idx + AA.nod -> N.dbf + AA.nod -> N.rel +*/ + +static int MMChangeFinalPartOfTheName(char *pszName, size_t nMaxSizeOfName, + const char *pszFinalPart, + const char *pszNewPart) +{ + char *pAux, *pszWhereToFind, *pszLastFound = nullptr; + ; + + if (!pszName || !pszFinalPart || !pszNewPart) + return 0; + if (MMIsEmptyString(pszName) || MMIsEmptyString(pszFinalPart) || + MMIsEmptyString(pszNewPart)) + return 0; + + if (strlen(pszName) - strlen(pszFinalPart) + strlen(pszNewPart) >= + nMaxSizeOfName) + return 1; // It's not possible to change the final part + + // It's the implementation on windows of the linux strrstr() + // pszLastFound = strrstr(pszWhereToFind, pszFinalPart); + pszWhereToFind = pszName; + while (nullptr != (pAux = strstr(pszWhereToFind, pszFinalPart))) + { + pszLastFound = pAux; + pszWhereToFind = pAux + strlen(pAux); + } + + if (!pszLastFound) + return 1; // Not found not changed + + memcpy(pszLastFound, pszNewPart, strlen(pszNewPart)); + + return 0; +} + +/* -------------------------------------------------------------------- */ +/* Layer Functions: initializing MiraMon layers */ +/* -------------------------------------------------------------------- */ +static int MMInitPointLayer(struct MiraMonVectLayerInfo *hMiraMonLayer) +{ + if (!hMiraMonLayer) + return 1; + + hMiraMonLayer->bIsPoint = 1; + + if (hMiraMonLayer->ReadOrWrite == MM_WRITING_MODE) + { + // Geometrical part + // Init header structure + hMiraMonLayer->TopHeader.nElemCount = 0; + MMInitBoundingBox(&hMiraMonLayer->TopHeader.hBB); + + hMiraMonLayer->TopHeader.bIs3d = 1; // Read description of bRealIs3d + hMiraMonLayer->TopHeader.aFileType[0] = 'P'; + hMiraMonLayer->TopHeader.aFileType[1] = 'N'; + hMiraMonLayer->TopHeader.aFileType[2] = 'T'; + + // Opening the binary file where sections TH, TL[...] and ZH-ZD[...]-ZL[...] + // are going to be written. + + snprintf(hMiraMonLayer->MMPoint.pszLayerName, + sizeof(hMiraMonLayer->MMPoint.pszLayerName), "%s.pnt", + hMiraMonLayer->pszSrcLayerName); + } + if (nullptr == (hMiraMonLayer->MMPoint.pF = + fopen_function(hMiraMonLayer->MMPoint.pszLayerName, + hMiraMonLayer->pszFlags))) + { + MMCPLError(CE_Failure, CPLE_OpenFailed, + "Error MMPoint.pF: Cannot open file %s.", + hMiraMonLayer->MMPoint.pszLayerName); + return 1; + } + fseek_function(hMiraMonLayer->MMPoint.pF, 0, SEEK_SET); + + if (hMiraMonLayer->ReadOrWrite == MM_WRITING_MODE) + { + // TL + snprintf(hMiraMonLayer->MMPoint.pszTLName, + sizeof(hMiraMonLayer->MMPoint.pszTLName), "%sT.~xy", + hMiraMonLayer->pszSrcLayerName); + + if (nullptr == (hMiraMonLayer->MMPoint.pFTL = + fopen_function(hMiraMonLayer->MMPoint.pszTLName, + hMiraMonLayer->pszFlags))) + { + MMCPLError(CE_Failure, CPLE_OpenFailed, + "Error MMPoint.pFTL: Cannot open file %s.", + hMiraMonLayer->MMPoint.pszTLName); + return 1; + } + fseek_function(hMiraMonLayer->MMPoint.pFTL, 0, SEEK_SET); + + if (MMInitFlush(&hMiraMonLayer->MMPoint.FlushTL, + hMiraMonLayer->MMPoint.pFTL, MM_1MB, + &hMiraMonLayer->MMPoint.pTL, 0, MM_SIZE_OF_TL)) + return 1; + + // 3D part + if (hMiraMonLayer->TopHeader.bIs3d) + { + snprintf(hMiraMonLayer->MMPoint.psz3DLayerName, + sizeof(hMiraMonLayer->MMPoint.psz3DLayerName), "%sT.~z", + hMiraMonLayer->pszSrcLayerName); + + if (nullptr == (hMiraMonLayer->MMPoint.pF3d = fopen_function( + hMiraMonLayer->MMPoint.psz3DLayerName, + hMiraMonLayer->pszFlags))) + { + MMCPLError(CE_Failure, CPLE_OpenFailed, + "Error MMPoint.pF3d: Cannot open file %s.", + hMiraMonLayer->MMPoint.psz3DLayerName); + return 1; + } + fseek_function(hMiraMonLayer->MMPoint.pF3d, 0, SEEK_SET); + } + } + // Zsection + if (hMiraMonLayer->TopHeader.bIs3d) + { + if (MMInitZSectionLayer(hMiraMonLayer, hMiraMonLayer->MMPoint.pF3d, + &hMiraMonLayer->MMPoint.pZSection)) + return 1; + + if (hMiraMonLayer->ReadOrWrite == MM_READING_MODE) + { + if (MMReadZSection(hMiraMonLayer, hMiraMonLayer->MMPoint.pF, + &hMiraMonLayer->MMPoint.pZSection)) + return 1; + + if (MMReadZDescriptionHeaders(hMiraMonLayer, + hMiraMonLayer->MMPoint.pF, + hMiraMonLayer->TopHeader.nElemCount, + &hMiraMonLayer->MMPoint.pZSection)) + return 1; + } + } + + // MiraMon metadata + if (hMiraMonLayer->ReadOrWrite == MM_WRITING_MODE) + { + snprintf(hMiraMonLayer->MMPoint.pszREL_LayerName, + sizeof(hMiraMonLayer->MMPoint.pszREL_LayerName), "%sT.rel", + hMiraMonLayer->pszSrcLayerName); + } + else + { + CPLStrlcpy(hMiraMonLayer->MMPoint.pszREL_LayerName, + hMiraMonLayer->pszSrcLayerName, + sizeof(hMiraMonLayer->MMPoint.pszREL_LayerName)); + if (MMChangeFinalPartOfTheName(hMiraMonLayer->MMPoint.pszREL_LayerName, + MM_CPL_PATH_BUF_SIZE, ".pnt", "T.rel")) + return 1; + } + + hMiraMonLayer->pszMainREL_LayerName = + hMiraMonLayer->MMPoint.pszREL_LayerName; + + if (hMiraMonLayer->ReadOrWrite == MM_READING_MODE) + { + // This file has to exist and be the appropriate version. + if (MMCheck_REL_FILE(hMiraMonLayer->MMPoint.pszREL_LayerName)) + return 1; + } + + // MIRAMON DATA BASE + // Creating the DBF file name + if (hMiraMonLayer->ReadOrWrite == MM_WRITING_MODE) + { + snprintf(hMiraMonLayer->MMPoint.MMAdmDB.pszExtDBFLayerName, + sizeof(hMiraMonLayer->MMPoint.MMAdmDB.pszExtDBFLayerName), + "%sT.dbf", hMiraMonLayer->pszSrcLayerName); + } + else + { + CPLStrlcpy(hMiraMonLayer->MMPoint.MMAdmDB.pszExtDBFLayerName, + hMiraMonLayer->pszSrcLayerName, + sizeof(hMiraMonLayer->MMPoint.MMAdmDB.pszExtDBFLayerName)); + + if (MMChangeFinalPartOfTheName( + hMiraMonLayer->MMPoint.MMAdmDB.pszExtDBFLayerName, + MM_CPL_PATH_BUF_SIZE, ".pnt", "T.dbf")) + return 1; + } + + if (hMiraMonLayer->ReadOrWrite == MM_READING_MODE) + { + if (MM_ReadExtendedDBFHeader(hMiraMonLayer)) + return 1; + } + + return 0; +} + +static int MMInitNodeLayer(struct MiraMonVectLayerInfo *hMiraMonLayer) +{ + struct MiraMonArcLayer *pMMArcLayer; + + if (!hMiraMonLayer) + return 1; + + if (hMiraMonLayer->bIsPolygon) + pMMArcLayer = &hMiraMonLayer->MMPolygon.MMArc; + else + pMMArcLayer = &hMiraMonLayer->MMArc; + + if (hMiraMonLayer->ReadOrWrite == MM_WRITING_MODE) + { + // Init header structure + pMMArcLayer->TopNodeHeader.aFileType[0] = 'N'; + pMMArcLayer->TopNodeHeader.aFileType[1] = 'O'; + pMMArcLayer->TopNodeHeader.aFileType[2] = 'D'; + + pMMArcLayer->TopNodeHeader.bIs3d = 1; // Read description of bRealIs3d + MMInitBoundingBox(&pMMArcLayer->TopNodeHeader.hBB); + } + + // Opening the binary file where sections TH, NH and NL[...] + // are going to be written. + strcpy(pMMArcLayer->MMNode.pszLayerName, pMMArcLayer->pszLayerName); + CPLStrlcpy(pMMArcLayer->MMNode.pszLayerName, + reset_extension(pMMArcLayer->MMNode.pszLayerName, "nod"), + sizeof(pMMArcLayer->MMNode.pszLayerName)); + + if (nullptr == (pMMArcLayer->MMNode.pF = + fopen_function(pMMArcLayer->MMNode.pszLayerName, + hMiraMonLayer->pszFlags))) + { + + MMCPLError(CE_Failure, CPLE_OpenFailed, + "Error MMNode.pF: Cannot open file %s.", + pMMArcLayer->MMNode.pszLayerName); + return 1; + } + fseek_function(pMMArcLayer->MMNode.pF, 0, SEEK_SET); + + if (hMiraMonLayer->ReadOrWrite == MM_WRITING_MODE) + { + // Node Header + pMMArcLayer->MMNode.nMaxNodeHeader = MM_FIRST_NUMBER_OF_NODES; + if (MMCheckSize_t(pMMArcLayer->MMNode.nMaxNodeHeader, + sizeof(*pMMArcLayer->MMNode.pNodeHeader))) + return 1; + + if (!pMMArcLayer->MMNode.nMaxNodeHeader) + { + MMCPLError(CE_Failure, CPLE_OutOfMemory, + "Error in MiraMon " + "driver: no nodes to write?"); + return 1; + } + + if (nullptr == + (pMMArcLayer->MMNode.pNodeHeader = (struct MM_NH *)calloc_function( + (size_t)pMMArcLayer->MMNode.nMaxNodeHeader * + sizeof(*pMMArcLayer->MMNode.pNodeHeader)))) + { + MMCPLError(CE_Failure, CPLE_OutOfMemory, + "Memory error in MiraMon " + "driver (MMInitNodeLayer())"); + return 1; + } + + if (hMiraMonLayer->LayerVersion == MM_32BITS_VERSION) + pMMArcLayer->MMNode.nSizeNodeHeader = MM_SIZE_OF_NH_32BITS; + else + pMMArcLayer->MMNode.nSizeNodeHeader = MM_SIZE_OF_NH_64BITS; + + // NL Section + strcpy(pMMArcLayer->MMNode.pszNLName, pMMArcLayer->MMNode.pszLayerName); + if (MMChangeFinalPartOfTheName(pMMArcLayer->MMNode.pszNLName, + MM_CPL_PATH_BUF_SIZE, ".nod", "N.~idx")) + return 1; + + if (nullptr == (pMMArcLayer->MMNode.pFNL = + fopen_function(pMMArcLayer->MMNode.pszNLName, + hMiraMonLayer->pszFlags))) + { + + MMCPLError(CE_Failure, CPLE_OpenFailed, + "Error MMNode.pFNL: Cannot open file %s.", + pMMArcLayer->MMNode.pszNLName); + return 1; + } + fseek_function(pMMArcLayer->MMNode.pFNL, 0, SEEK_SET); + + if (MMInitFlush(&pMMArcLayer->MMNode.FlushNL, pMMArcLayer->MMNode.pFNL, + MM_1MB, &pMMArcLayer->MMNode.pNL, 0, 0)) + return 1; + + // Creating the DBF file name + strcpy(pMMArcLayer->MMNode.MMAdmDB.pszExtDBFLayerName, + pMMArcLayer->MMNode.pszLayerName); + if (MMChangeFinalPartOfTheName( + pMMArcLayer->MMNode.MMAdmDB.pszExtDBFLayerName, + MM_CPL_PATH_BUF_SIZE, ".nod", "N.dbf")) + return 1; + + // MiraMon metadata + strcpy(pMMArcLayer->MMNode.pszREL_LayerName, + pMMArcLayer->MMNode.pszLayerName); + if (MMChangeFinalPartOfTheName(pMMArcLayer->MMNode.pszREL_LayerName, + MM_CPL_PATH_BUF_SIZE, ".nod", "N.rel")) + return 1; + } + return 0; +} + +static int MMInitArcLayer(struct MiraMonVectLayerInfo *hMiraMonLayer) +{ + struct MiraMonArcLayer *pMMArcLayer; + struct MM_TH *pArcTopHeader; + + if (!hMiraMonLayer) + return 1; + + if (hMiraMonLayer->bIsPolygon) + { + pMMArcLayer = &hMiraMonLayer->MMPolygon.MMArc; + pArcTopHeader = &hMiraMonLayer->MMPolygon.TopArcHeader; + } + else + { + pMMArcLayer = &hMiraMonLayer->MMArc; + pArcTopHeader = &hMiraMonLayer->TopHeader; + } + + // Init header structure + hMiraMonLayer->bIsArc = 1; + + if (hMiraMonLayer->ReadOrWrite == MM_WRITING_MODE) + { + pArcTopHeader->bIs3d = 1; // Read description of bRealIs3d + MMInitBoundingBox(&pArcTopHeader->hBB); + + pArcTopHeader->aFileType[0] = 'A'; + pArcTopHeader->aFileType[1] = 'R'; + pArcTopHeader->aFileType[2] = 'C'; + + if (hMiraMonLayer->bIsPolygon) + { + snprintf(pMMArcLayer->pszLayerName, + sizeof(pMMArcLayer->pszLayerName), "%s_bound.arc", + hMiraMonLayer->pszSrcLayerName); + } + else + { + snprintf(pMMArcLayer->pszLayerName, + sizeof(pMMArcLayer->pszLayerName), "%s.arc", + hMiraMonLayer->pszSrcLayerName); + } + } + + if (nullptr == (pMMArcLayer->pF = fopen_function(pMMArcLayer->pszLayerName, + hMiraMonLayer->pszFlags))) + { + MMCPLError(CE_Failure, CPLE_OpenFailed, + "Error pMMArcLayer->pF: Cannot open file %s.", + pMMArcLayer->pszLayerName); + return 1; + } + + if (hMiraMonLayer->ReadOrWrite == MM_READING_MODE && + hMiraMonLayer->bIsPolygon) + { + fseek_function(pMMArcLayer->pF, 0, SEEK_SET); + if (MMReadHeader(pMMArcLayer->pF, + &hMiraMonLayer->MMPolygon.TopArcHeader)) + { + MMCPLError(CE_Failure, CPLE_NotSupported, + "Error reading the format in file %s.", + pMMArcLayer->pszLayerName); + return 1; + } + // 3D information is in arcs file + hMiraMonLayer->TopHeader.bIs3d = + hMiraMonLayer->MMPolygon.TopArcHeader.bIs3d; + } + + // AH + if (hMiraMonLayer->LayerVersion == MM_32BITS_VERSION) + pMMArcLayer->nSizeArcHeader = MM_SIZE_OF_AH_32BITS; + else + pMMArcLayer->nSizeArcHeader = MM_SIZE_OF_AH_64BITS; + + if (hMiraMonLayer->ReadOrWrite == MM_WRITING_MODE) + pMMArcLayer->nMaxArcHeader = MM_FIRST_NUMBER_OF_ARCS; + else + pMMArcLayer->nMaxArcHeader = pArcTopHeader->nElemCount; + + if (pMMArcLayer->nMaxArcHeader) + { + if (MMCheckSize_t(pMMArcLayer->nMaxArcHeader, + sizeof(*pMMArcLayer->pArcHeader))) + return 1; + if (nullptr == (pMMArcLayer->pArcHeader = (struct MM_AH *) + calloc_function((size_t)pMMArcLayer->nMaxArcHeader * + sizeof(*pMMArcLayer->pArcHeader)))) + { + MMCPLError(CE_Failure, CPLE_OutOfMemory, + "Memory error in MiraMon " + "driver (MMInitArcLayer())"); + return 1; + } + if (hMiraMonLayer->ReadOrWrite == MM_READING_MODE) + { + if (MMReadAHArcSection(hMiraMonLayer)) + { + MMCPLError(CE_Failure, CPLE_NotSupported, + "Error reading the format in file %s.", + pMMArcLayer->pszLayerName); + return 1; + } + } + } + else + pMMArcLayer->pArcHeader = nullptr; + + // AL + if (hMiraMonLayer->ReadOrWrite == MM_WRITING_MODE) + { + pMMArcLayer->nALElementSize = MM_SIZE_OF_AL; + + if (hMiraMonLayer->bIsPolygon) + { + snprintf(pMMArcLayer->pszALName, sizeof(pMMArcLayer->pszALName), + "%s_boundA.~xy", hMiraMonLayer->pszSrcLayerName); + } + else + { + snprintf(pMMArcLayer->pszALName, sizeof(pMMArcLayer->pszALName), + "%sA.~xy", hMiraMonLayer->pszSrcLayerName); + } + + if (nullptr == (pMMArcLayer->pFAL = fopen_function( + pMMArcLayer->pszALName, hMiraMonLayer->pszFlags))) + { + MMCPLError(CE_Failure, CPLE_OpenFailed, + "Error pMMArcLayer->pFAL: Cannot open file %s.", + pMMArcLayer->pszALName); + return 1; + } + fseek_function(pMMArcLayer->pFAL, 0, SEEK_SET); + + if (MMInitFlush(&pMMArcLayer->FlushAL, pMMArcLayer->pFAL, MM_1MB, + &pMMArcLayer->pAL, 0, 0)) + return 1; + } + + // 3D + if (pArcTopHeader->bIs3d) + { + if (hMiraMonLayer->ReadOrWrite == MM_WRITING_MODE) + { + if (hMiraMonLayer->bIsPolygon) + { + snprintf(pMMArcLayer->psz3DLayerName, + sizeof(pMMArcLayer->psz3DLayerName), "%s_boundA.~z", + hMiraMonLayer->pszSrcLayerName); + } + else + { + snprintf(pMMArcLayer->psz3DLayerName, + sizeof(pMMArcLayer->psz3DLayerName), "%sA.~z", + hMiraMonLayer->pszSrcLayerName); + } + + if (nullptr == + (pMMArcLayer->pF3d = fopen_function(pMMArcLayer->psz3DLayerName, + hMiraMonLayer->pszFlags))) + { + MMCPLError(CE_Failure, CPLE_OpenFailed, + "Error pMMArcLayer->pF3d: Cannot open file %s.", + pMMArcLayer->psz3DLayerName); + return 1; + } + fseek_function(pMMArcLayer->pF3d, 0, SEEK_SET); + } + + if (MMInitZSectionLayer(hMiraMonLayer, pMMArcLayer->pF3d, + &pMMArcLayer->pZSection)) + { + MMCPLError(CE_Failure, CPLE_NotSupported, + "Error reading the format in file %s %d.", + pMMArcLayer->pszLayerName, 6); + return 1; + } + + if (hMiraMonLayer->ReadOrWrite == MM_READING_MODE) + { + if (MMReadZSection(hMiraMonLayer, pMMArcLayer->pF, + &pMMArcLayer->pZSection)) + { + MMCPLError(CE_Failure, CPLE_NotSupported, + "Error reading the format in file %s.", + pMMArcLayer->pszLayerName); + return 1; + } + + if (MMReadZDescriptionHeaders(hMiraMonLayer, pMMArcLayer->pF, + pArcTopHeader->nElemCount, + &pMMArcLayer->pZSection)) + { + MMCPLError(CE_Failure, CPLE_NotSupported, + "Error reading the format in file %s.", + pMMArcLayer->pszLayerName); + return 1; + } + } + } + // MiraMon metadata + if (hMiraMonLayer->bIsPolygon) + { + if (hMiraMonLayer->ReadOrWrite == MM_WRITING_MODE) + { + snprintf(pMMArcLayer->pszREL_LayerName, + sizeof(pMMArcLayer->pszREL_LayerName), "%s_boundA.rel", + hMiraMonLayer->pszSrcLayerName); + } + else + { + strcpy(pMMArcLayer->pszREL_LayerName, pMMArcLayer->pszLayerName); + if (MMChangeFinalPartOfTheName(pMMArcLayer->pszREL_LayerName, + MM_CPL_PATH_BUF_SIZE, ".arc", + "A.rel")) + return 1; + } + } + else + { + if (hMiraMonLayer->ReadOrWrite == MM_WRITING_MODE) + { + snprintf(pMMArcLayer->pszREL_LayerName, + sizeof(pMMArcLayer->pszREL_LayerName), "%sA.rel", + hMiraMonLayer->pszSrcLayerName); + } + else + { + CPLStrlcpy(pMMArcLayer->pszREL_LayerName, + hMiraMonLayer->pszSrcLayerName, + sizeof(pMMArcLayer->pszREL_LayerName)); + if (MMChangeFinalPartOfTheName(pMMArcLayer->pszREL_LayerName, + MM_CPL_PATH_BUF_SIZE, ".arc", + "A.rel")) + return 1; + } + } + + if (hMiraMonLayer->ReadOrWrite == MM_READING_MODE) + { + // This file has to exist and be the appropriate version. + if (MMCheck_REL_FILE(pMMArcLayer->pszREL_LayerName)) + return 1; + } + + if (!hMiraMonLayer->bIsPolygon) + hMiraMonLayer->pszMainREL_LayerName = pMMArcLayer->pszREL_LayerName; + + // MIRAMON DATA BASE + // Creating the DBF file name + if (hMiraMonLayer->bIsPolygon) + { + if (hMiraMonLayer->ReadOrWrite == MM_WRITING_MODE) + { + snprintf(pMMArcLayer->MMAdmDB.pszExtDBFLayerName, + sizeof(pMMArcLayer->MMAdmDB.pszExtDBFLayerName), + "%s_boundA.dbf", hMiraMonLayer->pszSrcLayerName); + } + else + { + strcpy(pMMArcLayer->MMAdmDB.pszExtDBFLayerName, + pMMArcLayer->pszLayerName); + if (MMChangeFinalPartOfTheName( + pMMArcLayer->MMAdmDB.pszExtDBFLayerName, + MM_CPL_PATH_BUF_SIZE, ".arc", "A.dbf")) + return 1; + } + } + else + { + if (hMiraMonLayer->ReadOrWrite == MM_WRITING_MODE) + { + snprintf(pMMArcLayer->MMAdmDB.pszExtDBFLayerName, + sizeof(pMMArcLayer->MMAdmDB.pszExtDBFLayerName), "%sA.dbf", + hMiraMonLayer->pszSrcLayerName); + } + else + { + CPLStrlcpy(pMMArcLayer->MMAdmDB.pszExtDBFLayerName, + hMiraMonLayer->pszSrcLayerName, + sizeof(pMMArcLayer->MMAdmDB.pszExtDBFLayerName)); + if (MMChangeFinalPartOfTheName( + pMMArcLayer->MMAdmDB.pszExtDBFLayerName, + MM_CPL_PATH_BUF_SIZE, ".arc", "A.dbf")) + return 1; + } + } + + if (hMiraMonLayer->ReadOrWrite == MM_READING_MODE) + { + if (MM_ReadExtendedDBFHeader(hMiraMonLayer)) + return 1; + } + + // Node part + if (MMInitNodeLayer(hMiraMonLayer)) + return 1; + if (hMiraMonLayer->LayerVersion == MM_32BITS_VERSION) + MMSet1_1Version(&pMMArcLayer->TopNodeHeader); + else + MMSet2_0Version(&pMMArcLayer->TopNodeHeader); + + return 0; +} + +static int MMInitPolygonLayer(struct MiraMonVectLayerInfo *hMiraMonLayer) +{ + struct MiraMonPolygonLayer *pMMPolygonLayer; + + if (!hMiraMonLayer) + return 1; + + pMMPolygonLayer = &hMiraMonLayer->MMPolygon; + + // Init header structure + hMiraMonLayer->bIsPolygon = 1; + + if (hMiraMonLayer->ReadOrWrite == MM_WRITING_MODE) + { + hMiraMonLayer->TopHeader.bIs3d = 1; // Read description of bRealIs3d + MMInitBoundingBox(&hMiraMonLayer->TopHeader.hBB); + + hMiraMonLayer->TopHeader.aFileType[0] = 'P'; + hMiraMonLayer->TopHeader.aFileType[1] = 'O'; + hMiraMonLayer->TopHeader.aFileType[2] = 'L'; + + snprintf(pMMPolygonLayer->pszLayerName, + sizeof(pMMPolygonLayer->pszLayerName), "%s.pol", + hMiraMonLayer->pszSrcLayerName); + } + + if (nullptr == + (pMMPolygonLayer->pF = fopen_function(pMMPolygonLayer->pszLayerName, + hMiraMonLayer->pszFlags))) + { + MMCPLError(CE_Failure, CPLE_OpenFailed, + "Error pMMPolygonLayer->pF: Cannot open file %s.", + pMMPolygonLayer->pszLayerName); + return 1; + } + + // PS + if (hMiraMonLayer->LayerVersion == MM_32BITS_VERSION) + pMMPolygonLayer->nPSElementSize = MM_SIZE_OF_PS_32BITS; + else + pMMPolygonLayer->nPSElementSize = MM_SIZE_OF_PS_64BITS; + + if (hMiraMonLayer->ReadOrWrite == MM_WRITING_MODE) + { + snprintf(pMMPolygonLayer->pszPSName, sizeof(pMMPolygonLayer->pszPSName), + "%sP.~PS", hMiraMonLayer->pszSrcLayerName); + + if (nullptr == + (pMMPolygonLayer->pFPS = fopen_function(pMMPolygonLayer->pszPSName, + hMiraMonLayer->pszFlags))) + { + MMCPLError(CE_Failure, CPLE_OpenFailed, + "Error pMMPolygonLayer->pFPS: Cannot open file %s.", + pMMPolygonLayer->pszPSName); + return 1; + } + fseek_function(pMMPolygonLayer->pFPS, 0, SEEK_SET); + + if (MMInitFlush(&pMMPolygonLayer->FlushPS, pMMPolygonLayer->pFPS, + MM_1MB, &pMMPolygonLayer->pPS, 0, + pMMPolygonLayer->nPSElementSize)) + return 1; + } + + // PH + if (hMiraMonLayer->LayerVersion == MM_32BITS_VERSION) + pMMPolygonLayer->nPHElementSize = MM_SIZE_OF_PH_32BITS; + else + pMMPolygonLayer->nPHElementSize = MM_SIZE_OF_PH_64BITS; + + if (hMiraMonLayer->ReadOrWrite == MM_WRITING_MODE) + pMMPolygonLayer->nMaxPolHeader = MM_FIRST_NUMBER_OF_POLYGONS + 1; + else + pMMPolygonLayer->nMaxPolHeader = hMiraMonLayer->TopHeader.nElemCount; + + if (pMMPolygonLayer->nMaxPolHeader) + { + if (MMCheckSize_t(pMMPolygonLayer->nMaxPolHeader, + sizeof(*pMMPolygonLayer->pPolHeader))) + return 1; + if (nullptr == + (pMMPolygonLayer->pPolHeader = (struct MM_PH *)calloc_function( + (size_t)pMMPolygonLayer->nMaxPolHeader * + sizeof(*pMMPolygonLayer->pPolHeader)))) + { + MMCPLError(CE_Failure, CPLE_OutOfMemory, + "Memory error in MiraMon " + "driver (MMInitPolygonLayer())"); + return 1; + } + } + else + pMMPolygonLayer->pPolHeader = nullptr; + + // PAL + if (hMiraMonLayer->LayerVersion == MM_32BITS_VERSION) + pMMPolygonLayer->nPALElementSize = MM_SIZE_OF_PAL_32BITS; + else + pMMPolygonLayer->nPALElementSize = MM_SIZE_OF_PAL_64BITS; + + if (hMiraMonLayer->ReadOrWrite == MM_WRITING_MODE) + { + // Universal polygon. + memset(pMMPolygonLayer->pPolHeader, 0, + sizeof(*pMMPolygonLayer->pPolHeader)); + hMiraMonLayer->TopHeader.nElemCount = 1; + + // PAL + snprintf(pMMPolygonLayer->pszPALName, + sizeof(pMMPolygonLayer->pszPALName), "%sP.~idx", + hMiraMonLayer->pszSrcLayerName); + + if (nullptr == (pMMPolygonLayer->pFPAL = + fopen_function(pMMPolygonLayer->pszPALName, + hMiraMonLayer->pszFlags))) + { + MMCPLError(CE_Failure, CPLE_OpenFailed, + "Error pMMPolygonLayer->pFPAL: Cannot open file %s.", + pMMPolygonLayer->pszPALName); + return 1; + } + fseek_function(pMMPolygonLayer->pFPAL, 0, SEEK_SET); + + if (MMInitFlush(&pMMPolygonLayer->FlushPAL, pMMPolygonLayer->pFPAL, + MM_1MB, &pMMPolygonLayer->pPAL, 0, 0)) + return 1; + } + + // MiraMon metadata + + if (hMiraMonLayer->ReadOrWrite == MM_WRITING_MODE) + { + snprintf(hMiraMonLayer->MMPolygon.pszREL_LayerName, + sizeof(hMiraMonLayer->MMPolygon.pszREL_LayerName), "%sP.rel", + hMiraMonLayer->pszSrcLayerName); + } + else + { + CPLStrlcpy(hMiraMonLayer->MMPolygon.pszREL_LayerName, + hMiraMonLayer->pszSrcLayerName, + sizeof(hMiraMonLayer->MMPolygon.pszREL_LayerName)); + + if (MMChangeFinalPartOfTheName( + hMiraMonLayer->MMPolygon.pszREL_LayerName, MM_CPL_PATH_BUF_SIZE, + ".pol", "P.rel")) + return 1; + } + + if (hMiraMonLayer->ReadOrWrite == MM_READING_MODE) + { + // This file has to exist and be the appropriate version. + if (MMCheck_REL_FILE(hMiraMonLayer->MMPolygon.pszREL_LayerName)) + return 1; + } + + hMiraMonLayer->pszMainREL_LayerName = + hMiraMonLayer->MMPolygon.pszREL_LayerName; + + // MIRAMON DATA BASE + if (hMiraMonLayer->ReadOrWrite == MM_WRITING_MODE) + { + snprintf(pMMPolygonLayer->MMAdmDB.pszExtDBFLayerName, + sizeof(pMMPolygonLayer->MMAdmDB.pszExtDBFLayerName), "%sP.dbf", + hMiraMonLayer->pszSrcLayerName); + } + else + { + CPLStrlcpy(pMMPolygonLayer->MMAdmDB.pszExtDBFLayerName, + hMiraMonLayer->pszSrcLayerName, + sizeof(pMMPolygonLayer->MMAdmDB.pszExtDBFLayerName)); + if (MMChangeFinalPartOfTheName( + pMMPolygonLayer->MMAdmDB.pszExtDBFLayerName, + MM_CPL_PATH_BUF_SIZE, ".pol", "P.dbf")) + return 1; + } + + if (hMiraMonLayer->ReadOrWrite == MM_READING_MODE) + { + if (MM_ReadExtendedDBFHeader(hMiraMonLayer)) + return 1; + } + + return 0; +} + +int MMInitLayerByType(struct MiraMonVectLayerInfo *hMiraMonLayer) +{ + if (!hMiraMonLayer) + return 1; + + if (hMiraMonLayer->eLT == MM_LayerType_Point || + hMiraMonLayer->eLT == MM_LayerType_Point3d) + { + if (hMiraMonLayer->ReadOrWrite == MM_WRITING_MODE) + { + snprintf(hMiraMonLayer->MMPoint.pszLayerName, + sizeof(hMiraMonLayer->MMPoint.pszLayerName), "%s.pnt", + hMiraMonLayer->pszSrcLayerName); + } + else + { + CPLStrlcpy(hMiraMonLayer->MMPoint.pszLayerName, + hMiraMonLayer->pszSrcLayerName, + sizeof(hMiraMonLayer->MMPoint.pszLayerName)); + } + if (hMiraMonLayer->MMMap && hMiraMonLayer->MMMap->fMMMap) + { + hMiraMonLayer->MMMap->nNumberOfLayers++; + fprintf_function(hMiraMonLayer->MMMap->fMMMap, "[VECTOR_%d]\n", + hMiraMonLayer->MMMap->nNumberOfLayers); + fprintf_function(hMiraMonLayer->MMMap->fMMMap, "Fitxer=%s.pnt\n", + MM_CPLGetBasename(hMiraMonLayer->pszSrcLayerName)); + } + + if (MMInitPointLayer(hMiraMonLayer)) + { + // Error specified inside the function + return 1; + } + return 0; + } + if (hMiraMonLayer->eLT == MM_LayerType_Arc || + hMiraMonLayer->eLT == MM_LayerType_Arc3d) + { + struct MiraMonArcLayer *pMMArcLayer = &hMiraMonLayer->MMArc; + + if (hMiraMonLayer->ReadOrWrite == MM_WRITING_MODE) + { + snprintf(pMMArcLayer->pszLayerName, + sizeof(pMMArcLayer->pszLayerName), "%s.arc", + hMiraMonLayer->pszSrcLayerName); + } + else + { + CPLStrlcpy(pMMArcLayer->pszLayerName, + hMiraMonLayer->pszSrcLayerName, + sizeof(pMMArcLayer->pszLayerName)); + } + + if (hMiraMonLayer->MMMap && hMiraMonLayer->MMMap->fMMMap) + { + hMiraMonLayer->MMMap->nNumberOfLayers++; + fprintf_function(hMiraMonLayer->MMMap->fMMMap, "[VECTOR_%d]\n", + hMiraMonLayer->MMMap->nNumberOfLayers); + fprintf_function(hMiraMonLayer->MMMap->fMMMap, "Fitxer=%s.arc\n", + MM_CPLGetBasename(hMiraMonLayer->pszSrcLayerName)); + } + + if (MMInitArcLayer(hMiraMonLayer)) + { + // Error specified inside the function + return 1; + } + return 0; + } + if (hMiraMonLayer->eLT == MM_LayerType_Pol || + hMiraMonLayer->eLT == MM_LayerType_Pol3d) + { + struct MiraMonPolygonLayer *pMMPolygonLayer = &hMiraMonLayer->MMPolygon; + + if (hMiraMonLayer->ReadOrWrite == MM_WRITING_MODE) + { + snprintf(pMMPolygonLayer->pszLayerName, + sizeof(pMMPolygonLayer->pszLayerName), "%s.pol", + hMiraMonLayer->pszSrcLayerName); + } + else + { + CPLStrlcpy(pMMPolygonLayer->pszLayerName, + hMiraMonLayer->pszSrcLayerName, + sizeof(pMMPolygonLayer->pszLayerName)); + } + + if (hMiraMonLayer->MMMap && hMiraMonLayer->MMMap->fMMMap) + { + hMiraMonLayer->MMMap->nNumberOfLayers++; + fprintf_function(hMiraMonLayer->MMMap->fMMMap, "[VECTOR_%d]\n", + hMiraMonLayer->MMMap->nNumberOfLayers); + fprintf_function(hMiraMonLayer->MMMap->fMMMap, "Fitxer=%s.pol\n", + MM_CPLGetBasename(hMiraMonLayer->pszSrcLayerName)); + } + + if (MMInitPolygonLayer(hMiraMonLayer)) + { + // Error specified inside the function + return 1; + } + + if (hMiraMonLayer->ReadOrWrite == MM_READING_MODE) + { + char *pszArcLayerName; + const char *pszExt; + // StringLine associated to the polygon + pszArcLayerName = MMReturnValueFromSectionINIFile( + pMMPolygonLayer->pszREL_LayerName, + SECTION_OVVW_ASPECTES_TECNICS, KEY_ArcSource); + if (pszArcLayerName) + { + MM_RemoveInitial_and_FinalQuotationMarks(pszArcLayerName); + + // If extension is not specified ".arc" will be used + pszExt = get_extension_function(pszArcLayerName); + if (MMIsEmptyString(pszExt)) + { + char *pszArcLayerNameAux = + calloc_function(strlen(pszArcLayerName) + 5); + if (!pszArcLayerNameAux) + { + MMCPLError(CE_Failure, CPLE_OutOfMemory, + "Memory error in MiraMon " + "driver (MMInitLayerByType())"); + free_function(pszArcLayerName); + return 1; + } + snprintf(pszArcLayerNameAux, strlen(pszArcLayerName) + 5, + "%s.arc", pszArcLayerName); + + free_function(pszArcLayerName); + pszArcLayerName = pszArcLayerNameAux; + } + + CPLStrlcpy( + pMMPolygonLayer->MMArc.pszLayerName, + form_filename_function( + get_path_function(hMiraMonLayer->pszSrcLayerName), + pszArcLayerName), + sizeof(pMMPolygonLayer->MMArc.pszLayerName)); + + free_function(pszArcLayerName); + } + else + { + // There is no arc layer on the metada file + MMCPLError( + CE_Failure, CPLE_OpenFailed, + "Error reading the ARC file in the metadata file %s.", + pMMPolygonLayer->pszREL_LayerName); + return 1; + } + + if (nullptr == (hMiraMonLayer->MMPolygon.MMArc.pF = fopen_function( + pMMPolygonLayer->MMArc.pszLayerName, + hMiraMonLayer->pszFlags))) + { + MMCPLError( + CE_Failure, CPLE_OpenFailed, + "Error pMMPolygonLayer.MMArc.pF: Cannot open file %s.", + pMMPolygonLayer->MMArc.pszLayerName); + return 1; + } + + if (MMReadHeader(hMiraMonLayer->MMPolygon.MMArc.pF, + &hMiraMonLayer->MMPolygon.TopArcHeader)) + { + MMCPLError(CE_Failure, CPLE_NotSupported, + "Error reading the format in file %s.", + pMMPolygonLayer->MMArc.pszLayerName); + return 1; + } + + if (MMReadPHPolygonSection(hMiraMonLayer)) + { + MMCPLError(CE_Failure, CPLE_NotSupported, + "Error reading the format in file %s.", + pMMPolygonLayer->MMArc.pszLayerName); + return 1; + } + + fclose_and_nullify(&hMiraMonLayer->MMPolygon.MMArc.pF); + } + else + { + // Creating the stringLine file associated to the polygon + snprintf(pMMPolygonLayer->MMArc.pszLayerName, + sizeof(pMMPolygonLayer->MMArc.pszLayerName), "%s.arc", + hMiraMonLayer->pszSrcLayerName); + } + + if (MMInitArcLayer(hMiraMonLayer)) + { + // Error specified inside the function + return 1; + } + + // Polygon is 3D if Arc is 3D, by definition. + hMiraMonLayer->TopHeader.bIs3d = + hMiraMonLayer->MMPolygon.TopArcHeader.bIs3d; + + if (hMiraMonLayer->LayerVersion == MM_32BITS_VERSION) + MMSet1_1Version(&pMMPolygonLayer->TopArcHeader); + else + MMSet2_0Version(&pMMPolygonLayer->TopArcHeader); + } + else if (hMiraMonLayer->ReadOrWrite == MM_WRITING_MODE) + { + // Trying to get DBF information + snprintf(hMiraMonLayer->MMAdmDBWriting.pszExtDBFLayerName, + sizeof(hMiraMonLayer->MMAdmDBWriting.pszExtDBFLayerName), + "%s.dbf", hMiraMonLayer->pszSrcLayerName); + } + + return 0; +} + +int MMInitLayer(struct MiraMonVectLayerInfo *hMiraMonLayer, + const char *pzFileName, int LayerVersion, char nMMRecode, + char nMMLanguage, struct MiraMonDataBase *pLayerDB, + MM_BOOLEAN ReadOrWrite, struct MiraMonVectMapInfo *MMMap) +{ + if (!hMiraMonLayer) + return 1; + + // Some variables must be initialized + MM_FillFieldDescriptorByLanguage(); + + memset(hMiraMonLayer, 0, sizeof(*hMiraMonLayer)); + + //hMiraMonLayer->Version = MM_VECTOR_LAYER_LAST_VERSION; + + hMiraMonLayer->ReadOrWrite = ReadOrWrite; + hMiraMonLayer->MMMap = MMMap; + + // Don't free in destructor + hMiraMonLayer->pLayerDB = pLayerDB; + + // Opening mode + strcpy(hMiraMonLayer->pszFlags, "wb+"); + + if (LayerVersion == MM_UNKNOWN_VERSION) + { + MMCPLError(CE_Failure, CPLE_NotSupported, + "Unknown version in MiraMon driver."); + return 1; + } + if (LayerVersion == MM_LAST_VERSION) + { + MMSet1_1Version(&hMiraMonLayer->TopHeader); + hMiraMonLayer->nHeaderDiskSize = MM_HEADER_SIZE_64_BITS; + hMiraMonLayer->LayerVersion = MM_64BITS_VERSION; + } + else if (LayerVersion == MM_32BITS_VERSION) + { + MMSet1_1Version(&hMiraMonLayer->TopHeader); + hMiraMonLayer->nHeaderDiskSize = MM_HEADER_SIZE_32_BITS; + hMiraMonLayer->LayerVersion = MM_32BITS_VERSION; + } + else + { + MMSet2_0Version(&hMiraMonLayer->TopHeader); + hMiraMonLayer->nHeaderDiskSize = MM_HEADER_SIZE_64_BITS; + hMiraMonLayer->LayerVersion = MM_64BITS_VERSION; + } + + hMiraMonLayer->pszSrcLayerName = strdup_function(pzFileName); + hMiraMonLayer->szLayerTitle = + strdup_function(get_filename_function(pzFileName)); + + if (!hMiraMonLayer->bIsBeenInit && + hMiraMonLayer->eLT != MM_LayerType_Unknown) + { + if (MMInitLayerByType(hMiraMonLayer)) + { + // Error specified inside the function + return 1; + } + hMiraMonLayer->bIsBeenInit = 1; + } + + // If more nNumStringToOperate is needed, it'll be increased. + hMiraMonLayer->nNumStringToOperate = 0; + if (MMResizeStringToOperateIfNeeded(hMiraMonLayer, 500)) + { + MMCPLError(CE_Failure, CPLE_OutOfMemory, + "Memory error in MiraMon " + "driver (MMInitLayer())"); + return 1; + } + + hMiraMonLayer->nMMLanguage = nMMLanguage; + + if (nMMRecode == MM_RECODE_UTF8) + hMiraMonLayer->nCharSet = MM_JOC_CARAC_UTF8_DBF; + else //if(nMMRecode==MM_RECODE_ANSI) + hMiraMonLayer->nCharSet = MM_JOC_CARAC_ANSI_DBASE; + return 0; +} + +/* -------------------------------------------------------------------- */ +/* Layer Functions: Closing MiraMon layers */ +/* -------------------------------------------------------------------- */ +static int MMClose3DSectionLayer(struct MiraMonVectLayerInfo *hMiraMonLayer, + MM_INTERNAL_FID nElements, FILE_TYPE *pF, + FILE_TYPE *pF3d, const char *pszF3d, + struct MM_ZSection *pZSection, + MM_FILE_OFFSET FinalOffset) +{ + int ret_code = 1; + if (!hMiraMonLayer) + return 1; + + // Avoid closing when it has no sense. But it's not an error. + // Just return elegantly. + if (!pF || !pF3d || !pszF3d || !pZSection) + return 0; + + if (hMiraMonLayer->bIsReal3d) + { + pZSection->ZSectionOffset = FinalOffset; + if (MMWriteZSection(pF, pZSection)) + goto end_label; + + // Header 3D. Writes it after header + if (MMWriteZDescriptionHeaders(hMiraMonLayer, pF, nElements, pZSection)) + goto end_label; + + // ZL section + pZSection->FlushZL.SizeOfBlockToBeSaved = 0; + if (MMAppendBlockToBuffer(&pZSection->FlushZL)) + goto end_label; + + if (MMMoveFromFileToFile(pF3d, pF, &pZSection->ZSectionOffset)) + goto end_label; + } + + ret_code = 0; +end_label: + fclose_and_nullify(&pF3d); + if (pszF3d && *pszF3d != '\0') + remove_function(pszF3d); + + return ret_code; +} + +static int MMClosePointLayer(struct MiraMonVectLayerInfo *hMiraMonLayer) +{ + int ret_code = 1; + if (!hMiraMonLayer) + return 1; + + if (hMiraMonLayer->ReadOrWrite == MM_WRITING_MODE) + { + hMiraMonLayer->nFinalElemCount = hMiraMonLayer->TopHeader.nElemCount; + hMiraMonLayer->TopHeader.bIs3d = hMiraMonLayer->bIsReal3d; + + if (MMWriteHeader(hMiraMonLayer->MMPoint.pF, &hMiraMonLayer->TopHeader)) + { + MMCPLError(CE_Failure, CPLE_NoWriteAccess, + "Error writing to file %s", + hMiraMonLayer->MMPoint.pszLayerName); + goto end_label; + } + hMiraMonLayer->OffsetCheck = hMiraMonLayer->nHeaderDiskSize; + + // TL Section + hMiraMonLayer->MMPoint.FlushTL.SizeOfBlockToBeSaved = 0; + if (MMAppendBlockToBuffer(&hMiraMonLayer->MMPoint.FlushTL)) + { + MMCPLError(CE_Failure, CPLE_NoWriteAccess, + "Error writing to file %s", + hMiraMonLayer->MMPoint.pszLayerName); + goto end_label; + } + if (MMMoveFromFileToFile(hMiraMonLayer->MMPoint.pFTL, + hMiraMonLayer->MMPoint.pF, + &hMiraMonLayer->OffsetCheck)) + { + MMCPLError(CE_Failure, CPLE_NoWriteAccess, + "Error writing to file %s", + hMiraMonLayer->MMPoint.pszLayerName); + goto end_label; + } + + fclose_and_nullify(&hMiraMonLayer->MMPoint.pFTL); + + if (*hMiraMonLayer->MMPoint.pszTLName != '\0') + remove_function(hMiraMonLayer->MMPoint.pszTLName); + + if (MMClose3DSectionLayer( + hMiraMonLayer, hMiraMonLayer->TopHeader.nElemCount, + hMiraMonLayer->MMPoint.pF, hMiraMonLayer->MMPoint.pF3d, + hMiraMonLayer->MMPoint.psz3DLayerName, + &hMiraMonLayer->MMPoint.pZSection, hMiraMonLayer->OffsetCheck)) + { + MMCPLError(CE_Failure, CPLE_NoWriteAccess, + "Error writing to file %s", + hMiraMonLayer->MMPoint.pszLayerName); + goto end_label; + } + } + + ret_code = 0; +end_label: + fclose_and_nullify(&hMiraMonLayer->MMPoint.pF); + return ret_code; +} + +static int MMCloseNodeLayer(struct MiraMonVectLayerInfo *hMiraMonLayer) +{ + int ret_code = 1; + struct MiraMonArcLayer *pMMArcLayer; + + if (!hMiraMonLayer) + return 1; + + if (hMiraMonLayer->bIsPolygon) + pMMArcLayer = &hMiraMonLayer->MMPolygon.MMArc; + else + pMMArcLayer = &hMiraMonLayer->MMArc; + + if (hMiraMonLayer->ReadOrWrite == MM_WRITING_MODE) + { + hMiraMonLayer->TopHeader.bIs3d = hMiraMonLayer->bIsReal3d; + + if (MMWriteHeader(pMMArcLayer->MMNode.pF, &pMMArcLayer->TopNodeHeader)) + goto end_label; + hMiraMonLayer->OffsetCheck = hMiraMonLayer->nHeaderDiskSize; + + // NH Section + if (MMWriteNHNodeSection(hMiraMonLayer, hMiraMonLayer->nHeaderDiskSize)) + goto end_label; + + // NL Section + pMMArcLayer->MMNode.FlushNL.SizeOfBlockToBeSaved = 0; + if (MMAppendBlockToBuffer(&pMMArcLayer->MMNode.FlushNL)) + goto end_label; + if (MMMoveFromFileToFile(pMMArcLayer->MMNode.pFNL, + pMMArcLayer->MMNode.pF, + &hMiraMonLayer->OffsetCheck)) + goto end_label; + + fclose_and_nullify(&pMMArcLayer->MMNode.pFNL); + if (*pMMArcLayer->MMNode.pszNLName != '\0') + remove_function(pMMArcLayer->MMNode.pszNLName); + } + + ret_code = 0; +end_label: + fclose_and_nullify(&pMMArcLayer->MMNode.pFNL); + + fclose_and_nullify(&pMMArcLayer->MMNode.pF); + + return ret_code; +} + +static int MMCloseArcLayer(struct MiraMonVectLayerInfo *hMiraMonLayer) +{ + int ret_code = 0; + struct MiraMonArcLayer *pMMArcLayer; + struct MM_TH *pArcTopHeader; + + if (!hMiraMonLayer) + return 1; + + if (hMiraMonLayer->bIsPolygon) + { + pMMArcLayer = &hMiraMonLayer->MMPolygon.MMArc; + pArcTopHeader = &hMiraMonLayer->MMPolygon.TopArcHeader; + } + else + { + pMMArcLayer = &hMiraMonLayer->MMArc; + pArcTopHeader = &hMiraMonLayer->TopHeader; + } + + if (hMiraMonLayer->ReadOrWrite == MM_WRITING_MODE) + { + hMiraMonLayer->nFinalElemCount = pArcTopHeader->nElemCount; + pArcTopHeader->bIs3d = hMiraMonLayer->bIsReal3d; + + if (MMWriteHeader(pMMArcLayer->pF, pArcTopHeader)) + { + MMCPLError(CE_Failure, CPLE_NoWriteAccess, + "Error writing to file %s", pMMArcLayer->pszLayerName); + goto end_label; + } + hMiraMonLayer->OffsetCheck = hMiraMonLayer->nHeaderDiskSize; + + // AH Section + if (MMWriteAHArcSection(hMiraMonLayer, hMiraMonLayer->OffsetCheck)) + { + MMCPLError(CE_Failure, CPLE_NoWriteAccess, + "Error writing to file %s", pMMArcLayer->pszLayerName); + goto end_label; + } + + // AL Section + pMMArcLayer->FlushAL.SizeOfBlockToBeSaved = 0; + if (MMAppendBlockToBuffer(&pMMArcLayer->FlushAL)) + { + MMCPLError(CE_Failure, CPLE_NoWriteAccess, + "Error writing to file %s", pMMArcLayer->pszLayerName); + goto end_label; + } + if (MMMoveFromFileToFile(pMMArcLayer->pFAL, pMMArcLayer->pF, + &hMiraMonLayer->OffsetCheck)) + { + MMCPLError(CE_Failure, CPLE_NoWriteAccess, + "Error writing to file %s", pMMArcLayer->pszLayerName); + goto end_label; + } + fclose_and_nullify(&pMMArcLayer->pFAL); + + if (*pMMArcLayer->pszALName != '\0') + remove_function(pMMArcLayer->pszALName); + + // 3D Section + if (MMClose3DSectionLayer( + hMiraMonLayer, pArcTopHeader->nElemCount, pMMArcLayer->pF, + pMMArcLayer->pF3d, pMMArcLayer->psz3DLayerName, + &pMMArcLayer->pZSection, hMiraMonLayer->OffsetCheck)) + { + MMCPLError(CE_Failure, CPLE_NoWriteAccess, + "Error writing to file %s", pMMArcLayer->pszLayerName); + goto end_label; + } + } + + ret_code = 0; +end_label: + fclose_and_nullify(&pMMArcLayer->pF); + + fclose_and_nullify(&pMMArcLayer->pFAL); + + if (MMCloseNodeLayer(hMiraMonLayer)) + ret_code = 1; + + return ret_code; +} + +static int MMClosePolygonLayer(struct MiraMonVectLayerInfo *hMiraMonLayer) +{ + int ret_code = 0; + struct MiraMonPolygonLayer *pMMPolygonLayer; + + if (!hMiraMonLayer) + return 1; + + pMMPolygonLayer = &hMiraMonLayer->MMPolygon; + + MMCloseArcLayer(hMiraMonLayer); + + if (hMiraMonLayer->ReadOrWrite == MM_WRITING_MODE) + { + hMiraMonLayer->nFinalElemCount = hMiraMonLayer->TopHeader.nElemCount; + hMiraMonLayer->TopHeader.bIs3d = hMiraMonLayer->bIsReal3d; + + if (MMWriteHeader(pMMPolygonLayer->pF, &hMiraMonLayer->TopHeader)) + { + MMCPLError(CE_Failure, CPLE_NoWriteAccess, + "Error writing to file %s", + pMMPolygonLayer->pszLayerName); + goto end_label; + } + hMiraMonLayer->OffsetCheck = hMiraMonLayer->nHeaderDiskSize; + + // PS Section + pMMPolygonLayer->FlushPS.SizeOfBlockToBeSaved = 0; + if (MMAppendBlockToBuffer(&pMMPolygonLayer->FlushPS)) + { + MMCPLError(CE_Failure, CPLE_NoWriteAccess, + "Error writing to file %s", + pMMPolygonLayer->pszLayerName); + goto end_label; + } + if (MMMoveFromFileToFile(pMMPolygonLayer->pFPS, pMMPolygonLayer->pF, + &hMiraMonLayer->OffsetCheck)) + { + MMCPLError(CE_Failure, CPLE_NoWriteAccess, + "Error writing to file %s", + pMMPolygonLayer->pszLayerName); + goto end_label; + } + + fclose_and_nullify(&pMMPolygonLayer->pFPS); + if (*pMMPolygonLayer->pszPSName != '\0') + remove_function(pMMPolygonLayer->pszPSName); + + // AH Section + if (MMWritePHPolygonSection(hMiraMonLayer, hMiraMonLayer->OffsetCheck)) + { + MMCPLError(CE_Failure, CPLE_NoWriteAccess, + "Error writing to file %s", + pMMPolygonLayer->pszLayerName); + goto end_label; + } + + // PAL Section + pMMPolygonLayer->FlushPAL.SizeOfBlockToBeSaved = 0; + if (MMAppendBlockToBuffer(&pMMPolygonLayer->FlushPAL)) + { + MMCPLError(CE_Failure, CPLE_NoWriteAccess, + "Error writing to file %s", + pMMPolygonLayer->pszLayerName); + goto end_label; + } + if (MMMoveFromFileToFile(pMMPolygonLayer->pFPAL, pMMPolygonLayer->pF, + &hMiraMonLayer->OffsetCheck)) + { + MMCPLError(CE_Failure, CPLE_NoWriteAccess, + "Error writing to file %s", + pMMPolygonLayer->pszLayerName); + goto end_label; + } + fclose_and_nullify(&pMMPolygonLayer->pFPAL); + + if (*pMMPolygonLayer->pszPALName != '\0') + remove_function(pMMPolygonLayer->pszPALName); + } + + ret_code = 0; + +end_label: + fclose_and_nullify(&pMMPolygonLayer->pF); + + fclose_and_nullify(&pMMPolygonLayer->pFPAL); + + return ret_code; +} + +int MMCloseLayer(struct MiraMonVectLayerInfo *hMiraMonLayer) +{ + int ret_code = 0; + //CheckMMVectorLayerVersion(hMiraMonLayer, 1) + + if (!hMiraMonLayer) + return 0; + + if (hMiraMonLayer->bIsPoint) + { + ret_code = MMClosePointLayer(hMiraMonLayer); + } + else if (hMiraMonLayer->bIsArc && !hMiraMonLayer->bIsPolygon) + { + ret_code = MMCloseArcLayer(hMiraMonLayer); + } + else if (hMiraMonLayer->bIsPolygon) + { + ret_code = MMClosePolygonLayer(hMiraMonLayer); + } + else if (hMiraMonLayer->bIsDBF) + { + // If no geometry, remove all created files + if (hMiraMonLayer->pszSrcLayerName) + remove_function(hMiraMonLayer->pszSrcLayerName); + if (hMiraMonLayer->szLayerTitle) + remove_function(hMiraMonLayer->szLayerTitle); + } + + // MiraMon metadata files + if (hMiraMonLayer->ReadOrWrite == MM_WRITING_MODE) + { + if (MMWriteVectorMetadata(hMiraMonLayer)) + { + MMCPLError(CE_Failure, CPLE_NoWriteAccess, + "Some error writing in metadata file of the layer"); + ret_code = 1; + } + } + + // MiraMon database files + if (MMCloseMMBD_XP(hMiraMonLayer)) + { + MMCPLError(CE_Failure, CPLE_NoWriteAccess, + "Some error writing in DBF file of the layer"); + ret_code = 1; + } + return ret_code; +} + +/* -------------------------------------------------------------------- */ +/* Layer Functions: Destroying (allocated memory) */ +/* -------------------------------------------------------------------- */ +static void MMDestroyMMAdmDB(struct MMAdmDatabase *pMMAdmDB) +{ + if (pMMAdmDB->pRecList) + { + free_function(pMMAdmDB->pRecList); + pMMAdmDB->pRecList = nullptr; + } + + if (pMMAdmDB->szRecordOnCourse) + { + free_function(pMMAdmDB->szRecordOnCourse); + pMMAdmDB->szRecordOnCourse = nullptr; + pMMAdmDB->nNumRecordOnCourse = 0; + } +} + +static int MMDestroyPointLayer(struct MiraMonVectLayerInfo *hMiraMonLayer) +{ + if (!hMiraMonLayer) + return 1; + + if (hMiraMonLayer->MMPoint.pTL) + { + free_function(hMiraMonLayer->MMPoint.pTL); + hMiraMonLayer->MMPoint.pTL = nullptr; + } + + MMDestroyZSectionDescription(&hMiraMonLayer->MMPoint.pZSection); + MMDestroyMMAdmDB(&hMiraMonLayer->MMPoint.MMAdmDB); + + return 0; +} + +static int MMDestroyNodeLayer(struct MiraMonVectLayerInfo *hMiraMonLayer) +{ + struct MiraMonArcLayer *pMMArcLayer; + + if (!hMiraMonLayer) + return 1; + + if (hMiraMonLayer->bIsPolygon) + pMMArcLayer = &hMiraMonLayer->MMPolygon.MMArc; + else + pMMArcLayer = &hMiraMonLayer->MMArc; + + if (pMMArcLayer->MMNode.pNL) + { + free_function(pMMArcLayer->MMNode.pNL); + pMMArcLayer->MMNode.pNL = nullptr; + } + + if (pMMArcLayer->MMNode.pNodeHeader) + { + free_function(pMMArcLayer->MMNode.pNodeHeader); + pMMArcLayer->MMNode.pNodeHeader = nullptr; + } + + MMDestroyMMAdmDB(&hMiraMonLayer->MMArc.MMNode.MMAdmDB); + return 0; +} + +static int MMDestroyArcLayer(struct MiraMonVectLayerInfo *hMiraMonLayer) +{ + struct MiraMonArcLayer *pMMArcLayer; + + if (!hMiraMonLayer) + return 1; + + if (hMiraMonLayer->bIsPolygon) + pMMArcLayer = &hMiraMonLayer->MMPolygon.MMArc; + else + pMMArcLayer = &hMiraMonLayer->MMArc; + + if (pMMArcLayer->pAL) + { + free_function(pMMArcLayer->pAL); + pMMArcLayer->pAL = nullptr; + } + if (pMMArcLayer->pArcHeader) + { + free_function(pMMArcLayer->pArcHeader); + pMMArcLayer->pArcHeader = nullptr; + } + + MMDestroyZSectionDescription(&pMMArcLayer->pZSection); + MMDestroyMMAdmDB(&pMMArcLayer->MMAdmDB); + + MMDestroyNodeLayer(hMiraMonLayer); + return 0; +} + +static int MMDestroyPolygonLayer(struct MiraMonVectLayerInfo *hMiraMonLayer) +{ + struct MiraMonPolygonLayer *pMMPolygonLayer; + + if (!hMiraMonLayer) + return 1; + + pMMPolygonLayer = &hMiraMonLayer->MMPolygon; + + MMDestroyArcLayer(hMiraMonLayer); + + if (pMMPolygonLayer->pPAL) + { + free_function(pMMPolygonLayer->pPAL); + pMMPolygonLayer->pPAL = nullptr; + } + + if (pMMPolygonLayer->pPS) + { + free_function(pMMPolygonLayer->pPS); + pMMPolygonLayer->pPS = nullptr; + } + + if (pMMPolygonLayer->pPolHeader) + { + free_function(pMMPolygonLayer->pPolHeader); + pMMPolygonLayer->pPolHeader = nullptr; + } + + MMDestroyMMAdmDB(&pMMPolygonLayer->MMAdmDB); + + return 0; +} + +int MMDestroyLayer(struct MiraMonVectLayerInfo *hMiraMonLayer) +{ + //CheckMMVectorLayerVersion(hMiraMonLayer, 1) + + if (!hMiraMonLayer) + return 1; + + if (hMiraMonLayer->bIsPoint) + MMDestroyPointLayer(hMiraMonLayer); + else if (hMiraMonLayer->bIsArc && !hMiraMonLayer->bIsPolygon) + MMDestroyArcLayer(hMiraMonLayer); + else if (hMiraMonLayer->bIsPolygon) + MMDestroyPolygonLayer(hMiraMonLayer); + + if (hMiraMonLayer->pszSrcLayerName) + { + free_function(hMiraMonLayer->pszSrcLayerName); + hMiraMonLayer->pszSrcLayerName = nullptr; + } + if (hMiraMonLayer->szLayerTitle) + { + free_function(hMiraMonLayer->szLayerTitle); + hMiraMonLayer->szLayerTitle = nullptr; + } + if (hMiraMonLayer->pSRS) + { + free_function(hMiraMonLayer->pSRS); + hMiraMonLayer->pSRS = nullptr; + } + + if (hMiraMonLayer->pMultRecordIndex) + { + free_function(hMiraMonLayer->pMultRecordIndex); + hMiraMonLayer->pMultRecordIndex = nullptr; + } + + if (hMiraMonLayer->ReadFeature.pNCoordRing) + { + free(hMiraMonLayer->ReadFeature.pNCoordRing); + hMiraMonLayer->ReadFeature.pNCoordRing = nullptr; + } + if (hMiraMonLayer->ReadFeature.pCoord) + { + free(hMiraMonLayer->ReadFeature.pCoord); + hMiraMonLayer->ReadFeature.pCoord = nullptr; + } + if (hMiraMonLayer->ReadFeature.pZCoord) + { + free(hMiraMonLayer->ReadFeature.pZCoord); + hMiraMonLayer->ReadFeature.pZCoord = nullptr; + } + if (hMiraMonLayer->ReadFeature.pRecords) + { + free(hMiraMonLayer->ReadFeature.pRecords); + hMiraMonLayer->ReadFeature.pRecords = nullptr; + } + if (hMiraMonLayer->ReadFeature.flag_VFG) + { + free(hMiraMonLayer->ReadFeature.flag_VFG); + hMiraMonLayer->ReadFeature.flag_VFG = nullptr; + } + + if (hMiraMonLayer->pArcs) + { + free_function(hMiraMonLayer->pArcs); + hMiraMonLayer->pArcs = nullptr; + } + + if (hMiraMonLayer->szStringToOperate) + { + free_function(hMiraMonLayer->szStringToOperate); + hMiraMonLayer->szStringToOperate = nullptr; + hMiraMonLayer->nNumStringToOperate = 0; + } + + if (hMiraMonLayer->pLayerDB) + { + if (hMiraMonLayer->pLayerDB->pFields) + { + free_function(hMiraMonLayer->pLayerDB->pFields); + hMiraMonLayer->pLayerDB->pFields = nullptr; + } + free_function(hMiraMonLayer->pLayerDB); + hMiraMonLayer->pLayerDB = nullptr; + } + + // Destroys all database objects + MMDestroyMMDB(hMiraMonLayer); + + return 0; +} + +/* -------------------------------------------------------------------- */ +/* Flush Layer Functions */ +/* -------------------------------------------------------------------- */ + +// Initializes a MM_FLUSH_INFO structure, which is used for buffering +// data before writing it to a file. +int MMInitFlush(struct MM_FLUSH_INFO *pFlush, FILE_TYPE *pF, GUInt64 nBlockSize, + char **pBuffer, MM_FILE_OFFSET DiskOffsetWhereToFlush, + GInt32 nMyDiskSize) +{ + memset(pFlush, 0, sizeof(*pFlush)); + *pBuffer = nullptr; + + pFlush->nMyDiskSize = nMyDiskSize; + pFlush->pF = pF; + pFlush->nBlockSize = nBlockSize; + pFlush->nNumBytes = 0; + if (MMCheckSize_t(nBlockSize, 1)) + return 1; + + if (!nBlockSize) + { + MMCPLError(CE_Failure, CPLE_OutOfMemory, + "Error in MiraMon " + "driver: MMInitFlush() with no bytes to process"); + return 1; + } + + if (nullptr == (*pBuffer = (char *)calloc_function((size_t)nBlockSize))) + { + MMCPLError(CE_Failure, CPLE_OutOfMemory, + "Memory error in MiraMon " + "driver (MMInitFlush())"); + return 1; + } + pFlush->OffsetWhereToFlush = DiskOffsetWhereToFlush; + pFlush->CurrentOffset = 0; + return 0; +} + +// Reads data from a file into a buffer. +int MMReadFlush(struct MM_FLUSH_INFO *pFlush) +{ + fseek_function(pFlush->pF, pFlush->OffsetWhereToFlush, SEEK_SET); + if (pFlush->nBlockSize != + (GUInt64)(fread_function(pFlush->pBlockWhereToSaveOrRead, 1, + (size_t)pFlush->nBlockSize, pFlush->pF))) + return 1; + return 0; +} + +// Flushes data from a buffer to a disk file. +static int MMFlushToDisk(struct MM_FLUSH_INFO *FlushInfo) +{ + if (!FlushInfo->nNumBytes) + return 0; + // Just flush to the disk at the correct place. + fseek_function(FlushInfo->pF, FlushInfo->OffsetWhereToFlush, SEEK_SET); + + if (FlushInfo->nNumBytes != + (GUInt64)fwrite_function(FlushInfo->pBlockWhereToSaveOrRead, 1, + (size_t)FlushInfo->nNumBytes, FlushInfo->pF)) + return 1; + FlushInfo->OffsetWhereToFlush += FlushInfo->nNumBytes; + FlushInfo->NTimesFlushed++; + FlushInfo->TotalSavedBytes += FlushInfo->nNumBytes; + FlushInfo->nNumBytes = 0; + + return 0; +} + +// Reads a block of data from a buffer in memory +int MMReadBlockFromBuffer(struct MM_FLUSH_INFO *FlushInfo) +{ + if (!FlushInfo->SizeOfBlockToBeSaved) + return 0; + + if (FlushInfo->pBlockToBeSaved) + { + memcpy(FlushInfo->pBlockToBeSaved, + (void *)((char *)FlushInfo->pBlockWhereToSaveOrRead + + FlushInfo->CurrentOffset), + FlushInfo->SizeOfBlockToBeSaved); + } + FlushInfo->CurrentOffset += FlushInfo->SizeOfBlockToBeSaved; + + return 0; +} + +// Appends a block of data to a buffer in memory, which is +// used for later flushing to disk. +int MMAppendBlockToBuffer(struct MM_FLUSH_INFO *FlushInfo) +{ + if (FlushInfo->SizeOfBlockToBeSaved) + { + // If all the bloc itself does not fit to the buffer, + // then all the block is written directly to the disk + if (FlushInfo->nNumBytes == 0 && + FlushInfo->SizeOfBlockToBeSaved >= FlushInfo->nBlockSize) + { + if (MMFlushToDisk(FlushInfo)) + return 1; + return 0; + } + + // There is space in FlushInfo->pBlockWhereToSaveOrRead? + if (FlushInfo->nNumBytes + FlushInfo->SizeOfBlockToBeSaved <= + FlushInfo->nBlockSize) + { + if (FlushInfo->pBlockToBeSaved) + { + memcpy((void *)((char *)FlushInfo->pBlockWhereToSaveOrRead + + FlushInfo->nNumBytes), + FlushInfo->pBlockToBeSaved, + FlushInfo->SizeOfBlockToBeSaved); + } + else // Add zero characters + { + char zero_caracters[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + memcpy((char *)FlushInfo->pBlockWhereToSaveOrRead + + FlushInfo->nNumBytes, + zero_caracters, FlushInfo->SizeOfBlockToBeSaved); + } + + FlushInfo->nNumBytes += FlushInfo->SizeOfBlockToBeSaved; + } + else + { + // Empty the buffer + if (MMFlushToDisk(FlushInfo)) + return 1; + // Append the pendant bytes + if (MMAppendBlockToBuffer(FlushInfo)) + return 1; + } + return 0; + } + // Just flush to the disc. + return MMFlushToDisk(FlushInfo); +} + +// Copy the contents of a temporary file to a final file. +// Used everywhere when closing layers. +int MMMoveFromFileToFile(FILE_TYPE *pSrcFile, FILE_TYPE *pDestFile, + MM_FILE_OFFSET *nOffset) +{ + size_t bufferSize = 1024 * 1024; // 1 MB buffer; + unsigned char *buffer; + size_t bytesRead, bytesWritten; + + if (!pSrcFile || !pDestFile || !nOffset) + return 0; + + buffer = (unsigned char *)calloc_function(bufferSize); + + if (!buffer) + return 1; + + fseek_function(pSrcFile, 0, SEEK_SET); + fseek_function(pDestFile, *nOffset, SEEK_SET); + while ((bytesRead = fread_function(buffer, sizeof(unsigned char), + bufferSize, pSrcFile)) > 0) + { + bytesWritten = fwrite_function(buffer, sizeof(unsigned char), bytesRead, + pDestFile); + if (bytesWritten != bytesRead) + { + free_function(buffer); + return 1; + } + if (nOffset) + (*nOffset) += bytesWritten; + } + free_function(buffer); + return 0; +} + +/* -------------------------------------------------------------------- */ +/* Layer: Offsets and variables types managing */ +/* -------------------------------------------------------------------- */ + +// Alineation described in format documents. +static void MMGetOffsetAlignedTo8(MM_FILE_OFFSET *Offset) +{ + MM_FILE_OFFSET reajust; + + if ((*Offset) % 8L) + { + reajust = 8 - ((*Offset) % 8L); + (*Offset) += reajust; + } +} + +// Reading integers depending on the version being read. +int MMReadGUInt64DependingOnVersion(struct MiraMonVectLayerInfo *hMiraMonLayer, + struct MM_FLUSH_INFO *FlushInfo, + GUInt64 *pnUI64) +{ + uint32_t nUL32; + + if (!hMiraMonLayer) + return 1; + + if (hMiraMonLayer->LayerVersion == MM_32BITS_VERSION) + { + FlushInfo->pBlockToBeSaved = (void *)&nUL32; + FlushInfo->SizeOfBlockToBeSaved = sizeof(nUL32); + if (MMReadBlockFromBuffer(FlushInfo)) + { + FlushInfo->pBlockToBeSaved = nullptr; + return 1; + } + *pnUI64 = (GUInt64)nUL32; + } + else + { + FlushInfo->pBlockToBeSaved = (void *)pnUI64; + FlushInfo->SizeOfBlockToBeSaved = sizeof(*pnUI64); + if (MMReadBlockFromBuffer(FlushInfo)) + { + FlushInfo->pBlockToBeSaved = nullptr; + return 1; + } + } + FlushInfo->pBlockToBeSaved = nullptr; + return 0; +} + +// Reading offsets depending on the version is being read. +int MMReadOffsetDependingOnVersion(struct MiraMonVectLayerInfo *hMiraMonLayer, + struct MM_FLUSH_INFO *FlushInfo, + MM_FILE_OFFSET *pnUI64) +{ + uint32_t nUL32; + + if (!hMiraMonLayer) + return 1; + + if (hMiraMonLayer->LayerVersion == MM_32BITS_VERSION) + { + FlushInfo->pBlockToBeSaved = (void *)&nUL32; + FlushInfo->SizeOfBlockToBeSaved = sizeof(nUL32); + if (MMReadBlockFromBuffer(FlushInfo)) + { + FlushInfo->pBlockToBeSaved = nullptr; + return 1; + } + *pnUI64 = (MM_FILE_OFFSET)nUL32; + } + else + { + FlushInfo->pBlockToBeSaved = (void *)pnUI64; + FlushInfo->SizeOfBlockToBeSaved = sizeof(*pnUI64); + if (MMReadBlockFromBuffer(FlushInfo)) + { + FlushInfo->pBlockToBeSaved = nullptr; + return 1; + } + } + FlushInfo->pBlockToBeSaved = nullptr; + return 0; +} + +// Appending integers depending on the version. +int MMAppendIntegerDependingOnVersion( + struct MiraMonVectLayerInfo *hMiraMonLayer, struct MM_FLUSH_INFO *FlushInfo, + uint32_t *nUL32, GUInt64 nUI64) +{ + int result; + + if (!hMiraMonLayer) + return 1; + + if (hMiraMonLayer->LayerVersion == MM_32BITS_VERSION) + { + *nUL32 = (uint32_t)nUI64; + FlushInfo->SizeOfBlockToBeSaved = sizeof(*nUL32); + hMiraMonLayer->OffsetCheck += FlushInfo->SizeOfBlockToBeSaved; + FlushInfo->pBlockToBeSaved = (void *)nUL32; + } + else + { + FlushInfo->SizeOfBlockToBeSaved = sizeof(nUI64); + hMiraMonLayer->OffsetCheck += FlushInfo->SizeOfBlockToBeSaved; + FlushInfo->pBlockToBeSaved = (void *)&nUI64; + } + result = MMAppendBlockToBuffer(FlushInfo); + FlushInfo->pBlockToBeSaved = nullptr; + return result; +} + +/* -------------------------------------------------------------------- */ +/* Layer: Reading and writing layer sections */ +/* This code follows the specifications of the following document: */ +/* https://www.miramon.cat/new_note/usa/notes/ \ */ +/* FormatFitxersTopologicsMiraMon.pdf */ +/* -------------------------------------------------------------------- */ +int MMReadAHArcSection(struct MiraMonVectLayerInfo *hMiraMonLayer) +{ + MM_INTERNAL_FID iElem, nElem; + struct MM_FLUSH_INFO FlushTMP; + char *pBuffer = nullptr; + MM_FILE_OFFSET nBlockSize; + struct MiraMonArcLayer *pMMArcLayer; + MM_N_VERTICES_TYPE nElementCount; + + if (!hMiraMonLayer) + return 1; + + if (hMiraMonLayer->bIsPolygon) + { + pMMArcLayer = &hMiraMonLayer->MMPolygon.MMArc; + nElem = hMiraMonLayer->MMPolygon.TopArcHeader.nElemCount; + } + else + { + pMMArcLayer = &hMiraMonLayer->MMArc; + nElem = hMiraMonLayer->TopHeader.nElemCount; + } + + if (MMCheckSize_t(nElem, pMMArcLayer->nSizeArcHeader)) + { + return 1; + } + + nBlockSize = nElem * (pMMArcLayer->nSizeArcHeader); + + if (MMInitFlush(&FlushTMP, pMMArcLayer->pF, nBlockSize, &pBuffer, + hMiraMonLayer->nHeaderDiskSize, 0)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + FlushTMP.pBlockWhereToSaveOrRead = (void *)pBuffer; + if (MMReadFlush(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + for (iElem = 0; iElem < nElem; iElem++) + { + // Bounding box + FlushTMP.pBlockToBeSaved = + (void *)&(pMMArcLayer->pArcHeader[iElem].dfBB.dfMinX); + FlushTMP.SizeOfBlockToBeSaved = + sizeof(pMMArcLayer->pArcHeader[iElem].dfBB.dfMinX); + if (MMReadBlockFromBuffer(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + FlushTMP.pBlockToBeSaved = + (void *)&pMMArcLayer->pArcHeader[iElem].dfBB.dfMaxX; + FlushTMP.SizeOfBlockToBeSaved = + sizeof(pMMArcLayer->pArcHeader[iElem].dfBB.dfMaxX); + if (MMReadBlockFromBuffer(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + FlushTMP.pBlockToBeSaved = + (void *)&pMMArcLayer->pArcHeader[iElem].dfBB.dfMinY; + FlushTMP.SizeOfBlockToBeSaved = + sizeof(pMMArcLayer->pArcHeader[iElem].dfBB.dfMinY); + if (MMReadBlockFromBuffer(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + FlushTMP.pBlockToBeSaved = + (void *)&pMMArcLayer->pArcHeader[iElem].dfBB.dfMaxY; + FlushTMP.SizeOfBlockToBeSaved = + sizeof(pMMArcLayer->pArcHeader[iElem].dfBB.dfMaxY); + if (MMReadBlockFromBuffer(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + // Element count: number of vertices of the arc + nElementCount = pMMArcLayer->pArcHeader[iElem].nElemCount; + if (MMReadGUInt64DependingOnVersion(hMiraMonLayer, &FlushTMP, + &nElementCount)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + pMMArcLayer->pArcHeader[iElem].nElemCount = nElementCount; + + // Offset: offset of the first vertice of the arc + if (MMReadOffsetDependingOnVersion( + hMiraMonLayer, &FlushTMP, + &pMMArcLayer->pArcHeader[iElem].nOffset)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + // First node: first node of the arc + if (MMReadGUInt64DependingOnVersion( + hMiraMonLayer, &FlushTMP, + &pMMArcLayer->pArcHeader[iElem].nFirstIdNode)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + // Last node: first node of the arc + if (MMReadGUInt64DependingOnVersion( + hMiraMonLayer, &FlushTMP, + &pMMArcLayer->pArcHeader[iElem].nLastIdNode)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + // Length of the arc + FlushTMP.pBlockToBeSaved = + (void *)&pMMArcLayer->pArcHeader[iElem].dfLength; + FlushTMP.SizeOfBlockToBeSaved = + sizeof(pMMArcLayer->pArcHeader[iElem].dfLength); + if (MMReadBlockFromBuffer(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + } + + if (pBuffer) + free_function(pBuffer); + return 0; +} + +int MMWriteAHArcSection(struct MiraMonVectLayerInfo *hMiraMonLayer, + MM_FILE_OFFSET DiskOffset) +{ + MM_INTERNAL_FID iElem; + struct MM_FLUSH_INFO FlushTMP; + char *pBuffer = nullptr; + uint32_t nUL32; + MM_FILE_OFFSET nOffsetDiff; + struct MiraMonArcLayer *pMMArcLayer; + + if (!hMiraMonLayer) + return 1; + + if (hMiraMonLayer->bIsPolygon) + pMMArcLayer = &hMiraMonLayer->MMPolygon.MMArc; + else + pMMArcLayer = &hMiraMonLayer->MMArc; + + nOffsetDiff = + hMiraMonLayer->nHeaderDiskSize + + hMiraMonLayer->nFinalElemCount * (pMMArcLayer->nSizeArcHeader); + + if (MMInitFlush(&FlushTMP, pMMArcLayer->pF, MM_1MB, &pBuffer, DiskOffset, + 0)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + FlushTMP.pBlockWhereToSaveOrRead = (void *)pBuffer; + for (iElem = 0; iElem < hMiraMonLayer->nFinalElemCount; iElem++) + { + // Bounding box + FlushTMP.SizeOfBlockToBeSaved = + sizeof(pMMArcLayer->pArcHeader[iElem].dfBB.dfMinX); + FlushTMP.pBlockToBeSaved = + (void *)&pMMArcLayer->pArcHeader[iElem].dfBB.dfMinX; + hMiraMonLayer->OffsetCheck += FlushTMP.SizeOfBlockToBeSaved; + if (MMAppendBlockToBuffer(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + FlushTMP.pBlockToBeSaved = + (void *)&pMMArcLayer->pArcHeader[iElem].dfBB.dfMaxX; + hMiraMonLayer->OffsetCheck += FlushTMP.SizeOfBlockToBeSaved; + if (MMAppendBlockToBuffer(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + FlushTMP.pBlockToBeSaved = + (void *)&pMMArcLayer->pArcHeader[iElem].dfBB.dfMinY; + hMiraMonLayer->OffsetCheck += FlushTMP.SizeOfBlockToBeSaved; + if (MMAppendBlockToBuffer(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + FlushTMP.pBlockToBeSaved = + (void *)&pMMArcLayer->pArcHeader[iElem].dfBB.dfMaxY; + hMiraMonLayer->OffsetCheck += FlushTMP.SizeOfBlockToBeSaved; + if (MMAppendBlockToBuffer(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + // Element count: number of vertices of the arc + if (MMAppendIntegerDependingOnVersion( + hMiraMonLayer, &FlushTMP, &nUL32, + pMMArcLayer->pArcHeader[iElem].nElemCount)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + // Offset: offset of the first vertice of the arc + if (MMAppendIntegerDependingOnVersion( + hMiraMonLayer, &FlushTMP, &nUL32, + pMMArcLayer->pArcHeader[iElem].nOffset + nOffsetDiff)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + // First node: first node of the arc + if (MMAppendIntegerDependingOnVersion( + hMiraMonLayer, &FlushTMP, &nUL32, + pMMArcLayer->pArcHeader[iElem].nFirstIdNode)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + // Last node: first node of the arc + if (MMAppendIntegerDependingOnVersion( + hMiraMonLayer, &FlushTMP, &nUL32, + pMMArcLayer->pArcHeader[iElem].nLastIdNode)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + // Length of the arc + FlushTMP.SizeOfBlockToBeSaved = + sizeof(pMMArcLayer->pArcHeader[iElem].dfLength); + hMiraMonLayer->OffsetCheck += FlushTMP.SizeOfBlockToBeSaved; + FlushTMP.pBlockToBeSaved = + (void *)&pMMArcLayer->pArcHeader[iElem].dfLength; + if (MMAppendBlockToBuffer(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + } + FlushTMP.SizeOfBlockToBeSaved = 0; + if (MMAppendBlockToBuffer(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + if (pBuffer) + free_function(pBuffer); + return 0; +} + +#ifdef JUST_IN_CASE_WE_NEED_IT_SOMEDAY +static int MMReadNHNodeSection(struct MiraMonVectLayerInfo *hMiraMonLayer) +{ + MM_INTERNAL_FID iElem, nElem; + struct MM_FLUSH_INFO FlushTMP; + char *pBuffer = nullptr; + MM_FILE_OFFSET nBlockSize; + struct MiraMonArcLayer *pMMArcLayer; + + if (hMiraMonLayer->bIsPolygon) + pMMArcLayer = &hMiraMonLayer->MMPolygon.MMArc; + else + pMMArcLayer = &hMiraMonLayer->MMArc; + + nElem = pMMArcLayer->TopNodeHeader.nElemCount; + + nBlockSize = nElem * pMMArcLayer->MMNode.nSizeNodeHeader; + + if (MMInitFlush(&FlushTMP, pMMArcLayer->MMNode.pF, nBlockSize, &pBuffer, + hMiraMonLayer->nHeaderDiskSize, 0)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + FlushTMP.pBlockWhereToSaveOrRead = (void *)pBuffer; + if (MMReadFlush(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + for (iElem = 0; iElem < nElem; iElem++) + { + // Arcs count + FlushTMP.pBlockToBeSaved = + (void *)&pMMArcLayer->MMNode.pNodeHeader[iElem].nArcsCount; + FlushTMP.SizeOfBlockToBeSaved = + sizeof(pMMArcLayer->MMNode.pNodeHeader[iElem].nArcsCount); + if (MMReadBlockFromBuffer(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + // Node type + FlushTMP.pBlockToBeSaved = + (void *)&pMMArcLayer->MMNode.pNodeHeader[iElem].cNodeType; + FlushTMP.SizeOfBlockToBeSaved = + sizeof(pMMArcLayer->MMNode.pNodeHeader[iElem].cNodeType); + if (MMReadBlockFromBuffer(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + FlushTMP.SizeOfBlockToBeSaved = 1; + FlushTMP.pBlockToBeSaved = (void *)nullptr; + if (MMReadBlockFromBuffer(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + // Offset: offset of the first arc to the node + if (MMReadOffsetDependingOnVersion( + hMiraMonLayer, &FlushTMP, + &pMMArcLayer->MMNode.pNodeHeader[iElem].nOffset)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + } + + if (pBuffer) + free_function(pBuffer); + return 0; +} +#endif // JUST_IN_CASE_WE_NEED_IT_SOMEDAY + +int MMWriteNHNodeSection(struct MiraMonVectLayerInfo *hMiraMonLayer, + MM_FILE_OFFSET DiskOffset) +{ + MM_INTERNAL_FID iElem; + struct MM_FLUSH_INFO FlushTMP; + char *pBuffer = nullptr; + uint32_t nUL32; + MM_FILE_OFFSET nOffsetDiff; + struct MiraMonArcLayer *pMMArcLayer; + + if (!hMiraMonLayer) + return 1; + + if (hMiraMonLayer->bIsPolygon) + pMMArcLayer = &hMiraMonLayer->MMPolygon.MMArc; + else + pMMArcLayer = &hMiraMonLayer->MMArc; + + nOffsetDiff = hMiraMonLayer->nHeaderDiskSize + + (pMMArcLayer->TopNodeHeader.nElemCount * + pMMArcLayer->MMNode.nSizeNodeHeader); + + if (MMInitFlush(&FlushTMP, pMMArcLayer->MMNode.pF, MM_1MB, &pBuffer, + DiskOffset, 0)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + FlushTMP.pBlockWhereToSaveOrRead = (void *)pBuffer; + for (iElem = 0; iElem < pMMArcLayer->TopNodeHeader.nElemCount; iElem++) + { + // Arcs count + FlushTMP.SizeOfBlockToBeSaved = + sizeof(pMMArcLayer->MMNode.pNodeHeader[iElem].nArcsCount); + hMiraMonLayer->OffsetCheck += FlushTMP.SizeOfBlockToBeSaved; + FlushTMP.pBlockToBeSaved = + (void *)&pMMArcLayer->MMNode.pNodeHeader[iElem].nArcsCount; + if (MMAppendBlockToBuffer(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + // Node type + FlushTMP.SizeOfBlockToBeSaved = + sizeof(pMMArcLayer->MMNode.pNodeHeader[iElem].cNodeType); + hMiraMonLayer->OffsetCheck += FlushTMP.SizeOfBlockToBeSaved; + FlushTMP.pBlockToBeSaved = + (void *)&pMMArcLayer->MMNode.pNodeHeader[iElem].cNodeType; + if (MMAppendBlockToBuffer(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + FlushTMP.SizeOfBlockToBeSaved = 1; + hMiraMonLayer->OffsetCheck += FlushTMP.SizeOfBlockToBeSaved; + FlushTMP.pBlockToBeSaved = (void *)nullptr; + if (MMAppendBlockToBuffer(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + // Offset: offset of the first arc to the node + if (MMAppendIntegerDependingOnVersion( + hMiraMonLayer, &FlushTMP, &nUL32, + pMMArcLayer->MMNode.pNodeHeader[iElem].nOffset + nOffsetDiff)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + } + FlushTMP.SizeOfBlockToBeSaved = 0; + if (MMAppendBlockToBuffer(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + if (pBuffer) + free_function(pBuffer); + return 0; +} + +int MMReadPHPolygonSection(struct MiraMonVectLayerInfo *hMiraMonLayer) +{ + MM_INTERNAL_FID iElem; + struct MM_FLUSH_INFO FlushTMP; + char *pBuffer = nullptr; + MM_FILE_OFFSET nBlockSize; + struct MiraMonPolygonLayer *pMMPolygonLayer; + + if (!hMiraMonLayer) + return 1; + + pMMPolygonLayer = &hMiraMonLayer->MMPolygon; + + if (MMCheckSize_t(hMiraMonLayer->TopHeader.nElemCount, + pMMPolygonLayer->nPHElementSize) || + MMCheckSize_t(hMiraMonLayer->MMPolygon.TopArcHeader.nElemCount, + hMiraMonLayer->MMPolygon.nPSElementSize)) + { + return 1; + } + nBlockSize = + hMiraMonLayer->TopHeader.nElemCount * (pMMPolygonLayer->nPHElementSize); + + if (MMInitFlush(&FlushTMP, pMMPolygonLayer->pF, nBlockSize, &pBuffer, + hMiraMonLayer->nHeaderDiskSize + + (hMiraMonLayer->MMPolygon.TopArcHeader.nElemCount * + hMiraMonLayer->MMPolygon.nPSElementSize), + 0)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + FlushTMP.pBlockWhereToSaveOrRead = (void *)pBuffer; + if (MMReadFlush(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + for (iElem = 0; iElem < hMiraMonLayer->TopHeader.nElemCount; iElem++) + { + // Bounding box + FlushTMP.pBlockToBeSaved = + (void *)&(pMMPolygonLayer->pPolHeader[iElem].dfBB.dfMinX); + FlushTMP.SizeOfBlockToBeSaved = + sizeof(pMMPolygonLayer->pPolHeader[iElem].dfBB.dfMinX); + if (MMReadBlockFromBuffer(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + FlushTMP.pBlockToBeSaved = + (void *)&pMMPolygonLayer->pPolHeader[iElem].dfBB.dfMaxX; + FlushTMP.SizeOfBlockToBeSaved = + sizeof(pMMPolygonLayer->pPolHeader[iElem].dfBB.dfMaxX); + if (MMReadBlockFromBuffer(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + FlushTMP.pBlockToBeSaved = + (void *)&pMMPolygonLayer->pPolHeader[iElem].dfBB.dfMinY; + FlushTMP.SizeOfBlockToBeSaved = + sizeof(pMMPolygonLayer->pPolHeader[iElem].dfBB.dfMinY); + if (MMReadBlockFromBuffer(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + FlushTMP.pBlockToBeSaved = + (void *)&pMMPolygonLayer->pPolHeader[iElem].dfBB.dfMaxY; + FlushTMP.SizeOfBlockToBeSaved = + sizeof(pMMPolygonLayer->pPolHeader[iElem].dfBB.dfMaxY); + if (MMReadBlockFromBuffer(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + // Arcs count: number of arcs of the polygon + if (MMReadGUInt64DependingOnVersion( + hMiraMonLayer, &FlushTMP, + &pMMPolygonLayer->pPolHeader[iElem].nArcsCount)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + // External arcs count: number of external arcs of the polygon + if (MMReadGUInt64DependingOnVersion( + hMiraMonLayer, &FlushTMP, + &pMMPolygonLayer->pPolHeader[iElem].nExternalRingsCount)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + // Rings count: number of rings of the polygon + if (MMReadGUInt64DependingOnVersion( + hMiraMonLayer, &FlushTMP, + &pMMPolygonLayer->pPolHeader[iElem].nRingsCount)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + // Offset: offset of the first vertex of the arc + if (MMReadOffsetDependingOnVersion( + hMiraMonLayer, &FlushTMP, + &pMMPolygonLayer->pPolHeader[iElem].nOffset)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + // Perimeter of the arc + FlushTMP.SizeOfBlockToBeSaved = + sizeof(pMMPolygonLayer->pPolHeader[iElem].dfPerimeter); + FlushTMP.pBlockToBeSaved = + (void *)&pMMPolygonLayer->pPolHeader[iElem].dfPerimeter; + if (MMReadBlockFromBuffer(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + // Area of the arc + FlushTMP.SizeOfBlockToBeSaved = + sizeof(pMMPolygonLayer->pPolHeader[iElem].dfArea); + FlushTMP.pBlockToBeSaved = + (void *)&pMMPolygonLayer->pPolHeader[iElem].dfArea; + if (MMReadBlockFromBuffer(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + } + + if (pBuffer) + free_function(pBuffer); + return 0; +} + +int MMWritePHPolygonSection(struct MiraMonVectLayerInfo *hMiraMonLayer, + MM_FILE_OFFSET DiskOffset) +{ + MM_INTERNAL_FID iElem; + struct MM_FLUSH_INFO FlushTMP; + char *pBuffer = nullptr; + uint32_t nUL32; + MM_FILE_OFFSET nOffsetDiff; + struct MiraMonPolygonLayer *pMMPolygonLayer; + + if (!hMiraMonLayer) + return 1; + + pMMPolygonLayer = &hMiraMonLayer->MMPolygon; + + if (!pMMPolygonLayer->pF) + return 0; + + if (!hMiraMonLayer->nFinalElemCount) + return 0; + + nOffsetDiff = DiskOffset + hMiraMonLayer->TopHeader.nElemCount * + (pMMPolygonLayer->nPHElementSize); + + if (MMInitFlush(&FlushTMP, pMMPolygonLayer->pF, MM_1MB, &pBuffer, + DiskOffset, 0)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + FlushTMP.pBlockWhereToSaveOrRead = (void *)pBuffer; + for (iElem = 0; iElem < hMiraMonLayer->nFinalElemCount; iElem++) + { + // Bounding box + FlushTMP.SizeOfBlockToBeSaved = + sizeof(pMMPolygonLayer->pPolHeader[iElem].dfBB.dfMinX); + FlushTMP.pBlockToBeSaved = + (void *)&pMMPolygonLayer->pPolHeader[iElem].dfBB.dfMinX; + hMiraMonLayer->OffsetCheck += FlushTMP.SizeOfBlockToBeSaved; + if (MMAppendBlockToBuffer(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + FlushTMP.pBlockToBeSaved = + (void *)&pMMPolygonLayer->pPolHeader[iElem].dfBB.dfMaxX; + hMiraMonLayer->OffsetCheck += FlushTMP.SizeOfBlockToBeSaved; + if (MMAppendBlockToBuffer(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + FlushTMP.pBlockToBeSaved = + (void *)&pMMPolygonLayer->pPolHeader[iElem].dfBB.dfMinY; + hMiraMonLayer->OffsetCheck += FlushTMP.SizeOfBlockToBeSaved; + if (MMAppendBlockToBuffer(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + FlushTMP.pBlockToBeSaved = + (void *)&pMMPolygonLayer->pPolHeader[iElem].dfBB.dfMaxY; + hMiraMonLayer->OffsetCheck += FlushTMP.SizeOfBlockToBeSaved; + if (MMAppendBlockToBuffer(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + // Arcs count: number of the arcs of the polygon + if (MMAppendIntegerDependingOnVersion( + hMiraMonLayer, &FlushTMP, &nUL32, + pMMPolygonLayer->pPolHeader[iElem].nArcsCount)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + // External arcs count: number of external arcs of the polygon + if (MMAppendIntegerDependingOnVersion( + hMiraMonLayer, &FlushTMP, &nUL32, + pMMPolygonLayer->pPolHeader[iElem].nExternalRingsCount)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + // Rings count: number of rings of the polygon + if (MMAppendIntegerDependingOnVersion( + hMiraMonLayer, &FlushTMP, &nUL32, + pMMPolygonLayer->pPolHeader[iElem].nRingsCount)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + // Offset: offset of the first vertex of the arc + if (MMAppendIntegerDependingOnVersion( + hMiraMonLayer, &FlushTMP, &nUL32, + pMMPolygonLayer->pPolHeader[iElem].nOffset + nOffsetDiff)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + // Perimeter of the arc + FlushTMP.SizeOfBlockToBeSaved = + sizeof(pMMPolygonLayer->pPolHeader[iElem].dfPerimeter); + hMiraMonLayer->OffsetCheck += FlushTMP.SizeOfBlockToBeSaved; + FlushTMP.pBlockToBeSaved = + (void *)&pMMPolygonLayer->pPolHeader[iElem].dfPerimeter; + if (MMAppendBlockToBuffer(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + // Area of the arc + FlushTMP.SizeOfBlockToBeSaved = + sizeof(pMMPolygonLayer->pPolHeader[iElem].dfArea); + hMiraMonLayer->OffsetCheck += FlushTMP.SizeOfBlockToBeSaved; + FlushTMP.pBlockToBeSaved = + (void *)&pMMPolygonLayer->pPolHeader[iElem].dfArea; + if (MMAppendBlockToBuffer(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + } + FlushTMP.SizeOfBlockToBeSaved = 0; + if (MMAppendBlockToBuffer(&FlushTMP)) + { + if (pBuffer) + free_function(pBuffer); + return 1; + } + + if (pBuffer) + free_function(pBuffer); + return 0; +} + +/* -------------------------------------------------------------------- */ +/* Feature Functions */ +/* -------------------------------------------------------------------- */ +int MMInitFeature(struct MiraMonFeature *hMMFeature) +{ + memset(hMMFeature, 0, sizeof(*hMMFeature)); + + hMMFeature->nMaxMRecords = MM_INIT_NUMBER_OF_RECORDS; + if (MMCheckSize_t(hMMFeature->nMaxMRecords, + sizeof(*(hMMFeature->pRecords)))) + return 1; + + if (!hMMFeature->nMaxMRecords) + return 0; // No elements nothing to do. + + if ((hMMFeature->pRecords = + calloc_function((size_t)hMMFeature->nMaxMRecords * + sizeof(*(hMMFeature->pRecords)))) == nullptr) + return 1; + + hMMFeature->pRecords[0].nMaxField = MM_INIT_NUMBER_OF_FIELDS; + hMMFeature->pRecords[0].nNumField = 0; + if (MMCheckSize_t(hMMFeature->pRecords[0].nMaxField, + sizeof(*(hMMFeature->pRecords[0].pField)))) + return 1; + if (nullptr == (hMMFeature->pRecords[0].pField = calloc_function( + (size_t)hMMFeature->pRecords[0].nMaxField * + sizeof(*(hMMFeature->pRecords[0].pField))))) + return 1; + + return 0; +} + +// Conserves all allocated memory but resets the information +void MMResetFeatureGeometry(struct MiraMonFeature *hMMFeature) +{ + if (hMMFeature->pNCoordRing) + { + memset(hMMFeature->pNCoordRing, 0, + (size_t)hMMFeature->nMaxpNCoordRing * + sizeof(*(hMMFeature->pNCoordRing))); + } + if (hMMFeature->pCoord) + { + memset(hMMFeature->pCoord, 0, + (size_t)hMMFeature->nMaxpCoord * sizeof(*(hMMFeature->pCoord))); + } + hMMFeature->nICoord = 0; + if (hMMFeature->pZCoord) + { + memset(hMMFeature->pZCoord, 0, + (size_t)hMMFeature->nMaxpZCoord * + sizeof(*(hMMFeature->pZCoord))); + } + hMMFeature->nNRings = 0; + hMMFeature->nIRing = 0; + + if (hMMFeature->flag_VFG) + { + memset(hMMFeature->flag_VFG, 0, + (size_t)hMMFeature->nMaxVFG * sizeof(*(hMMFeature->flag_VFG))); + } +} + +// Preserves all allocated memory but initializes it to zero. +void MMResetFeatureRecord(struct MiraMonFeature *hMMFeature) +{ + MM_EXT_DBF_N_MULTIPLE_RECORDS nIRecord; + MM_EXT_DBF_N_FIELDS nIField; + + if (!hMMFeature->pRecords) + return; + + for (nIRecord = 0; nIRecord < hMMFeature->nMaxMRecords; nIRecord++) + { + if (!hMMFeature->pRecords[nIRecord].pField) + continue; + for (nIField = 0; nIField < hMMFeature->pRecords[nIRecord].nMaxField; + nIField++) + { + if (hMMFeature->pRecords[nIRecord].pField[nIField].pDinValue) + *(hMMFeature->pRecords[nIRecord].pField[nIField].pDinValue) = + '\0'; + hMMFeature->pRecords[nIRecord].pField[nIField].bIsValid = 0; + } + } +} + +// Destroys all allocated memory +void MMDestroyFeature(struct MiraMonFeature *hMMFeature) +{ + if (hMMFeature->pCoord) + { + free_function(hMMFeature->pCoord); + hMMFeature->pCoord = nullptr; + } + if (hMMFeature->pZCoord) + { + free_function(hMMFeature->pZCoord); + hMMFeature->pZCoord = nullptr; + } + if (hMMFeature->pNCoordRing) + { + free_function(hMMFeature->pNCoordRing); + hMMFeature->pNCoordRing = nullptr; + } + + if (hMMFeature->flag_VFG) + { + free_function(hMMFeature->flag_VFG); + hMMFeature->flag_VFG = nullptr; + } + + if (hMMFeature->pRecords) + { + MM_EXT_DBF_N_MULTIPLE_RECORDS nIRecord; + MM_EXT_DBF_N_FIELDS nIField; + + for (nIRecord = 0; nIRecord < hMMFeature->nMaxMRecords; nIRecord++) + { + if (!hMMFeature->pRecords[nIRecord].pField) + continue; + for (nIField = 0; + nIField < hMMFeature->pRecords[nIRecord].nMaxField; nIField++) + { + if (hMMFeature->pRecords[nIRecord].pField[nIField].pDinValue) + free_function(hMMFeature->pRecords[nIRecord] + .pField[nIField] + .pDinValue); + } + free_function(hMMFeature->pRecords[nIRecord].pField); + } + free_function(hMMFeature->pRecords); + hMMFeature->pRecords = nullptr; + } + + hMMFeature->nNRings = 0; + hMMFeature->nNumMRecords = 0; + hMMFeature->nMaxMRecords = 0; +} + +// Creates a MiraMon polygon, multipolygon, or linestring (arc) feature. +static int MMCreateFeaturePolOrArc(struct MiraMonVectLayerInfo *hMiraMonLayer, + struct MiraMonFeature *hMMFeature) +{ + double *pZ = nullptr; + struct MM_POINT_2D *pCoord, *pCoordReal; + MM_POLYGON_RINGS_COUNT nIPart; + MM_N_VERTICES_TYPE nIVertice; + double dtempx, dtempy; + MM_POLYGON_RINGS_COUNT nExternalRingsCount; + struct MM_PH *pCurrentPolHeader = nullptr; + struct MM_AH *pCurrentArcHeader; + // To access how many points have been stored in the last stringline + struct MM_AH *pLastArcHeader = nullptr; + struct MM_NH *pCurrentNodeHeader, *pCurrentNodeHeaderPlus1 = nullptr; + uint32_t UnsignedLongNumber; + struct MiraMonArcLayer *pMMArc; + struct MiraMonNodeLayer *pMMNode; + struct MM_TH *pArcTopHeader; + struct MM_TH *pNodeTopHeader; + char VFG = 0; + MM_FILE_OFFSET nOffsetTmp; + struct MM_ZD *pZDesc = nullptr; + struct MM_FLUSH_INFO *pFlushAL, *pFlushNL, *pFlushZL, *pFlushPS, *pFlushPAL; + MM_N_VERTICES_TYPE nPolVertices = 0; + MM_BOOLEAN bReverseArc; + int prevCoord = -1; + + if (!hMiraMonLayer) + return MM_FATAL_ERROR_WRITING_FEATURES; + + if (!hMMFeature) + return MM_FATAL_ERROR_WRITING_FEATURES; + + // Setting pointer to 3D structure (if exists). + if (hMiraMonLayer->TopHeader.bIs3d) + pZ = hMMFeature->pZCoord; + + // Setting pointers to arc/node structures. + if (hMiraMonLayer->bIsPolygon) + { + pMMArc = &hMiraMonLayer->MMPolygon.MMArc; + pArcTopHeader = &hMiraMonLayer->MMPolygon.TopArcHeader; + + pMMNode = &hMiraMonLayer->MMPolygon.MMArc.MMNode; + pNodeTopHeader = &hMiraMonLayer->MMPolygon.MMArc.TopNodeHeader; + } + else + { + pMMArc = &hMiraMonLayer->MMArc; + pArcTopHeader = &hMiraMonLayer->TopHeader; + + pMMNode = &hMiraMonLayer->MMArc.MMNode; + pNodeTopHeader = &hMiraMonLayer->MMArc.TopNodeHeader; + } + + // Setting pointers to polygon structures + if (hMiraMonLayer->bIsPolygon) + { + if (MMResizePolHeaderPointer(&hMiraMonLayer->MMPolygon.pPolHeader, + &hMiraMonLayer->MMPolygon.nMaxPolHeader, + hMiraMonLayer->TopHeader.nElemCount, + MM_INCR_NUMBER_OF_POLYGONS, 0)) + { + MMCPLError(CE_Failure, CPLE_OutOfMemory, + "Memory error in MiraMon " + "driver (MMResizePolHeaderPointer())"); + return MM_FATAL_ERROR_WRITING_FEATURES; + } + + pCurrentPolHeader = hMiraMonLayer->MMPolygon.pPolHeader + + hMiraMonLayer->TopHeader.nElemCount; + MMInitBoundingBox(&pCurrentPolHeader->dfBB); + + pCurrentPolHeader->dfPerimeter = 0; + pCurrentPolHeader->dfArea = 0L; + } + + // Setting flushes to all sections described in + // format specifications document. + pFlushAL = &pMMArc->FlushAL; + pFlushNL = &pMMNode->FlushNL; + pFlushZL = &pMMArc->pZSection.FlushZL; + pFlushPS = &hMiraMonLayer->MMPolygon.FlushPS; + pFlushPAL = &hMiraMonLayer->MMPolygon.FlushPAL; + + pFlushNL->pBlockWhereToSaveOrRead = (void *)pMMNode->pNL; + pFlushAL->pBlockWhereToSaveOrRead = (void *)pMMArc->pAL; + if (hMiraMonLayer->TopHeader.bIs3d) + pFlushZL->pBlockWhereToSaveOrRead = (void *)pMMArc->pZSection.pZL; + if (hMiraMonLayer->bIsPolygon) + { + pFlushPS->pBlockWhereToSaveOrRead = + (void *)hMiraMonLayer->MMPolygon.pPS; + pFlushPAL->pBlockWhereToSaveOrRead = + (void *)hMiraMonLayer->MMPolygon.pPAL; + } + + // Creation of the MiraMon extended database + if (!hMiraMonLayer->bIsPolygon) + { + if (hMiraMonLayer->TopHeader.nElemCount == 0) + { + MMCPLDebug("MiraMon", "Creating MiraMon database"); + if (MMCreateMMDB(hMiraMonLayer)) + return MM_FATAL_ERROR_WRITING_FEATURES; + MMCPLDebug("MiraMon", "MiraMon database created. " + "Creating features..."); + } + } + else + { // Universal polygon has been created + if (hMiraMonLayer->TopHeader.nElemCount == 1) + { + MMCPLDebug("MiraMon", "Creating MiraMon database"); + if (MMCreateMMDB(hMiraMonLayer)) + return MM_FATAL_ERROR_WRITING_FEATURES; + MMCPLDebug("MiraMon", "MiraMon database created. " + "Creating features..."); + + // Universal polygon have a record with ID_GRAFIC=0 and blancs + if (MMAddPolygonRecordToMMDB(hMiraMonLayer, nullptr, 0, 0, nullptr)) + return MM_FATAL_ERROR_WRITING_FEATURES; + } + } + + // Checking if its possible continue writing the file due + // to version limitations. + if (hMiraMonLayer->LayerVersion == MM_32BITS_VERSION) + { + MM_FILE_OFFSET nNodeOffset, nArcOffset; + MM_INTERNAL_FID nArcElemCount, nNodeElemCount; + nNodeOffset = pFlushNL->TotalSavedBytes + pFlushNL->nNumBytes; + nArcOffset = pMMArc->nOffsetArc; + + nArcElemCount = pArcTopHeader->nElemCount; + nNodeElemCount = pNodeTopHeader->nElemCount; + for (nIPart = 0; nIPart < hMMFeature->nNRings; nIPart++, + nArcElemCount++, + nNodeElemCount += (hMiraMonLayer->bIsPolygon ? 1 : 2)) + { + // There is space for the element that is going to be written? + // Polygon or arc + if (MMCheckVersionForFID(hMiraMonLayer, + hMiraMonLayer->TopHeader.nElemCount)) + { + MMCPLError(CE_Failure, CPLE_NotSupported, + "Error in MMCheckVersionForFID() (1)"); + return MM_STOP_WRITING_FEATURES; + } + + // Arc if there is no polygon + if (MMCheckVersionForFID(hMiraMonLayer, nArcElemCount)) + { + MMCPLError(CE_Failure, CPLE_NotSupported, + "Error in MMCheckVersionForFID() (2)"); + return MM_STOP_WRITING_FEATURES; + } + + // Nodes + if (MMCheckVersionForFID(hMiraMonLayer, nNodeElemCount)) + { + MMCPLError(CE_Failure, CPLE_NotSupported, + "Error in MMCheckVersionForFID() (3)"); + return MM_STOP_WRITING_FEATURES; + } + + // There is space for the last node(s) that is(are) going to be written? + if (!hMiraMonLayer->bIsPolygon) + { + if (MMCheckVersionForFID(hMiraMonLayer, nNodeElemCount + 1)) + { + MMCPLError(CE_Failure, CPLE_NotSupported, + "Error in MMCheckVersionForFID() (4)"); + return MM_STOP_WRITING_FEATURES; + } + } + + // Checking offsets + // AL: check the last point + if (MMCheckVersionOffset(hMiraMonLayer, nArcOffset)) + { + MMCPLDebug("MiraMon", "Error in MMCheckVersionOffset() (0)"); + return MM_STOP_WRITING_FEATURES; + } + // Setting next offset + nArcOffset += + (hMMFeature->pNCoordRing[nIPart]) * pMMArc->nALElementSize; + + // NL: check the last node + if (hMiraMonLayer->bIsPolygon) + nNodeOffset += (hMMFeature->nNRings) * MM_SIZE_OF_NL_32BITS; + else + nNodeOffset += (2 * hMMFeature->nNRings) * MM_SIZE_OF_NL_32BITS; + + if (MMCheckVersionOffset(hMiraMonLayer, nNodeOffset)) + { + MMCPLDebug("MiraMon", "Error in MMCheckVersionOffset() (1)"); + return MM_STOP_WRITING_FEATURES; + } + // Setting next offset + nNodeOffset += MM_SIZE_OF_NL_32BITS; + + if (!hMiraMonLayer->bIsPolygon) + { + if (MMCheckVersionOffset(hMiraMonLayer, nNodeOffset)) + { + MMCPLDebug("MiraMon", + "Error in MMCheckVersionOffset() (2)"); + return MM_STOP_WRITING_FEATURES; + } + // Setting next offset + nNodeOffset += MM_SIZE_OF_NL_32BITS; + } + + // 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)) + { + MMCPLDebug("MiraMon", + "Error in MMCheckVersionFor3DOffset()"); + return MM_STOP_WRITING_FEATURES; + } + } + } + } + + // Going through parts of the feature. + nExternalRingsCount = 0; + pCoord = hMMFeature->pCoord; + + if (!pCoord) + return MM_FATAL_ERROR_WRITING_FEATURES; + + // Doing real job + for (nIPart = 0; nIPart < hMMFeature->nNRings; nIPart++, + pArcTopHeader->nElemCount++, + pNodeTopHeader->nElemCount += (hMiraMonLayer->bIsPolygon ? 1 : 2)) + { + // Resize structures if necessary + if (MMResizeArcHeaderPointer( + &pMMArc->pArcHeader, &pMMArc->nMaxArcHeader, + pArcTopHeader->nElemCount + 1, MM_INCR_NUMBER_OF_ARCS, 0)) + { + MMCPLDebug("MiraMon", "Error in MMResizeArcHeaderPointer()"); + MMCPLError(CE_Failure, CPLE_OutOfMemory, + "Memory error in MiraMon " + "driver (MMCreateFeaturePolOrArc())"); + return MM_FATAL_ERROR_WRITING_FEATURES; + } + if (MMResizeNodeHeaderPointer( + &pMMNode->pNodeHeader, &pMMNode->nMaxNodeHeader, + hMiraMonLayer->bIsPolygon ? pNodeTopHeader->nElemCount + 1 + : pNodeTopHeader->nElemCount + 2, + MM_INCR_NUMBER_OF_NODES, 0)) + { + MMCPLDebug("MiraMon", "Error in MMResizeNodeHeaderPointer()"); + MMCPLError(CE_Failure, CPLE_OutOfMemory, + "Memory error in MiraMon " + "driver (MMCreateFeaturePolOrArc())"); + return MM_FATAL_ERROR_WRITING_FEATURES; + } + + if (hMiraMonLayer->TopHeader.bIs3d) + { + if (MMResizeZSectionDescrPointer( + &pMMArc->pZSection.pZDescription, + &pMMArc->pZSection.nMaxZDescription, pMMArc->nMaxArcHeader, + MM_INCR_NUMBER_OF_ARCS, 0)) + { + MMCPLDebug("MiraMon", + "Error in MMResizeZSectionDescrPointer()"); + MMCPLError(CE_Failure, CPLE_OutOfMemory, + "Memory error in MiraMon " + "driver (MMCreateFeaturePolOrArc())"); + return MM_FATAL_ERROR_WRITING_FEATURES; + } + pZDesc = pMMArc->pZSection.pZDescription; + } + + // Setting pointers to current headers + pCurrentArcHeader = pMMArc->pArcHeader + pArcTopHeader->nElemCount; + MMInitBoundingBox(&pCurrentArcHeader->dfBB); + + pCurrentNodeHeader = pMMNode->pNodeHeader + pNodeTopHeader->nElemCount; + if (!hMiraMonLayer->bIsPolygon) + pCurrentNodeHeaderPlus1 = pCurrentNodeHeader + 1; + + // Initializing feature information (section AH/PH) + pCurrentArcHeader->nElemCount = hMMFeature->pNCoordRing[nIPart]; + pCurrentArcHeader->dfLength = 0.0; + pCurrentArcHeader->nOffset = + pFlushAL->TotalSavedBytes + pFlushAL->nNumBytes; + + // Dumping vertices and calculating stuff that + // MiraMon needs (longitude/perimeter, area) + bReverseArc = FALSE; + if (hMiraMonLayer->bIsPolygon) + { + VFG = hMMFeature->flag_VFG[nIPart]; + bReverseArc = (VFG & MM_ROTATE_ARC) ? TRUE : FALSE; + } + + if (bReverseArc) + { + prevCoord = 1; // to find previous coordinate + pCoordReal = pCoord + pCurrentArcHeader->nElemCount - 1; + } + else + { + prevCoord = -1; // to find previous coordinate + pCoordReal = pCoord; + } + + for (nIVertice = 0; nIVertice < pCurrentArcHeader->nElemCount; + nIVertice++, (bReverseArc) ? pCoordReal-- : pCoordReal++) + { + // Writing the arc in the normal way + pFlushAL->SizeOfBlockToBeSaved = sizeof(pCoordReal->dfX); + pFlushAL->pBlockToBeSaved = (void *)&(pCoord + nIVertice)->dfX; + if (MMAppendBlockToBuffer(pFlushAL)) + { + MMCPLDebug("MiraMon", "Error in MMAppendBlockToBuffer() (1)"); + return MM_FATAL_ERROR_WRITING_FEATURES; + } + + pFlushAL->pBlockToBeSaved = (void *)&(pCoord + nIVertice)->dfY; + if (MMAppendBlockToBuffer(pFlushAL)) + { + MMCPLDebug("MiraMon", "Error in MMAppendBlockToBuffer() (2)"); + return MM_FATAL_ERROR_WRITING_FEATURES; + } + + // Calculating stuff using the inverse coordinates if it's needed + MMUpdateBoundingBoxXY(&pCurrentArcHeader->dfBB, pCoordReal); + if (nIVertice == 0 || + nIVertice == pCurrentArcHeader->nElemCount - 1) + MMUpdateBoundingBoxXY(&pNodeTopHeader->hBB, pCoordReal); + if (nIVertice > 0) + { + dtempx = pCoordReal->dfX - (pCoordReal + prevCoord)->dfX; + dtempy = pCoordReal->dfY - (pCoordReal + prevCoord)->dfY; + pCurrentArcHeader->dfLength += + sqrt(dtempx * dtempx + dtempy * dtempy); + if (hMiraMonLayer->bIsPolygon && pCurrentPolHeader) + { + pCurrentPolHeader->dfArea += + (pCoordReal->dfX * (pCoordReal + prevCoord)->dfY - + (pCoordReal + prevCoord)->dfX * pCoordReal->dfY); + } + } + } + if (bReverseArc) + pCoord = pCoordReal + pCurrentArcHeader->nElemCount + 1; + else + pCoord += pCurrentArcHeader->nElemCount; + + nPolVertices += pCurrentArcHeader->nElemCount; + + // Updating bounding boxes + MMUpdateBoundingBox(&pArcTopHeader->hBB, &pCurrentArcHeader->dfBB); + if (hMiraMonLayer->bIsPolygon) + MMUpdateBoundingBox(&hMiraMonLayer->TopHeader.hBB, + &pCurrentArcHeader->dfBB); + + pMMArc->nOffsetArc += + (pCurrentArcHeader->nElemCount) * pMMArc->nALElementSize; + + pCurrentArcHeader->nFirstIdNode = (2 * pArcTopHeader->nElemCount); + if (hMiraMonLayer->bIsPolygon) + { + pCurrentArcHeader->nFirstIdNode = pArcTopHeader->nElemCount; + pCurrentArcHeader->nLastIdNode = pArcTopHeader->nElemCount; + } + else + { + pCurrentArcHeader->nFirstIdNode = (2 * pArcTopHeader->nElemCount); + pCurrentArcHeader->nLastIdNode = + (2 * pArcTopHeader->nElemCount + 1); + } + if (MMAddArcRecordToMMDB(hMiraMonLayer, hMMFeature, + pArcTopHeader->nElemCount, pCurrentArcHeader)) + { + MMCPLDebug("MiraMon", "Error in MMAddArcRecordToMMDB()"); + return MM_FATAL_ERROR_WRITING_FEATURES; + } + + // Node Stuff: writing NL section + pCurrentNodeHeader->nArcsCount = 1; + if (hMiraMonLayer->bIsPolygon) + pCurrentNodeHeader->cNodeType = MM_RING_NODE; + else + pCurrentNodeHeader->cNodeType = MM_FINAL_NODE; + + pCurrentNodeHeader->nOffset = + pFlushNL->TotalSavedBytes + pFlushNL->nNumBytes; + if (MMAppendIntegerDependingOnVersion(hMiraMonLayer, pFlushNL, + &UnsignedLongNumber, + pArcTopHeader->nElemCount)) + { + MMCPLDebug("MiraMon", + "Error in MMAppendIntegerDependingOnVersion()"); + return MM_FATAL_ERROR_WRITING_FEATURES; + } + + // 8bytes alignment + nOffsetTmp = pFlushNL->TotalSavedBytes + pFlushNL->nNumBytes; + MMGetOffsetAlignedTo8(&nOffsetTmp); + if (nOffsetTmp != pFlushNL->TotalSavedBytes + pFlushNL->nNumBytes) + { + pFlushNL->SizeOfBlockToBeSaved = + (size_t)(nOffsetTmp - + (pFlushNL->TotalSavedBytes + pFlushNL->nNumBytes)); + pFlushNL->pBlockToBeSaved = (void *)nullptr; + if (MMAppendBlockToBuffer(pFlushNL)) + { + MMCPLDebug("MiraMon", "Error in MMAppendBlockToBuffer() (3)"); + return MM_FATAL_ERROR_WRITING_FEATURES; + } + } + if (MMAddNodeRecordToMMDB(hMiraMonLayer, pNodeTopHeader->nElemCount, + pCurrentNodeHeader)) + { + MMCPLDebug("MiraMon", "Error in MMAddNodeRecordToMMDB()"); + return MM_FATAL_ERROR_WRITING_FEATURES; + } + + if (!hMiraMonLayer->bIsPolygon) + { + pCurrentNodeHeaderPlus1->nArcsCount = 1; + if (hMiraMonLayer->bIsPolygon) + pCurrentNodeHeaderPlus1->cNodeType = MM_RING_NODE; + else + pCurrentNodeHeaderPlus1->cNodeType = MM_FINAL_NODE; + + pCurrentNodeHeaderPlus1->nOffset = + pFlushNL->TotalSavedBytes + pFlushNL->nNumBytes; + + if (MMAppendIntegerDependingOnVersion(hMiraMonLayer, pFlushNL, + &UnsignedLongNumber, + pArcTopHeader->nElemCount)) + { + MMCPLDebug("MiraMon", + "Error in MMAppendIntegerDependingOnVersion()"); + return MM_FATAL_ERROR_WRITING_FEATURES; + } + + // 8bytes alignment + nOffsetTmp = pFlushNL->TotalSavedBytes + pFlushNL->nNumBytes; + MMGetOffsetAlignedTo8(&nOffsetTmp); + if (nOffsetTmp != pFlushNL->TotalSavedBytes + pFlushNL->nNumBytes) + { + pFlushNL->SizeOfBlockToBeSaved = + (size_t)(nOffsetTmp - + (pFlushNL->TotalSavedBytes + pFlushNL->nNumBytes)); + pFlushNL->pBlockToBeSaved = (void *)nullptr; + if (MMAppendBlockToBuffer(pFlushNL)) + { + MMCPLDebug("MiraMon", "Error in MMAppendBlockToBuffer()"); + return MM_FATAL_ERROR_WRITING_FEATURES; + } + } + if (MMAddNodeRecordToMMDB(hMiraMonLayer, + pNodeTopHeader->nElemCount + 1, + pCurrentNodeHeaderPlus1)) + { + MMCPLDebug("MiraMon", "Error in MMAddNodeRecordToMMDB()"); + return MM_FATAL_ERROR_WRITING_FEATURES; + } + } + + // 3D stuff + if (hMiraMonLayer->TopHeader.bIs3d && pZDesc) + { + pZDesc[pArcTopHeader->nElemCount].dfBBminz = + STATISTICAL_UNDEF_VALUE; + pZDesc[pArcTopHeader->nElemCount].dfBBmaxz = + -STATISTICAL_UNDEF_VALUE; + for (nIVertice = 0; nIVertice < pCurrentArcHeader->nElemCount; + nIVertice++, pZ++) + { + pFlushZL->SizeOfBlockToBeSaved = sizeof(*pZ); + pFlushZL->pBlockToBeSaved = (void *)pZ; + if (MMAppendBlockToBuffer(pFlushZL)) + { + MMCPLDebug("MiraMon", "Error in MMAppendBlockToBuffer()"); + return MM_FATAL_ERROR_WRITING_FEATURES; + } + + if (pZDesc[pArcTopHeader->nElemCount].dfBBminz > *pZ) + pZDesc[pArcTopHeader->nElemCount].dfBBminz = *pZ; + if (pZDesc[pArcTopHeader->nElemCount].dfBBmaxz < *pZ) + pZDesc[pArcTopHeader->nElemCount].dfBBmaxz = *pZ; + } + 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); + } + } + + // Exclusive polygon stuff + if (hMiraMonLayer->bIsPolygon && pCurrentPolHeader) + { + // PS SECTION + if (MMAppendIntegerDependingOnVersion(hMiraMonLayer, pFlushPS, + &UnsignedLongNumber, 0)) + { + MMCPLDebug("MiraMon", + "Error in MMAppendIntegerDependingOnVersion()"); + return MM_FATAL_ERROR_WRITING_FEATURES; + } + + if (MMAppendIntegerDependingOnVersion( + hMiraMonLayer, pFlushPS, &UnsignedLongNumber, + hMiraMonLayer->TopHeader.nElemCount)) + { + MMCPLDebug("MiraMon", + "Error in MMAppendIntegerDependingOnVersion()"); + return MM_FATAL_ERROR_WRITING_FEATURES; + } + + // PAL SECTION + // Vertices of rings defining + // holes in polygons are in a counterclockwise direction. + // Holes are at the end of all external rings that contain the holes!! + if (VFG & MM_EXTERIOR_ARC_SIDE) + nExternalRingsCount++; + + pCurrentPolHeader->nArcsCount++; + //(MM_POLYGON_ARCS_COUNT)hMMFeature->nNRings; + if (VFG & MM_EXTERIOR_ARC_SIDE) + pCurrentPolHeader + ->nExternalRingsCount++; //= nExternalRingsCount; + + if (VFG & MM_END_ARC_IN_RING) + pCurrentPolHeader->nRingsCount++; //= hMMFeature->nNRings; + if (nIPart == 0) + { + pCurrentPolHeader->nOffset = + pFlushPAL->TotalSavedBytes + pFlushPAL->nNumBytes; + } + + if (nIPart == hMMFeature->nNRings - 1) + pCurrentPolHeader->dfArea /= 2; + + pFlushPAL->SizeOfBlockToBeSaved = 1; + pFlushPAL->pBlockToBeSaved = (void *)&VFG; + if (MMAppendBlockToBuffer(pFlushPAL)) + { + MMCPLDebug("MiraMon", "Error in MMAppendBlockToBuffer()"); + return MM_FATAL_ERROR_WRITING_FEATURES; + } + + if (MMAppendIntegerDependingOnVersion(hMiraMonLayer, pFlushPAL, + &UnsignedLongNumber, + pArcTopHeader->nElemCount)) + { + MMCPLDebug("MiraMon", + "Error in MMAppendIntegerDependingOnVersion()"); + return MM_FATAL_ERROR_WRITING_FEATURES; + } + + // 8bytes alignment + if (nIPart == hMMFeature->nNRings - 1) + { + nOffsetTmp = pFlushPAL->TotalSavedBytes + pFlushPAL->nNumBytes; + MMGetOffsetAlignedTo8(&nOffsetTmp); + + if (nOffsetTmp != + pFlushPAL->TotalSavedBytes + pFlushPAL->nNumBytes) + { + pFlushPAL->SizeOfBlockToBeSaved = + (size_t)(nOffsetTmp - (pFlushPAL->TotalSavedBytes + + pFlushPAL->nNumBytes)); + pFlushPAL->pBlockToBeSaved = (void *)nullptr; + if (MMAppendBlockToBuffer(pFlushPAL)) + { + MMCPLDebug("MiraMon", + "Error in MMAppendBlockToBuffer()"); + return MM_FATAL_ERROR_WRITING_FEATURES; + } + } + } + + MMUpdateBoundingBox(&pCurrentPolHeader->dfBB, + &pCurrentArcHeader->dfBB); + pCurrentPolHeader->dfPerimeter += pCurrentArcHeader->dfLength; + } + } + + // Updating element count and if the polygon is multipart. + // MiraMon does not accept multipoints or multilines, only multipolygons. + if (hMiraMonLayer->bIsPolygon) + { + if (MMAddPolygonRecordToMMDB(hMiraMonLayer, hMMFeature, + hMiraMonLayer->TopHeader.nElemCount, + nPolVertices, pCurrentPolHeader)) + { + MMCPLDebug("MiraMon", "Error in MMAddPolygonRecordToMMDB()"); + return MM_FATAL_ERROR_WRITING_FEATURES; + } + hMiraMonLayer->TopHeader.nElemCount++; + + if (nExternalRingsCount > 1) + hMiraMonLayer->TopHeader.bIsMultipolygon = TRUE; + } + + return MM_CONTINUE_WRITING_FEATURES; +} // End of de MMCreateFeaturePolOrArc() + +// Creates a MiraMon DBF record when not associated with a geometric feature. +static int MMCreateRecordDBF(struct MiraMonVectLayerInfo *hMiraMonLayer, + struct MiraMonFeature *hMMFeature) +{ + int result; + + if (!hMiraMonLayer) + return MM_FATAL_ERROR_WRITING_FEATURES; + + if (hMiraMonLayer->TopHeader.nElemCount == 0) + { + if (MMCreateMMDB(hMiraMonLayer)) + return MM_FATAL_ERROR_WRITING_FEATURES; + } + + result = MMAddDBFRecordToMMDB(hMiraMonLayer, hMMFeature); + if (result == MM_FATAL_ERROR_WRITING_FEATURES || + result == MM_STOP_WRITING_FEATURES) + return result; + + // Everything OK. + return MM_CONTINUE_WRITING_FEATURES; +} // End of de MMCreateRecordDBF() + +// Creates a MiraMon point feature. +static int MMCreateFeaturePoint(struct MiraMonVectLayerInfo *hMiraMonLayer, + struct MiraMonFeature *hMMFeature) +{ + double *pZ = nullptr; + struct MM_POINT_2D *pCoord; + MM_POLYGON_RINGS_COUNT nIPart; + MM_N_VERTICES_TYPE nIVertice, nCoord; + struct MM_ZD *pZDescription = nullptr; + MM_INTERNAL_FID nElemCount; + int result; + + if (!hMiraMonLayer) + return MM_FATAL_ERROR_WRITING_FEATURES; + + if (!hMMFeature) + return MM_STOP_WRITING_FEATURES; + + if (hMiraMonLayer->TopHeader.bIs3d) + pZ = hMMFeature->pZCoord; + + nElemCount = hMiraMonLayer->TopHeader.nElemCount; + for (nIPart = 0, pCoord = hMMFeature->pCoord; nIPart < hMMFeature->nNRings; + nIPart++, nElemCount++) + { + nCoord = hMMFeature->pNCoordRing[nIPart]; + + // Checking if its possible continue writing the file due + // to version limitations. + if (MMCheckVersionForFID(hMiraMonLayer, + hMiraMonLayer->TopHeader.nElemCount + nCoord)) + { + MMCPLError(CE_Failure, CPLE_NotSupported, + "Error in MMCheckVersionForFID() (5)"); + return MM_STOP_WRITING_FEATURES; + } + + if (hMiraMonLayer->TopHeader.bIs3d) + { + if (nElemCount == 0) + { + if (MMCheckVersionFor3DOffset(hMiraMonLayer, 0, nElemCount + 1)) + return MM_STOP_WRITING_FEATURES; + } + else + { + pZDescription = hMiraMonLayer->MMPoint.pZSection.pZDescription; + if (!pZDescription) + { + MMCPLError(CE_Failure, CPLE_ObjectNull, + "Error: pZDescription should not be nullptr"); + return MM_STOP_WRITING_FEATURES; + } + if (MMCheckVersionFor3DOffset( + hMiraMonLayer, + pZDescription[nElemCount - 1].nOffsetZ + sizeof(*pZ), + nElemCount + 1)) + return MM_STOP_WRITING_FEATURES; + } + } + + // Doing real job + // Memory issues + if (hMiraMonLayer->TopHeader.bIs3d && pZ) + { + if (MMResizeZSectionDescrPointer( + &hMiraMonLayer->MMPoint.pZSection.pZDescription, + &hMiraMonLayer->MMPoint.pZSection.nMaxZDescription, + nElemCount, MM_INCR_NUMBER_OF_POINTS, 0)) + { + MMCPLError(CE_Failure, CPLE_OutOfMemory, + "Memory error in MiraMon " + "driver (MMCreateFeaturePoint())"); + return MM_FATAL_ERROR_WRITING_FEATURES; + } + + pZDescription = hMiraMonLayer->MMPoint.pZSection.pZDescription; + if (!pZDescription) + { + MMCPLError(CE_Failure, CPLE_ObjectNull, + "Error: pZDescription should not be nullptr"); + return MM_STOP_WRITING_FEATURES; + } + + pZDescription[nElemCount].dfBBminz = *pZ; + pZDescription[nElemCount].dfBBmaxz = *pZ; + pZDescription[nElemCount].nZCount = 1; + if (nElemCount == 0) + pZDescription[nElemCount].nOffsetZ = 0; + else + pZDescription[nElemCount].nOffsetZ = + pZDescription[nElemCount - 1].nOffsetZ + sizeof(*pZ); + } + + // Flush settings + hMiraMonLayer->MMPoint.FlushTL.pBlockWhereToSaveOrRead = + (void *)hMiraMonLayer->MMPoint.pTL; + if (hMiraMonLayer->TopHeader.bIs3d) + hMiraMonLayer->MMPoint.pZSection.FlushZL.pBlockWhereToSaveOrRead = + (void *)hMiraMonLayer->MMPoint.pZSection.pZL; + + // Dump point or points (MiraMon does not have multiple points) + for (nIVertice = 0; nIVertice < nCoord; nIVertice++, pCoord++) + { + // Updating the bounding box of the layer + MMUpdateBoundingBoxXY(&hMiraMonLayer->TopHeader.hBB, pCoord); + + // Adding the point at the memory block + hMiraMonLayer->MMPoint.FlushTL.SizeOfBlockToBeSaved = + sizeof(pCoord->dfX); + hMiraMonLayer->MMPoint.FlushTL.pBlockToBeSaved = + (void *)&pCoord->dfX; + if (MMAppendBlockToBuffer(&hMiraMonLayer->MMPoint.FlushTL)) + return MM_FATAL_ERROR_WRITING_FEATURES; + hMiraMonLayer->MMPoint.FlushTL.pBlockToBeSaved = + (void *)&pCoord->dfY; + if (MMAppendBlockToBuffer(&hMiraMonLayer->MMPoint.FlushTL)) + return MM_FATAL_ERROR_WRITING_FEATURES; + + // Adding the 3D part, if exists, at the memory block + if (hMiraMonLayer->TopHeader.bIs3d && pZ) + { + hMiraMonLayer->MMPoint.pZSection.FlushZL.SizeOfBlockToBeSaved = + sizeof(*pZ); + hMiraMonLayer->MMPoint.pZSection.FlushZL.pBlockToBeSaved = + (void *)pZ; + if (MMAppendBlockToBuffer( + &hMiraMonLayer->MMPoint.pZSection.FlushZL)) + return MM_FATAL_ERROR_WRITING_FEATURES; + + if (!pZDescription) + { + MMCPLError(CE_Failure, CPLE_ObjectNull, + "Error: pZDescription should not be nullptr"); + return MM_STOP_WRITING_FEATURES; + } + + if (pZDescription[nElemCount].dfBBminz > *pZ) + pZDescription[nElemCount].dfBBminz = *pZ; + if (pZDescription[nElemCount].dfBBmaxz < *pZ) + pZDescription[nElemCount].dfBBmaxz = *pZ; + + if (hMiraMonLayer->MMPoint.pZSection.ZHeader.dfBBminz > *pZ) + hMiraMonLayer->MMPoint.pZSection.ZHeader.dfBBminz = *pZ; + if (hMiraMonLayer->MMPoint.pZSection.ZHeader.dfBBmaxz < *pZ) + hMiraMonLayer->MMPoint.pZSection.ZHeader.dfBBmaxz = *pZ; + + pZ++; + } + } + + if (hMiraMonLayer->TopHeader.nElemCount == 0) + { + if (MMCreateMMDB(hMiraMonLayer)) + return MM_FATAL_ERROR_WRITING_FEATURES; + } + + result = MMAddPointRecordToMMDB(hMiraMonLayer, hMMFeature, nElemCount); + if (result == MM_FATAL_ERROR_WRITING_FEATURES || + result == MM_STOP_WRITING_FEATURES) + return result; + } + // Updating nElemCount at the header of the layer + hMiraMonLayer->TopHeader.nElemCount = nElemCount; + + // Everything OK. + return MM_CONTINUE_WRITING_FEATURES; +} // End of de MMCreateFeaturePoint() + +// Checks whether a given Feature ID (FID) exceeds the maximum allowed +// index for 2 GB vectors in a specific MiraMon layer. +int MMCheckVersionForFID(struct MiraMonVectLayerInfo *hMiraMonLayer, + MM_INTERNAL_FID FID) +{ + if (!hMiraMonLayer) + return 1; + + if (hMiraMonLayer->LayerVersion != MM_32BITS_VERSION) + return 0; + + if (FID >= MAXIMUM_OBJECT_INDEX_IN_2GB_VECTORS) + return 1; + return 0; +} + +// Checks whether a given offset exceeds the maximum allowed +// index for 2 GB vectors in a specific MiraMon layer. +int MMCheckVersionOffset(struct MiraMonVectLayerInfo *hMiraMonLayer, + MM_FILE_OFFSET OffsetToCheck) +{ + if (!hMiraMonLayer) + return 1; + + // Checking if the final version is 1.1 or 2.0 + if (hMiraMonLayer->LayerVersion != MM_32BITS_VERSION) + return 0; + + // User decided that if necessary, output version can be 2.0 + if (OffsetToCheck < MAXIMUM_OFFSET_IN_2GB_VECTORS) + return 0; + + return 1; +} + +// 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_FILE_OFFSET LastOffset; + + if (!hMiraMonLayer) + return 1; + + // Checking if the final version is 1.1 or 2.0 + if (hMiraMonLayer->LayerVersion != MM_32BITS_VERSION) + 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; + + LastOffset += MM_SIZE_OF_ZH; + LastOffset += nElemCount * MM_SIZE_OF_ZD_32_BITS; + + if (LastOffset < MAXIMUM_OFFSET_IN_2GB_VECTORS) + return 0; + + return 1; +} + +// Adds a feature in a MiraMon layer. +int MMAddFeature(struct MiraMonVectLayerInfo *hMiraMonLayer, + struct MiraMonFeature *hMiraMonFeature) +{ + int re; + MM_INTERNAL_FID previousFID = 0; + + if (!hMiraMonLayer) + return MM_FATAL_ERROR_WRITING_FEATURES; + + if (!hMiraMonLayer->bIsBeenInit) + { + if (MMInitLayerByType(hMiraMonLayer)) + { + MMCPLDebug("MiraMon", "Error in MMInitLayerByType()"); + return MM_FATAL_ERROR_WRITING_FEATURES; + } + hMiraMonLayer->bIsBeenInit = 1; + } + + if (hMiraMonFeature) + previousFID = hMiraMonLayer->TopHeader.nElemCount; + + if (hMiraMonLayer->bIsPoint) + { + re = LOG_ACTION(MMCreateFeaturePoint(hMiraMonLayer, hMiraMonFeature)); + if (hMiraMonFeature) + { + hMiraMonFeature->nReadFeatures = + hMiraMonLayer->TopHeader.nElemCount - previousFID; + } + return re; + } + if (hMiraMonLayer->bIsArc || hMiraMonLayer->bIsPolygon) + { + re = + LOG_ACTION(MMCreateFeaturePolOrArc(hMiraMonLayer, hMiraMonFeature)); + if (hMiraMonFeature) + { + hMiraMonFeature->nReadFeatures = + hMiraMonLayer->TopHeader.nElemCount - previousFID; + } + return re; + } + if (hMiraMonLayer->bIsDBF) + { + // Adding a record to DBF file + re = LOG_ACTION(MMCreateRecordDBF(hMiraMonLayer, hMiraMonFeature)); + if (hMiraMonFeature) + { + hMiraMonFeature->nReadFeatures = + hMiraMonLayer->TopHeader.nElemCount - previousFID; + } + return re; + } + + return MM_CONTINUE_WRITING_FEATURES; +} + +/* -------------------------------------------------------------------- */ +/* Tools used by MiraMon. */ +/* -------------------------------------------------------------------- */ + +void MMInitBoundingBox(struct MMBoundingBox *dfBB) +{ + if (!dfBB) + return; + dfBB->dfMinX = STATISTICAL_UNDEF_VALUE; + dfBB->dfMaxX = -STATISTICAL_UNDEF_VALUE; + dfBB->dfMinY = STATISTICAL_UNDEF_VALUE; + dfBB->dfMaxY = -STATISTICAL_UNDEF_VALUE; +} + +void MMUpdateBoundingBox(struct MMBoundingBox *dfBBToBeAct, + struct MMBoundingBox *dfBBWithData) +{ + if (!dfBBToBeAct) + return; + + if (dfBBToBeAct->dfMinX > dfBBWithData->dfMinX) + dfBBToBeAct->dfMinX = dfBBWithData->dfMinX; + + if (dfBBToBeAct->dfMinY > dfBBWithData->dfMinY) + dfBBToBeAct->dfMinY = dfBBWithData->dfMinY; + + if (dfBBToBeAct->dfMaxX < dfBBWithData->dfMaxX) + dfBBToBeAct->dfMaxX = dfBBWithData->dfMaxX; + + if (dfBBToBeAct->dfMaxY < dfBBWithData->dfMaxY) + dfBBToBeAct->dfMaxY = dfBBWithData->dfMaxY; +} + +void MMUpdateBoundingBoxXY(struct MMBoundingBox *dfBB, + struct MM_POINT_2D *pCoord) +{ + if (!pCoord) + return; + + if (pCoord->dfX < dfBB->dfMinX) + dfBB->dfMinX = pCoord->dfX; + + if (pCoord->dfY < dfBB->dfMinY) + dfBB->dfMinY = pCoord->dfY; + + if (pCoord->dfX > dfBB->dfMaxX) + dfBB->dfMaxX = pCoord->dfX; + + if (pCoord->dfY > dfBB->dfMaxY) + dfBB->dfMaxY = pCoord->dfY; +} + +/* -------------------------------------------------------------------- */ +/* Resize structures for reuse */ +/* -------------------------------------------------------------------- */ +int MMResizeMiraMonFieldValue(struct MiraMonFieldValue **pFieldValue, + MM_EXT_DBF_N_MULTIPLE_RECORDS *nMax, + MM_EXT_DBF_N_MULTIPLE_RECORDS nNum, + MM_EXT_DBF_N_MULTIPLE_RECORDS nIncr, + MM_EXT_DBF_N_MULTIPLE_RECORDS nProposedMax) +{ + MM_EXT_DBF_N_MULTIPLE_RECORDS nPrevMax; + MM_EXT_DBF_N_MULTIPLE_RECORDS nNewMax; + void *pTmp; + + if (nNum < *nMax) + return 0; + + nPrevMax = *nMax; + nNewMax = max_function(nNum + nIncr, nProposedMax); + if (MMCheckSize_t(nNewMax, sizeof(**pFieldValue))) + { + return 1; + } + if ((pTmp = realloc_function( + *pFieldValue, (size_t)nNewMax * sizeof(**pFieldValue))) == nullptr) + { + MMCPLError(CE_Failure, CPLE_OutOfMemory, + "Memory error in MiraMon " + "driver (MMResizeMiraMonFieldValue())"); + return 1; + } + *nMax = nNewMax; + *pFieldValue = pTmp; + + memset((*pFieldValue) + nPrevMax, 0, + (size_t)(*nMax - nPrevMax) * sizeof(**pFieldValue)); + return 0; +} + +int MMResizeMiraMonPolygonArcs(struct MM_PAL_MEM **pFID, + MM_POLYGON_ARCS_COUNT *nMax, + MM_POLYGON_ARCS_COUNT nNum, + MM_POLYGON_ARCS_COUNT nIncr, + MM_POLYGON_ARCS_COUNT nProposedMax) +{ + MM_POLYGON_ARCS_COUNT nPrevMax; + MM_POLYGON_ARCS_COUNT nNewMax; + void *pTmp; + + if (nNum < *nMax) + return 0; + + nPrevMax = *nMax; + nNewMax = max_function(nNum + nIncr, nProposedMax); + if (MMCheckSize_t(nNewMax, sizeof(**pFID))) + { + return 1; + } + if (nNewMax == 0 && *pFID) + return 0; + if ((pTmp = realloc_function(*pFID, (size_t)nNewMax * sizeof(**pFID))) == + nullptr) + { + MMCPLError(CE_Failure, CPLE_OutOfMemory, + "Memory error in MiraMon " + "driver (MMResizeMiraMonPolygonArcs())"); + return 1; + } + *nMax = nNewMax; + *pFID = pTmp; + + memset((*pFID) + nPrevMax, 0, (size_t)(*nMax - nPrevMax) * sizeof(**pFID)); + return 0; +} + +int MMResizeMiraMonRecord(struct MiraMonRecord **pMiraMonRecord, + MM_EXT_DBF_N_MULTIPLE_RECORDS *nMax, + MM_EXT_DBF_N_MULTIPLE_RECORDS nNum, + MM_EXT_DBF_N_MULTIPLE_RECORDS nIncr, + MM_EXT_DBF_N_MULTIPLE_RECORDS nProposedMax) +{ + MM_EXT_DBF_N_MULTIPLE_RECORDS nPrevMax; + MM_EXT_DBF_N_MULTIPLE_RECORDS nNewMax; + void *pTmp; + + if (nNum < *nMax) + return 0; + + nPrevMax = *nMax; + nNewMax = max_function(nNum + nIncr, nProposedMax); + if (MMCheckSize_t(nNewMax, sizeof(**pMiraMonRecord))) + { + return 1; + } + if (nNewMax == 0 && *pMiraMonRecord) + return 0; + if ((pTmp = realloc_function(*pMiraMonRecord, + (size_t)nNewMax * sizeof(**pMiraMonRecord))) == + nullptr) + { + MMCPLError(CE_Failure, CPLE_OutOfMemory, + "Memory error in MiraMon " + "driver (MMResizeMiraMonRecord())"); + return 1; + } + *nMax = nNewMax; + *pMiraMonRecord = pTmp; + + memset((*pMiraMonRecord) + nPrevMax, 0, + (size_t)(*nMax - nPrevMax) * sizeof(**pMiraMonRecord)); + return 0; +} + +int MMResizeZSectionDescrPointer(struct MM_ZD **pZDescription, GUInt64 *nMax, + GUInt64 nNum, GUInt64 nIncr, + GUInt64 nProposedMax) +{ + GUInt64 nNewMax, nPrevMax; + void *pTmp; + + if (nNum < *nMax) + return 0; + + nPrevMax = *nMax; + nNewMax = max_function(nNum + nIncr, nProposedMax); + if (MMCheckSize_t(nNewMax, sizeof(**pZDescription))) + { + return 1; + } + if (nNewMax == 0 && *pZDescription) + return 0; + if ((pTmp = realloc_function(*pZDescription, + (size_t)nNewMax * sizeof(**pZDescription))) == + nullptr) + { + MMCPLError(CE_Failure, CPLE_OutOfMemory, + "Memory error in MiraMon " + "driver (MMResizeZSectionDescrPointer())"); + return 1; + } + *nMax = nNewMax; + *pZDescription = pTmp; + + memset((*pZDescription) + nPrevMax, 0, + (size_t)(*nMax - nPrevMax) * sizeof(**pZDescription)); + return 0; +} + +int MMResizeNodeHeaderPointer(struct MM_NH **pNodeHeader, GUInt64 *nMax, + GUInt64 nNum, GUInt64 nIncr, GUInt64 nProposedMax) +{ + GUInt64 nNewMax, nPrevMax; + void *pTmp; + + if (nNum < *nMax) + return 0; + + nPrevMax = *nMax; + nNewMax = max_function(nNum + nIncr, nProposedMax); + if (MMCheckSize_t(nNewMax, sizeof(**pNodeHeader))) + { + return 1; + } + if (nNewMax == 0 && *pNodeHeader) + return 0; + if ((pTmp = realloc_function( + *pNodeHeader, (size_t)nNewMax * sizeof(**pNodeHeader))) == nullptr) + { + MMCPLError(CE_Failure, CPLE_OutOfMemory, + "Memory error in MiraMon " + "driver (MMResizeNodeHeaderPointer())"); + return 1; + } + *nMax = nNewMax; + *pNodeHeader = pTmp; + + memset((*pNodeHeader) + nPrevMax, 0, + (size_t)(*nMax - nPrevMax) * sizeof(**pNodeHeader)); + return 0; +} + +int MMResizeArcHeaderPointer(struct MM_AH **pArcHeader, GUInt64 *nMax, + GUInt64 nNum, GUInt64 nIncr, GUInt64 nProposedMax) +{ + GUInt64 nNewMax, nPrevMax; + void *pTmp; + + if (nNum < *nMax) + return 0; + + nPrevMax = *nMax; + nNewMax = max_function(nNum + nIncr, nProposedMax); + if (MMCheckSize_t(nNewMax, sizeof(**pArcHeader))) + { + return 1; + } + if (nNewMax == 0 && *pArcHeader) + return 0; + if ((pTmp = realloc_function( + *pArcHeader, (size_t)nNewMax * sizeof(**pArcHeader))) == nullptr) + { + MMCPLError(CE_Failure, CPLE_OutOfMemory, + "Memory error in MiraMon " + "driver (MMResizeArcHeaderPointer())"); + return 1; + } + *nMax = nNewMax; + *pArcHeader = pTmp; + + memset((*pArcHeader) + nPrevMax, 0, + (size_t)(*nMax - nPrevMax) * sizeof(**pArcHeader)); + return 0; +} + +int MMResizePolHeaderPointer(struct MM_PH **pPolHeader, GUInt64 *nMax, + GUInt64 nNum, GUInt64 nIncr, GUInt64 nProposedMax) +{ + GUInt64 nNewMax, nPrevMax; + void *pTmp; + + if (nNum < *nMax) + return 0; + + nPrevMax = *nMax; + nNewMax = max_function(nNum + nIncr, nProposedMax); + if (MMCheckSize_t(nNewMax, sizeof(**pPolHeader))) + { + return 1; + } + if (nNewMax == 0 && *pPolHeader) + return 0; + if ((pTmp = realloc_function( + *pPolHeader, (size_t)nNewMax * sizeof(**pPolHeader))) == nullptr) + { + MMCPLError(CE_Failure, CPLE_OutOfMemory, + "Memory error in MiraMon " + "driver (MMResizePolHeaderPointer())"); + return 1; + } + *nMax = nNewMax; + *pPolHeader = pTmp; + + memset((*pPolHeader) + nPrevMax, 0, + (size_t)(*nMax - nPrevMax) * sizeof(**pPolHeader)); + return 0; +} + +int MMResize_MM_N_VERTICES_TYPE_Pointer(MM_N_VERTICES_TYPE **pVrt, + MM_N_VERTICES_TYPE *nMax, + MM_N_VERTICES_TYPE nNum, + MM_N_VERTICES_TYPE nIncr, + MM_N_VERTICES_TYPE nProposedMax) +{ + MM_N_VERTICES_TYPE nNewMax, nPrevMax; + void *pTmp; + + if (nNum < *nMax) + return 0; + + nPrevMax = *nMax; + nNewMax = max_function(nNum + nIncr, nProposedMax); + if (MMCheckSize_t(nNewMax, sizeof(**pVrt))) + { + return 1; + } + if (nNewMax == 0 && *pVrt) + return 0; + if ((pTmp = realloc_function(*pVrt, (size_t)nNewMax * sizeof(**pVrt))) == + nullptr) + { + MMCPLError(CE_Failure, CPLE_OutOfMemory, + "Memory error in MiraMon " + "driver (MMResize_MM_N_VERTICES_TYPE_Pointer())"); + return 1; + } + *nMax = nNewMax; + *pVrt = pTmp; + + memset((*pVrt) + nPrevMax, 0, (size_t)(*nMax - nPrevMax) * sizeof(**pVrt)); + return 0; +} + +int MMResizeVFGPointer(char **pInt, MM_INTERNAL_FID *nMax, MM_INTERNAL_FID nNum, + MM_INTERNAL_FID nIncr, MM_INTERNAL_FID nProposedMax) +{ + MM_N_VERTICES_TYPE nNewMax, nPrevMax; + void *pTmp; + + if (nNum < *nMax) + return 0; + + nPrevMax = *nMax; + nNewMax = max_function(nNum + nIncr, nProposedMax); + if (MMCheckSize_t(nNewMax, sizeof(**pInt))) + { + return 1; + } + if (nNewMax == 0 && *pInt) + return 0; + if ((pTmp = realloc_function(*pInt, (size_t)nNewMax * sizeof(**pInt))) == + nullptr) + { + MMCPLError(CE_Failure, CPLE_OutOfMemory, + "Memory error in MiraMon " + "driver (MMResizeVFGPointer())"); + return 1; + } + *nMax = nNewMax; + *pInt = pTmp; + + memset((*pInt) + nPrevMax, 0, (size_t)(*nMax - nPrevMax) * sizeof(**pInt)); + return 0; +} + +int MMResizeMM_POINT2DPointer(struct MM_POINT_2D **pPoint2D, + MM_N_VERTICES_TYPE *nMax, MM_N_VERTICES_TYPE nNum, + MM_N_VERTICES_TYPE nIncr, + MM_N_VERTICES_TYPE nProposedMax) +{ + MM_N_VERTICES_TYPE nNewMax, nPrevMax; + void *pTmp; + + if (nNum < *nMax) + return 0; + + nPrevMax = *nMax; + nNewMax = max_function(nNum + nIncr, nProposedMax); + if (MMCheckSize_t(nNewMax, sizeof(**pPoint2D))) + { + return 1; + } + if (nNewMax == 0 && *pPoint2D) + return 0; + if ((pTmp = realloc_function(*pPoint2D, (size_t)nNewMax * + sizeof(**pPoint2D))) == nullptr) + { + MMCPLError(CE_Failure, CPLE_OutOfMemory, + "Memory error in MiraMon " + "driver (MMResizeMM_POINT2DPointer())"); + return 1; + } + *nMax = nNewMax; + *pPoint2D = pTmp; + + memset((*pPoint2D) + nPrevMax, 0, + (size_t)(*nMax - nPrevMax) * sizeof(**pPoint2D)); + return 0; +} + +int MMResizeDoublePointer(MM_COORD_TYPE **pDouble, MM_N_VERTICES_TYPE *nMax, + MM_N_VERTICES_TYPE nNum, MM_N_VERTICES_TYPE nIncr, + MM_N_VERTICES_TYPE nProposedMax) +{ + MM_N_VERTICES_TYPE nNewMax, nPrevMax; + void *pTmp; + + if (nNum < *nMax) + return 0; + + nPrevMax = *nMax; + nNewMax = max_function(nNum + nIncr, nProposedMax); + if (MMCheckSize_t(nNewMax, sizeof(**pDouble))) + { + return 1; + } + if (nNewMax == 0 && *pDouble) + return 0; + if ((pTmp = realloc_function(*pDouble, (size_t)nNewMax * + sizeof(**pDouble))) == nullptr) + { + MMCPLError(CE_Failure, CPLE_OutOfMemory, + "Memory error in MiraMon " + "driver (MMResizeDoublePointer())"); + return 1; + } + *nMax = nNewMax; + *pDouble = pTmp; + + memset((*pDouble) + nPrevMax, 0, + (size_t)(*nMax - nPrevMax) * sizeof(**pDouble)); + return 0; +} + +int MMResizeStringToOperateIfNeeded(struct MiraMonVectLayerInfo *hMiraMonLayer, + MM_EXT_DBF_N_FIELDS nNewSize) +{ + if (!hMiraMonLayer) + return 1; + + if (nNewSize >= hMiraMonLayer->nNumStringToOperate) + { + char *p; + if (MMCheckSize_t(nNewSize, 1)) + return 1; + p = (char *)calloc_function((size_t)nNewSize); + if (!p) + { + MMCPLError(CE_Failure, CPLE_OutOfMemory, + "Memory error in MiraMon " + "driver (MMResizeStringToOperateIfNeeded())"); + return 1; + } + free_function(hMiraMonLayer->szStringToOperate); + hMiraMonLayer->szStringToOperate = p; + hMiraMonLayer->nNumStringToOperate = nNewSize; + } + return 0; +} + +// Checks if a string is empty +int MMIsEmptyString(const char *string) +{ + const char *ptr = string; + + for (; *ptr; ptr++) + if (*ptr != ' ' && *ptr != '\t') + return 0; + + return 1; +} + +/* -------------------------------------------------------------------- */ +/* Metadata Functions */ +/* -------------------------------------------------------------------- */ + +// Returns the value of an INI file. Used to read MiraMon metadata +char *MMReturnValueFromSectionINIFile(const char *filename, const char *section, + const char *key) +{ + char *value = nullptr; +#ifndef GDAL_COMPILATION + char line[1024]; +#endif + const char *pszLine; + char *section_header = nullptr; + size_t key_len = 0; + + FILE_TYPE *file = fopen_function(filename, "rb"); + if (file == nullptr) + { + MMCPLError(CE_Failure, CPLE_OpenFailed, "Cannot open INI file %s.", + filename); + return nullptr; + } + + if (key) + key_len = strlen(key); + +#ifndef GDAL_COMPILATION + while (fgets(line, (int)sizeof(line), file)) + { + pszLine = line; +#else + while ((pszLine = CPLReadLine2L(file, 1024, nullptr)) != nullptr) + { +#endif + char *pszString = + CPLRecode_function(pszLine, CPL_ENC_ISO8859_1, CPL_ENC_UTF8); + + // Skip comments and empty lines + if (*pszString == ';' || *pszString == '#' || *pszString == '\n' || + *pszString == '\r') + { + free_function(pszString); + // Move to next line + continue; + } + + // Check for section header + if (*pszString == '[') + { + char *section_end = strchr(pszString, ']'); + if (section_end != nullptr) + { + *section_end = '\0'; // Terminate the string at ']' + if (section_header) + free_function(section_header); + section_header = + strdup_function(pszString + 1); // Skip the '[' + } + free_function(pszString); + continue; + } + + if (key) + { + // If the current line belongs to the desired section + if (section_header != nullptr && + strcmp(section_header, section) == 0) + { + // Check if the line contains the desired key + if (strncmp(pszString, key, key_len) == 0 && + pszString[key_len] == '=') + { + // Extract the value + char *value_start = pszString + key_len + 1; + char *value_end = strstr(value_start, "\r\n"); + if (value_end != nullptr) + { + *value_end = + '\0'; // Terminate the string at newline character if found + } + else + { + value_end = strstr(value_start, "\n"); + if (value_end != nullptr) + { + *value_end = + '\0'; // Terminate the string at newline character if found + } + else + { + value_end = strstr(value_start, "\r"); + if (value_end != nullptr) + { + *value_end = + '\0'; // Terminate the string at newline character if found + } + } + } + + value = strdup_function(value_start); + fclose_function(file); + free_function(section_header); // Free allocated memory + free_function(pszString); + return value; + } + } + } + else + { + value = section_header; // Freed out + fclose_function(file); + free_function(pszString); + return value; + } + free_function(pszString); + } + + if (section_header) + free_function(section_header); // Free allocated memory + fclose_function(file); + return value; +} + +// Retrieves EPSG codes from a CSV file based on provided geodetic identifiers. +int MMReturnCodeFromMM_m_idofic(char *pMMSRS_or_pSRS, char *szResult, + MM_BYTE direction) +{ + char *aMMIDDBFFile = nullptr; //m_idofic.dbf + FILE_TYPE *pfMMSRS; + const char *pszLine; + size_t nLong; + char *id_geodes, *psidgeodes, *epsg; + + if (!pMMSRS_or_pSRS) + { + return 1; + } + +#ifdef GDAL_COMPILATION + aMMIDDBFFile = strdup_function(CPLFindFile("gdal", "MM_m_idofic.csv")); +#else + { + char temp_file[MM_CPL_PATH_BUF_SIZE]; + MuntaPath(DirectoriPrograma, strcpy(temp_file, "m_idofic.csv"), TRUE); + aMMIDDBFFile = strdup_function(temp_file); + } +#endif + + if (!aMMIDDBFFile) + { + MMCPLError(CE_Failure, CPLE_OpenFailed, + "Error opening data\\MM_m_idofic.csv.\n"); + return 1; + } + + // Opening the file with SRS information + if (nullptr == (pfMMSRS = fopen_function(aMMIDDBFFile, "r"))) + { + free_function(aMMIDDBFFile); + MMCPLError(CE_Failure, CPLE_OpenFailed, + "Error opening data\\MM_m_idofic.csv.\n"); + return 1; + } + free_function(aMMIDDBFFile); + + // Checking the header of the csv file + pszLine = CPLReadLine2L(pfMMSRS, 1024, nullptr); + if (!pszLine) + + { + fclose_function(pfMMSRS); + MMCPLError(CE_Failure, CPLE_NotSupported, + "Wrong format in data\\MM_m_idofic.csv.\n"); + return 1; + } + id_geodes = strstr(pszLine, "ID_GEODES"); + if (!id_geodes) + { + fclose_function(pfMMSRS); + MMCPLError(CE_Failure, CPLE_NotSupported, + "Wrong format in data\\MM_m_idofic.csv.\n"); + return 1; + } + id_geodes[strlen("ID_GEODES")] = '\0'; + psidgeodes = strstr(pszLine, "PSIDGEODES"); + if (!psidgeodes) + { + fclose_function(pfMMSRS); + MMCPLError(CE_Failure, CPLE_NotSupported, + "Wrong format in data\\MM_m_idofic.csv.\n"); + return 1; + } + psidgeodes[strlen("PSIDGEODES")] = '\0'; + + // Is PSIDGEODES in first place? + if (strncmp(pszLine, psidgeodes, strlen("PSIDGEODES"))) + { + fclose_function(pfMMSRS); + MMCPLError(CE_Failure, CPLE_NotSupported, + "Wrong format in data\\MM_m_idofic.csv.\n"); + return 1; + } + // Is ID_GEODES after PSIDGEODES? + if (strncmp(pszLine + strlen("PSIDGEODES") + 1, "ID_GEODES", + strlen("ID_GEODES"))) + { + fclose_function(pfMMSRS); + MMCPLError(CE_Failure, CPLE_NotSupported, + "Wrong format in data\\MM_m_idofic.csv.\n"); + return 1; + } + + // Looking for the information. + while ((pszLine = CPLReadLine2L(pfMMSRS, 1024, nullptr)) != nullptr) + { + id_geodes = strstr(pszLine, ";"); + if (!id_geodes) + { + fclose_function(pfMMSRS); + MMCPLError(CE_Failure, CPLE_NotSupported, + "Wrong format in data\\MM_m_idofic.csv.\n"); + return 1; + } + + psidgeodes = strstr(id_geodes + 1, ";"); + if (!psidgeodes) + { + fclose_function(pfMMSRS); + MMCPLError(CE_Failure, CPLE_NotSupported, + "Wrong format in data\\MM_m_idofic.csv.\n"); + return 1; + } + + id_geodes[(ptrdiff_t)psidgeodes - (ptrdiff_t)id_geodes] = '\0'; + psidgeodes = strdup_function(pszLine); + psidgeodes[(ptrdiff_t)id_geodes - (ptrdiff_t)pszLine] = '\0'; + id_geodes++; + + if (direction == EPSG_FROM_MMSRS) + { + // I have pMMSRS and I want pSRS + if (strcmp(pMMSRS_or_pSRS, id_geodes)) + { + free_function(psidgeodes); + continue; + } + + epsg = strstr(psidgeodes, "EPSG:"); + nLong = strlen("EPSG:"); + if (epsg && !strncmp(epsg, psidgeodes, nLong)) + { + if (epsg[nLong] != '\0') + { + strcpy(szResult, epsg + nLong); + free_function(psidgeodes); + fclose_function(pfMMSRS); + return 0; // found + } + else + { + fclose_function(pfMMSRS); + *szResult = '\0'; + free_function(psidgeodes); + return 1; // not found + } + } + } + else + { + // I have pSRS and I want pMMSRS + epsg = strstr(psidgeodes, "EPSG:"); + nLong = strlen("EPSG:"); + if (epsg && !strncmp(epsg, psidgeodes, nLong)) + { + if (epsg[nLong] != '\0') + { + if (!strcmp(pMMSRS_or_pSRS, epsg + nLong)) + { + strcpy(szResult, id_geodes); + fclose_function(pfMMSRS); + free_function(psidgeodes); + return 0; // found + } + } + } + } + free_function(psidgeodes); + } + + fclose_function(pfMMSRS); + return 1; // not found +} + +#define LineReturn "\r\n" + +// Generates an idientifier that REL 4 MiraMon metadata needs. +static void MMGenerateFileIdentifierFromMetadataFileName(char *pMMFN, + char *aFileIdentifier) +{ + char aCharRand[8]; + static const char aCharset[] = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + int i, len_charset; + + memset(aFileIdentifier, '\0', MM_MAX_LEN_LAYER_IDENTIFIER); + + aCharRand[0] = '_'; + len_charset = (int)strlen(aCharset); + for (i = 1; i < 7; i++) + { + // coverity[dont_call] + aCharRand[i] = aCharset[rand() % (len_charset - 1)]; + } + aCharRand[7] = '\0'; + CPLStrlcpy(aFileIdentifier, pMMFN, MM_MAX_LEN_LAYER_IDENTIFIER - 7); + strcat(aFileIdentifier, aCharRand); + return; +} + +// Converts a string from UTF-8 to ANSI to be written in a REL 4 file +static void +MMWrite_ANSI_MetadataKeyDescriptor(struct MiraMonVectorMetaData *hMMMD, + FILE_TYPE *pF, const char *pszEng, + const char *pszCat, const char *pszEsp) +{ + char *pszString = nullptr; + + switch (hMMMD->nMMLanguage) + { + case MM_CAT_LANGUAGE: + pszString = + CPLRecode_function(pszCat, CPL_ENC_UTF8, CPL_ENC_ISO8859_1); + break; + case MM_SPA_LANGUAGE: + pszString = + CPLRecode_function(pszEsp, CPL_ENC_UTF8, CPL_ENC_ISO8859_1); + break; + default: + case MM_ENG_LANGUAGE: + pszString = + CPLRecode_function(pszEng, CPL_ENC_UTF8, CPL_ENC_ISO8859_1); + break; + } + if (pszString) + { + fprintf_function(pF, "%s", KEY_descriptor); + fprintf_function(pF, "="); + fprintf_function(pF, "%s", pszString); + fprintf_function(pF, "%s", LineReturn); + CPLFree_function(pszString); + } +} + +/* + Writes a MiraMon REL 4 metadata file. Next sections are included: + VERSION, METADADES, IDENTIFICATION, EXTENT, OVERVIEW, + TAULA_PRINCIPAL and GEOMETRIA_I_TOPOLOGIA + + Please, consult the meaning of all them at: + https://www.miramon.cat/help/eng/GeMPlus/ClausREL.htm +*/ +static int MMWriteMetadataFile(struct MiraMonVectorMetaData *hMMMD) +{ + char aMessage[MM_MESSAGE_LENGTH], + aFileIdentifier[MM_MAX_LEN_LAYER_IDENTIFIER], aMMIDSRS[MM_MAX_ID_SNY]; + MM_EXT_DBF_N_FIELDS nIField; + FILE_TYPE *pF; + time_t currentTime; + char aTimeString[200]; + + if (!hMMMD->aLayerName) + return 0; + + if (nullptr == (pF = fopen_function(hMMMD->aLayerName, "wb"))) + { + MMCPLError(CE_Failure, CPLE_OpenFailed, "The file %s must exist.", + hMMMD->aLayerName); + return 1; + } + + // Writing MiraMon version section + fprintf_function(pF, "[%s]" LineReturn, SECTION_VERSIO); + + fprintf_function(pF, "%s=%u" LineReturn, KEY_Vers, (unsigned)MM_VERS); + fprintf_function(pF, "%s=%u" LineReturn, KEY_SubVers, (unsigned)MM_SUBVERS); + + fprintf_function(pF, "%s=%u" LineReturn, KEY_VersMetaDades, + (unsigned)MM_VERS_METADADES); + fprintf_function(pF, "%s=%u" LineReturn, KEY_SubVersMetaDades, + (unsigned)MM_SUBVERS_METADADES); + + // Writing METADADES section + fprintf_function(pF, "\r\n[%s]" LineReturn, SECTION_METADADES); + CPLStrlcpy(aMessage, hMMMD->aLayerName, sizeof(aMessage)); + MMGenerateFileIdentifierFromMetadataFileName(aMessage, aFileIdentifier); + fprintf_function(pF, "%s=%s" LineReturn, KEY_FileIdentifier, + aFileIdentifier); + fprintf_function(pF, "%s=%s" LineReturn, KEY_language, KEY_Value_eng); + fprintf_function(pF, "%s=%s" LineReturn, KEY_MDIdiom, KEY_Value_eng); + fprintf_function(pF, "%s=%s" LineReturn, KEY_characterSet, + KEY_Value_characterSet); + + // Writing IDENTIFICATION section + fprintf_function(pF, LineReturn "[%s]" LineReturn, SECTION_IDENTIFICATION); + fprintf_function(pF, "%s=%s" LineReturn, KEY_code, aFileIdentifier); + fprintf_function(pF, "%s=" LineReturn, KEY_codeSpace); + if (hMMMD->szLayerTitle && !MMIsEmptyString(hMMMD->szLayerTitle)) + { + if (hMMMD->ePlainLT == MM_LayerType_Point) + fprintf_function(pF, "%s=%s (pnt)" LineReturn, KEY_DatasetTitle, + hMMMD->szLayerTitle); + if (hMMMD->ePlainLT == MM_LayerType_Arc) + fprintf_function(pF, "%s=%s (arc)" LineReturn, KEY_DatasetTitle, + hMMMD->szLayerTitle); + if (hMMMD->ePlainLT == MM_LayerType_Pol) + fprintf_function(pF, "%s=%s (pol)" LineReturn, KEY_DatasetTitle, + hMMMD->szLayerTitle); + } + fprintf_function(pF, "%s=%s" LineReturn, KEY_language, KEY_Value_eng); + + if (hMMMD->ePlainLT != MM_LayerType_Node) + { + if (hMMMD->pSRS && hMMMD->ePlainLT != MM_LayerType_Pol) + { + fprintf_function(pF, LineReturn "[%s:%s]" LineReturn, + SECTION_SPATIAL_REFERENCE_SYSTEM, + SECTION_HORIZONTAL); + if (!ReturnMMIDSRSFromEPSGCodeSRS(hMMMD->pSRS, aMMIDSRS) && + !MMIsEmptyString(aMMIDSRS)) + fprintf_function(pF, "%s=%s" LineReturn, + KEY_HorizontalSystemIdentifier, aMMIDSRS); + else + { + MMCPLWarning(CE_Warning, CPLE_NotSupported, + "The MiraMon driver cannot assign any HRS."); + // Horizontal Reference System + fprintf_function(pF, "%s=plane" LineReturn, + KEY_HorizontalSystemIdentifier); + fprintf_function(pF, "%s=local" LineReturn, + KEY_HorizontalSystemDefinition); + if (hMMMD->pXUnit) + fprintf_function(pF, "%s=%s" LineReturn, KEY_unitats, + hMMMD->pXUnit); + if (hMMMD->pYUnit) + { + if (!hMMMD->pXUnit || + strcasecmp(hMMMD->pXUnit, hMMMD->pYUnit)) + fprintf_function(pF, "%s=%s" LineReturn, KEY_unitatsY, + hMMMD->pYUnit); + } + } + } + else + { + fprintf_function(pF, "%s=plane" LineReturn, + KEY_HorizontalSystemIdentifier); + fprintf_function(pF, "%s=local" LineReturn, + KEY_HorizontalSystemDefinition); + if (hMMMD->pXUnit) + { + fprintf_function(pF, "%s=%s" LineReturn, KEY_unitats, + hMMMD->pXUnit); + if (hMMMD->pYUnit) + { + if (!hMMMD->pXUnit || + strcasecmp(hMMMD->pXUnit, hMMMD->pYUnit)) + fprintf_function(pF, "%s=%s" LineReturn, KEY_unitatsY, + hMMMD->pYUnit); + } + } + } + } + + // Writing OVERVIEW:ASPECTES_TECNICS in polygon metadata file. + // ArcSource=fitx_pol.arc + if (hMMMD->ePlainLT == MM_LayerType_Pol) + { + fprintf_function(pF, LineReturn "[%s]" LineReturn, + SECTION_OVVW_ASPECTES_TECNICS); + fprintf_function(pF, "%s=\"%s\"" LineReturn, KEY_ArcSource, + hMMMD->aArcFile); + } + + // Writing EXTENT section + fprintf_function(pF, LineReturn "[%s]" LineReturn, SECTION_EXTENT); + fprintf_function(pF, "%s=0" LineReturn, KEY_toler_env); + + if (hMMMD->hBB.dfMinX != MM_UNDEFINED_STATISTICAL_VALUE && + hMMMD->hBB.dfMaxX != -MM_UNDEFINED_STATISTICAL_VALUE && + hMMMD->hBB.dfMinY != MM_UNDEFINED_STATISTICAL_VALUE && + hMMMD->hBB.dfMaxY != -MM_UNDEFINED_STATISTICAL_VALUE) + { + fprintf_function(pF, "%s=%lf" LineReturn, KEY_MinX, hMMMD->hBB.dfMinX); + fprintf_function(pF, "%s=%lf" LineReturn, KEY_MaxX, hMMMD->hBB.dfMaxX); + fprintf_function(pF, "%s=%lf" LineReturn, KEY_MinY, hMMMD->hBB.dfMinY); + fprintf_function(pF, "%s=%lf" LineReturn, KEY_MaxY, hMMMD->hBB.dfMaxY); + } + + // Writing OVERVIEW section + fprintf_function(pF, LineReturn "[%s]" LineReturn, SECTION_OVERVIEW); + + currentTime = time(nullptr); + +#ifdef GDAL_COMPILATION + { + struct tm ltime; + VSILocalTime(¤tTime, <ime); + snprintf(aTimeString, sizeof(aTimeString), + "%04d%02d%02d %02d%02d%02d%02d+00:00", ltime.tm_year + 1900, + ltime.tm_mon + 1, ltime.tm_mday, ltime.tm_hour, ltime.tm_min, + ltime.tm_sec, 0); + fprintf_function(pF, "%s=%s" LineReturn, KEY_CreationDate, aTimeString); + fprintf_function(pF, LineReturn); + } +#else + { + struct tm *pLocalTime; + pLocalTime = localtime(¤tTime); + snprintf(aTimeString, sizeof(aTimeString), + "%04d%02d%02d %02d%02d%02d%02d+00:00", + pLocalTime->tm_year + 1900, pLocalTime->tm_mon + 1, + pLocalTime->tm_mday, pLocalTime->tm_hour, pLocalTime->tm_min, + pLocalTime->tm_sec, 0); + fprintf_function(pF, "%s=%s" LineReturn, KEY_CreationDate, aTimeString); + fprintf_function(pF, LineReturn); + } +#endif + + // Writing TAULA_PRINCIPAL section + fprintf_function(pF, "[%s]" LineReturn, SECTION_TAULA_PRINCIPAL); + fprintf_function(pF, "IdGrafic=%s" LineReturn, szMMNomCampIdGraficDefecte); + fprintf_function(pF, "TipusRelacio=RELACIO_1_1_DICC" LineReturn); + + fprintf_function(pF, LineReturn); + fprintf_function(pF, "[%s:%s]" LineReturn, SECTION_TAULA_PRINCIPAL, + szMMNomCampIdGraficDefecte); + fprintf_function(pF, "visible=1" LineReturn); + fprintf_function(pF, "MostrarUnitats=0" LineReturn); + + MMWrite_ANSI_MetadataKeyDescriptor( + hMMMD, pF, szInternalGraphicIdentifierEng, + szInternalGraphicIdentifierCat, szInternalGraphicIdentifierSpa); + + if (hMMMD->ePlainLT == MM_LayerType_Arc) + { + fprintf_function(pF, LineReturn); + fprintf_function(pF, "[%s:%s]" LineReturn, SECTION_TAULA_PRINCIPAL, + szMMNomCampNVertexsDefecte); + fprintf_function(pF, "visible=0" LineReturn); + fprintf_function(pF, "simbolitzable=0" LineReturn); + fprintf_function(pF, "MostrarUnitats=0" LineReturn); + MMWrite_ANSI_MetadataKeyDescriptor(hMMMD, pF, szNumberOfVerticesEng, + szNumberOfVerticesCat, + szNumberOfVerticesSpa); + + fprintf_function(pF, LineReturn); + fprintf_function(pF, "[%s:%s]" LineReturn, SECTION_TAULA_PRINCIPAL, + szMMNomCampLongitudArcDefecte); + fprintf_function(pF, "simbolitzable=0" LineReturn); + fprintf_function(pF, "MostrarUnitats=0" LineReturn); + MMWrite_ANSI_MetadataKeyDescriptor( + hMMMD, pF, szLengthOfAarcEng, szLengthOfAarcCat, szLengthOfAarcSpa); + + fprintf_function(pF, LineReturn); + fprintf_function(pF, "[%s:%s]" LineReturn, SECTION_TAULA_PRINCIPAL, + szMMNomCampNodeIniDefecte); + fprintf_function(pF, "visible=0" LineReturn); + fprintf_function(pF, "simbolitzable=0" LineReturn); + fprintf_function(pF, "MostrarUnitats=0" LineReturn); + MMWrite_ANSI_MetadataKeyDescriptor(hMMMD, pF, szInitialNodeEng, + szInitialNodeCat, szInitialNodeSpa); + + fprintf_function(pF, LineReturn); + fprintf_function(pF, "[%s:%s]" LineReturn, SECTION_TAULA_PRINCIPAL, + szMMNomCampNodeFiDefecte); + fprintf_function(pF, "visible=0" LineReturn); + fprintf_function(pF, "simbolitzable=0" LineReturn); + fprintf_function(pF, "MostrarUnitats=0" LineReturn); + MMWrite_ANSI_MetadataKeyDescriptor(hMMMD, pF, szFinalNodeEng, + szFinalNodeCat, szFinalNodeSpa); + + fprintf_function(pF, LineReturn); + fprintf_function(pF, "[GEOMETRIA_I_TOPOLOGIA]" LineReturn); + fprintf_function(pF, "NomCampNVertexs=%s" LineReturn, + szMMNomCampNVertexsDefecte); + fprintf_function(pF, "NomCampLongitudArc=%s" LineReturn, + szMMNomCampLongitudArcDefecte); + fprintf_function(pF, "NomCampNodeIni=%s" LineReturn, + szMMNomCampNodeIniDefecte); + fprintf_function(pF, "NomCampNodeFi=%s" LineReturn, + szMMNomCampNodeFiDefecte); + } + else if (hMMMD->ePlainLT == MM_LayerType_Node) + { + fprintf_function(pF, LineReturn); + fprintf_function(pF, "[%s:%s]" LineReturn, SECTION_TAULA_PRINCIPAL, + szMMNomCampArcsANodeDefecte); + fprintf_function(pF, "visible=0" LineReturn); + fprintf_function(pF, "simbolitzable=0" LineReturn); + fprintf_function(pF, "MostrarUnitats=0" LineReturn); + MMWrite_ANSI_MetadataKeyDescriptor(hMMMD, pF, szNumberOfArcsToNodeEng, + szNumberOfArcsToNodeCat, + szNumberOfArcsToNodeSpa); + + fprintf_function(pF, LineReturn); + fprintf_function(pF, "[%s:%s]" LineReturn, SECTION_TAULA_PRINCIPAL, + szMMNomCampTipusNodeDefecte); + fprintf_function(pF, "visible=0" LineReturn); + fprintf_function(pF, "simbolitzable=0" LineReturn); + fprintf_function(pF, "MostrarUnitats=0" LineReturn); + MMWrite_ANSI_MetadataKeyDescriptor(hMMMD, pF, szNodeTypeEng, + szNodeTypeCat, szNodeTypeSpa); + } + else if (hMMMD->ePlainLT == MM_LayerType_Pol) + { + fprintf_function(pF, LineReturn); + fprintf_function(pF, "[%s:%s]" LineReturn, SECTION_TAULA_PRINCIPAL, + szMMNomCampNVertexsDefecte); + fprintf_function(pF, "visible=0" LineReturn); + fprintf_function(pF, "simbolitzable=0" LineReturn); + fprintf_function(pF, "MostrarUnitats=0" LineReturn); + MMWrite_ANSI_MetadataKeyDescriptor(hMMMD, pF, szNumberOfVerticesEng, + szNumberOfVerticesCat, + szNumberOfVerticesSpa); + + fprintf_function(pF, LineReturn); + fprintf_function(pF, "[%s:%s]" LineReturn, SECTION_TAULA_PRINCIPAL, + szMMNomCampPerimetreDefecte); + fprintf_function(pF, "simbolitzable=0" LineReturn); + fprintf_function(pF, "MostrarUnitats=0" LineReturn); + MMWrite_ANSI_MetadataKeyDescriptor( + hMMMD, pF, szPerimeterOfThePolygonEng, szPerimeterOfThePolygonCat, + szPerimeterOfThePolygonSpa); + + fprintf_function(pF, LineReturn); + fprintf_function(pF, "[%s:%s]" LineReturn, SECTION_TAULA_PRINCIPAL, + szMMNomCampAreaDefecte); + fprintf_function(pF, "simbolitzable=0" LineReturn); + fprintf_function(pF, "MostrarUnitats=0" LineReturn); + MMWrite_ANSI_MetadataKeyDescriptor(hMMMD, pF, szAreaOfThePolygonEng, + szAreaOfThePolygonCat, + szAreaOfThePolygonSpa); + + fprintf_function(pF, LineReturn); + fprintf_function(pF, "[%s:%s]" LineReturn, SECTION_TAULA_PRINCIPAL, + szMMNomCampNArcsDefecte); + fprintf_function(pF, "visible=0" LineReturn); + fprintf_function(pF, "simbolitzable=0" LineReturn); + fprintf_function(pF, "MostrarUnitats=0" LineReturn); + MMWrite_ANSI_MetadataKeyDescriptor( + hMMMD, pF, szNumberOfArcsEng, szNumberOfArcsCat, szNumberOfArcsSpa); + + fprintf_function(pF, LineReturn); + fprintf_function(pF, "[%s:%s]" LineReturn, SECTION_TAULA_PRINCIPAL, + szMMNomCampNPoligonsDefecte); + fprintf_function(pF, "visible=0" LineReturn); + fprintf_function(pF, "simbolitzable=0" LineReturn); + fprintf_function(pF, "MostrarUnitats=0" LineReturn); + MMWrite_ANSI_MetadataKeyDescriptor( + hMMMD, pF, szNumberOfElementaryPolygonsEng, + szNumberOfElementaryPolygonsCat, szNumberOfElementaryPolygonsSpa); + + fprintf_function(pF, LineReturn); + fprintf_function(pF, "[GEOMETRIA_I_TOPOLOGIA]" LineReturn); + fprintf_function(pF, "NomCampNVertexs=%s" LineReturn, + szMMNomCampNVertexsDefecte); + fprintf_function(pF, "NomCampPerimetre=%s" LineReturn, + szMMNomCampPerimetreDefecte); + fprintf_function(pF, "NomCampArea=%s" LineReturn, + szMMNomCampAreaDefecte); + fprintf_function(pF, "NomCampNArcs=%s" LineReturn, + szMMNomCampNArcsDefecte); + fprintf_function(pF, "NomCampNPoligons=%s" LineReturn, + szMMNomCampNPoligonsDefecte); + } + + if (hMMMD->pLayerDB && hMMMD->pLayerDB->nNFields > 0) + { + // For each field of the databes + for (nIField = 0; nIField < hMMMD->pLayerDB->nNFields; nIField++) + { + if (!MMIsEmptyString( + hMMMD->pLayerDB->pFields[nIField].pszFieldDescription) && + !MMIsEmptyString( + hMMMD->pLayerDB->pFields[nIField].pszFieldName)) + { + fprintf_function( + pF, LineReturn "[%s:%s]" LineReturn, + SECTION_TAULA_PRINCIPAL, + hMMMD->pLayerDB->pFields[nIField].pszFieldName); + + MMWrite_ANSI_MetadataKeyDescriptor( + hMMMD, pF, + hMMMD->pLayerDB->pFields[nIField].pszFieldDescription, + hMMMD->pLayerDB->pFields[nIField].pszFieldDescription, + hMMMD->pLayerDB->pFields[nIField].pszFieldDescription); + } + } + } + fclose_function(pF); + return 0; +} + +// Writes metadata files for MiraMon vector layers +static int MMWriteVectorMetadataFile(struct MiraMonVectLayerInfo *hMiraMonLayer, + int layerPlainType, int layerMainPlainType) +{ + struct MiraMonVectorMetaData hMMMD; + + if (!hMiraMonLayer) + return 1; + + // MiraMon writes a REL file of each .pnt, .arc, .nod or .pol + memset(&hMMMD, 0, sizeof(hMMMD)); + hMMMD.ePlainLT = layerPlainType; + hMMMD.pSRS = hMiraMonLayer->pSRS; + hMMMD.nMMLanguage = hMiraMonLayer->nMMLanguage; + + hMMMD.szLayerTitle = hMiraMonLayer->szLayerTitle; + if (layerPlainType == MM_LayerType_Point) + { + hMMMD.aLayerName = hMiraMonLayer->MMPoint.pszREL_LayerName; + if (MMIsEmptyString(hMMMD.aLayerName)) + return 0; // If no file, no error. Just continue. + memcpy(&hMMMD.hBB, &hMiraMonLayer->TopHeader.hBB, sizeof(hMMMD.hBB)); + hMMMD.pLayerDB = hMiraMonLayer->pLayerDB; + return MMWriteMetadataFile(&hMMMD); + } + else if (layerPlainType == MM_LayerType_Arc) + { + // Arcs and not polygons + if (layerMainPlainType == MM_LayerType_Arc) + { + hMMMD.aLayerName = hMiraMonLayer->MMArc.pszREL_LayerName; + if (MMIsEmptyString(hMMMD.aLayerName)) + return 0; // If no file, no error. Just continue. + memcpy(&hMMMD.hBB, &hMiraMonLayer->TopHeader.hBB, + sizeof(hMMMD.hBB)); + hMMMD.pLayerDB = hMiraMonLayer->pLayerDB; + } + // Arcs and polygons + else + { + // Arc from polygon + hMMMD.aLayerName = hMiraMonLayer->MMPolygon.MMArc.pszREL_LayerName; + if (MMIsEmptyString(hMMMD.aLayerName)) + return 0; // If no file, no error. Just continue. + + memcpy(&hMMMD.hBB, &hMiraMonLayer->MMPolygon.TopArcHeader.hBB, + sizeof(hMMMD.hBB)); + hMMMD.pLayerDB = nullptr; + } + return MMWriteMetadataFile(&hMMMD); + } + else if (layerPlainType == MM_LayerType_Pol) + { + int nResult; + + hMMMD.aLayerName = hMiraMonLayer->MMPolygon.pszREL_LayerName; + + if (MMIsEmptyString(hMMMD.aLayerName)) + return 0; // If no file, no error. Just continue. + + memcpy(&hMMMD.hBB, &hMiraMonLayer->TopHeader.hBB, sizeof(hMMMD.hBB)); + hMMMD.pLayerDB = hMiraMonLayer->pLayerDB; + hMMMD.aArcFile = strdup_function( + get_filename_function(hMiraMonLayer->MMPolygon.MMArc.pszLayerName)); + nResult = MMWriteMetadataFile(&hMMMD); + free_function(hMMMD.aArcFile); + return nResult; + } + else if (layerPlainType == MM_LayerType_Node) + { + // Node from arc + if (layerMainPlainType == MM_LayerType_Arc) + { + hMMMD.aLayerName = hMiraMonLayer->MMArc.MMNode.pszREL_LayerName; + if (MMIsEmptyString(hMMMD.aLayerName)) + return 0; // If no file, no error. Just continue. + memcpy(&hMMMD.hBB, &hMiraMonLayer->MMArc.TopNodeHeader.hBB, + sizeof(hMMMD.hBB)); + } + else // Node from polygon + { + hMMMD.aLayerName = + hMiraMonLayer->MMPolygon.MMArc.MMNode.pszREL_LayerName; + if (MMIsEmptyString(hMMMD.aLayerName)) + return 0; // If no file, no error. Just continue. + memcpy(&hMMMD.hBB, + &hMiraMonLayer->MMPolygon.MMArc.TopNodeHeader.hBB, + sizeof(hMMMD.hBB)); + } + hMMMD.pLayerDB = nullptr; + return MMWriteMetadataFile(&hMMMD); + } + return 0; +} + +int MMWriteVectorMetadata(struct MiraMonVectLayerInfo *hMiraMonLayer) +{ + if (!hMiraMonLayer) + return 1; + + if (hMiraMonLayer->bIsPoint) + return MMWriteVectorMetadataFile(hMiraMonLayer, MM_LayerType_Point, + MM_LayerType_Point); + if (hMiraMonLayer->bIsArc && !hMiraMonLayer->bIsPolygon) + { + if (MMWriteVectorMetadataFile(hMiraMonLayer, MM_LayerType_Node, + MM_LayerType_Arc)) + return 1; + return MMWriteVectorMetadataFile(hMiraMonLayer, MM_LayerType_Arc, + MM_LayerType_Arc); + } + if (hMiraMonLayer->bIsPolygon) + { + if (MMWriteVectorMetadataFile(hMiraMonLayer, MM_LayerType_Node, + MM_LayerType_Pol)) + return 1; + if (MMWriteVectorMetadataFile(hMiraMonLayer, MM_LayerType_Arc, + MM_LayerType_Pol)) + return 1; + return MMWriteVectorMetadataFile(hMiraMonLayer, MM_LayerType_Pol, + MM_LayerType_Pol); + } + if (hMiraMonLayer->bIsDBF) + { + return MMWriteVectorMetadataFile(hMiraMonLayer, MM_LayerType_Unknown, + MM_LayerType_Unknown); + } + return 0; +} + +// Verifies the version of a MiraMon REL 4 file. +int MMCheck_REL_FILE(const char *szREL_file) +{ + char *pszLine; + FILE_TYPE *pF; + + // Does the REL file exist? + pF = fopen_function(szREL_file, "r"); + if (!pF) + { + MMCPLError(CE_Failure, CPLE_OpenFailed, "The file %s must exist.", + szREL_file); + return 1; + } + fclose_function(pF); + + // Does the REL file have VERSION? + pszLine = + MMReturnValueFromSectionINIFile(szREL_file, SECTION_VERSIO, nullptr); + if (!pszLine) + { + MMCPLError(CE_Failure, CPLE_OpenFailed, + "The file \"%s\" must be REL4. " + "You can use ConvREL.exe from MiraMon software " + "to convert this file to REL4.", + szREL_file); + return 1; + } + free_function(pszLine); + + // Does the REL file have the correct VERSION? + // Vers>=4? + pszLine = + MMReturnValueFromSectionINIFile(szREL_file, SECTION_VERSIO, KEY_Vers); + if (pszLine) + { + if (*pszLine == '\0' || atoi(pszLine) < (int)MM_VERS) + { + MMCPLError(CE_Failure, CPLE_OpenFailed, + "The file \"%s\" must have %s>=%d.", szREL_file, + KEY_Vers, MM_VERS); + free_function(pszLine); + return 1; + } + free_function(pszLine); + } + else + { + MMCPLError(CE_Failure, CPLE_OpenFailed, + "The file \"%s\" must have %s>=%d.", szREL_file, KEY_Vers, + MM_VERS); + return 1; + } + + // SubVers>=3? + pszLine = MMReturnValueFromSectionINIFile(szREL_file, SECTION_VERSIO, + KEY_SubVers); + if (pszLine) + { + if (*pszLine == '\0' || atoi(pszLine) < (int)MM_SUBVERS) + { + MMCPLError(CE_Failure, CPLE_OpenFailed, + "The file \"%s\" must have %s>=%d.", szREL_file, + KEY_SubVers, MM_SUBVERS); + + free_function(pszLine); + return 1; + } + free_function(pszLine); + } + else + { + MMCPLError(CE_Failure, CPLE_OpenFailed, + "The file \"%s\" must have %s>=%d.", szREL_file, KEY_SubVers, + MM_SUBVERS); + return 1; + } + + // VersMetaDades>=5? + pszLine = MMReturnValueFromSectionINIFile(szREL_file, SECTION_VERSIO, + KEY_VersMetaDades); + if (pszLine) + { + if (*pszLine == '\0' || atoi(pszLine) < (int)MM_VERS_METADADES) + { + MMCPLError(CE_Failure, CPLE_OpenFailed, + "The file \"%s\" must have %s>=%d.", szREL_file, + KEY_VersMetaDades, MM_VERS_METADADES); + free_function(pszLine); + return 1; + } + free_function(pszLine); + } + else + { + MMCPLError(CE_Failure, CPLE_OpenFailed, + "The file \"%s\" must have %s>=%d.", szREL_file, + KEY_VersMetaDades, MM_VERS_METADADES); + return 1; + } + + // SubVersMetaDades>=0? + pszLine = MMReturnValueFromSectionINIFile(szREL_file, SECTION_VERSIO, + KEY_SubVersMetaDades); + if (pszLine) + { + if (*pszLine == '\0' || atoi(pszLine) < (int)MM_SUBVERS_METADADES) + { + MMCPLError(CE_Failure, CPLE_OpenFailed, + "The file \"%s\" must have %s>=%d.", szREL_file, + KEY_SubVersMetaDades, MM_SUBVERS_METADADES); + free_function(pszLine); + return 1; + } + free_function(pszLine); + } + else + { + MMCPLError(CE_Failure, CPLE_OpenFailed, + "The file \"%s\" must have %s>=%d.", szREL_file, + KEY_SubVersMetaDades, MM_SUBVERS_METADADES); + return 1; + } + return 0; +} + +/* -------------------------------------------------------------------- */ +/* MiraMon database functions */ +/* -------------------------------------------------------------------- */ + +// Initializes a MiraMon database associated with a vector layer: +// Sets the usual fields that MiraMon needs and after them, adds +// all fields of the input layer +static int MMInitMMDB(struct MiraMonVectLayerInfo *hMiraMonLayer, + struct MMAdmDatabase *pMMAdmDB) +{ + if (!hMiraMonLayer) + return 1; + + if (!pMMAdmDB) + return 1; + + if (MMIsEmptyString(pMMAdmDB->pszExtDBFLayerName)) + return 0; // No file, no error. Just continue + + strcpy(pMMAdmDB->pMMBDXP->ReadingMode, "wb+"); + if (FALSE == + MM_CreateDBFFile(pMMAdmDB->pMMBDXP, pMMAdmDB->pszExtDBFLayerName)) + return 1; + + // Opening the file + if (nullptr == (pMMAdmDB->pFExtDBF = + fopen_function(pMMAdmDB->pszExtDBFLayerName, + "r+b"))) //hMiraMonLayer->pszFlags))) + { + MMCPLError(CE_Failure, CPLE_OpenFailed, + "Error pMMAdmDB: Cannot open file %s.", + pMMAdmDB->pszExtDBFLayerName); + return 1; + } + fseek_function(pMMAdmDB->pFExtDBF, pMMAdmDB->pMMBDXP->FirstRecordOffset, + SEEK_SET); + + if (MMInitFlush(&pMMAdmDB->FlushRecList, pMMAdmDB->pFExtDBF, MM_1MB, + &pMMAdmDB->pRecList, pMMAdmDB->pMMBDXP->FirstRecordOffset, + 0)) + return 1; + + pMMAdmDB->nNumRecordOnCourse = + (GUInt64)pMMAdmDB->pMMBDXP->BytesPerRecord + 1; + if (MMCheckSize_t(pMMAdmDB->nNumRecordOnCourse, 1)) + return 1; + pMMAdmDB->szRecordOnCourse = + calloc_function((size_t)pMMAdmDB->nNumRecordOnCourse); + if (!pMMAdmDB->szRecordOnCourse) + { + MMCPLError(CE_Failure, CPLE_OutOfMemory, + "Memory error in MiraMon " + "driver (MMInitMMDB())"); + return 1; + } + return 0; +} + +// Creates a MiraMon database associated with a vector layer. +// It determines the number of fields and initializes the database header +// accordingly. Depending on the layer type (point, arc, polygon, or generic), +// it defines the fields and initializes the corresponding MiraMon database +// structures. +int MMCreateMMDB(struct MiraMonVectLayerInfo *hMiraMonLayer) +{ + struct MM_DATA_BASE_XP *pBD_XP = nullptr, *pBD_XP_Aux = nullptr; + struct MM_FIELD MMField; + size_t nIFieldLayer; + MM_EXT_DBF_N_FIELDS nIField = 0; + MM_EXT_DBF_N_FIELDS nNFields; + + if (!hMiraMonLayer) + return 1; + + if (hMiraMonLayer->bIsPoint) + { + if (hMiraMonLayer->pLayerDB) + nNFields = + MM_PRIVATE_POINT_DB_FIELDS + hMiraMonLayer->pLayerDB->nNFields; + else + nNFields = MM_PRIVATE_POINT_DB_FIELDS; + pBD_XP = hMiraMonLayer->MMPoint.MMAdmDB.pMMBDXP = + MM_CreateDBFHeader(nNFields, hMiraMonLayer->nCharSet); + + if (!pBD_XP) + return 1; + + if (0 == (nIField = (MM_EXT_DBF_N_FIELDS)MM_DefineFirstPointFieldsDB_XP( + pBD_XP))) + return 1; + } + else if (hMiraMonLayer->bIsArc && !hMiraMonLayer->bIsPolygon) + { + if (hMiraMonLayer->pLayerDB) + nNFields = + MM_PRIVATE_ARC_DB_FIELDS + hMiraMonLayer->pLayerDB->nNFields; + else + nNFields = MM_PRIVATE_ARC_DB_FIELDS; + + pBD_XP = hMiraMonLayer->MMArc.MMAdmDB.pMMBDXP = + MM_CreateDBFHeader(nNFields, hMiraMonLayer->nCharSet); + + if (!pBD_XP) + return 1; + + if (0 == (nIField = (MM_EXT_DBF_N_FIELDS)MM_DefineFirstArcFieldsDB_XP( + pBD_XP))) + return 1; + + pBD_XP_Aux = hMiraMonLayer->MMArc.MMNode.MMAdmDB.pMMBDXP = + MM_CreateDBFHeader(3, hMiraMonLayer->nCharSet); + + if (!pBD_XP_Aux) + return 1; + + if (0 == MM_DefineFirstNodeFieldsDB_XP(pBD_XP_Aux)) + return 1; + } + else if (hMiraMonLayer->bIsPolygon) + { + if (hMiraMonLayer->pLayerDB) + nNFields = MM_PRIVATE_POLYGON_DB_FIELDS + + hMiraMonLayer->pLayerDB->nNFields; + else + nNFields = MM_PRIVATE_POLYGON_DB_FIELDS; + + pBD_XP = hMiraMonLayer->MMPolygon.MMAdmDB.pMMBDXP = + MM_CreateDBFHeader(nNFields, hMiraMonLayer->nCharSet); + + if (!pBD_XP) + return 1; + + if (0 == + (nIField = + (MM_EXT_DBF_N_FIELDS)MM_DefineFirstPolygonFieldsDB_XP(pBD_XP))) + return 1; + + pBD_XP_Aux = hMiraMonLayer->MMPolygon.MMArc.MMAdmDB.pMMBDXP = + MM_CreateDBFHeader(5, hMiraMonLayer->nCharSet); + + if (!pBD_XP_Aux) + return 1; + + if (0 == MM_DefineFirstArcFieldsDB_XP(pBD_XP_Aux)) + return 1; + + pBD_XP_Aux = hMiraMonLayer->MMPolygon.MMArc.MMNode.MMAdmDB.pMMBDXP = + MM_CreateDBFHeader(3, hMiraMonLayer->nCharSet); + + if (!pBD_XP_Aux) + return 1; + + if (0 == MM_DefineFirstNodeFieldsDB_XP(pBD_XP_Aux)) + return 1; + } + else if (hMiraMonLayer->bIsDBF) + { + // Creating only a DBF + if (hMiraMonLayer->pLayerDB) + nNFields = hMiraMonLayer->pLayerDB->nNFields; + else + nNFields = 0; + + pBD_XP = hMiraMonLayer->MMAdmDBWriting.pMMBDXP = + MM_CreateDBFHeader(nNFields, hMiraMonLayer->nCharSet); + + if (!pBD_XP) + return 1; + } + else + return 0; + + // After private MiraMon fields, other fields are added. + // If names are no compatible, some changes are done. + if (hMiraMonLayer->pLayerDB) + { + for (nIFieldLayer = 0; nIField < nNFields; nIField++, nIFieldLayer++) + { + MM_InitializeField(&MMField); + CPLStrlcpy( + MMField.FieldName, + hMiraMonLayer->pLayerDB->pFields[nIFieldLayer].pszFieldName, + MM_MAX_LON_FIELD_NAME_DBF); + + CPLStrlcpy(MMField.FieldDescription[0], + hMiraMonLayer->pLayerDB->pFields[nIFieldLayer] + .pszFieldDescription, + MM_MAX_BYTES_FIELD_DESC); + + MMField.BytesPerField = + hMiraMonLayer->pLayerDB->pFields[nIFieldLayer].nFieldSize; + switch (hMiraMonLayer->pLayerDB->pFields[nIFieldLayer].eFieldType) + { + case MM_Numeric: + MMField.FieldType = 'N'; + if (hMiraMonLayer->pLayerDB->pFields[nIFieldLayer] + .bIs64BitInteger) + MMField.Is64 = 1; + if (MMField.BytesPerField == 0) + MMField.BytesPerField = MM_MAX_AMPLADA_CAMP_N_DBF; + break; + case MM_Character: + MMField.FieldType = 'C'; + if (MMField.BytesPerField == 0) + MMField.BytesPerField = MM_MAX_AMPLADA_CAMP_C_DBF; + break; + case MM_Data: + MMField.FieldType = 'D'; + if (MMField.BytesPerField == 0) + MMField.BytesPerField = MM_MAX_AMPLADA_CAMP_D_DBF; + break; + case MM_Logic: + MMField.FieldType = 'L'; + if (MMField.BytesPerField == 0) + MMField.BytesPerField = 1; + break; + default: + MMField.FieldType = 'C'; + if (MMField.BytesPerField == 0) + MMField.BytesPerField = MM_MAX_AMPLADA_CAMP_C_DBF; + }; + + MMField.DecimalsIfFloat = + (MM_BYTE)hMiraMonLayer->pLayerDB->pFields[nIFieldLayer] + .nNumberOfDecimals; + + MM_DuplicateFieldDBXP(pBD_XP->pField + nIField, &MMField); + MM_ModifyFieldNameAndDescriptorIfPresentBD_XP( + pBD_XP->pField + nIField, pBD_XP, FALSE, 0); + if (pBD_XP->pField[nIField].FieldType == 'F') + pBD_XP->pField[nIField].FieldType = 'N'; + } + } + + if (hMiraMonLayer->bIsPoint) + { + if (MMInitMMDB(hMiraMonLayer, &hMiraMonLayer->MMPoint.MMAdmDB)) + return 1; + } + else if (hMiraMonLayer->bIsArc && !hMiraMonLayer->bIsPolygon) + { + if (MMInitMMDB(hMiraMonLayer, &hMiraMonLayer->MMArc.MMAdmDB)) + return 1; + + if (MMInitMMDB(hMiraMonLayer, &hMiraMonLayer->MMArc.MMNode.MMAdmDB)) + return 1; + } + else if (hMiraMonLayer->bIsPolygon) + { + if (MMInitMMDB(hMiraMonLayer, &hMiraMonLayer->MMPolygon.MMAdmDB)) + return 1; + + if (MMInitMMDB(hMiraMonLayer, &hMiraMonLayer->MMPolygon.MMArc.MMAdmDB)) + return 1; + + if (MMInitMMDB(hMiraMonLayer, + &hMiraMonLayer->MMPolygon.MMArc.MMNode.MMAdmDB)) + return 1; + } + else if (hMiraMonLayer->bIsDBF) + { + if (MMInitMMDB(hMiraMonLayer, &hMiraMonLayer->MMAdmDBWriting)) + return 1; + } + return 0; +} + +// Checks and fits the width of a specific field in a MiraMon database +// associated with a vector layer. It examines the length of the provided +// value and resizes the field width, if necessary, to accommodate the new +// value. If the new width exceeds the current width of the field, +// it updates the database structure, including the field width and +// the size of the record. Additionally, it reallocates memory if needed +// for the record handling buffer. + +static int +MMTestAndFixValueToRecordDBXP(struct MiraMonVectLayerInfo *hMiraMonLayer, + struct MMAdmDatabase *pMMAdmDB, + MM_EXT_DBF_N_FIELDS nIField, char *szValue) +{ + struct MM_FIELD *camp; + MM_BYTES_PER_FIELD_TYPE_DBF nNewWidth; + + if (!hMiraMonLayer) + return 1; + + camp = pMMAdmDB->pMMBDXP->pField + nIField; + + if (!szValue) + return 0; + + nNewWidth = (MM_BYTES_PER_FIELD_TYPE_DBF)strlen(szValue); + if (MMResizeStringToOperateIfNeeded(hMiraMonLayer, nNewWidth + 1)) + return 1; + + if (nNewWidth > camp->BytesPerField) + { + if (MM_WriteNRecordsMMBD_XPFile(pMMAdmDB)) + return 1; + + // Flushing all to be flushed + pMMAdmDB->FlushRecList.SizeOfBlockToBeSaved = 0; + if (MMAppendBlockToBuffer(&pMMAdmDB->FlushRecList)) + return 1; + + pMMAdmDB->pMMBDXP->pfDataBase = pMMAdmDB->pFExtDBF; + + if (MM_ChangeDBFWidthField( + pMMAdmDB->pMMBDXP, nIField, nNewWidth, + pMMAdmDB->pMMBDXP->pField[nIField].DecimalsIfFloat, + (MM_BYTE)MM_NOU_N_DECIMALS_NO_APLICA)) + return 1; + + // The record on course also has to change its size. + if ((GUInt64)pMMAdmDB->pMMBDXP->BytesPerRecord + 1 >= + pMMAdmDB->nNumRecordOnCourse) + { + void *pTmp; + if (nullptr == (pTmp = realloc_function( + pMMAdmDB->szRecordOnCourse, + (size_t)pMMAdmDB->pMMBDXP->BytesPerRecord + 1))) + { + MMCPLError(CE_Failure, CPLE_OutOfMemory, + "Memory error in MiraMon " + "driver (MMTestAndFixValueToRecordDBXP())"); + return 1; + } + pMMAdmDB->szRecordOnCourse = pTmp; + } + + // File has changed its size, so it has to be updated + // at the Flush tool + fseek_function(pMMAdmDB->pFExtDBF, 0, SEEK_END); + pMMAdmDB->FlushRecList.OffsetWhereToFlush = + ftell_function(pMMAdmDB->pFExtDBF); + } + return 0; +} + +static int +MMWriteValueToszStringToOperate(struct MiraMonVectLayerInfo *hMiraMonLayer, + const struct MM_FIELD *camp, const void *valor, + MM_BOOLEAN is_64) +{ + if (!hMiraMonLayer) + return 1; + + if (!camp) + return 0; + + if (MMResizeStringToOperateIfNeeded(hMiraMonLayer, + camp->BytesPerField + 10)) + return 1; + + if (!valor) + *hMiraMonLayer->szStringToOperate = '\0'; + else + { + if (camp->FieldType == 'N') + { + if (!is_64) + { + snprintf(hMiraMonLayer->szStringToOperate, + (size_t)hMiraMonLayer->nNumStringToOperate, "%*.*f", + camp->BytesPerField, camp->DecimalsIfFloat, + *(const double *)valor); + } + else + { + snprintf(hMiraMonLayer->szStringToOperate, + (size_t)hMiraMonLayer->nNumStringToOperate, "%*lld", + camp->BytesPerField, *(const GInt64 *)valor); + } + } + else + { + snprintf(hMiraMonLayer->szStringToOperate, + (size_t)hMiraMonLayer->nNumStringToOperate, "%-*s", + camp->BytesPerField, (const char *)valor); + } + } + + return 0; +} + +int MMWriteValueToRecordDBXP(struct MiraMonVectLayerInfo *hMiraMonLayer, + char *registre, const struct MM_FIELD *camp, + const void *valor, MM_BOOLEAN is_64) +{ + if (!hMiraMonLayer) + return 1; + + if (!camp) + return 0; + + if (MMWriteValueToszStringToOperate(hMiraMonLayer, camp, valor, is_64)) + return 1; + + memcpy(registre + camp->AccumulatedBytes, hMiraMonLayer->szStringToOperate, + camp->BytesPerField); + return 0; +} + +static int MMAddFeatureRecordToMMDB(struct MiraMonVectLayerInfo *hMiraMonLayer, + struct MiraMonFeature *hMMFeature, + struct MMAdmDatabase *pMMAdmDB, + char *pszRecordOnCourse, + struct MM_FLUSH_INFO *pFlushRecList, + MM_EXT_DBF_N_RECORDS *nNumRecords, + MM_EXT_DBF_N_FIELDS nNumPrivateMMField) +{ + MM_EXT_DBF_N_MULTIPLE_RECORDS nIRecord; + MM_EXT_DBF_N_FIELDS nIField; + struct MM_DATA_BASE_XP *pBD_XP = nullptr; + + if (!hMiraMonLayer) + return 1; + + if (!hMMFeature) + return 1; + + pBD_XP = pMMAdmDB->pMMBDXP; + for (nIRecord = 0; nIRecord < hMMFeature->nNumMRecords; nIRecord++) + { + for (nIField = 0; nIField < hMMFeature->pRecords[nIRecord].nNumField; + nIField++) + { + // A field with no valid value is written as blank + if (!hMMFeature->pRecords[nIRecord].pField[nIField].bIsValid) + { + memset( + pszRecordOnCourse + + pBD_XP->pField[nIField + nNumPrivateMMField] + .AccumulatedBytes, + ' ', + pBD_XP->pField[nIField + nNumPrivateMMField].BytesPerField); + + continue; + } + if (pBD_XP->pField[nIField + nNumPrivateMMField].FieldType == 'C') + { + if (MMWriteValueToRecordDBXP(hMiraMonLayer, pszRecordOnCourse, + pBD_XP->pField + nIField + + nNumPrivateMMField, + hMMFeature->pRecords[nIRecord] + .pField[nIField] + .pDinValue, + FALSE)) + return 1; + } + else if (pBD_XP->pField[nIField + nNumPrivateMMField].FieldType == + 'N') + { + if (pBD_XP->pField[nIField + nNumPrivateMMField].Is64) + { + if (MMWriteValueToRecordDBXP( + hMiraMonLayer, pszRecordOnCourse, + pBD_XP->pField + nIField + nNumPrivateMMField, + &hMMFeature->pRecords[nIRecord] + .pField[nIField] + .iValue, + TRUE)) + return 1; + } + else + { + if (MMWriteValueToRecordDBXP( + hMiraMonLayer, pszRecordOnCourse, + pBD_XP->pField + nIField + nNumPrivateMMField, + &hMMFeature->pRecords[nIRecord] + .pField[nIField] + .dValue, + FALSE)) + return 1; + } + } + else if (pBD_XP->pField[nIField + nNumPrivateMMField].FieldType == + 'D') + { + if (MMWriteValueToRecordDBXP(hMiraMonLayer, pszRecordOnCourse, + pBD_XP->pField + nIField + + nNumPrivateMMField, + hMMFeature->pRecords[nIRecord] + .pField[nIField] + .pDinValue, + FALSE)) + return 1; + } + } + + if (MMAppendBlockToBuffer(pFlushRecList)) + return 1; + + (*nNumRecords)++; + } + return 0; +} + +// Adds feature records to a MiraMon database associated with a vector layer. +static int MMDetectAndFixDBFWidthChange( + struct MiraMonVectLayerInfo *hMiraMonLayer, + struct MiraMonFeature *hMMFeature, struct MMAdmDatabase *pMMAdmDB, + MM_EXT_DBF_N_FIELDS nNumPrivateMMField, + MM_EXT_DBF_N_MULTIPLE_RECORDS nIRecord, MM_EXT_DBF_N_FIELDS nIField) +{ + if (!hMiraMonLayer) + return 1; + + if (!hMMFeature) + return 1; + + if (nIRecord >= hMMFeature->nNumMRecords) + return 1; + + if (nIField >= hMMFeature->pRecords[nIRecord].nNumField) + return 1; + + if (MMTestAndFixValueToRecordDBXP( + hMiraMonLayer, pMMAdmDB, nIField + nNumPrivateMMField, + hMMFeature->pRecords[nIRecord].pField[nIField].pDinValue)) + return 1; + + // We analyze next fields + if (nIField == hMMFeature->pRecords[nIRecord].nNumField - 1) + { + if (nIRecord + 1 < hMMFeature->nNumMRecords) + { + if (MMDetectAndFixDBFWidthChange(hMiraMonLayer, hMMFeature, + pMMAdmDB, nNumPrivateMMField, + nIRecord + 1, 0)) + return 1; + } + else + return 0; + } + else + { + if (nIField + 1 < hMMFeature->pRecords[nIRecord].nNumField) + { + if (MMDetectAndFixDBFWidthChange(hMiraMonLayer, hMMFeature, + pMMAdmDB, nNumPrivateMMField, + nIRecord, nIField + 1)) + return 1; + } + else + return 0; + } + return 0; +} // End of MMDetectAndFixDBFWidthChange() + +// Adds a DBF record to a MiraMon table associated with a vector layer. +// It sets up flush settings for writing to the table and initializes +// variables needed for the process. Then, it checks and fixes the width +// change if necessary. +int MMAddDBFRecordToMMDB(struct MiraMonVectLayerInfo *hMiraMonLayer, + struct MiraMonFeature *hMMFeature) +{ + struct MM_DATA_BASE_XP *pBD_XP = nullptr; + MM_EXT_DBF_N_FIELDS nNumPrivateMMField = 0; + struct MM_FLUSH_INFO *pFlushRecList; + + if (!hMiraMonLayer) + return MM_FATAL_ERROR_WRITING_FEATURES; + + pBD_XP = hMiraMonLayer->MMAdmDBWriting.pMMBDXP; + + // Test length + if (hMMFeature && hMMFeature->nNumMRecords && + hMMFeature->pRecords[0].nNumField) + { + if (MMDetectAndFixDBFWidthChange(hMiraMonLayer, hMMFeature, + &hMiraMonLayer->MMAdmDBWriting, + nNumPrivateMMField, 0, 0)) + return MM_FATAL_ERROR_WRITING_FEATURES; + } + + // Adding record to the MiraMon table (extended DBF) + // Flush settings + pFlushRecList = &hMiraMonLayer->MMAdmDBWriting.FlushRecList; + pFlushRecList->pBlockWhereToSaveOrRead = + (void *)hMiraMonLayer->MMAdmDBWriting.pRecList; + + pFlushRecList->pBlockToBeSaved = + (void *)hMiraMonLayer->MMAdmDBWriting.szRecordOnCourse; + pFlushRecList->SizeOfBlockToBeSaved = pBD_XP->BytesPerRecord; + + if (MMAddFeatureRecordToMMDB( + hMiraMonLayer, hMMFeature, &hMiraMonLayer->MMAdmDBWriting, + hMiraMonLayer->MMAdmDBWriting.szRecordOnCourse, pFlushRecList, + &hMiraMonLayer->MMAdmDBWriting.pMMBDXP->nRecords, + nNumPrivateMMField)) + return MM_FATAL_ERROR_WRITING_FEATURES; + + // In this case, the number of features is also updated + hMiraMonLayer->TopHeader.nElemCount = + hMiraMonLayer->MMAdmDBWriting.pMMBDXP->nRecords; + + return MM_CONTINUE_WRITING_FEATURES; +} + +// Adds a point record to a MiraMon table associated with a vector layer. +int MMAddPointRecordToMMDB(struct MiraMonVectLayerInfo *hMiraMonLayer, + struct MiraMonFeature *hMMFeature, + MM_INTERNAL_FID nElemCount) +{ + struct MM_DATA_BASE_XP *pBD_XP = nullptr; + MM_EXT_DBF_N_FIELDS nNumPrivateMMField = MM_PRIVATE_POINT_DB_FIELDS; + struct MM_FLUSH_INFO *pFlushRecList; + + if (!hMiraMonLayer) + return MM_FATAL_ERROR_WRITING_FEATURES; + + if (!hMMFeature) + return MM_FATAL_ERROR_WRITING_FEATURES; + + // In V1.1 only _UI32_MAX records number is allowed + if (MMCheckVersionForFID(hMiraMonLayer, + hMiraMonLayer->MMPoint.MMAdmDB.pMMBDXP->nRecords + + hMMFeature->nNumMRecords)) + { + MMCPLError(CE_Failure, CPLE_NotSupported, + "Error in MMCheckVersionForFID() (6)"); + return MM_STOP_WRITING_FEATURES; + } + + pBD_XP = hMiraMonLayer->MMPoint.MMAdmDB.pMMBDXP; + + // Test length + // Private fields + // ID_GRAFIC + if (MMWriteValueToszStringToOperate(hMiraMonLayer, pBD_XP->pField, + &nElemCount, TRUE)) + return MM_FATAL_ERROR_WRITING_FEATURES; + if (MMTestAndFixValueToRecordDBXP(hMiraMonLayer, + &hMiraMonLayer->MMPoint.MMAdmDB, 0, + hMiraMonLayer->szStringToOperate)) + return MM_FATAL_ERROR_WRITING_FEATURES; + + // GDAL fields + if (hMMFeature->nNumMRecords && hMMFeature->pRecords[0].nNumField) + { + if (MMDetectAndFixDBFWidthChange(hMiraMonLayer, hMMFeature, + &hMiraMonLayer->MMPoint.MMAdmDB, + nNumPrivateMMField, 0, 0)) + return MM_FATAL_ERROR_WRITING_FEATURES; + } + + // Now length is sure, write + memset(hMiraMonLayer->MMPoint.MMAdmDB.szRecordOnCourse, 0, + pBD_XP->BytesPerRecord); + MMWriteValueToRecordDBXP(hMiraMonLayer, + hMiraMonLayer->MMPoint.MMAdmDB.szRecordOnCourse, + pBD_XP->pField, &nElemCount, TRUE); + + // Adding record to the MiraMon table (extended DBF) + // Flush settings + pFlushRecList = &hMiraMonLayer->MMPoint.MMAdmDB.FlushRecList; + pFlushRecList->pBlockWhereToSaveOrRead = + (void *)hMiraMonLayer->MMPoint.MMAdmDB.pRecList; + + pFlushRecList->pBlockToBeSaved = + (void *)hMiraMonLayer->MMPoint.MMAdmDB.szRecordOnCourse; + pFlushRecList->SizeOfBlockToBeSaved = pBD_XP->BytesPerRecord; + + if (MMAddFeatureRecordToMMDB( + hMiraMonLayer, hMMFeature, &hMiraMonLayer->MMPoint.MMAdmDB, + hMiraMonLayer->MMPoint.MMAdmDB.szRecordOnCourse, pFlushRecList, + &hMiraMonLayer->MMPoint.MMAdmDB.pMMBDXP->nRecords, + nNumPrivateMMField)) + return MM_FATAL_ERROR_WRITING_FEATURES; + return MM_CONTINUE_WRITING_FEATURES; +} + +// Adds a stringline record to a MiraMon table associated with a vector layer. +int MMAddArcRecordToMMDB(struct MiraMonVectLayerInfo *hMiraMonLayer, + struct MiraMonFeature *hMMFeature, + MM_INTERNAL_FID nElemCount, struct MM_AH *pArcHeader) +{ + struct MM_DATA_BASE_XP *pBD_XP = nullptr; + struct MiraMonArcLayer *pMMArcLayer; + MM_EXT_DBF_N_FIELDS nNumPrivateMMField = MM_PRIVATE_ARC_DB_FIELDS; + struct MM_FLUSH_INFO *pFlushRecList; + + if (!hMiraMonLayer) + return MM_FATAL_ERROR_WRITING_FEATURES; + + if (hMiraMonLayer->bIsPolygon) + pMMArcLayer = &hMiraMonLayer->MMPolygon.MMArc; + else + pMMArcLayer = &hMiraMonLayer->MMArc; + + // In V1.1 only _UI32_MAX records number is allowed + if (hMiraMonLayer->bIsPolygon) + { + if (MMCheckVersionForFID(hMiraMonLayer, + pMMArcLayer->MMAdmDB.pMMBDXP->nRecords + 1)) + { + MMCPLError(CE_Failure, CPLE_NotSupported, + "Error in MMCheckVersionForFID() (7)"); + return MM_STOP_WRITING_FEATURES; + } + } + else + { + if (MMCheckVersionForFID(hMiraMonLayer, + pMMArcLayer->MMAdmDB.pMMBDXP->nRecords + + hMMFeature->nNumMRecords)) + { + MMCPLError(CE_Failure, CPLE_NotSupported, + "Error in MMCheckVersionForFID() (8)"); + return MM_STOP_WRITING_FEATURES; + } + } + + pBD_XP = pMMArcLayer->MMAdmDB.pMMBDXP; + + // Test length + // Private fields + // ID_GRAFIC + if (MMWriteValueToszStringToOperate(hMiraMonLayer, pBD_XP->pField, + &nElemCount, TRUE)) + return MM_FATAL_ERROR_WRITING_FEATURES; + if (MMTestAndFixValueToRecordDBXP(hMiraMonLayer, &pMMArcLayer->MMAdmDB, 0, + hMiraMonLayer->szStringToOperate)) + return MM_FATAL_ERROR_WRITING_FEATURES; + + // N_VERTEXS + if (MMWriteValueToszStringToOperate(hMiraMonLayer, pBD_XP->pField + 1, + &pArcHeader->nElemCount, TRUE)) + return MM_FATAL_ERROR_WRITING_FEATURES; + if (MMTestAndFixValueToRecordDBXP(hMiraMonLayer, &pMMArcLayer->MMAdmDB, 1, + hMiraMonLayer->szStringToOperate)) + return MM_FATAL_ERROR_WRITING_FEATURES; + + // LENGTH + if (MMWriteValueToszStringToOperate(hMiraMonLayer, pBD_XP->pField + 2, + &pArcHeader->dfLength, FALSE)) + return MM_FATAL_ERROR_WRITING_FEATURES; + if (MMTestAndFixValueToRecordDBXP(hMiraMonLayer, &pMMArcLayer->MMAdmDB, 2, + hMiraMonLayer->szStringToOperate)) + return MM_FATAL_ERROR_WRITING_FEATURES; + + // NODE_INI + if (MMWriteValueToszStringToOperate(hMiraMonLayer, pBD_XP->pField + 3, + &pArcHeader->nFirstIdNode, TRUE)) + return MM_FATAL_ERROR_WRITING_FEATURES; + if (MMTestAndFixValueToRecordDBXP(hMiraMonLayer, &pMMArcLayer->MMAdmDB, 3, + hMiraMonLayer->szStringToOperate)) + return MM_FATAL_ERROR_WRITING_FEATURES; + + // NODE_FI + if (MMWriteValueToszStringToOperate(hMiraMonLayer, pBD_XP->pField + 4, + &pArcHeader->nLastIdNode, TRUE)) + return MM_FATAL_ERROR_WRITING_FEATURES; + if (MMTestAndFixValueToRecordDBXP(hMiraMonLayer, &pMMArcLayer->MMAdmDB, 4, + hMiraMonLayer->szStringToOperate)) + return MM_FATAL_ERROR_WRITING_FEATURES; + + // GDAL fields + if (!hMiraMonLayer->bIsPolygon) + { + if (hMMFeature->nNumMRecords && hMMFeature->pRecords[0].nNumField) + { + if (MMDetectAndFixDBFWidthChange(hMiraMonLayer, hMMFeature, + &pMMArcLayer->MMAdmDB, + nNumPrivateMMField, 0, 0)) + return MM_FATAL_ERROR_WRITING_FEATURES; + } + } + + // Now length is sure, write + memset(pMMArcLayer->MMAdmDB.szRecordOnCourse, 0, pBD_XP->BytesPerRecord); + MMWriteValueToRecordDBXP(hMiraMonLayer, + pMMArcLayer->MMAdmDB.szRecordOnCourse, + pBD_XP->pField, &nElemCount, TRUE); + + MMWriteValueToRecordDBXP(hMiraMonLayer, + pMMArcLayer->MMAdmDB.szRecordOnCourse, + pBD_XP->pField + 1, &pArcHeader->nElemCount, TRUE); + + MMWriteValueToRecordDBXP(hMiraMonLayer, + pMMArcLayer->MMAdmDB.szRecordOnCourse, + pBD_XP->pField + 2, &pArcHeader->dfLength, FALSE); + + MMWriteValueToRecordDBXP( + hMiraMonLayer, pMMArcLayer->MMAdmDB.szRecordOnCourse, + pBD_XP->pField + 3, &pArcHeader->nFirstIdNode, TRUE); + + MMWriteValueToRecordDBXP( + hMiraMonLayer, pMMArcLayer->MMAdmDB.szRecordOnCourse, + pBD_XP->pField + 4, &pArcHeader->nLastIdNode, TRUE); + + // Adding record to the MiraMon table (extended DBF) + // Flush settings + pFlushRecList = &pMMArcLayer->MMAdmDB.FlushRecList; + pFlushRecList->pBlockWhereToSaveOrRead = + (void *)pMMArcLayer->MMAdmDB.pRecList; + + pFlushRecList->SizeOfBlockToBeSaved = pBD_XP->BytesPerRecord; + pFlushRecList->pBlockToBeSaved = + (void *)pMMArcLayer->MMAdmDB.szRecordOnCourse; + + if (hMiraMonLayer->bIsPolygon) + { + if (MMAppendBlockToBuffer(pFlushRecList)) + return MM_FATAL_ERROR_WRITING_FEATURES; + pMMArcLayer->MMAdmDB.pMMBDXP->nRecords++; + return MM_CONTINUE_WRITING_FEATURES; + } + + pFlushRecList->SizeOfBlockToBeSaved = pBD_XP->BytesPerRecord; + if (MMAddFeatureRecordToMMDB( + hMiraMonLayer, hMMFeature, &pMMArcLayer->MMAdmDB, + pMMArcLayer->MMAdmDB.szRecordOnCourse, pFlushRecList, + &pMMArcLayer->MMAdmDB.pMMBDXP->nRecords, nNumPrivateMMField)) + return MM_FATAL_ERROR_WRITING_FEATURES; + return MM_CONTINUE_WRITING_FEATURES; +} + +// Adds a node record to a MiraMon table associated with a vector layer. +int MMAddNodeRecordToMMDB(struct MiraMonVectLayerInfo *hMiraMonLayer, + MM_INTERNAL_FID nElemCount, struct MM_NH *pNodeHeader) +{ + struct MM_DATA_BASE_XP *pBD_XP = nullptr; + struct MiraMonNodeLayer *pMMNodeLayer; + double nDoubleValue; + + if (!hMiraMonLayer) + return 1; + + if (hMiraMonLayer->bIsPolygon) + pMMNodeLayer = &hMiraMonLayer->MMPolygon.MMArc.MMNode; + else + pMMNodeLayer = &hMiraMonLayer->MMArc.MMNode; + + if (!pMMNodeLayer) + { + MMCPLError(CE_Failure, CPLE_NotSupported, + "Error in pMMNodeLayer() (1)"); + return MM_STOP_WRITING_FEATURES; + } + + // In V1.1 only _UI32_MAX records number is allowed + if (MMCheckVersionForFID(hMiraMonLayer, + pMMNodeLayer->MMAdmDB.pMMBDXP->nRecords + 1)) + { + MMCPLError(CE_Failure, CPLE_NotSupported, + "Error in MMCheckVersionForFID() (9)"); + return MM_STOP_WRITING_FEATURES; + } + + // Test length + // Private fields + // ID_GRAFIC + if (MMWriteValueToszStringToOperate(hMiraMonLayer, + pMMNodeLayer->MMAdmDB.pMMBDXP->pField, + &nElemCount, TRUE)) + return MM_FATAL_ERROR_WRITING_FEATURES; + if (MMTestAndFixValueToRecordDBXP(hMiraMonLayer, &pMMNodeLayer->MMAdmDB, 0, + hMiraMonLayer->szStringToOperate)) + return MM_FATAL_ERROR_WRITING_FEATURES; + + // ARCS_A_NOD + nDoubleValue = pNodeHeader->nArcsCount; + if (MMWriteValueToszStringToOperate( + hMiraMonLayer, pMMNodeLayer->MMAdmDB.pMMBDXP->pField + 1, + &nDoubleValue, FALSE)) + return MM_FATAL_ERROR_WRITING_FEATURES; + if (MMTestAndFixValueToRecordDBXP(hMiraMonLayer, &pMMNodeLayer->MMAdmDB, 1, + hMiraMonLayer->szStringToOperate)) + return MM_FATAL_ERROR_WRITING_FEATURES; + + // TIPUS_NODE + nDoubleValue = pNodeHeader->cNodeType; + if (MMWriteValueToszStringToOperate( + hMiraMonLayer, pMMNodeLayer->MMAdmDB.pMMBDXP->pField + 2, + &nDoubleValue, FALSE)) + return MM_FATAL_ERROR_WRITING_FEATURES; + if (MMTestAndFixValueToRecordDBXP(hMiraMonLayer, &pMMNodeLayer->MMAdmDB, 2, + hMiraMonLayer->szStringToOperate)) + return MM_FATAL_ERROR_WRITING_FEATURES; + + // Adding record to the MiraMon table (extended DBF) + // Flush settings + pMMNodeLayer->MMAdmDB.FlushRecList.pBlockWhereToSaveOrRead = + (void *)pMMNodeLayer->MMAdmDB.pRecList; + + pBD_XP = pMMNodeLayer->MMAdmDB.pMMBDXP; + + pMMNodeLayer->MMAdmDB.FlushRecList.SizeOfBlockToBeSaved = + pBD_XP->BytesPerRecord; + pMMNodeLayer->MMAdmDB.FlushRecList.pBlockToBeSaved = + (void *)pMMNodeLayer->MMAdmDB.szRecordOnCourse; + + memset(pMMNodeLayer->MMAdmDB.szRecordOnCourse, 0, pBD_XP->BytesPerRecord); + MMWriteValueToRecordDBXP(hMiraMonLayer, + pMMNodeLayer->MMAdmDB.szRecordOnCourse, + pBD_XP->pField, &nElemCount, TRUE); + + nDoubleValue = pNodeHeader->nArcsCount; + MMWriteValueToRecordDBXP(hMiraMonLayer, + pMMNodeLayer->MMAdmDB.szRecordOnCourse, + pBD_XP->pField + 1, &nDoubleValue, FALSE); + + nDoubleValue = pNodeHeader->cNodeType; + MMWriteValueToRecordDBXP(hMiraMonLayer, + pMMNodeLayer->MMAdmDB.szRecordOnCourse, + pBD_XP->pField + 2, &nDoubleValue, FALSE); + + if (MMAppendBlockToBuffer(&pMMNodeLayer->MMAdmDB.FlushRecList)) + return MM_FATAL_ERROR_WRITING_FEATURES; + pMMNodeLayer->MMAdmDB.pMMBDXP->nRecords++; + return MM_CONTINUE_WRITING_FEATURES; +} + +// Adds a polygon or multipolygon record to a MiraMon table +// associated with a vector layer. +int MMAddPolygonRecordToMMDB(struct MiraMonVectLayerInfo *hMiraMonLayer, + struct MiraMonFeature *hMMFeature, + MM_INTERNAL_FID nElemCount, + MM_N_VERTICES_TYPE nVerticesCount, + struct MM_PH *pPolHeader) +{ + struct MM_DATA_BASE_XP *pBD_XP = nullptr; + MM_EXT_DBF_N_FIELDS nNumPrivateMMField = MM_PRIVATE_POLYGON_DB_FIELDS; + struct MM_FLUSH_INFO *pFlushRecList; + + if (!hMiraMonLayer) + return MM_FATAL_ERROR_WRITING_FEATURES; + + // In V1.1 only _UI32_MAX records number is allowed + if (MMCheckVersionForFID( + hMiraMonLayer, hMiraMonLayer->MMPolygon.MMAdmDB.pMMBDXP->nRecords + + (hMMFeature ? hMMFeature->nNumMRecords : 0))) + return MM_STOP_WRITING_FEATURES; + + pBD_XP = hMiraMonLayer->MMPolygon.MMAdmDB.pMMBDXP; + + // Test length + // Private fields + // ID_GRAFIC + if (MMWriteValueToszStringToOperate(hMiraMonLayer, pBD_XP->pField, + &nElemCount, TRUE)) + return MM_FATAL_ERROR_WRITING_FEATURES; + if (MMTestAndFixValueToRecordDBXP(hMiraMonLayer, + &hMiraMonLayer->MMPolygon.MMAdmDB, 0, + hMiraMonLayer->szStringToOperate)) + return MM_FATAL_ERROR_WRITING_FEATURES; + + // The other fields are valid if pPolHeader exists (it is not + // the universal polygon) + if (pPolHeader) + { + // N_VERTEXS + if (MMWriteValueToszStringToOperate(hMiraMonLayer, pBD_XP->pField + 1, + &nVerticesCount, TRUE)) + return MM_FATAL_ERROR_WRITING_FEATURES; + if (MMTestAndFixValueToRecordDBXP(hMiraMonLayer, + &hMiraMonLayer->MMPolygon.MMAdmDB, 1, + hMiraMonLayer->szStringToOperate)) + return MM_FATAL_ERROR_WRITING_FEATURES; + + // PERIMETER + if (MMWriteValueToszStringToOperate(hMiraMonLayer, pBD_XP->pField + 2, + &pPolHeader->dfPerimeter, FALSE)) + return MM_FATAL_ERROR_WRITING_FEATURES; + if (MMTestAndFixValueToRecordDBXP(hMiraMonLayer, + &hMiraMonLayer->MMPolygon.MMAdmDB, 2, + hMiraMonLayer->szStringToOperate)) + return MM_FATAL_ERROR_WRITING_FEATURES; + + // AREA + if (MMWriteValueToszStringToOperate(hMiraMonLayer, pBD_XP->pField + 3, + &pPolHeader->dfArea, FALSE)) + return MM_FATAL_ERROR_WRITING_FEATURES; + if (MMTestAndFixValueToRecordDBXP(hMiraMonLayer, + &hMiraMonLayer->MMPolygon.MMAdmDB, 3, + hMiraMonLayer->szStringToOperate)) + return MM_FATAL_ERROR_WRITING_FEATURES; + + // N_ARCS + if (MMWriteValueToszStringToOperate(hMiraMonLayer, pBD_XP->pField + 4, + &pPolHeader->nArcsCount, TRUE)) + return MM_FATAL_ERROR_WRITING_FEATURES; + if (MMTestAndFixValueToRecordDBXP(hMiraMonLayer, + &hMiraMonLayer->MMPolygon.MMAdmDB, 4, + hMiraMonLayer->szStringToOperate)) + return MM_FATAL_ERROR_WRITING_FEATURES; + + // N_POLIG + if (MMWriteValueToszStringToOperate(hMiraMonLayer, pBD_XP->pField + 5, + &pPolHeader->nRingsCount, TRUE)) + return MM_FATAL_ERROR_WRITING_FEATURES; + if (MMTestAndFixValueToRecordDBXP(hMiraMonLayer, + &hMiraMonLayer->MMPolygon.MMAdmDB, 5, + hMiraMonLayer->szStringToOperate)) + return MM_FATAL_ERROR_WRITING_FEATURES; + } + + // GDAL fields + if (hMMFeature && hMMFeature->nNumMRecords && + hMMFeature->pRecords[0].nNumField) + { + if (MMDetectAndFixDBFWidthChange(hMiraMonLayer, hMMFeature, + &hMiraMonLayer->MMPolygon.MMAdmDB, + nNumPrivateMMField, 0, 0)) + return MM_FATAL_ERROR_WRITING_FEATURES; + } + + // Adding record to the MiraMon table (extended DBF) + // Flush settings + pFlushRecList = &hMiraMonLayer->MMPolygon.MMAdmDB.FlushRecList; + pFlushRecList->pBlockWhereToSaveOrRead = + (void *)hMiraMonLayer->MMPolygon.MMAdmDB.pRecList; + + pFlushRecList->SizeOfBlockToBeSaved = pBD_XP->BytesPerRecord; + pFlushRecList->pBlockToBeSaved = + (void *)hMiraMonLayer->MMPolygon.MMAdmDB.szRecordOnCourse; + + // Now length is sure, write + memset(hMiraMonLayer->MMPolygon.MMAdmDB.szRecordOnCourse, ' ', + pBD_XP->BytesPerRecord); + if (MMWriteValueToRecordDBXP( + hMiraMonLayer, hMiraMonLayer->MMPolygon.MMAdmDB.szRecordOnCourse, + pBD_XP->pField, &nElemCount, TRUE)) + return MM_FATAL_ERROR_WRITING_FEATURES; + + if (!hMMFeature) + { + if (MMAppendBlockToBuffer(pFlushRecList)) + return MM_FATAL_ERROR_WRITING_FEATURES; + hMiraMonLayer->MMPolygon.MMAdmDB.pMMBDXP->nRecords++; + return MM_CONTINUE_WRITING_FEATURES; + } + + if (pPolHeader) + { + MMWriteValueToRecordDBXP( + hMiraMonLayer, hMiraMonLayer->MMPolygon.MMAdmDB.szRecordOnCourse, + pBD_XP->pField + 1, &nVerticesCount, TRUE); + + MMWriteValueToRecordDBXP( + hMiraMonLayer, hMiraMonLayer->MMPolygon.MMAdmDB.szRecordOnCourse, + pBD_XP->pField + 2, &pPolHeader->dfPerimeter, FALSE); + + MMWriteValueToRecordDBXP( + hMiraMonLayer, hMiraMonLayer->MMPolygon.MMAdmDB.szRecordOnCourse, + pBD_XP->pField + 3, &pPolHeader->dfArea, FALSE); + + MMWriteValueToRecordDBXP( + hMiraMonLayer, hMiraMonLayer->MMPolygon.MMAdmDB.szRecordOnCourse, + pBD_XP->pField + 4, &pPolHeader->nArcsCount, TRUE); + + MMWriteValueToRecordDBXP( + hMiraMonLayer, hMiraMonLayer->MMPolygon.MMAdmDB.szRecordOnCourse, + pBD_XP->pField + 5, &pPolHeader->nRingsCount, TRUE); + } + + pFlushRecList->SizeOfBlockToBeSaved = pBD_XP->BytesPerRecord; + if (MMAddFeatureRecordToMMDB( + hMiraMonLayer, hMMFeature, &hMiraMonLayer->MMPolygon.MMAdmDB, + hMiraMonLayer->MMPolygon.MMAdmDB.szRecordOnCourse, pFlushRecList, + &hMiraMonLayer->MMPolygon.MMAdmDB.pMMBDXP->nRecords, + nNumPrivateMMField)) + return MM_FATAL_ERROR_WRITING_FEATURES; + return MM_CONTINUE_WRITING_FEATURES; +} + +// Close the MiraMon database associated with a vector layer. +static int MMCloseMMBD_XPFile(struct MiraMonVectLayerInfo *hMiraMonLayer, + struct MMAdmDatabase *MMAdmDB) +{ + int ret_code = 1; + if (!hMiraMonLayer) + return 1; + + if (hMiraMonLayer->ReadOrWrite == MM_WRITING_MODE) + { + if (!MMAdmDB->pFExtDBF) + { + // In case of 0 elements created we have to + // create an empty DBF + if (hMiraMonLayer->bIsPolygon) + { + if (hMiraMonLayer->TopHeader.nElemCount <= 1) + { + if (MMCreateMMDB(hMiraMonLayer)) + { + MMCPLError(CE_Failure, CPLE_OutOfMemory, + "Memory error in MiraMon " + "driver (MMCreateMMDB())"); + goto end_label; + } + } + } + else if (hMiraMonLayer->bIsPoint || hMiraMonLayer->bIsArc) + { + if (hMiraMonLayer->TopHeader.nElemCount == 0) + { + if (MMCreateMMDB(hMiraMonLayer)) + { + MMCPLError(CE_Failure, CPLE_OutOfMemory, + "Memory error in MiraMon " + "driver (MMCreateMMDB())"); + goto end_label; + } + } + } + } + + if (MM_WriteNRecordsMMBD_XPFile(MMAdmDB)) + goto end_label; + + // Flushing all to be flushed + MMAdmDB->FlushRecList.SizeOfBlockToBeSaved = 0; + if (MMAppendBlockToBuffer(&MMAdmDB->FlushRecList)) + goto end_label; + } + + ret_code = 0; +end_label: + // Closing database files + fclose_and_nullify(&MMAdmDB->pFExtDBF); + + return ret_code; +} + +int MMCloseMMBD_XP(struct MiraMonVectLayerInfo *hMiraMonLayer) +{ + int ret_code = 0; + if (!hMiraMonLayer) + return 1; + + if (hMiraMonLayer->pMMBDXP) + { + fclose_and_nullify(&hMiraMonLayer->pMMBDXP->pfDataBase); + } + + if (hMiraMonLayer->bIsPoint) + ret_code = + MMCloseMMBD_XPFile(hMiraMonLayer, &hMiraMonLayer->MMPoint.MMAdmDB); + else if (hMiraMonLayer->bIsArc && !hMiraMonLayer->bIsPolygon) + { + if (MMCloseMMBD_XPFile(hMiraMonLayer, &hMiraMonLayer->MMArc.MMAdmDB)) + ret_code = 1; + if (MMCloseMMBD_XPFile(hMiraMonLayer, + &hMiraMonLayer->MMArc.MMNode.MMAdmDB)) + ret_code = 1; + } + else if (hMiraMonLayer->bIsPolygon) + { + if (MMCloseMMBD_XPFile(hMiraMonLayer, + &hMiraMonLayer->MMPolygon.MMAdmDB)) + ret_code = 1; + if (MMCloseMMBD_XPFile(hMiraMonLayer, + &hMiraMonLayer->MMPolygon.MMArc.MMAdmDB)) + ret_code = 1; + if (MMCloseMMBD_XPFile(hMiraMonLayer, + &hMiraMonLayer->MMPolygon.MMArc.MMNode.MMAdmDB)) + ret_code = 1; + } + else if (hMiraMonLayer->bIsDBF) + ret_code = + MMCloseMMBD_XPFile(hMiraMonLayer, &hMiraMonLayer->MMAdmDBWriting); + + return ret_code; +} + +// Destroys the memory used to create a MiraMon table associated +// with a vector layer. +static void MMDestroyMMDBFile(struct MiraMonVectLayerInfo *hMiraMonLayer, + struct MMAdmDatabase *pMMAdmDB) +{ + if (!hMiraMonLayer) + return; + + if (pMMAdmDB && pMMAdmDB->szRecordOnCourse) + { + free_function(pMMAdmDB->szRecordOnCourse); + pMMAdmDB->szRecordOnCourse = nullptr; + } + if (hMiraMonLayer->szStringToOperate) + { + free_function(hMiraMonLayer->szStringToOperate); + hMiraMonLayer->szStringToOperate = nullptr; + hMiraMonLayer->nNumStringToOperate = 0; + } + + if (pMMAdmDB && pMMAdmDB->pMMBDXP) + { + MM_ReleaseDBFHeader(pMMAdmDB->pMMBDXP); + hMiraMonLayer->pMMBDXP = pMMAdmDB->pMMBDXP = nullptr; + } + if (pMMAdmDB && pMMAdmDB->pRecList) + { + free_function(pMMAdmDB->pRecList); + pMMAdmDB->pRecList = nullptr; + } +} + +void MMDestroyMMDB(struct MiraMonVectLayerInfo *hMiraMonLayer) +{ + if (!hMiraMonLayer) + return; + + if (hMiraMonLayer->bIsPoint) + { + MMDestroyMMDBFile(hMiraMonLayer, &hMiraMonLayer->MMPoint.MMAdmDB); + return; + } + if (hMiraMonLayer->bIsArc && !hMiraMonLayer->bIsPolygon) + { + MMDestroyMMDBFile(hMiraMonLayer, &hMiraMonLayer->MMArc.MMAdmDB); + MMDestroyMMDBFile(hMiraMonLayer, &hMiraMonLayer->MMArc.MMNode.MMAdmDB); + return; + } + if (hMiraMonLayer->bIsPolygon) + { + MMDestroyMMDBFile(hMiraMonLayer, &hMiraMonLayer->MMPolygon.MMAdmDB); + MMDestroyMMDBFile(hMiraMonLayer, + &hMiraMonLayer->MMPolygon.MMArc.MMAdmDB); + MMDestroyMMDBFile(hMiraMonLayer, + &hMiraMonLayer->MMPolygon.MMArc.MMNode.MMAdmDB); + } + if (hMiraMonLayer->bIsDBF) + MMDestroyMMDBFile(hMiraMonLayer, &hMiraMonLayer->MMAdmDBWriting); +} +#ifdef GDAL_COMPILATION +CPL_C_END // Necessary for compiling in GDAL project +#endif diff --git a/ogr/ogrsf_frmts/miramon/mm_wrlayr.h b/ogr/ogrsf_frmts/miramon/mm_wrlayr.h new file mode 100644 index 000000000000..a1d7bbea6307 --- /dev/null +++ b/ogr/ogrsf_frmts/miramon/mm_wrlayr.h @@ -0,0 +1,222 @@ +#ifndef __MM_WRLAYR_H +#define __MM_WRLAYR_H + +/* -------------------------------------------------------------------- */ +/* Necessary functions to read/write a MiraMon Vector File */ +/* -------------------------------------------------------------------- */ + +#include "mm_gdal_driver_structs.h" +#ifndef GDAL_COMPILATION +#include "gdalmmf.h" // For PTR_MM_CPLRecode, ptr_MM_CPLRecode(), ... +#else +#include "ogr_api.h" // For OGRLayerH +CPL_C_START // Necessary for compiling in GDAL project +#endif + +#ifndef GDAL_COMPILATION +#include "memo.h" +#include "F64_str.h" //For FILE_64 +#include "FILE_64.h" // Per a fseek_64(),... +#include "bd_xp.h" //For MAX_LON_CAMP_DBF +#include "deftoler.h" // For QUASI_IGUAL +//#include "LbTopStr.h" // For struct GEOMETRIC_I_TOPOLOGIC_POL +//#include "str_snyd.h" // For struct SNY_TRANSFORMADOR_GEODESIA +#include "nomsfitx.h" // Per a CanviaExtensio() +#include "fitxers.h" // Per a removeAO() +#include "cadenes.h" // Per a EsCadenaDeBlancs() +#define calloc_function(a) MM_calloc((a)) +#define realloc_function MM_realloc +#define free_function(a) MM_free((a)) +#define fopen_function(f, a) fopenAO_64((f), (a)) +#define fflush_function fflush_64 +#define fclose_function(f) fclose_64((f)) +#define ftell_function(f) ftell_64((f)) +#define fwrite_function(p, s, r, f) fwrite_64((p), (s), (r), (f)) +#define fread_function(p, s, r, f) fread_64((p), (s), (r), (f)) +#define fseek_function(f, s, g) fseek_64((f), (s), (g)) +#define TruncateFile_function(a, b) TruncaFitxer_64((a), (b)) +#define strdup_function(p) strdup((p)) +#define get_filename_function TreuAdreca +#define get_path_function DonaAdreca +#define fprintf_function fprintf_64 +#define max_function(a, b) max((a), (b)) +#define get_extension_function(a) extensio(a) +#define reset_extension(a, b) CanviaExtensio((a), (b)) +#define remove_function(a) removeAO((a)) +#define OGR_F_GetFieldAsString_function(a, b) \ + ptr_MM_OGR_F_GetFieldAsString((a), (b)) +#define OGR_F_Destroy_function(a) ptr_MM_OGR_F_Destroy((a)) +#define GDALClose_function(a) ptr_MM_GDALClose((a)) +#define OGR_Fld_GetNameRef_function(a) ptr_MM_OGR_Fld_GetNameRef((a)) +#define OGR_FD_GetFieldDefn_function(a, b) ptr_MM_OGR_FD_GetFieldDefn((a), (b)) +#define GDALOpenEx_function(a, b, c, d, e) \ + ptr_MM_GDALOpenEx((a), (b), (c), (d), (e)) +#define OGR_FD_GetFieldCount_function(a) ptr_MM_OGR_FD_GetFieldCount((a)) +#define OGR_L_GetLayerDefn_function(a) ptr_MM_OGR_L_GetLayerDefn((a)) +#define OGR_L_GetNextFeature_function(a) ptr_MM_OGR_L_GetNextFeature((a)) +#define OGR_L_ResetReading_function(a) ptr_MM_OGR_L_ResetReading((a)) +#define GDALDatasetGetLayer_function(a, b) ptr_MM_GDALDatasetGetLayer((a), (b)) +#define CPLRecode_function(a, b, c) ptr_MM_CPLRecode((a), (b), (c)) +#define CPLFree_function(a) ptr_MM_CPLFree((a)) +#define form_filename_function(a, b) MuntaPath((a), (b), TRUE) +#define MM_CPLGetBasename(a) TreuAdreca((a)) +#define MM_IsNANDouble(x) EsNANDouble((x)) +#define MM_IsDoubleInfinite(x) EsDoubleInfinit((x)) +#else +#define calloc_function(a) VSICalloc(1, (a)) +#define realloc_function VSIRealloc +#define free_function(a) VSIFree((a)) +#define fopen_function(f, a) VSIFOpenL((f), (a)) +#define fflush_function VSIFFlushL +#define fclose_function(f) VSIFCloseL((f)) +#define ftell_function(f) VSIFTellL((f)) +#define fwrite_function(p, s, r, f) VSIFWriteL((p), (s), (r), (f)) +#define fread_function(p, s, r, f) VSIFReadL((p), (s), (r), (f)) +#define fseek_function(f, s, g) VSIFSeekL((f), (s), (g)) +#define TruncateFile_function(a, b) VSIFTruncateL((a), (b)) +#define strdup_function(p) CPLStrdup((p)) +#define get_filename_function CPLGetFilename +#define get_path_function CPLGetPath +#define fprintf_function VSIFPrintfL +#define max_function(a, b) MAX((a), (b)) +#define get_extension_function(a) CPLGetExtension((a)) +#define reset_extension(a, b) CPLResetExtension((a), (b)) +#define remove_function(a) VSIUnlink((a)) +#define OGR_F_GetFieldAsString_function(a, b) OGR_F_GetFieldAsString((a), (b)) +#define OGR_F_Destroy_function(a) OGR_F_Destroy((a)) +#define GDALClose_function(a) GDALClose((a)) +#define OGR_Fld_GetNameRef_function(a) OGR_Fld_GetNameRef((a)) +#define OGR_FD_GetFieldDefn_function(a, b) OGR_FD_GetFieldDefn((a), (b)) +#define GDALOpenEx_function(a, b, c, d, e) GDALOpenEx((a), (b), (c), (d), (e)) +#define OGR_FD_GetFieldCount_function(a) OGR_FD_GetFieldCount((a)) +#define OGR_L_GetLayerDefn_function(a) OGR_L_GetLayerDefn((a)) +#define OGR_L_GetNextFeature_function(a) OGR_L_GetNextFeature((a)) +#define OGR_L_ResetReading_function(a) OGR_L_ResetReading((a)) +#define GDALDatasetGetLayer_function(a, b) GDALDatasetGetLayer((a), (b)) +#define CPLRecode_function(a, b, c) CPLRecode((a), (b), (c)) +#define CPLFree_function(a) CPLFree((a)) +#define form_filename_function(a, b) CPLFormFilename((a), (b), "") +#define MM_CPLGetBasename(a) CPLGetBasename((a)) +#define MM_IsNANDouble(x) CPLIsNan((x)) +#define MM_IsDoubleInfinite(x) CPLIsInf((x)) +#endif + +/* -------------------------------------------------------------------- */ +/* Functions */ +/* -------------------------------------------------------------------- */ +// MM-GDAL functions +#ifdef GDAL_COMPILATION +#define MMCPLError CPLError +#define MMCPLWarning CPLError +#define MMCPLDebug CPLDebugOnly +#else + void + MMCPLError(int code, const char *fmt, ...); +void MMCPLWarning(int code, const char *fmt, ...); +void MMCPLDebug(int code, const char *fmt, ...); +#endif + +// Layer functions +int MMInitLayer(struct MiraMonVectLayerInfo *hMiraMonLayer, + const char *pzFileName, int LayerVersion, char nMMRecode, + char nMMLanguage, struct MiraMonDataBase *pLayerDB, + MM_BOOLEAN ReadOrWrite, struct MiraMonVectMapInfo *MMMap); +int MMInitLayerByType(struct MiraMonVectLayerInfo *hMiraMonLayer); +int MMDestroyLayer(struct MiraMonVectLayerInfo *hMiraMonLayer); +int MMCloseLayer(struct MiraMonVectLayerInfo *hMiraMonLayer); +int MMReadHeader(FILE_TYPE *pF, struct MM_TH *pMMHeader); +void MMInitHeader(struct MM_TH *pMMHeader, int layerType, int nVersion); +int MMWriteEmptyHeader(FILE_TYPE *pF, int layerType, int nVersion); +int MMReadAHArcSection(struct MiraMonVectLayerInfo *hMiraMonLayer); +int MMReadPHPolygonSection(struct MiraMonVectLayerInfo *hMiraMonLayer); +int MMReadZDescriptionHeaders(struct MiraMonVectLayerInfo *hMiraMonLayer, + FILE_TYPE *pF, MM_INTERNAL_FID nElements, + struct MM_ZSection *pZSection); +int MMReadZSection(struct MiraMonVectLayerInfo *hMiraMonLayer, FILE_TYPE *pF, + struct MM_ZSection *pZSection); + +// Feature functions +int MMInitFeature(struct MiraMonFeature *MMFeature); +void MMResetFeatureGeometry(struct MiraMonFeature *MMFeature); +void MMResetFeatureRecord(struct MiraMonFeature *hMMFeature); +void MMDestroyFeature(struct MiraMonFeature *MMFeature); +int MMAddFeature(struct MiraMonVectLayerInfo *hMiraMonLayer, + struct MiraMonFeature *hMiraMonFeature); +int MMCheckSize_t(GUInt64 nCount, GUInt64 nSize); +int MMGetVectorVersion(struct MM_TH *pTopHeader); +int MMInitFlush(struct MM_FLUSH_INFO *pFlush, FILE_TYPE *pF, GUInt64 nBlockSize, + char **pBuffer, MM_FILE_OFFSET DiskOffsetWhereToFlush, + GInt32 nMyDiskSize); +int MMReadFlush(struct MM_FLUSH_INFO *pFlush); +int MMReadBlockFromBuffer(struct MM_FLUSH_INFO *FlushInfo); +int MMReadGUInt64DependingOnVersion(struct MiraMonVectLayerInfo *hMiraMonLayer, + struct MM_FLUSH_INFO *FlushInfo, + GUInt64 *pnUI64); +int MMReadOffsetDependingOnVersion(struct MiraMonVectLayerInfo *hMiraMonLayer, + struct MM_FLUSH_INFO *FlushInfo, + MM_FILE_OFFSET *nUI64); + +// Tool functions +char *MMReturnValueFromSectionINIFile(const char *filename, const char *section, + const char *key); + +// In order to be efficient we reserve space to reuse it every +// time a feature is added. +int MMResizeMiraMonFieldValue(struct MiraMonFieldValue **pFieldValue, + MM_EXT_DBF_N_FIELDS *nMax, + MM_EXT_DBF_N_FIELDS nNum, + MM_EXT_DBF_N_FIELDS nIncr, + MM_EXT_DBF_N_FIELDS nProposedMax); + +int MMResizeMiraMonPolygonArcs(struct MM_PAL_MEM **pFID, + MM_POLYGON_ARCS_COUNT *nMax, + MM_POLYGON_ARCS_COUNT nNum, + MM_POLYGON_ARCS_COUNT nIncr, + MM_POLYGON_ARCS_COUNT nProposedMax); + +int MMResizeMiraMonRecord(struct MiraMonRecord **pMiraMonRecord, + MM_EXT_DBF_N_MULTIPLE_RECORDS *nMax, + MM_EXT_DBF_N_MULTIPLE_RECORDS nNum, + MM_EXT_DBF_N_MULTIPLE_RECORDS nIncr, + MM_EXT_DBF_N_MULTIPLE_RECORDS nProposedMax); + +int MMResize_MM_N_VERTICES_TYPE_Pointer(MM_N_VERTICES_TYPE **pUI64, + MM_N_VERTICES_TYPE *nMax, + MM_N_VERTICES_TYPE nNum, + MM_N_VERTICES_TYPE nIncr, + MM_N_VERTICES_TYPE nProposedMax); + +int MMResizeVFGPointer(char **pInt, MM_INTERNAL_FID *nMax, MM_INTERNAL_FID nNum, + MM_INTERNAL_FID nIncr, MM_INTERNAL_FID nProposedMax); + +int MMResizeMM_POINT2DPointer(struct MM_POINT_2D **pPoint2D, + MM_N_VERTICES_TYPE *nMax, MM_N_VERTICES_TYPE nNum, + MM_N_VERTICES_TYPE nIncr, + MM_N_VERTICES_TYPE nProposedMax); + +int MMResizeDoublePointer(MM_COORD_TYPE **pDouble, MM_N_VERTICES_TYPE *nMax, + MM_N_VERTICES_TYPE nNum, MM_N_VERTICES_TYPE nIncr, + MM_N_VERTICES_TYPE nProposedMax); +int MMResizeStringToOperateIfNeeded(struct MiraMonVectLayerInfo *hMiraMonLayer, + MM_EXT_DBF_N_FIELDS nNewSize); +int MMIsEmptyString(const char *string); +int MMGetNFieldValue(const char *pszStringList, GUInt32 nIRecord, + char *pszPartOfRawValue, size_t nSizeOfRawValue); +// Metadata functions +int MMReturnCodeFromMM_m_idofic(char *pMMSRS_or_pSRS, char *result, + MM_BYTE direction); + +#define EPSG_FROM_MMSRS 0 +#define MMSRS_FROM_EPSG 1 +#define ReturnEPSGCodeSRSFromMMIDSRS(pMMSRS, szResult) \ + MMReturnCodeFromMM_m_idofic((pMMSRS), (szResult), EPSG_FROM_MMSRS) +#define ReturnMMIDSRSFromEPSGCodeSRS(pSRS, szResult) \ + MMReturnCodeFromMM_m_idofic((pSRS), (szResult), MMSRS_FROM_EPSG) + +int MMWriteVectorMetadata(struct MiraMonVectLayerInfo *hMiraMonLayer); +int MMCheck_REL_FILE(const char *szREL_file); + +#ifdef GDAL_COMPILATION +CPL_C_END // Necessary for compiling in GDAL project +#endif +#endif //__MM_WRLAYR_H diff --git a/ogr/ogrsf_frmts/miramon/ogrmiramon.h b/ogr/ogrsf_frmts/miramon/ogrmiramon.h new file mode 100644 index 000000000000..a57f6570db97 --- /dev/null +++ b/ogr/ogrsf_frmts/miramon/ogrmiramon.h @@ -0,0 +1,169 @@ +/****************************************************************************** + * $Id$ + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: C++ classes for the MiraMon driver + * Author: Abel Pau + ****************************************************************************** + * Copyright (c) 2024, Xavier Pons + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#ifndef OGRMIRAMON_H_INCLUDED +#define OGRMIRAMON_H_INCLUDED + +#include "ogrsf_frmts.h" +#include "ogr_api.h" +#include "cpl_string.h" +#include "mm_wrlayr.h" + +/************************************************************************/ +/* OGRMiraMonLayer */ +/************************************************************************/ + +class OGRMiraMonLayer final + : public OGRLayer, + public OGRGetNextFeatureThroughRaw +{ + GDALDataset *m_poDS; + OGRSpatialReference *m_poSRS; + OGRFeatureDefn *m_poFeatureDefn; + + GUIntBig m_iNextFID; + + // Pointer to one of three possible MiraMon layers: points, + // arcs or polygons. Every time a feature is read this pointer + // points to the appropriate layer + struct MiraMonVectLayerInfo *phMiraMonLayer; + + // When writing a layer + struct MiraMonVectLayerInfo hMiraMonLayerPNT; // MiraMon points layer + struct MiraMonVectLayerInfo hMiraMonLayerARC; // MiraMon arcs layer + struct MiraMonVectLayerInfo hMiraMonLayerPOL; // MiraMon polygons layer + + // When reading a layer or the result of writing is only a DBF + struct MiraMonVectLayerInfo hMiraMonLayerReadOrNonGeom; + + struct MiraMonFeature hMMFeature; // Feature reading/writing + + bool m_bUpdate; + + VSILFILE *m_fp = nullptr; + + // Array of doubles used in the field features processing + double *padfValues; + GInt64 *pnInt64Values; + + OGRFeature *GetNextRawFeature(); + OGRFeature *GetFeature(GIntBig nFeatureId) override; + void GoToFieldOfMultipleRecord(MM_INTERNAL_FID iFID, + MM_EXT_DBF_N_RECORDS nIRecord, + MM_EXT_DBF_N_FIELDS nIField); + + OGRErr MMDumpVertices(OGRGeometryH hGeom, MM_BOOLEAN bExternalRing, + MM_BOOLEAN bUseVFG); + OGRErr MMProcessGeometry(OGRGeometryH poGeom, OGRFeature *poFeature, + MM_BOOLEAN bcalculateRecord); + OGRErr MMProcessMultiGeometry(OGRGeometryH hGeom, OGRFeature *poFeature); + OGRErr MMLoadGeometry(OGRGeometryH hGeom); + OGRErr MMWriteGeometry(); + GIntBig GetFeatureCount(int bForce) override; + + public: + bool bValidFile; + + OGRMiraMonLayer(GDALDataset *poDS, const char *pszFilename, VSILFILE *fp, + const OGRSpatialReference *poSRS, int bUpdate, + CSLConstList papszOpenOptions, + struct MiraMonVectMapInfo *MMMap); + virtual ~OGRMiraMonLayer(); + + void ResetReading() override; + DEFINE_GET_NEXT_FEATURE_THROUGH_RAW(OGRMiraMonLayer) + + OGRErr TranslateFieldsToMM(); + OGRErr TranslateFieldsValuesToMM(OGRFeature *poFeature); + OGRErr GetExtent(OGREnvelope *psExtent, int bForce) override; + + OGRFeatureDefn *GetLayerDefn() override; + + virtual OGRErr GetExtent(int iGeomField, OGREnvelope *psExtent, + int bForce) override + { + return OGRLayer::GetExtent(iGeomField, psExtent, bForce); + } + + OGRErr ICreateFeature(OGRFeature *poFeature) override; + + virtual OGRErr CreateField(const OGRFieldDefn *poField, + int bApproxOK = TRUE) override; + + int TestCapability(const char *) override; + void AddToFileList(CPLStringList &oFileList); + + GDALDataset *GetDataset() override + { + return m_poDS; + } +}; + +/************************************************************************/ +/* OGRMiraMonDataSource */ +/************************************************************************/ + +class OGRMiraMonDataSource final : public OGRDataSource +{ + OGRMiraMonLayer **papoLayers; + int nLayers; + char *pszRootName; + char *pszDSName; + bool bUpdate; + struct MiraMonVectMapInfo MMMap; + + public: + OGRMiraMonDataSource(); + ~OGRMiraMonDataSource(); + + int Open(const char *pszFilename, VSILFILE *fp, + const OGRSpatialReference *poSRS, int bUpdate, + CSLConstList papszOpenOptions); + int Create(const char *pszFilename, char **papszOptions); + + const char *GetName() override + { + return pszDSName; + } + + int GetLayerCount() override + { + return nLayers; + } + + OGRLayer *GetLayer(int) override; + char **GetFileList() override; + + OGRLayer *ICreateLayer(const char *pszLayerName, + const OGRGeomFieldDefn *poGeomFieldDefn, + CSLConstList papszOptions) override; + + int TestCapability(const char *) override; +}; + +#endif /* OGRMIRAMON_H_INCLUDED */ diff --git a/ogr/ogrsf_frmts/miramon/ogrmiramondatasource.cpp b/ogr/ogrsf_frmts/miramon/ogrmiramondatasource.cpp new file mode 100644 index 000000000000..92b2572c29dd --- /dev/null +++ b/ogr/ogrsf_frmts/miramon/ogrmiramondatasource.cpp @@ -0,0 +1,291 @@ +/****************************************************************************** + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRMiraMonDataSource class. + * Author: Abel Pau + ****************************************************************************** + * Copyright (c) 2024, Xavier Pons + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrmiramon.h" + +/****************************************************************************/ +/* OGRMiraMonDataSource() */ +/****************************************************************************/ +OGRMiraMonDataSource::OGRMiraMonDataSource() + : papoLayers(nullptr), nLayers(0), pszRootName(nullptr), pszDSName(nullptr), + bUpdate(false) + +{ + MMMap.nNumberOfLayers = 0; + MMMap.fMMMap = nullptr; +} + +/****************************************************************************/ +/* ~OGRMiraMonDataSource() */ +/****************************************************************************/ + +OGRMiraMonDataSource::~OGRMiraMonDataSource() + +{ + for (int i = 0; i < nLayers; i++) + delete papoLayers[i]; + CPLFree(papoLayers); + CPLFree(pszDSName); + CPLFree(pszRootName); + + if (MMMap.fMMMap) + VSIFCloseL(MMMap.fMMMap); +} + +/****************************************************************************/ +/* Open() */ +/****************************************************************************/ + +int OGRMiraMonDataSource::Open(const char *pszFilename, VSILFILE *fp, + const OGRSpatialReference *poSRS, int bUpdateIn, + CSLConstList papszOpenOptionsUsr) + +{ + bUpdate = CPL_TO_BOOL(bUpdateIn); + + OGRMiraMonLayer *poLayer = new OGRMiraMonLayer( + this, pszFilename, fp, poSRS, bUpdate, papszOpenOptionsUsr, &MMMap); + if (!poLayer->bValidFile) + { + delete poLayer; + return FALSE; + } + papoLayers = static_cast(CPLRealloc( + papoLayers, + (size_t)(sizeof(OGRMiraMonLayer *) * ((size_t)nLayers + (size_t)1)))); + papoLayers[nLayers] = poLayer; + nLayers++; + + if (pszDSName) + { + const char *pszExtension = CPLGetExtension(pszDSName); + if (!EQUAL(pszExtension, "pol") && !EQUAL(pszExtension, "arc") && + !EQUAL(pszExtension, "pnt")) + { + CPLStrlcpy( + MMMap.pszMapName, + CPLFormFilename(pszDSName, CPLGetBasename(pszDSName), "mmm"), + sizeof(MMMap.pszMapName)); + if (!MMMap.nNumberOfLayers) + { + MMMap.fMMMap = VSIFOpenL(MMMap.pszMapName, "w+"); + if (!MMMap.fMMMap) + { + // It could be an error but it is not so important + // to stop the process. This map is an extra element + // to open all layers in one click, at least in MiraMon + // software. + *MMMap.pszMapName = '\0'; + } + else + { + VSIFPrintfL(MMMap.fMMMap, "[VERSIO]\n"); + VSIFPrintfL(MMMap.fMMMap, "Vers=2\n"); + VSIFPrintfL(MMMap.fMMMap, "SubVers=0\n"); + VSIFPrintfL(MMMap.fMMMap, "variant=b\n"); + VSIFPrintfL(MMMap.fMMMap, "\n"); + VSIFPrintfL(MMMap.fMMMap, "[DOCUMENT]\n"); + VSIFPrintfL(MMMap.fMMMap, "Titol= %s(map)\n", + CPLGetBasename(poLayer->GetName())); + VSIFPrintfL(MMMap.fMMMap, "\n"); + } + } + } + else + *MMMap.pszMapName = '\0'; + } + else + *MMMap.pszMapName = '\0'; + + if (pszDSName) + CPLFree(pszDSName); + pszDSName = CPLStrdup(pszFilename); + + return TRUE; +} + +/****************************************************************************/ +/* Create() */ +/* */ +/* Create a new datasource. This does not really do anything */ +/* currently but save the name. */ +/****************************************************************************/ + +int OGRMiraMonDataSource::Create(const char *pszDataSetName, + char ** /* papszOptions */) + +{ + bUpdate = TRUE; + pszDSName = CPLStrdup(pszDataSetName); + pszRootName = CPLStrdup(pszDataSetName); + + return TRUE; +} + +/****************************************************************************/ +/* ICreateLayer() */ +/****************************************************************************/ + +OGRLayer * +OGRMiraMonDataSource::ICreateLayer(const char *pszLayerName, + const OGRGeomFieldDefn *poGeomFieldDefn, + CSLConstList papszOptions) +{ + CPLAssert(nullptr != pszLayerName); + + const auto eType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone; + const auto poSRS = + poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr; + + // It's a seed to be able to generate a random identifier in + // MMGenerateFileIdentifierFromMetadataFileName() function + srand((unsigned int)time(nullptr)); + + if (OGR_GT_HasM(eType)) + { + CPLError(CE_Warning, CPLE_NotSupported, + "Measures in this layer will be ignored."); + } + + /* -------------------------------------------------------------------- */ + /* If the dataset has an extension, it is understood that the path */ + /* of the file is where to write, and the layer name is the */ + /* dataset name (without extension). */ + /* -------------------------------------------------------------------- */ + const char *pszExtension = CPLGetExtension(pszRootName); + char *pszFullMMLayerName; + if (EQUAL(pszExtension, "pol") || EQUAL(pszExtension, "arc") || + EQUAL(pszExtension, "pnt")) + { + char *pszMMLayerName; + pszMMLayerName = CPLStrdup(CPLResetExtension(pszRootName, "")); + pszMMLayerName[strlen(pszMMLayerName) - 1] = '\0'; + + pszFullMMLayerName = CPLStrdup((const char *)pszMMLayerName); + + // Checking that the folder where to write exists + const char *szDestFolder = CPLGetDirname(pszFullMMLayerName); + if (!STARTS_WITH(szDestFolder, "/vsimem")) + { + VSIStatBufL sStat; + if (VSIStatL(szDestFolder, &sStat) != 0 || + !VSI_ISDIR(sStat.st_mode)) + { + CPLFree(pszMMLayerName); + CPLFree(pszFullMMLayerName); + CPLError(CE_Failure, CPLE_AppDefined, + "The folder %s does not exist.", szDestFolder); + return nullptr; + } + } + CPLFree(pszMMLayerName); + } + else + { + const char *osPath; + + osPath = pszRootName; + pszFullMMLayerName = + CPLStrdup(CPLFormFilename(pszRootName, pszLayerName, "")); + + /* -------------------------------------------------------------------- */ + /* Let's create the folder if it's not already created. */ + /* (only the las level of the folder) */ + /* -------------------------------------------------------------------- */ + if (!STARTS_WITH(osPath, "/vsimem")) + { + VSIStatBufL sStat; + if (VSIStatL(osPath, &sStat) != 0 || !VSI_ISDIR(sStat.st_mode)) + { + if (VSIMkdir(osPath, 0755) != 0) + { + CPLFree(pszFullMMLayerName); + CPLError(CE_Failure, CPLE_AppDefined, + "Unable to create the folder %s.", pszRootName); + return nullptr; + } + } + } + } + + /* -------------------------------------------------------------------- */ + /* Return open layer handle. */ + /* -------------------------------------------------------------------- */ + if (Open(pszFullMMLayerName, nullptr, poSRS, TRUE, papszOptions)) + { + CPLFree(pszFullMMLayerName); + auto poLayer = papoLayers[nLayers - 1]; + return poLayer; + } + + CPLFree(pszFullMMLayerName); + return nullptr; +} + +/****************************************************************************/ +/* TestCapability() */ +/****************************************************************************/ + +int OGRMiraMonDataSource::TestCapability(const char *pszCap) + +{ + if (EQUAL(pszCap, ODsCCreateLayer)) + return bUpdate; + else if (EQUAL(pszCap, ODsCZGeometries)) + return TRUE; + + return FALSE; +} + +/****************************************************************************/ +/* GetLayer() */ +/****************************************************************************/ + +OGRLayer *OGRMiraMonDataSource::GetLayer(int iLayer) + +{ + if (iLayer < 0 || iLayer >= nLayers) + return nullptr; + + return papoLayers[iLayer]; +} + +/************************************************************************/ +/* GetFileList() */ +/************************************************************************/ + +char **OGRMiraMonDataSource::GetFileList() +{ + CPLStringList oFileList; + GetLayerCount(); + for (int i = 0; i < nLayers; i++) + { + OGRMiraMonLayer *poLayer = papoLayers[i]; + poLayer->AddToFileList(oFileList); + } + return oFileList.StealList(); +} diff --git a/ogr/ogrsf_frmts/miramon/ogrmiramondriver.cpp b/ogr/ogrsf_frmts/miramon/ogrmiramondriver.cpp new file mode 100644 index 000000000000..a4b96da1fe2a --- /dev/null +++ b/ogr/ogrsf_frmts/miramon/ogrmiramondriver.cpp @@ -0,0 +1,212 @@ +/****************************************************************************** + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRMiraMonDriver class. + * Author: Abel Pau + ****************************************************************************** + * Copyright (c) 2024, Xavier Pons + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ + +#include "ogrmiramon.h" + +/****************************************************************************/ +/* OGRMMDriverIdentify() */ +/****************************************************************************/ + +static int OGRMiraMonDriverIdentify(GDALOpenInfo *poOpenInfo) + +{ + if (poOpenInfo->fpL == nullptr || poOpenInfo->nHeaderBytes < 7) + return FALSE; + else if (EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "PNT") || + EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "ARC") || + EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "POL")) + { + // Format + if ((poOpenInfo->pabyHeader[0] == 'P' && + poOpenInfo->pabyHeader[1] == 'N' && + poOpenInfo->pabyHeader[2] == 'T') || + (poOpenInfo->pabyHeader[0] == 'A' && + poOpenInfo->pabyHeader[1] == 'R' && + poOpenInfo->pabyHeader[2] == 'C') || + (poOpenInfo->pabyHeader[0] == 'P' && + poOpenInfo->pabyHeader[1] == 'O' && + poOpenInfo->pabyHeader[2] == 'L')) + { + // Version 1.1 or 2.0 + if ((poOpenInfo->pabyHeader[3] == ' ' && + poOpenInfo->pabyHeader[4] == '1' && + poOpenInfo->pabyHeader[5] == '.' && + poOpenInfo->pabyHeader[6] == '1') || + (poOpenInfo->pabyHeader[3] == ' ' && + poOpenInfo->pabyHeader[4] == '2' && + poOpenInfo->pabyHeader[5] == '.' && + poOpenInfo->pabyHeader[6] == '0')) + { + return TRUE; + } + } + } + + return FALSE; +} + +/****************************************************************************/ +/* OGRMiraMonDriverOpen() */ +/****************************************************************************/ + +static GDALDataset *OGRMiraMonDriverOpen(GDALOpenInfo *poOpenInfo) + +{ + if (OGRMiraMonDriverIdentify(poOpenInfo) == FALSE) + return nullptr; + + OGRMiraMonDataSource *poDS = new OGRMiraMonDataSource(); + + if (poDS != nullptr && poOpenInfo->eAccess == GA_Update) + { + CPLError(CE_Failure, CPLE_OpenFailed, + "MiraMonVector driver does not support update."); + delete poDS; + poDS = nullptr; + } + else + { + if (!poDS->Open(poOpenInfo->pszFilename, nullptr, nullptr, + poOpenInfo->eAccess == GA_Update, + poOpenInfo->papszOpenOptions)) + { + delete poDS; + poDS = nullptr; + } + } + + return poDS; +} + +/****************************************************************************/ +/* OGRMiraMonDriverCreate() */ +/****************************************************************************/ + +static GDALDataset * +OGRMiraMonDriverCreate(const char *pszName, CPL_UNUSED int /*nBands*/, + CPL_UNUSED int /*nXSize*/, CPL_UNUSED int /*nYSize*/, + CPL_UNUSED GDALDataType /*eDT*/, char **papszOptions) +{ + OGRMiraMonDataSource *poDS = new OGRMiraMonDataSource(); + + if (poDS->Create(pszName, papszOptions)) + return poDS; + + delete poDS; + return nullptr; +} + +/****************************************************************************/ +/* RegisterOGRMM() */ +/****************************************************************************/ + +void RegisterOGRMiraMon() + +{ + if (GDALGetDriverByName("MiraMonVector") != nullptr) + return; + + GDALDriver *poDriver = new GDALDriver(); + poDriver->SetDescription("MiraMonVector"); + poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES"); + poDriver->SetMetadataItem(GDAL_DCAP_CREATE_LAYER, "YES"); + poDriver->SetMetadataItem(GDAL_DCAP_CREATE_FIELD, "YES"); + poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, + "MiraMon Vectors (.pol, .arc, .pnt)"); + poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "pol arc pnt"); + poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, + "drivers/vector/miramon.html"); + poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES"); + poDriver->SetMetadataItem(GDAL_DCAP_Z_GEOMETRIES, "YES"); + + poDriver->SetMetadataItem( + GDAL_DMD_OPENOPTIONLIST, + "" + " " + " " + " " + ""); + + poDriver->SetMetadataItem( + GDAL_DS_LAYER_CREATIONOPTIONLIST, + "" + " " + " " + " " + ""); + + poDriver->SetMetadataItem( + GDAL_DMD_CREATIONFIELDDATATYPES, + "Integer Integer64 Real String Date Time " + "Binary IntegerList Integer64List RealList StringList"); + poDriver->pfnOpen = OGRMiraMonDriverOpen; + poDriver->pfnIdentify = OGRMiraMonDriverIdentify; + poDriver->pfnCreate = OGRMiraMonDriverCreate; + + GetGDALDriverManager()->RegisterDriver(poDriver); +} diff --git a/ogr/ogrsf_frmts/miramon/ogrmiramonlayer.cpp b/ogr/ogrsf_frmts/miramon/ogrmiramonlayer.cpp new file mode 100644 index 000000000000..ad04304d48c5 --- /dev/null +++ b/ogr/ogrsf_frmts/miramon/ogrmiramonlayer.cpp @@ -0,0 +1,2761 @@ +/****************************************************************************** + * + * Project: OpenGIS Simple Features Reference Implementation + * Purpose: Implements OGRMiraMonLayer class. + * Author: Abel Pau + ****************************************************************************** + * Copyright (c) 2024, Xavier Pons + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ****************************************************************************/ +#include "ogrmiramon.h" + +#include "mm_gdal_functions.h" // For MMCreateExtendedDBFIndex() +#include "mm_rdlayr.h" // For MMInitLayerToRead() +#include // For std::clamp() +#include // For std::string +#include // For std::max + +/****************************************************************************/ +/* OGRMiraMonLayer() */ +/****************************************************************************/ +OGRMiraMonLayer::OGRMiraMonLayer(GDALDataset *poDS, const char *pszFilename, + VSILFILE *fp, const OGRSpatialReference *poSRS, + int bUpdateIn, CSLConstList papszOpenOptions, + struct MiraMonVectMapInfo *MMMap) + : m_poDS(poDS), m_poSRS(nullptr), m_poFeatureDefn(nullptr), m_iNextFID(0), + phMiraMonLayer(nullptr), hMiraMonLayerPNT(), hMiraMonLayerARC(), + hMiraMonLayerPOL(), hMiraMonLayerReadOrNonGeom(), hMMFeature(), + m_bUpdate(CPL_TO_BOOL(bUpdateIn)), + m_fp(fp ? fp : VSIFOpenL(pszFilename, (bUpdateIn ? "r+" : "r"))), + padfValues(nullptr), pnInt64Values(nullptr), bValidFile(false) +{ + + CPLDebugOnly("MiraMon", "Creating/Opening MiraMon layer..."); + /* -------------------------------------------------------------------- */ + /* Create the feature definition */ + /* -------------------------------------------------------------------- */ + m_poFeatureDefn = new OGRFeatureDefn(CPLGetBasename(pszFilename)); + SetDescription(m_poFeatureDefn->GetName()); + m_poFeatureDefn->Reference(); + + if (m_bUpdate) + { + /* ---------------------------------------------------------------- */ + /* Establish the version to use */ + /* ---------------------------------------------------------------- */ + const char *pszVersion = CSLFetchNameValue(papszOpenOptions, "Version"); + int nMMVersion; + + if (pszVersion) + { + if (EQUAL(pszVersion, "V1.1")) + nMMVersion = MM_32BITS_VERSION; + else if (EQUAL(pszVersion, "V2.0") || + EQUAL(pszVersion, "last_version")) + nMMVersion = MM_64BITS_VERSION; + else + nMMVersion = MM_32BITS_VERSION; // Default + } + else + nMMVersion = MM_32BITS_VERSION; // Default + + /* ---------------------------------------------------------------- */ + /* Establish the charset of the .dbf files */ + /* ---------------------------------------------------------------- */ + const char *pszdbfEncoding = + CSLFetchNameValue(papszOpenOptions, "DBFEncoding"); + char nMMRecode; + + if (pszdbfEncoding) + { + if (EQUAL(pszdbfEncoding, "UTF8")) + nMMRecode = MM_RECODE_UTF8; + else //if (EQUAL(pszdbfEncoding, "ANSI")) + nMMRecode = MM_RECODE_ANSI; + } + else + nMMRecode = MM_RECODE_ANSI; // Default + + /* ----------------------------------------------------------------- */ + /* Establish the descriptors language when */ + /* creating .rel files */ + /* ----------------------------------------------------------------- */ + const char *pszLanguage = + CSLFetchNameValue(papszOpenOptions, "CreationLanguage"); + char nMMLanguage; + + if (pszLanguage) + { + if (EQUAL(pszLanguage, "CAT")) + nMMLanguage = MM_CAT_LANGUAGE; + else if (EQUAL(pszLanguage, "SPA")) + nMMLanguage = MM_SPA_LANGUAGE; + else + nMMLanguage = MM_ENG_LANGUAGE; + } + else + nMMLanguage = MM_DEF_LANGUAGE; // Default + + /* ---------------------------------------------------------------- */ + /* Preparing to write the layer */ + /* ---------------------------------------------------------------- */ + // Init the feature (memory, num,...) + if (MMInitFeature(&hMMFeature)) + { + bValidFile = false; + return; + } + + // Init the Layers (not in disk, only in memory until + // the first element is read) + CPLDebugOnly("MiraMon", "Initializing MiraMon points layer..."); + if (MMInitLayer(&hMiraMonLayerPNT, pszFilename, nMMVersion, nMMRecode, + nMMLanguage, nullptr, MM_WRITING_MODE, MMMap)) + { + bValidFile = false; + return; + } + hMiraMonLayerPNT.bIsBeenInit = 0; + + CPLDebugOnly("MiraMon", "Initializing MiraMon arcs layer..."); + if (MMInitLayer(&hMiraMonLayerARC, pszFilename, nMMVersion, nMMRecode, + nMMLanguage, nullptr, MM_WRITING_MODE, MMMap)) + { + bValidFile = false; + return; + } + hMiraMonLayerARC.bIsBeenInit = 0; + + CPLDebugOnly("MiraMon", "Initializing MiraMon polygons layer..."); + if (MMInitLayer(&hMiraMonLayerPOL, pszFilename, nMMVersion, nMMRecode, + nMMLanguage, nullptr, MM_WRITING_MODE, MMMap)) + { + bValidFile = false; + return; + } + hMiraMonLayerPOL.bIsBeenInit = 0; + + // Just in case that there is no geometry but some other + // information to get. A DBF will be generated + CPLDebugOnly("MiraMon", "Initializing MiraMon only-ext-DBF layer..."); + if (MMInitLayer(&hMiraMonLayerReadOrNonGeom, pszFilename, nMMVersion, + nMMRecode, nMMLanguage, nullptr, MM_WRITING_MODE, + nullptr)) + { + bValidFile = false; + return; + } + hMiraMonLayerPOL.bIsBeenInit = 0; + + // This helps the map to be created + //GetLayerDefn()->SetName(hMiraMonLayerPNT.pszSrcLayerName); + m_poFeatureDefn->SetName(hMiraMonLayerPNT.pszSrcLayerName); + + // Saving the HRS in the layer structure + if (poSRS) + { + const char *pszAuthorityName = poSRS->GetAuthorityName(nullptr); + const char *pszAuthorityCode = poSRS->GetAuthorityCode(nullptr); + + if (pszAuthorityName && pszAuthorityCode && + EQUAL(pszAuthorityName, "EPSG")) + { + CPLDebugOnly("MiraMon", "Setting EPSG code %s", + pszAuthorityCode); + hMiraMonLayerPNT.pSRS = CPLStrdup(pszAuthorityCode); + hMiraMonLayerARC.pSRS = CPLStrdup(pszAuthorityCode); + hMiraMonLayerPOL.pSRS = CPLStrdup(pszAuthorityCode); + } + } + } + else + { + if (m_fp == nullptr) + { + bValidFile = false; + return; + } + + /* ------------------------------------------------------------------*/ + /* Read the header. */ + /* ------------------------------------------------------------------*/ + int nMMLayerVersion; + + if (MMInitLayerToRead(&hMiraMonLayerReadOrNonGeom, m_fp, pszFilename)) + { + phMiraMonLayer = &hMiraMonLayerReadOrNonGeom; + bValidFile = false; + return; + } + phMiraMonLayer = &hMiraMonLayerReadOrNonGeom; + + nMMLayerVersion = MMGetVectorVersion(&phMiraMonLayer->TopHeader); + if (nMMLayerVersion == MM_UNKNOWN_VERSION) + { + CPLError(CE_Failure, CPLE_NotSupported, + "MiraMon version file unknown."); + bValidFile = false; + return; + } + if (phMiraMonLayer->bIsPoint) + { + if (phMiraMonLayer->TopHeader.bIs3d) + m_poFeatureDefn->SetGeomType(wkbPoint25D); + else + m_poFeatureDefn->SetGeomType(wkbPoint); + } + else if (phMiraMonLayer->bIsArc && !phMiraMonLayer->bIsPolygon) + { + if (phMiraMonLayer->TopHeader.bIs3d) + m_poFeatureDefn->SetGeomType(wkbLineString25D); + else + m_poFeatureDefn->SetGeomType(wkbLineString); + } + else if (phMiraMonLayer->bIsPolygon) + { + // 3D + if (phMiraMonLayer->TopHeader.bIs3d) + { + if (phMiraMonLayer->TopHeader.bIsMultipolygon) + m_poFeatureDefn->SetGeomType(wkbMultiPolygon25D); + else + m_poFeatureDefn->SetGeomType(wkbPolygon25D); + } + else + { + if (phMiraMonLayer->TopHeader.bIsMultipolygon) + m_poFeatureDefn->SetGeomType(wkbMultiPolygon); + else + m_poFeatureDefn->SetGeomType(wkbPolygon); + } + } + else + { + CPLError(CE_Failure, CPLE_NotSupported, + "MiraMon file type not supported."); + bValidFile = false; + return; + } + + if (phMiraMonLayer->TopHeader.bIs3d) + { + const char *szHeight = + CSLFetchNameValue(papszOpenOptions, "Height"); + if (szHeight) + { + if (EQUAL(szHeight, "Highest")) + phMiraMonLayer->nSelectCoordz = MM_SELECT_HIGHEST_COORDZ; + else if (EQUAL(szHeight, "Lowest")) + phMiraMonLayer->nSelectCoordz = MM_SELECT_LOWEST_COORDZ; + else + phMiraMonLayer->nSelectCoordz = MM_SELECT_FIRST_COORDZ; + } + else + phMiraMonLayer->nSelectCoordz = MM_SELECT_FIRST_COORDZ; + } + + /* ------------------------------------------------------------ */ + /* Establish the descriptors language when */ + /* opening .rel files */ + /* ------------------------------------------------------------ */ + const char *pszLanguage = + CSLFetchNameValue(papszOpenOptions, "OpenLanguage"); + + if (pszLanguage) + { + if (EQUAL(pszLanguage, "CAT")) + phMiraMonLayer->nMMLanguage = MM_CAT_LANGUAGE; + else if (EQUAL(pszLanguage, "SPA")) + phMiraMonLayer->nMMLanguage = MM_SPA_LANGUAGE; + else + phMiraMonLayer->nMMLanguage = MM_ENG_LANGUAGE; + } + else + phMiraMonLayer->nMMLanguage = MM_DEF_LANGUAGE; // Default + + if (phMiraMonLayer->nSRS_EPSG != 0) + { + m_poSRS = new OGRSpatialReference(); + m_poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); + if (m_poSRS->importFromEPSG(phMiraMonLayer->nSRS_EPSG) != + OGRERR_NONE) + { + delete m_poSRS; + m_poSRS = nullptr; + } + else + m_poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(m_poSRS); + } + + // If there is associated information + if (phMiraMonLayer->pMMBDXP) + { + if (!phMiraMonLayer->pMMBDXP->pfDataBase) + { + if ((phMiraMonLayer->pMMBDXP->pfDataBase = fopen_function( + phMiraMonLayer->pMMBDXP->szFileName, "r")) == nullptr) + { + CPLDebugOnly("MiraMon", "File '%s' cannot be opened.", + phMiraMonLayer->pMMBDXP->szFileName); + bValidFile = false; + return; + } + + if (phMiraMonLayer->pMMBDXP->nFields == 0) + { + // TODO: is this correct? At least this prevents a + // nullptr dereference of phMiraMonLayer->pMMBDXP->pField + // below + CPLDebug("MiraMon", + "phMiraMonLayer->pMMBDXP->nFields == 0"); + bValidFile = false; + return; + } + + // First time we open the extended DBF we create an index + // to fastly find all non geometrical features. + phMiraMonLayer->pMultRecordIndex = MMCreateExtendedDBFIndex( + phMiraMonLayer->pMMBDXP->pfDataBase, + phMiraMonLayer->pMMBDXP->nRecords, + phMiraMonLayer->pMMBDXP->FirstRecordOffset, + phMiraMonLayer->pMMBDXP->BytesPerRecord, + phMiraMonLayer->pMMBDXP + ->pField[phMiraMonLayer->pMMBDXP->IdGraficField] + .AccumulatedBytes, + phMiraMonLayer->pMMBDXP + ->pField[phMiraMonLayer->pMMBDXP->IdGraficField] + .BytesPerField, + &phMiraMonLayer->isListField, &phMiraMonLayer->nMaxN); + + // Creation of maximum number needed for processing + // multiple records + if (phMiraMonLayer->pMultRecordIndex) + { + padfValues = static_cast(CPLCalloc( + (size_t)phMiraMonLayer->nMaxN, sizeof(*padfValues))); + + pnInt64Values = static_cast(CPLCalloc( + (size_t)phMiraMonLayer->nMaxN, sizeof(*pnInt64Values))); + } + + phMiraMonLayer->iMultiRecord = + MM_MULTIRECORD_NO_MULTIRECORD; // No option iMultiRecord + const char *szMultiRecord = + CSLFetchNameValue(papszOpenOptions, "MultiRecordIndex"); + if (phMiraMonLayer->isListField && szMultiRecord) + { + if (EQUAL(szMultiRecord, "Last")) + phMiraMonLayer->iMultiRecord = MM_MULTIRECORD_LAST; + else if (EQUAL(szMultiRecord, "JSON")) + phMiraMonLayer->iMultiRecord = MM_MULTIRECORD_JSON; + else + phMiraMonLayer->iMultiRecord = atoi(szMultiRecord); + } + } + + for (MM_EXT_DBF_N_FIELDS nIField = 0; + nIField < phMiraMonLayer->pMMBDXP->nFields; nIField++) + { + OGRFieldDefn oField("", OFTString); + oField.SetName( + phMiraMonLayer->pMMBDXP->pField[nIField].FieldName); + + oField.SetAlternativeName( + phMiraMonLayer->pMMBDXP->pField[nIField] + .FieldDescription[phMiraMonLayer->nMMLanguage < + MM_NUM_IDIOMES_MD_MULTIDIOMA + ? phMiraMonLayer->nMMLanguage + : 0]); + + if (phMiraMonLayer->pMMBDXP->pField[nIField].FieldType == 'C') + { + // It's a list? + if (phMiraMonLayer->iMultiRecord == + MM_MULTIRECORD_NO_MULTIRECORD) + { + if (phMiraMonLayer->isListField) + oField.SetType(OFTStringList); + else + oField.SetType(OFTString); + } + // It's a serialized JSON array + else if (phMiraMonLayer->iMultiRecord == + MM_MULTIRECORD_JSON) + { + oField.SetType(OFTString); + oField.SetSubType(OFSTJSON); + } + else // iMultiRecord decides which Record translate + oField.SetType(OFTString); + } + else if (phMiraMonLayer->pMMBDXP->pField[nIField].FieldType == + 'N') + { + // It's a list? + if (phMiraMonLayer->iMultiRecord == + MM_MULTIRECORD_NO_MULTIRECORD) + { + if (phMiraMonLayer->pMMBDXP->pField[nIField] + .DecimalsIfFloat) + oField.SetType(phMiraMonLayer->isListField + ? OFTRealList + : OFTReal); + else + oField.SetType(phMiraMonLayer->isListField + ? OFTIntegerList + : OFTInteger); + } + // It's a serialized JSON array + else if (phMiraMonLayer->iMultiRecord == + MM_MULTIRECORD_JSON) + { + oField.SetType(OFTString); + oField.SetSubType(OFSTJSON); + } + else + { + if (phMiraMonLayer->pMMBDXP->pField[nIField] + .DecimalsIfFloat) + oField.SetType(OFTReal); + else + oField.SetType(OFTInteger); + } + } + else if (phMiraMonLayer->pMMBDXP->pField[nIField].FieldType == + 'D') + { + // It's a serialized JSON array + oField.SetType(OFTDate); + if (phMiraMonLayer->iMultiRecord == MM_MULTIRECORD_JSON) + { + oField.SetType(OFTString); + oField.SetSubType(OFSTJSON); + } + } + + oField.SetWidth( + phMiraMonLayer->pMMBDXP->pField[nIField].BytesPerField); + oField.SetPrecision( + phMiraMonLayer->pMMBDXP->pField[nIField].DecimalsIfFloat); + + m_poFeatureDefn->AddFieldDefn(&oField); + } + } + } + + bValidFile = true; +} + +/****************************************************************************/ +/* ~OGRMiraMonLayer() */ +/****************************************************************************/ + +OGRMiraMonLayer::~OGRMiraMonLayer() + +{ + if (m_nFeaturesRead > 0 && m_poFeatureDefn != nullptr) + { + CPLDebugOnly("MiraMon", "%d features read on layer '%s'.", + static_cast(m_nFeaturesRead), + m_poFeatureDefn->GetName()); + } + + if (hMiraMonLayerPOL.bIsPolygon) + { + CPLDebugOnly("MiraMon", "Closing MiraMon polygons layer..."); + if (MMCloseLayer(&hMiraMonLayerPOL)) + { + CPLDebugOnly("MiraMon", "Error closing polygons layer"); + } + if (hMiraMonLayerPOL.TopHeader.nElemCount) + { + CPLDebugOnly("MiraMon", + sprintf_UINT64 " polygon(s) written in file %s.pol", + hMiraMonLayerPOL.TopHeader.nElemCount, + hMiraMonLayerPOL.pszSrcLayerName); + } + CPLDebugOnly("MiraMon", "MiraMon polygons layer closed"); + } + else if (hMiraMonLayerPOL.ReadOrWrite == MM_WRITING_MODE) + { + CPLDebugOnly("MiraMon", "No MiraMon polygons layer created."); + } + + if (hMiraMonLayerARC.bIsArc) + { + CPLDebugOnly("MiraMon", "Closing MiraMon arcs layer..."); + if (MMCloseLayer(&hMiraMonLayerARC)) + { + CPLDebugOnly("MiraMon", "Error closing arcs layer"); + } + if (hMiraMonLayerARC.TopHeader.nElemCount) + { + CPLDebugOnly("MiraMon", + sprintf_UINT64 " arc(s) written in file %s.arc", + hMiraMonLayerARC.TopHeader.nElemCount, + hMiraMonLayerARC.pszSrcLayerName); + } + + CPLDebugOnly("MiraMon", "MiraMon arcs layer closed"); + } + else if (hMiraMonLayerARC.ReadOrWrite == MM_WRITING_MODE) + { + CPLDebugOnly("MiraMon", "No MiraMon arcs layer created."); + } + + if (hMiraMonLayerPNT.bIsPoint) + { + CPLDebugOnly("MiraMon", "Closing MiraMon points layer..."); + if (MMCloseLayer(&hMiraMonLayerPNT)) + { + CPLDebugOnly("MiraMon", "Error closing points layer"); + } + if (hMiraMonLayerPNT.TopHeader.nElemCount) + { + CPLDebugOnly("MiraMon", + sprintf_UINT64 " point(s) written in file %s.pnt", + hMiraMonLayerPNT.TopHeader.nElemCount, + hMiraMonLayerPNT.pszSrcLayerName); + } + CPLDebugOnly("MiraMon", "MiraMon points layer closed"); + } + else if (hMiraMonLayerPNT.ReadOrWrite == MM_WRITING_MODE) + { + CPLDebugOnly("MiraMon", "No MiraMon points layer created."); + } + + if (hMiraMonLayerARC.ReadOrWrite == MM_WRITING_MODE) + { + if (hMiraMonLayerReadOrNonGeom.bIsDBF) + { + if (hMiraMonLayerReadOrNonGeom.ReadOrWrite == MM_WRITING_MODE) + { + CPLDebugOnly("MiraMon", "Closing MiraMon DBF table ..."); + } + MMCloseLayer(&hMiraMonLayerReadOrNonGeom); + if (hMiraMonLayerReadOrNonGeom.ReadOrWrite == MM_WRITING_MODE) + { + CPLDebugOnly("MiraMon", "MiraMon DBF table closed"); + } + } + else if (hMiraMonLayerReadOrNonGeom.ReadOrWrite == MM_WRITING_MODE) + { + CPLDebugOnly("MiraMon", "No MiraMon DBF table created."); + } + } + else + { + if (hMiraMonLayerReadOrNonGeom.ReadOrWrite == MM_WRITING_MODE) + { + CPLDebugOnly("MiraMon", "Closing MiraMon layer ..."); + } + MMCloseLayer(&hMiraMonLayerReadOrNonGeom); + if (hMiraMonLayerReadOrNonGeom.ReadOrWrite == MM_WRITING_MODE) + { + CPLDebugOnly("MiraMon", "MiraMon layer closed"); + } + } + + if (hMiraMonLayerPOL.ReadOrWrite == MM_WRITING_MODE) + { + MMCPLDebug("MiraMon", "Destroying MiraMon polygons layer memory"); + } + MMDestroyLayer(&hMiraMonLayerPOL); + if (hMiraMonLayerPOL.ReadOrWrite == MM_WRITING_MODE) + { + MMCPLDebug("MiraMon", "MiraMon polygons layer memory destroyed"); + } + + if (hMiraMonLayerARC.ReadOrWrite == MM_WRITING_MODE) + { + MMCPLDebug("MiraMon", "Destroying MiraMon arcs layer memory"); + } + MMDestroyLayer(&hMiraMonLayerARC); + if (hMiraMonLayerARC.ReadOrWrite == MM_WRITING_MODE) + { + MMCPLDebug("MiraMon", "MiraMon arcs layer memory destroyed"); + } + + if (hMiraMonLayerPNT.ReadOrWrite == MM_WRITING_MODE) + { + MMCPLDebug("MiraMon", "Destroying MiraMon points layer memory"); + } + MMDestroyLayer(&hMiraMonLayerPNT); + if (hMiraMonLayerPNT.ReadOrWrite == MM_WRITING_MODE) + { + MMCPLDebug("MiraMon", "MiraMon points layer memory destroyed"); + } + + if (hMiraMonLayerReadOrNonGeom.ReadOrWrite == MM_WRITING_MODE) + { + MMCPLDebug("MiraMon", "Destroying MiraMon DBF table layer memory"); + } + else + { + MMCPLDebug("MiraMon", "Destroying MiraMon layer memory"); + } + + MMDestroyLayer(&hMiraMonLayerReadOrNonGeom); + if (hMiraMonLayerReadOrNonGeom.ReadOrWrite == MM_WRITING_MODE) + { + MMCPLDebug("MiraMon", "MiraMon DBF table layer memory destroyed"); + } + else + { + MMCPLDebug("MiraMon", "MiraMon layer memory destroyed"); + } + + memset(&hMiraMonLayerReadOrNonGeom, 0, sizeof(hMiraMonLayerReadOrNonGeom)); + memset(&hMiraMonLayerPNT, 0, sizeof(hMiraMonLayerPNT)); + memset(&hMiraMonLayerARC, 0, sizeof(hMiraMonLayerARC)); + memset(&hMiraMonLayerPOL, 0, sizeof(hMiraMonLayerPOL)); + + MMCPLDebug("MiraMon", "Destroying MiraMon temporary feature memory"); + MMDestroyFeature(&hMMFeature); + MMCPLDebug("MiraMon", "MiraMon temporary feature memory"); + memset(&hMMFeature, 0, sizeof(hMMFeature)); + + /* -------------------------------------------------------------------- */ + /* Clean up. */ + /* -------------------------------------------------------------------- */ + + if (m_poFeatureDefn) + m_poFeatureDefn->Release(); + + if (m_poSRS) + m_poSRS->Release(); + + if (m_fp != nullptr) + VSIFCloseL(m_fp); + + if (padfValues != nullptr) + CPLFree(padfValues); + + if (pnInt64Values != nullptr) + CPLFree(pnInt64Values); +} + +/****************************************************************************/ +/* ResetReading() */ +/****************************************************************************/ + +void OGRMiraMonLayer::ResetReading() + +{ + if (m_iNextFID == 0) + return; + + m_iNextFID = 0; + + //VSIFSeekL(m_fp, 0, SEEK_SET); + if (!phMiraMonLayer) + return; + + if (phMiraMonLayer->bIsPoint && phMiraMonLayer->MMPoint.pF) + { + VSIFSeekL(phMiraMonLayer->MMPoint.pF, 0, SEEK_SET); + return; + } + if (phMiraMonLayer->bIsArc && !phMiraMonLayer->bIsPolygon && + phMiraMonLayer->MMArc.pF) + { + VSIFSeekL(phMiraMonLayer->MMArc.pF, 0, SEEK_SET); + return; + } + if (phMiraMonLayer->bIsPolygon && phMiraMonLayer->MMPolygon.pF) + { + VSIFSeekL(phMiraMonLayer->MMPolygon.pF, 0, SEEK_SET); + return; + } +} + +/****************************************************************************/ +/* GetNextRawFeature() */ +/****************************************************************************/ + +void OGRMiraMonLayer::GoToFieldOfMultipleRecord(MM_INTERNAL_FID iFID, + MM_EXT_DBF_N_RECORDS nIRecord, + MM_EXT_DBF_N_FIELDS nIField) + +{ + // Not an error. Simply there are no features, but there are fields + if (!phMiraMonLayer->pMultRecordIndex) + return; + + fseek_function( + phMiraMonLayer->pMMBDXP->pfDataBase, + phMiraMonLayer->pMultRecordIndex[iFID].offset + + (MM_FILE_OFFSET)nIRecord * phMiraMonLayer->pMMBDXP->BytesPerRecord + + phMiraMonLayer->pMMBDXP->pField[nIField].AccumulatedBytes, + SEEK_SET); +} + +/****************************************************************************/ +/* GetNextRawFeature() */ +/****************************************************************************/ + +OGRFeature *OGRMiraMonLayer::GetNextRawFeature() +{ + if (!phMiraMonLayer) + return nullptr; + + if (m_iNextFID >= (GUInt64)phMiraMonLayer->TopHeader.nElemCount) + return nullptr; + + OGRFeature *poFeature = GetFeature(m_iNextFID); + + if (!poFeature) + return nullptr; + + m_iNextFID++; + return poFeature; +} + +/****************************************************************************/ +/* GetFeature() */ +/****************************************************************************/ + +OGRFeature *OGRMiraMonLayer::GetFeature(GIntBig nFeatureId) + +{ + OGRGeometry *poGeom = nullptr; + OGRPoint *poPoint = nullptr; + OGRLineString *poLS = nullptr; + MM_INTERNAL_FID nIElem; + MM_EXT_DBF_N_MULTIPLE_RECORDS nIRecord = 0; + + if (!phMiraMonLayer) + return nullptr; + + if (nFeatureId < 0) + return nullptr; + + if (phMiraMonLayer->bIsPolygon) + { + if (nFeatureId == GINTBIG_MAX) + return nullptr; + + nIElem = (MM_INTERNAL_FID)(nFeatureId + 1); + } + else + nIElem = (MM_INTERNAL_FID)nFeatureId; + + if (nIElem >= phMiraMonLayer->TopHeader.nElemCount) + return nullptr; + + /* -------------------------------------------------------------------- */ + /* Read nFeatureId feature directly from the file. */ + /* -------------------------------------------------------------------- */ + if (nIElem < phMiraMonLayer->TopHeader.nElemCount) + { + switch (phMiraMonLayer->eLT) + { + case MM_LayerType_Point: + case MM_LayerType_Point3d: + // Read point + poGeom = new OGRPoint(); + poPoint = poGeom->toPoint(); + + // Get X,Y (z). MiraMon has no multipoints + if (MMGetGeoFeatureFromVector(phMiraMonLayer, nIElem)) + { + delete poGeom; + return nullptr; + } + + poPoint->setX(phMiraMonLayer->ReadFeature.pCoord[0].dfX); + poPoint->setY(phMiraMonLayer->ReadFeature.pCoord[0].dfY); + if (phMiraMonLayer->TopHeader.bIs3d) + poPoint->setZ(phMiraMonLayer->ReadFeature.pZCoord[0]); + break; + + case MM_LayerType_Arc: + case MM_LayerType_Arc3d: + poGeom = new OGRLineString(); + poLS = poGeom->toLineString(); + + // Get X,Y (Z) n times MiraMon has no multilines + if (MMGetGeoFeatureFromVector(phMiraMonLayer, nIElem)) + { + delete poGeom; + return nullptr; + } + + for (MM_N_VERTICES_TYPE nIVrt = 0; + nIVrt < phMiraMonLayer->ReadFeature.pNCoordRing[0]; + nIVrt++) + { + if (phMiraMonLayer->TopHeader.bIs3d) + poLS->addPoint( + phMiraMonLayer->ReadFeature.pCoord[nIVrt].dfX, + phMiraMonLayer->ReadFeature.pCoord[nIVrt].dfY, + phMiraMonLayer->ReadFeature.pZCoord[nIVrt]); + else + poLS->addPoint( + phMiraMonLayer->ReadFeature.pCoord[nIVrt].dfX, + phMiraMonLayer->ReadFeature.pCoord[nIVrt].dfY); + } + break; + + case MM_LayerType_Pol: + case MM_LayerType_Pol3d: + // Read polygon + auto poPoly = std::make_unique(); + MM_POLYGON_RINGS_COUNT nIRing; + MM_N_VERTICES_TYPE nIVrtAcum; + + if (phMiraMonLayer->TopHeader.bIsMultipolygon) + { + OGRMultiPolygon *poMP = nullptr; + + poGeom = new OGRMultiPolygon(); + poMP = poGeom->toMultiPolygon(); + + // Get X,Y (Z) n times MiraMon has no multilines + if (MMGetGeoFeatureFromVector(phMiraMonLayer, nIElem)) + { + delete poGeom; + return nullptr; + } + + nIVrtAcum = 0; + if (!phMiraMonLayer->bIsPolygon && + !(phMiraMonLayer->ReadFeature.flag_VFG[0] & + MM_EXTERIOR_ARC_SIDE)) + { + CPLError(CE_Failure, CPLE_NoWriteAccess, + "\nWrong polygon format."); + delete poGeom; + return nullptr; + } + + for (nIRing = 0; + nIRing < phMiraMonLayer->ReadFeature.nNRings; nIRing++) + { + auto poRing = std::make_unique(); + + for (MM_N_VERTICES_TYPE nIVrt = 0; + nIVrt < + phMiraMonLayer->ReadFeature.pNCoordRing[nIRing]; + nIVrt++) + { + if (phMiraMonLayer->TopHeader.bIs3d) + { + poRing->addPoint(phMiraMonLayer->ReadFeature + .pCoord[nIVrtAcum] + .dfX, + phMiraMonLayer->ReadFeature + .pCoord[nIVrtAcum] + .dfY, + phMiraMonLayer->ReadFeature + .pZCoord[nIVrtAcum]); + } + else + { + poRing->addPoint(phMiraMonLayer->ReadFeature + .pCoord[nIVrtAcum] + .dfX, + phMiraMonLayer->ReadFeature + .pCoord[nIVrtAcum] + .dfY); + } + + nIVrtAcum++; + } + + // If I'm going to start a new polygon... + if ((nIRing + 1 < phMiraMonLayer->ReadFeature.nNRings && + ((phMiraMonLayer->ReadFeature + .flag_VFG[nIRing + 1]) & + MM_EXTERIOR_ARC_SIDE)) || + nIRing + 1 >= phMiraMonLayer->ReadFeature.nNRings) + { + poPoly->addRingDirectly(poRing.release()); + poMP->addGeometryDirectly(poPoly.release()); + poPoly = std::make_unique(); + } + else + poPoly->addRingDirectly(poRing.release()); + } + } + else + { + OGRPolygon *poP = nullptr; + + poGeom = new OGRPolygon(); + poP = poGeom->toPolygon(); + + // Get X,Y (Z) n times because MiraMon has no multilinetrings + if (MMGetGeoFeatureFromVector(phMiraMonLayer, nIElem)) + { + delete poGeom; + return nullptr; + } + + if (phMiraMonLayer->ReadFeature.nNRings && + phMiraMonLayer->ReadFeature.nNumpCoord) + { + nIVrtAcum = 0; + if (!(phMiraMonLayer->ReadFeature.flag_VFG[0] & + MM_EXTERIOR_ARC_SIDE)) + { + CPLError(CE_Failure, CPLE_AssertionFailed, + "\nWrong polygon format."); + delete poGeom; + return nullptr; + } + + for (nIRing = 0; + nIRing < phMiraMonLayer->ReadFeature.nNRings; + nIRing++) + { + auto poRing = std::make_unique(); + + for (MM_N_VERTICES_TYPE nIVrt = 0; + nIVrt < phMiraMonLayer->ReadFeature + .pNCoordRing[nIRing]; + nIVrt++) + { + if (phMiraMonLayer->TopHeader.bIs3d) + { + poRing->addPoint(phMiraMonLayer->ReadFeature + .pCoord[nIVrtAcum] + .dfX, + phMiraMonLayer->ReadFeature + .pCoord[nIVrtAcum] + .dfY, + phMiraMonLayer->ReadFeature + .pZCoord[nIVrtAcum]); + } + else + { + poRing->addPoint(phMiraMonLayer->ReadFeature + .pCoord[nIVrtAcum] + .dfX, + phMiraMonLayer->ReadFeature + .pCoord[nIVrtAcum] + .dfY); + } + + nIVrtAcum++; + } + poP->addRingDirectly(poRing.release()); + } + } + } + + break; + } + + if (poGeom == nullptr) + return nullptr; + } + + /* -------------------------------------------------------------------- */ + /* Create feature. */ + /* -------------------------------------------------------------------- */ + auto poFeature = std::make_unique(m_poFeatureDefn); + if (poGeom) + { + poGeom->assignSpatialReference(m_poSRS); + poFeature->SetGeometryDirectly(poGeom); + } + + /* -------------------------------------------------------------------- */ + /* Process field values if its possible. */ + /* -------------------------------------------------------------------- */ + if (phMiraMonLayer->pMMBDXP && + (MM_EXT_DBF_N_RECORDS)nIElem < phMiraMonLayer->pMMBDXP->nRecords) + { + MM_EXT_DBF_N_FIELDS nIField; + + for (nIField = 0; nIField < phMiraMonLayer->pMMBDXP->nFields; nIField++) + { + if (MMResizeStringToOperateIfNeeded( + phMiraMonLayer, + phMiraMonLayer->pMMBDXP->pField[nIField].BytesPerField)) + { + return nullptr; + } + + if (poFeature->GetDefnRef()->GetFieldDefn(nIField)->GetType() == + OFTStringList || + (poFeature->GetDefnRef()->GetFieldDefn(nIField)->GetType() == + OFTString && + poFeature->GetDefnRef()->GetFieldDefn(nIField)->GetSubType() == + OFSTJSON)) + { + if (!phMiraMonLayer->pMultRecordIndex || + phMiraMonLayer->pMultRecordIndex[nIElem].nMR == 0) + { + memset( + phMiraMonLayer->szStringToOperate, 0, + phMiraMonLayer->pMMBDXP->pField[nIField].BytesPerField); + continue; + } + if (poFeature->GetDefnRef() + ->GetFieldDefn(nIField) + ->GetSubType() == OFSTJSON) + { + if (MMResizeStringToOperateIfNeeded( + phMiraMonLayer, + phMiraMonLayer->pMMBDXP->BytesPerRecord + + 2 * phMiraMonLayer->pMultRecordIndex[nIElem] + .nMR + + 8)) + { + return nullptr; + } + std::string szStringToOperate = "["; + for (nIRecord = 0; + nIRecord < + phMiraMonLayer->pMultRecordIndex[nIElem].nMR; + nIRecord++) + { + GoToFieldOfMultipleRecord(nIElem, nIRecord, nIField); + + fread_function(phMiraMonLayer->szStringToOperate, + phMiraMonLayer->pMMBDXP->pField[nIField] + .BytesPerField, + 1, phMiraMonLayer->pMMBDXP->pfDataBase); + phMiraMonLayer + ->szStringToOperate[phMiraMonLayer->pMMBDXP + ->pField[nIField] + .BytesPerField] = '\0'; + MM_RemoveLeadingWhitespaceOfString( + phMiraMonLayer->szStringToOperate); + MM_RemoveWhitespacesFromEndOfString( + phMiraMonLayer->szStringToOperate); + + if (phMiraMonLayer->pMMBDXP->CharSet == + MM_JOC_CARAC_OEM850_DBASE) + MM_oemansi_n( + phMiraMonLayer->szStringToOperate, + phMiraMonLayer->pMMBDXP->pField[nIField] + .BytesPerField); + + if (phMiraMonLayer->pMMBDXP->CharSet != + MM_JOC_CARAC_UTF8_DBF) + { + // MiraMon encoding is ISO 8859-1 (Latin1) -> Recode to UTF-8 + char *pszString = + CPLRecode(phMiraMonLayer->szStringToOperate, + CPL_ENC_ISO8859_1, CPL_ENC_UTF8); + + CPLStrlcpy( + phMiraMonLayer->szStringToOperate, pszString, + (size_t)phMiraMonLayer->pMMBDXP->pField[nIField] + .BytesPerField + + 1); + + CPLFree(pszString); + } + szStringToOperate.append( + phMiraMonLayer->szStringToOperate); + + if (nIRecord < + phMiraMonLayer->pMultRecordIndex[nIElem].nMR - 1) + { + szStringToOperate.append(","); + } + else + { + szStringToOperate.append("]"); + } + } + poFeature->SetField(nIField, szStringToOperate.c_str()); + } + else + { + CPLStringList aosValues; + for (nIRecord = 0; + nIRecord < + phMiraMonLayer->pMultRecordIndex[nIElem].nMR; + nIRecord++) + { + GoToFieldOfMultipleRecord(nIElem, nIRecord, nIField); + memset(phMiraMonLayer->szStringToOperate, 0, + phMiraMonLayer->pMMBDXP->pField[nIField] + .BytesPerField); + fread_function(phMiraMonLayer->szStringToOperate, + phMiraMonLayer->pMMBDXP->pField[nIField] + .BytesPerField, + 1, phMiraMonLayer->pMMBDXP->pfDataBase); + phMiraMonLayer + ->szStringToOperate[phMiraMonLayer->pMMBDXP + ->pField[nIField] + .BytesPerField] = '\0'; + MM_RemoveWhitespacesFromEndOfString( + phMiraMonLayer->szStringToOperate); + + if (phMiraMonLayer->pMMBDXP->CharSet == + MM_JOC_CARAC_OEM850_DBASE) + MM_oemansi_n( + phMiraMonLayer->szStringToOperate, + phMiraMonLayer->pMMBDXP->pField[nIField] + .BytesPerField); + + if (phMiraMonLayer->pMMBDXP->CharSet != + MM_JOC_CARAC_UTF8_DBF) + { + // MiraMon encoding is ISO 8859-1 (Latin1) -> Recode to UTF-8 + char *pszString = + CPLRecode(phMiraMonLayer->szStringToOperate, + CPL_ENC_ISO8859_1, CPL_ENC_UTF8); + + CPLStrlcpy( + phMiraMonLayer->szStringToOperate, pszString, + (size_t)phMiraMonLayer->pMMBDXP->pField[nIField] + .BytesPerField + + 1); + + CPLFree(pszString); + } + aosValues.AddString(phMiraMonLayer->szStringToOperate); + } + poFeature->SetField(nIField, aosValues.List()); + } + } + else if (poFeature->GetDefnRef() + ->GetFieldDefn(nIField) + ->GetType() == OFTString) + { + if (!phMiraMonLayer->pMultRecordIndex || + phMiraMonLayer->pMultRecordIndex[nIElem].nMR == 0) + { + memset( + phMiraMonLayer->szStringToOperate, 0, + phMiraMonLayer->pMMBDXP->pField[nIField].BytesPerField); + continue; + } + if (phMiraMonLayer->iMultiRecord != + MM_MULTIRECORD_NO_MULTIRECORD) + { + if (phMiraMonLayer->iMultiRecord == MM_MULTIRECORD_LAST) + GoToFieldOfMultipleRecord( + nIElem, + phMiraMonLayer->pMultRecordIndex[nIElem].nMR - 1, + nIField); + else if ((MM_EXT_DBF_N_MULTIPLE_RECORDS) + phMiraMonLayer->iMultiRecord < + phMiraMonLayer->pMultRecordIndex[nIElem].nMR) + GoToFieldOfMultipleRecord( + nIElem, + (MM_EXT_DBF_N_MULTIPLE_RECORDS) + phMiraMonLayer->iMultiRecord, + nIField); + else + { + memset(phMiraMonLayer->szStringToOperate, 0, + phMiraMonLayer->pMMBDXP->pField[nIField] + .BytesPerField); + continue; + } + } + else + GoToFieldOfMultipleRecord(nIElem, 0, nIField); + + memset(phMiraMonLayer->szStringToOperate, 0, + phMiraMonLayer->pMMBDXP->pField[nIField].BytesPerField); + fread_function( + phMiraMonLayer->szStringToOperate, + phMiraMonLayer->pMMBDXP->pField[nIField].BytesPerField, 1, + phMiraMonLayer->pMMBDXP->pfDataBase); + phMiraMonLayer + ->szStringToOperate[phMiraMonLayer->pMMBDXP->pField[nIField] + .BytesPerField] = '\0'; + MM_RemoveWhitespacesFromEndOfString( + phMiraMonLayer->szStringToOperate); + + if (phMiraMonLayer->pMMBDXP->CharSet == + MM_JOC_CARAC_OEM850_DBASE) + MM_oemansi(phMiraMonLayer->szStringToOperate); + + if (phMiraMonLayer->pMMBDXP->CharSet != MM_JOC_CARAC_UTF8_DBF) + { + // MiraMon encoding is ISO 8859-1 (Latin1) -> Recode to UTF-8 + char *pszString = + CPLRecode(phMiraMonLayer->szStringToOperate, + CPL_ENC_ISO8859_1, CPL_ENC_UTF8); + CPLStrlcpy(phMiraMonLayer->szStringToOperate, pszString, + (size_t)phMiraMonLayer->pMMBDXP->pField[nIField] + .BytesPerField + + 1); + CPLFree(pszString); + } + poFeature->SetField(nIField, phMiraMonLayer->szStringToOperate); + } + else if (poFeature->GetDefnRef() + ->GetFieldDefn(nIField) + ->GetType() == OFTIntegerList || + poFeature->GetDefnRef() + ->GetFieldDefn(nIField) + ->GetType() == OFTRealList) + { + if (!phMiraMonLayer->pMultRecordIndex || + phMiraMonLayer->pMultRecordIndex[nIElem].nMR == 0) + { + memset( + phMiraMonLayer->szStringToOperate, 0, + phMiraMonLayer->pMMBDXP->pField[nIField].BytesPerField); + continue; + } + for (nIRecord = 0; + nIRecord < phMiraMonLayer->pMultRecordIndex[nIElem].nMR; + nIRecord++) + { + GoToFieldOfMultipleRecord(nIElem, nIRecord, nIField); + memset( + phMiraMonLayer->szStringToOperate, 0, + phMiraMonLayer->pMMBDXP->pField[nIField].BytesPerField); + fread_function( + phMiraMonLayer->szStringToOperate, + phMiraMonLayer->pMMBDXP->pField[nIField].BytesPerField, + 1, phMiraMonLayer->pMMBDXP->pfDataBase); + phMiraMonLayer->szStringToOperate[phMiraMonLayer->pMMBDXP + ->pField[nIField] + .BytesPerField] = + '\0'; + + padfValues[nIRecord] = + atof(phMiraMonLayer->szStringToOperate); + } + + poFeature->SetField( + nIField, phMiraMonLayer->pMultRecordIndex[nIElem].nMR, + padfValues); + } + else if (poFeature->GetDefnRef() + ->GetFieldDefn(nIField) + ->GetType() == OFTInteger64List) + { + if (!phMiraMonLayer->pMultRecordIndex || + phMiraMonLayer->pMultRecordIndex[nIElem].nMR == 0) + { + memset( + phMiraMonLayer->szStringToOperate, 0, + phMiraMonLayer->pMMBDXP->pField[nIField].BytesPerField); + continue; + } + for (nIRecord = 0; + nIRecord < phMiraMonLayer->pMultRecordIndex[nIElem].nMR; + nIRecord++) + { + GoToFieldOfMultipleRecord(nIElem, nIRecord, nIField); + memset( + phMiraMonLayer->szStringToOperate, 0, + phMiraMonLayer->pMMBDXP->pField[nIField].BytesPerField); + fread_function( + phMiraMonLayer->szStringToOperate, + phMiraMonLayer->pMMBDXP->pField[nIField].BytesPerField, + 1, phMiraMonLayer->pMMBDXP->pfDataBase); + phMiraMonLayer->szStringToOperate[phMiraMonLayer->pMMBDXP + ->pField[nIField] + .BytesPerField] = + '\0'; + + pnInt64Values[nIRecord] = + CPLAtoGIntBig(phMiraMonLayer->szStringToOperate); + } + + poFeature->SetField( + nIField, phMiraMonLayer->pMultRecordIndex[nIElem].nMR, + pnInt64Values); + } + else if (poFeature->GetDefnRef() + ->GetFieldDefn(nIField) + ->GetType() == OFTInteger || + poFeature->GetDefnRef() + ->GetFieldDefn(nIField) + ->GetType() == OFTInteger64 || + poFeature->GetDefnRef() + ->GetFieldDefn(nIField) + ->GetType() == OFTReal) + { + if (!phMiraMonLayer->pMultRecordIndex || + phMiraMonLayer->pMultRecordIndex[nIElem].nMR == 0) + { + memset( + phMiraMonLayer->szStringToOperate, 0, + phMiraMonLayer->pMMBDXP->pField[nIField].BytesPerField); + continue; + } + if (phMiraMonLayer->iMultiRecord != + MM_MULTIRECORD_NO_MULTIRECORD) + { + if (phMiraMonLayer->iMultiRecord == MM_MULTIRECORD_LAST) + GoToFieldOfMultipleRecord( + nIElem, + phMiraMonLayer->pMultRecordIndex[nIElem].nMR - 1, + nIField); + else if ((MM_EXT_DBF_N_MULTIPLE_RECORDS) + phMiraMonLayer->iMultiRecord < + phMiraMonLayer->pMultRecordIndex[nIElem].nMR) + GoToFieldOfMultipleRecord( + nIElem, + (MM_EXT_DBF_N_MULTIPLE_RECORDS) + phMiraMonLayer->iMultiRecord, + nIField); + else + { + memset(phMiraMonLayer->szStringToOperate, 0, + phMiraMonLayer->pMMBDXP->pField[nIField] + .BytesPerField); + continue; + } + } + else + GoToFieldOfMultipleRecord(nIElem, 0, nIField); + + memset(phMiraMonLayer->szStringToOperate, 0, + phMiraMonLayer->pMMBDXP->pField[nIField].BytesPerField); + fread_function( + phMiraMonLayer->szStringToOperate, + phMiraMonLayer->pMMBDXP->pField[nIField].BytesPerField, 1, + phMiraMonLayer->pMMBDXP->pfDataBase); + phMiraMonLayer + ->szStringToOperate[phMiraMonLayer->pMMBDXP->pField[nIField] + .BytesPerField] = '\0'; + MM_RemoveWhitespacesFromEndOfString( + phMiraMonLayer->szStringToOperate); + poFeature->SetField(nIField, + atof(phMiraMonLayer->szStringToOperate)); + } + else if (poFeature->GetDefnRef() + ->GetFieldDefn(nIField) + ->GetType() == OFTDate) + { + if (!phMiraMonLayer->pMultRecordIndex || + phMiraMonLayer->pMultRecordIndex[nIElem].nMR == 0) + { + memset( + phMiraMonLayer->szStringToOperate, 0, + phMiraMonLayer->pMMBDXP->pField[nIField].BytesPerField); + continue; + } + if (phMiraMonLayer->iMultiRecord != + MM_MULTIRECORD_NO_MULTIRECORD) + { + if (phMiraMonLayer->iMultiRecord == MM_MULTIRECORD_LAST) + GoToFieldOfMultipleRecord( + nIElem, + phMiraMonLayer->pMultRecordIndex[nIElem].nMR - 1, + nIField); + else if ((MM_EXT_DBF_N_MULTIPLE_RECORDS) + phMiraMonLayer->iMultiRecord < + phMiraMonLayer->pMultRecordIndex[nIElem].nMR) + GoToFieldOfMultipleRecord( + nIElem, + (MM_EXT_DBF_N_MULTIPLE_RECORDS) + phMiraMonLayer->iMultiRecord, + nIField); + else + { + memset(phMiraMonLayer->szStringToOperate, 0, + phMiraMonLayer->pMMBDXP->pField[nIField] + .BytesPerField); + continue; + } + } + else + GoToFieldOfMultipleRecord(nIElem, 0, nIField); + + memset(phMiraMonLayer->szStringToOperate, 0, + phMiraMonLayer->pMMBDXP->pField[nIField].BytesPerField); + fread_function( + phMiraMonLayer->szStringToOperate, + phMiraMonLayer->pMMBDXP->pField[nIField].BytesPerField, 1, + phMiraMonLayer->pMMBDXP->pfDataBase); + phMiraMonLayer + ->szStringToOperate[phMiraMonLayer->pMMBDXP->pField[nIField] + .BytesPerField] = '\0'; + + MM_RemoveWhitespacesFromEndOfString( + phMiraMonLayer->szStringToOperate); + if (!MMIsEmptyString(phMiraMonLayer->szStringToOperate)) + { + char pszDate_5[5]; + char pszDate_3[3]; + int Year, Month, Day; + + CPLStrlcpy(pszDate_5, phMiraMonLayer->szStringToOperate, 5); + pszDate_5[4] = '\0'; + Year = atoi(pszDate_5); + + CPLStrlcpy(pszDate_3, phMiraMonLayer->szStringToOperate + 4, + 3); + (pszDate_3)[2] = '\0'; + Month = atoi(pszDate_3); + + CPLStrlcpy(pszDate_3, phMiraMonLayer->szStringToOperate + 6, + 3); + (pszDate_3)[2] = '\0'; + Day = atoi(pszDate_3); + + poFeature->SetField(nIField, Year, Month, Day); + } + else + poFeature->SetField(nIField, + phMiraMonLayer->szStringToOperate); + } + } + } + + // Even in case of polygons, where the first feature is jumped + // the ID of the first feature has to be 0, the second, 1,... + poFeature->SetFID(nFeatureId); + + m_nFeaturesRead++; + return poFeature.release(); +} + +/****************************************************************************/ +/* GetFeatureCount() */ +/****************************************************************************/ +GIntBig OGRMiraMonLayer::GetFeatureCount(int bForce) +{ + if (!phMiraMonLayer || m_poFilterGeom != nullptr || + m_poAttrQuery != nullptr) + return OGRLayer::GetFeatureCount(bForce); + + if (phMiraMonLayer->bIsPolygon) + { + return std::max((GIntBig)0, + (GIntBig)(phMiraMonLayer->TopHeader.nElemCount - 1)); + } + return (GIntBig)phMiraMonLayer->TopHeader.nElemCount; +} + +/****************************************************************************/ +/* MMProcessMultiGeometry() */ +/****************************************************************************/ +OGRErr OGRMiraMonLayer::MMProcessMultiGeometry(OGRGeometryH hGeom, + OGRFeature *poFeature) + +{ + OGRErr eErr = OGRERR_NONE; + OGRGeometry *poGeom = LOG_ACTION(OGRGeometry::FromHandle(hGeom)); + + if (poGeom == nullptr) + { + CPLError( + CE_Failure, CPLE_AppDefined, + "\nFeatures without geometry not supported by MiraMon writer."); + return LOG_ACTION(OGRERR_FAILURE); + } + + // Multigeometry field processing (just in case of a MG inside a MG) + if (wkbFlatten(poGeom->getGeometryType()) == wkbGeometryCollection) + { + int nGeom = OGR_G_GetGeometryCount(OGRGeometry::ToHandle(poGeom)); + for (int iGeom = 0; iGeom < nGeom; iGeom++) + { + OGRGeometryH poSubGeometry = + OGR_G_GetGeometryRef(OGRGeometry::ToHandle(poGeom), iGeom); + eErr = MMProcessMultiGeometry(poSubGeometry, poFeature); + if (eErr != OGRERR_NONE) + return eErr; + } + return eErr; + } + // Converting multilines and multi points to simple ones + if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString || + wkbFlatten(poGeom->getGeometryType()) == wkbMultiPoint) + { + int nGeom = OGR_G_GetGeometryCount(OGRGeometry::ToHandle(poGeom)); + for (int iGeom = 0; iGeom < nGeom; iGeom++) + { + OGRGeometryH poSubGeometry = + OGR_G_GetGeometryRef(OGRGeometry::ToHandle(poGeom), iGeom); + eErr = MMProcessGeometry(poSubGeometry, poFeature, (iGeom == 0)); + if (eErr != OGRERR_NONE) + return eErr; + } + return eErr; + } + + // Processing a simple geometry + return LOG_ACTION( + MMProcessGeometry(OGRGeometry::ToHandle(poGeom), poFeature, TRUE)); +} + +/****************************************************************************/ +/* MMProcessGeometry() */ +/****************************************************************************/ +OGRErr OGRMiraMonLayer::MMProcessGeometry(OGRGeometryH hGeom, + OGRFeature *poFeature, + MM_BOOLEAN bcalculateRecord) + +{ + OGRErr eErr = OGRERR_NONE; + OGRGeometry *poGeom = nullptr; + if (hGeom) + { + poGeom = OGRGeometry::FromHandle(hGeom); + + // Translating types from GDAL to MiraMon + int eLT = LOG_ACTION(poGeom->getGeometryType()); + switch (wkbFlatten(eLT)) + { + case wkbPoint: + phMiraMonLayer = LOG_ACTION(&hMiraMonLayerPNT); + if (OGR_G_Is3D(hGeom)) + phMiraMonLayer->eLT = MM_LayerType_Point3d; + else + phMiraMonLayer->eLT = MM_LayerType_Point; + break; + case wkbLineString: + phMiraMonLayer = LOG_ACTION(&hMiraMonLayerARC); + if (OGR_G_Is3D(hGeom)) + phMiraMonLayer->eLT = MM_LayerType_Arc3d; + else + phMiraMonLayer->eLT = MM_LayerType_Arc; + break; + case wkbPolygon: + case wkbMultiPolygon: + case wkbPolyhedralSurface: + case wkbTIN: + case wkbTriangle: + phMiraMonLayer = LOG_ACTION(&hMiraMonLayerPOL); + if (OGR_G_Is3D(hGeom)) + phMiraMonLayer->eLT = MM_LayerType_Pol3d; + else + phMiraMonLayer->eLT = MM_LayerType_Pol; + break; + case wkbUnknown: + default: + { + CPLError(CE_Warning, CPLE_NotSupported, + "MiraMon " + "does not support geometry type '%d'", + eLT); + return OGRERR_UNSUPPORTED_GEOMETRY_TYPE; + } + } + } + else + { + // Processing only the table. A DBF will be generated + phMiraMonLayer = LOG_ACTION(&hMiraMonLayerReadOrNonGeom); + phMiraMonLayer->eLT = MM_LayerType_Unknown; + } + + /* -------------------------------------------------------------------- */ + /* Field translation from GDAL to MiraMon */ + /* -------------------------------------------------------------------- */ + // Reset the object where read coordinates are going to be stored + MMResetFeatureGeometry(&hMMFeature); + if (bcalculateRecord) + { + MMResetFeatureRecord(&hMMFeature); + if (!phMiraMonLayer->pLayerDB) + { + eErr = TranslateFieldsToMM(); + if (eErr != OGRERR_NONE) + return eErr; + } + // Content field translation from GDAL to MiraMon + eErr = TranslateFieldsValuesToMM(poFeature); + if (eErr != OGRERR_NONE) + { + CPLDebugOnly("MiraMon", "Error in MMProcessGeometry()"); + return eErr; + } + } + + /* -------------------------------------------------------------------- */ + /* Write Geometry */ + /* -------------------------------------------------------------------- */ + + // Reads objects with coordinates and transform them to MiraMon + if (poGeom) + { + eErr = MMLoadGeometry(OGRGeometry::ToHandle(poGeom)); + } + else + { + if (!phMiraMonLayer->bIsBeenInit) + { + phMiraMonLayer->bIsDBF = TRUE; + if (MMInitLayerByType(phMiraMonLayer)) + eErr = OGRERR_FAILURE; + + phMiraMonLayer->bIsBeenInit = 1; + } + } + + // Writes coordinates to the disk + if (eErr == OGRERR_NONE) + return MMWriteGeometry(); + CPLDebugOnly("MiraMon", "Error in MMProcessGeometry()"); + return eErr; +} + +/****************************************************************************/ +/* ICreateFeature() */ +/****************************************************************************/ + +OGRErr OGRMiraMonLayer::ICreateFeature(OGRFeature *poFeature) + +{ + OGRErr eErr = OGRERR_NONE; + + if (!m_bUpdate) + { + CPLError(CE_Failure, CPLE_NoWriteAccess, + "Cannot create features on a read-only dataset."); + return OGRERR_FAILURE; + } + + /* -------------------------------------------------------------------- */ + /* Write out the feature */ + /* -------------------------------------------------------------------- */ + OGRGeometry *poGeom = LOG_ACTION(poFeature->GetGeometryRef()); + + // Processing a feature without geometry. + if (poGeom == nullptr) + { + eErr = LOG_ACTION(MMProcessGeometry(nullptr, poFeature, TRUE)); + if (phMiraMonLayer->bIsDBF) + poFeature->SetFID(phMiraMonLayer->TopHeader.nElemCount - 1); + return eErr; + } + + // Converting to simple geometries + if (wkbFlatten(poGeom->getGeometryType()) == wkbGeometryCollection) + { + int nGeom = + LOG_ACTION(OGR_G_GetGeometryCount(OGRGeometry::ToHandle(poGeom))); + for (int iGeom = 0; iGeom < nGeom; iGeom++) + { + OGRGeometryH poSubGeometry = LOG_ACTION( + OGR_G_GetGeometryRef(OGRGeometry::ToHandle(poGeom), iGeom)); + eErr = LOG_ACTION(MMProcessMultiGeometry(poSubGeometry, poFeature)); + if (eErr != OGRERR_NONE) + return eErr; + } + + return eErr; + } + + // Processing the geometry + eErr = LOG_ACTION( + MMProcessMultiGeometry(OGRGeometry::ToHandle(poGeom), poFeature)); + + // Set the FID from 0 index + if (phMiraMonLayer) + { + if (phMiraMonLayer->bIsPolygon && + phMiraMonLayer->TopHeader.nElemCount > 1) + poFeature->SetFID((GIntBig)phMiraMonLayer->TopHeader.nElemCount - + 2); + else if (phMiraMonLayer->TopHeader.nElemCount > 0) + poFeature->SetFID((GIntBig)phMiraMonLayer->TopHeader.nElemCount - + 1); + } + return eErr; +} + +/****************************************************************************/ +/* MMDumpVertices() */ +/****************************************************************************/ + +OGRErr OGRMiraMonLayer::MMDumpVertices(OGRGeometryH hGeom, + MM_BOOLEAN bExternalRing, + MM_BOOLEAN bUseVFG) +{ + // If the MiraMonLayer structure has not been init, + // here is the moment to do that. + if (!phMiraMonLayer) + return OGRERR_FAILURE; + + if (!phMiraMonLayer->bIsBeenInit) + { + if (MMInitLayerByType(phMiraMonLayer)) + return OGRERR_FAILURE; + phMiraMonLayer->bIsBeenInit = 1; + } + if (MMResize_MM_N_VERTICES_TYPE_Pointer( + &hMMFeature.pNCoordRing, &hMMFeature.nMaxpNCoordRing, + (MM_N_VERTICES_TYPE)hMMFeature.nNRings + 1, MM_MEAN_NUMBER_OF_RINGS, + 0)) + return OGRERR_FAILURE; + + if (bUseVFG) + { + if (MMResizeVFGPointer(&hMMFeature.flag_VFG, &hMMFeature.nMaxVFG, + (MM_INTERNAL_FID)hMMFeature.nNRings + 1, + MM_MEAN_NUMBER_OF_RINGS, 0)) + return OGRERR_FAILURE; + + hMMFeature.flag_VFG[hMMFeature.nIRing] = MM_END_ARC_IN_RING; + if (bExternalRing) + hMMFeature.flag_VFG[hMMFeature.nIRing] |= MM_EXTERIOR_ARC_SIDE; + // In MiraMon the external ring is clockwise and the internals are + // coounterclockwise. + OGRGeometry *poGeom = OGRGeometry::FromHandle(hGeom); + if ((bExternalRing && !poGeom->toLinearRing()->isClockwise()) || + (!bExternalRing && poGeom->toLinearRing()->isClockwise())) + hMMFeature.flag_VFG[hMMFeature.nIRing] |= MM_ROTATE_ARC; + } + + hMMFeature.pNCoordRing[hMMFeature.nIRing] = OGR_G_GetPointCount(hGeom); + + if (MMResizeMM_POINT2DPointer(&hMMFeature.pCoord, &hMMFeature.nMaxpCoord, + hMMFeature.nICoord + + hMMFeature.pNCoordRing[hMMFeature.nIRing], + MM_MEAN_NUMBER_OF_NCOORDS, 0)) + return OGRERR_FAILURE; + if (MMResizeDoublePointer(&hMMFeature.pZCoord, &hMMFeature.nMaxpZCoord, + hMMFeature.nICoord + + hMMFeature.pNCoordRing[hMMFeature.nIRing], + MM_MEAN_NUMBER_OF_NCOORDS, 0)) + return OGRERR_FAILURE; + + for (int iPoint = 0; + (MM_N_VERTICES_TYPE)iPoint < hMMFeature.pNCoordRing[hMMFeature.nIRing]; + iPoint++) + { + hMMFeature.pCoord[hMMFeature.nICoord].dfX = OGR_G_GetX(hGeom, iPoint); + hMMFeature.pCoord[hMMFeature.nICoord].dfY = OGR_G_GetY(hGeom, iPoint); + if (OGR_G_GetCoordinateDimension(hGeom) == 2) + hMMFeature.pZCoord[hMMFeature.nICoord] = + MM_NODATA_COORD_Z; // Possible rare case + else + { + hMMFeature.pZCoord[hMMFeature.nICoord] = OGR_G_GetZ(hGeom, iPoint); + phMiraMonLayer->bIsReal3d = 1; + } + + hMMFeature.nICoord++; + } + hMMFeature.nIRing++; + hMMFeature.nNRings++; + return OGRERR_NONE; +} + +/****************************************************************************/ +/* MMLoadGeometry() */ +/* */ +/* Loads on a MiraMon object Feature all coordinates from feature */ +/* */ +/****************************************************************************/ +OGRErr OGRMiraMonLayer::MMLoadGeometry(OGRGeometryH hGeom) + +{ + OGRErr eErr = OGRERR_NONE; + MM_BOOLEAN bExternalRing; + + /* -------------------------------------------------------------------- */ + /* This is a geometry with sub-geometries. */ + /* -------------------------------------------------------------------- */ + int nGeom = OGR_G_GetGeometryCount(hGeom); + + int eLT = LOG_ACTION(wkbFlatten(OGR_G_GetGeometryType(hGeom))); + + if (eLT == wkbMultiPolygon || eLT == wkbPolyhedralSurface || + eLT == wkbTIN || eLT == wkbTriangle) + { + for (int iGeom = 0; iGeom < nGeom; iGeom++) + { + OGRGeometryH poSubGeometry = OGR_G_GetGeometryRef(hGeom, iGeom); + + // Reads all coordinates + eErr = MMLoadGeometry(poSubGeometry); + if (eErr != OGRERR_NONE) + return eErr; + } + } + else if (eLT == wkbPolygon) + { + for (int iGeom = 0; iGeom < nGeom && eErr == OGRERR_NONE; iGeom++) + { + OGRGeometryH poSubGeometry = OGR_G_GetGeometryRef(hGeom, iGeom); + + if (iGeom == 0) + bExternalRing = true; + else + bExternalRing = false; + + eErr = MMDumpVertices(poSubGeometry, bExternalRing, TRUE); + if (eErr != OGRERR_NONE) + return eErr; + } + } + else if (eLT == wkbPoint || eLT == wkbLineString) + { + // Reads all coordinates + eErr = MMDumpVertices(hGeom, true, FALSE); + + if (eErr != OGRERR_NONE) + return eErr; + } + else if (eLT == wkbGeometryCollection) + { + CPLError( + CE_Failure, CPLE_NotSupported, + "MiraMon: wkbGeometryCollection inside a wkbGeometryCollection?"); + return OGRERR_UNSUPPORTED_GEOMETRY_TYPE; + } + + return OGRERR_NONE; +} + +/****************************************************************************/ +/* WriteGeometry() */ +/* */ +/* Writes a geometry to the file. */ +/****************************************************************************/ + +OGRErr OGRMiraMonLayer::MMWriteGeometry() + +{ + OGRErr eErr = MMAddFeature(phMiraMonLayer, &hMMFeature); + + if (eErr == MM_FATAL_ERROR_WRITING_FEATURES) + { + CPLDebugOnly("MiraMon", "Error in MMAddFeature() " + "MM_FATAL_ERROR_WRITING_FEATURES"); + CPLError(CE_Failure, CPLE_FileIO, "MiraMon write failure: %s", + VSIStrerror(errno)); + return OGRERR_FAILURE; + } + if (eErr == MM_STOP_WRITING_FEATURES) + { + 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)."); + return OGRERR_FAILURE; + } + + return OGRERR_NONE; +} + +/****************************************************************************/ +/* TranslateFieldsToMM() */ +/* */ +/* Translase ogr Fields to a structure that MiraMon can understand */ +/****************************************************************************/ + +OGRErr OGRMiraMonLayer::TranslateFieldsToMM() + +{ + if (m_poFeatureDefn->GetFieldCount() == 0) + return OGRERR_NONE; + + CPLDebugOnly("MiraMon", "Translating fields to MiraMon..."); + // If the structure is filled we do anything + if (phMiraMonLayer->pLayerDB) + return OGRERR_NONE; + + phMiraMonLayer->pLayerDB = static_cast( + VSICalloc(sizeof(*phMiraMonLayer->pLayerDB), 1)); + if (!phMiraMonLayer->pLayerDB) + return OGRERR_NOT_ENOUGH_MEMORY; + + phMiraMonLayer->pLayerDB->pFields = + static_cast( + VSICalloc(m_poFeatureDefn->GetFieldCount(), + sizeof(*(phMiraMonLayer->pLayerDB->pFields)))); + if (!phMiraMonLayer->pLayerDB->pFields) + return OGRERR_NOT_ENOUGH_MEMORY; + + phMiraMonLayer->pLayerDB->nNFields = 0; + if (phMiraMonLayer->pLayerDB->pFields) + { + memset(phMiraMonLayer->pLayerDB->pFields, 0, + m_poFeatureDefn->GetFieldCount() * + sizeof(*phMiraMonLayer->pLayerDB->pFields)); + for (MM_EXT_DBF_N_FIELDS iField = 0; + iField < (MM_EXT_DBF_N_FIELDS)m_poFeatureDefn->GetFieldCount(); + iField++) + { + switch (m_poFeatureDefn->GetFieldDefn(iField)->GetType()) + { + case OFTInteger: + case OFTIntegerList: + phMiraMonLayer->pLayerDB->pFields[iField].eFieldType = + MM_Numeric; + phMiraMonLayer->pLayerDB->pFields[iField] + .nNumberOfDecimals = 0; + break; + + case OFTInteger64: + case OFTInteger64List: + phMiraMonLayer->pLayerDB->pFields[iField].bIs64BitInteger = + TRUE; + phMiraMonLayer->pLayerDB->pFields[iField].eFieldType = + MM_Numeric; + phMiraMonLayer->pLayerDB->pFields[iField] + .nNumberOfDecimals = 0; + break; + + case OFTReal: + case OFTRealList: + phMiraMonLayer->pLayerDB->pFields[iField].eFieldType = + MM_Numeric; + phMiraMonLayer->pLayerDB->pFields[iField] + .nNumberOfDecimals = + m_poFeatureDefn->GetFieldDefn(iField)->GetPrecision(); + break; + + case OFTBinary: + phMiraMonLayer->pLayerDB->pFields[iField].eFieldType = + MM_Character; + break; + + case OFTDate: + phMiraMonLayer->pLayerDB->pFields[iField].eFieldType = + MM_Data; + break; + + case OFTTime: + case OFTDateTime: + phMiraMonLayer->pLayerDB->pFields[iField].eFieldType = + MM_Character; + break; + + case OFTString: + case OFTStringList: + default: + phMiraMonLayer->pLayerDB->pFields[iField].eFieldType = + MM_Character; + break; + } + if (m_poFeatureDefn->GetFieldDefn(iField)->GetType() == OFTDate) + phMiraMonLayer->pLayerDB->pFields[iField].nFieldSize = 8; + else + { + // As https://gdal.org/api/ogrfeature_cpp.html indicates that + // precision (number of digits after decimal point) is optional, + // and a 0 is probably the default value, in that case we prefer + // to save all the guaranteed significant figures in a double + // (needed if a field contains, for instance, coordinates in + // geodetic degrees and a 1:1000 map precision applies). + if (m_poFeatureDefn->GetFieldDefn(iField)->GetPrecision() == 0) + { + if (m_poFeatureDefn->GetFieldDefn(iField)->GetType() == + OFTReal || + m_poFeatureDefn->GetFieldDefn(iField)->GetType() == + OFTRealList) + { + phMiraMonLayer->pLayerDB->pFields[iField].nFieldSize = + 20; + phMiraMonLayer->pLayerDB->pFields[iField] + .nNumberOfDecimals = MAX_RELIABLE_SF_DOUBLE; + } + else + { + phMiraMonLayer->pLayerDB->pFields[iField].nFieldSize = + m_poFeatureDefn->GetFieldDefn(iField)->GetWidth(); + if (phMiraMonLayer->pLayerDB->pFields[iField] + .nFieldSize == 0) + phMiraMonLayer->pLayerDB->pFields[iField] + .nFieldSize = 1; + } + } + else + { + // One more space for the "." + phMiraMonLayer->pLayerDB->pFields[iField].nFieldSize = + (unsigned int)(m_poFeatureDefn->GetFieldDefn(iField) + ->GetWidth() + + 1); + } + } + + // Recode from UTF-8 if necessary + if (phMiraMonLayer->nCharSet != MM_JOC_CARAC_UTF8_DBF) + { + char *pszString = CPLRecode( + m_poFeatureDefn->GetFieldDefn(iField)->GetNameRef(), + CPL_ENC_UTF8, CPL_ENC_ISO8859_1); + CPLStrlcpy( + phMiraMonLayer->pLayerDB->pFields[iField].pszFieldName, + pszString, MM_MAX_LON_FIELD_NAME_DBF); + CPLFree(pszString); + } + else + { + CPLStrlcpy( + phMiraMonLayer->pLayerDB->pFields[iField].pszFieldName, + m_poFeatureDefn->GetFieldDefn(iField)->GetNameRef(), + MM_MAX_LON_FIELD_NAME_DBF); + } + + if (m_poFeatureDefn->GetFieldDefn(iField)->GetAlternativeNameRef()) + { + if (phMiraMonLayer->nCharSet != MM_JOC_CARAC_UTF8_DBF) + { + char *pszString = + CPLRecode(m_poFeatureDefn->GetFieldDefn(iField) + ->GetAlternativeNameRef(), + CPL_ENC_UTF8, CPL_ENC_ISO8859_1); + CPLStrlcpy(phMiraMonLayer->pLayerDB->pFields[iField] + .pszFieldDescription, + pszString, MM_MAX_BYTES_FIELD_DESC); + CPLFree(pszString); + } + else + { + CPLStrlcpy(phMiraMonLayer->pLayerDB->pFields[iField] + .pszFieldDescription, + m_poFeatureDefn->GetFieldDefn(iField) + ->GetAlternativeNameRef(), + MM_MAX_BYTES_FIELD_DESC); + } + } + phMiraMonLayer->pLayerDB->nNFields++; + } + } + + CPLDebugOnly("MiraMon", "Fields to MiraMon translated."); + return OGRERR_NONE; +} + +/****************************************************************************/ +/* TranslateFieldsValuesToMM() */ +/* */ +/* Translate ogr Fields to a structure that MiraMon can understand */ +/****************************************************************************/ + +OGRErr OGRMiraMonLayer::TranslateFieldsValuesToMM(OGRFeature *poFeature) + +{ + if (m_poFeatureDefn->GetFieldCount() == 0) + { + // MiraMon have private DataBase records + hMMFeature.nNumMRecords = 1; + return OGRERR_NONE; + } + + MM_EXT_DBF_N_MULTIPLE_RECORDS nIRecord; + int nNumFields = m_poFeatureDefn->GetFieldCount(); + MM_EXT_DBF_N_MULTIPLE_RECORDS nNumRecords, nRealNumRecords; + hMMFeature.nNumMRecords = 0; + + for (int iField = 0; iField < nNumFields; iField++) + { + OGRFieldType eFType = m_poFeatureDefn->GetFieldDefn(iField)->GetType(); + const char *pszRawValue = poFeature->GetFieldAsString(iField); + + if (eFType == OFTStringList) + { + char **papszValues = poFeature->GetFieldAsStringList(iField); + nRealNumRecords = nNumRecords = CSLCount(papszValues); + if (nNumRecords == 0) + nNumRecords++; + hMMFeature.nNumMRecords = + max_function(hMMFeature.nNumMRecords, nNumRecords); + if (MMResizeMiraMonRecord( + &hMMFeature.pRecords, &hMMFeature.nMaxMRecords, + hMMFeature.nNumMRecords, MM_INC_NUMBER_OF_RECORDS, + hMMFeature.nNumMRecords)) + return OGRERR_NOT_ENOUGH_MEMORY; + + for (nIRecord = 0; nIRecord < nRealNumRecords; nIRecord++) + { + hMMFeature.pRecords[nIRecord].nNumField = + m_poFeatureDefn->GetFieldCount(); + + if (MMResizeMiraMonFieldValue( + &(hMMFeature.pRecords[nIRecord].pField), + &hMMFeature.pRecords[nIRecord].nMaxField, + hMMFeature.pRecords[nIRecord].nNumField, + MM_INC_NUMBER_OF_FIELDS, + hMMFeature.pRecords[nIRecord].nNumField)) + return OGRERR_NOT_ENOUGH_MEMORY; + + if (phMiraMonLayer->nCharSet != MM_JOC_CARAC_UTF8_DBF) + { + // MiraMon encoding is ISO 8859-1 (Latin1) -> Recode from UTF-8 + char *pszString = CPLRecode( + papszValues[nIRecord], CPL_ENC_UTF8, CPL_ENC_ISO8859_1); + if (MM_SecureCopyStringFieldValue( + &hMMFeature.pRecords[nIRecord] + .pField[iField] + .pDinValue, + pszString, + &hMMFeature.pRecords[nIRecord] + .pField[iField] + .nNumDinValue)) + { + CPLFree(pszString); + return OGRERR_NOT_ENOUGH_MEMORY; + } + CPLFree(pszString); + } + else + { + if (MM_SecureCopyStringFieldValue( + &hMMFeature.pRecords[nIRecord] + .pField[iField] + .pDinValue, + papszValues[nIRecord], + &hMMFeature.pRecords[nIRecord] + .pField[iField] + .nNumDinValue)) + return OGRERR_NOT_ENOUGH_MEMORY; + } + hMMFeature.pRecords[nIRecord].pField[iField].bIsValid = 1; + } + } + else if (eFType == OFTIntegerList) + { + int nCount = 0; + const int *panValues = + poFeature->GetFieldAsIntegerList(iField, &nCount); + + nRealNumRecords = nNumRecords = nCount; + if (nNumRecords == 0) + nNumRecords++; + hMMFeature.nNumMRecords = + max_function(hMMFeature.nNumMRecords, nNumRecords); + if (MMResizeMiraMonRecord( + &hMMFeature.pRecords, &hMMFeature.nMaxMRecords, + hMMFeature.nNumMRecords, MM_INC_NUMBER_OF_RECORDS, + hMMFeature.nNumMRecords)) + return OGRERR_NOT_ENOUGH_MEMORY; + + // It will contains the i-th element of the list. + for (nIRecord = 0; nIRecord < nRealNumRecords; nIRecord++) + { + hMMFeature.pRecords[nIRecord].nNumField = nNumFields; + + if (MMResizeMiraMonFieldValue( + &(hMMFeature.pRecords[nIRecord].pField), + &hMMFeature.pRecords[nIRecord].nMaxField, + hMMFeature.pRecords[nIRecord].nNumField, + MM_INC_NUMBER_OF_FIELDS, + hMMFeature.pRecords[nIRecord].nNumField)) + return OGRERR_NOT_ENOUGH_MEMORY; + + hMMFeature.pRecords[nIRecord].pField[iField].dValue = + panValues[nIRecord]; + + if (MM_SecureCopyStringFieldValue( + &hMMFeature.pRecords[nIRecord].pField[iField].pDinValue, + CPLSPrintf("%d", panValues[nIRecord]), + &hMMFeature.pRecords[nIRecord] + .pField[iField] + .nNumDinValue)) + return OGRERR_NOT_ENOUGH_MEMORY; + + hMMFeature.pRecords[nIRecord].pField[iField].bIsValid = 1; + } + } + else if (eFType == OFTInteger64List) + { + int nCount = 0; + const GIntBig *panValues = + poFeature->GetFieldAsInteger64List(iField, &nCount); + + nRealNumRecords = nNumRecords = nCount; + if (nNumRecords == 0) + nNumRecords++; + hMMFeature.nNumMRecords = + max_function(hMMFeature.nNumMRecords, nNumRecords); + if (MMResizeMiraMonRecord( + &hMMFeature.pRecords, &hMMFeature.nMaxMRecords, + hMMFeature.nNumMRecords, MM_INC_NUMBER_OF_RECORDS, + hMMFeature.nNumMRecords)) + return OGRERR_NOT_ENOUGH_MEMORY; + + // It will contains the i-th element of the list. + for (nIRecord = 0; nIRecord < nRealNumRecords; nIRecord++) + { + hMMFeature.pRecords[nIRecord].nNumField = nNumFields; + + if (MMResizeMiraMonFieldValue( + &(hMMFeature.pRecords[nIRecord].pField), + &hMMFeature.pRecords[nIRecord].nMaxField, + hMMFeature.pRecords[nIRecord].nNumField, + MM_INC_NUMBER_OF_FIELDS, + hMMFeature.pRecords[nIRecord].nNumField)) + return OGRERR_NOT_ENOUGH_MEMORY; + + hMMFeature.pRecords[nIRecord].pField[iField].iValue = + panValues[nIRecord]; + + if (MM_SecureCopyStringFieldValue( + &hMMFeature.pRecords[nIRecord].pField[iField].pDinValue, + CPLSPrintf("%" CPL_FRMT_GB_WITHOUT_PREFIX "d", + panValues[nIRecord]), + &hMMFeature.pRecords[nIRecord] + .pField[iField] + .nNumDinValue)) + return OGRERR_NOT_ENOUGH_MEMORY; + hMMFeature.pRecords[nIRecord].pField[iField].bIsValid = 1; + } + } + else if (eFType == OFTRealList) + { + int nCount = 0; + const double *padfRLValues = + poFeature->GetFieldAsDoubleList(iField, &nCount); + //char format[23]; + + nRealNumRecords = nNumRecords = nCount; + if (nNumRecords == 0) + nNumRecords++; + hMMFeature.nNumMRecords = + max_function(hMMFeature.nNumMRecords, nNumRecords); + if (MMResizeMiraMonRecord( + &hMMFeature.pRecords, &hMMFeature.nMaxMRecords, + hMMFeature.nNumMRecords, MM_INC_NUMBER_OF_RECORDS, + hMMFeature.nNumMRecords)) + return OGRERR_NOT_ENOUGH_MEMORY; + + // It will contains the i-th element of the list. + for (nIRecord = 0; nIRecord < nRealNumRecords; nIRecord++) + { + hMMFeature.pRecords[nIRecord].nNumField = iField; + + if (MMResizeMiraMonFieldValue( + &(hMMFeature.pRecords[nIRecord].pField), + &hMMFeature.pRecords[nIRecord].nMaxField, + hMMFeature.pRecords[nIRecord].nNumField, + MM_INC_NUMBER_OF_FIELDS, + hMMFeature.pRecords[nIRecord].nNumField)) + return OGRERR_NOT_ENOUGH_MEMORY; + + hMMFeature.pRecords[nIRecord].pField[iField].dValue = + padfRLValues[nIRecord]; + + // TODO: decide how many decimals use. If possible. + //CPLStrlcpy(format, CPLSPrintf("%f", padfRLValues[nIRecord]),23); + + if (MM_SecureCopyStringFieldValue( + &hMMFeature.pRecords[nIRecord].pField[iField].pDinValue, + CPLSPrintf("%f", padfRLValues[nIRecord]), + &hMMFeature.pRecords[nIRecord] + .pField[iField] + .nNumDinValue)) + return OGRERR_NOT_ENOUGH_MEMORY; + hMMFeature.pRecords[nIRecord].pField[iField].bIsValid = 1; + } + } + else if (eFType == OFTString) + { + hMMFeature.nNumMRecords = max_function(hMMFeature.nNumMRecords, 1); + hMMFeature.pRecords[0].nNumField = nNumFields; + if (MMResizeMiraMonFieldValue(&(hMMFeature.pRecords[0].pField), + &hMMFeature.pRecords[0].nMaxField, + hMMFeature.pRecords[0].nNumField, + MM_INC_NUMBER_OF_FIELDS, + hMMFeature.pRecords[0].nNumField)) + return OGRERR_NOT_ENOUGH_MEMORY; + + if (MMIsEmptyString(pszRawValue)) + hMMFeature.pRecords[0].pField[iField].bIsValid = 0; + { + if (phMiraMonLayer->nCharSet != MM_JOC_CARAC_UTF8_DBF) + { + // MiraMon encoding is ISO 8859-1 (Latin1) -> Recode from UTF-8 + char *pszString = + CPLRecode(pszRawValue, CPL_ENC_UTF8, CPL_ENC_ISO8859_1); + if (MM_SecureCopyStringFieldValue( + &hMMFeature.pRecords[0].pField[iField].pDinValue, + pszString, + &hMMFeature.pRecords[0] + .pField[iField] + .nNumDinValue)) + { + CPLFree(pszString); + return OGRERR_NOT_ENOUGH_MEMORY; + } + CPLFree(pszString); + } + else + { + if (MM_SecureCopyStringFieldValue( + &hMMFeature.pRecords[0].pField[iField].pDinValue, + pszRawValue, + &hMMFeature.pRecords[0] + .pField[iField] + .nNumDinValue)) + { + return OGRERR_NOT_ENOUGH_MEMORY; + } + } + } + hMMFeature.pRecords[0].pField[iField].bIsValid = 1; + } + else if (eFType == OFTDate) + { + char szDate[15]; + + hMMFeature.nNumMRecords = max_function(hMMFeature.nNumMRecords, 1); + hMMFeature.pRecords[0].nNumField = nNumFields; + if (MMResizeMiraMonFieldValue(&(hMMFeature.pRecords[0].pField), + &hMMFeature.pRecords[0].nMaxField, + hMMFeature.pRecords[0].nNumField, + MM_INC_NUMBER_OF_FIELDS, + hMMFeature.pRecords[0].nNumField)) + return OGRERR_NOT_ENOUGH_MEMORY; + + if (MMIsEmptyString(pszRawValue)) + hMMFeature.pRecords[0].pField[iField].bIsValid = 0; + else + { + const OGRField *poField = poFeature->GetRawFieldRef(iField); + if (poField->Date.Year >= 0) + snprintf(szDate, sizeof(szDate), "%04d%02d%02d", + poField->Date.Year, poField->Date.Month, + poField->Date.Day); + else + snprintf(szDate, sizeof(szDate), "%04d%02d%02d", 0, 0, 0); + + if (MM_SecureCopyStringFieldValue( + &hMMFeature.pRecords[0].pField[iField].pDinValue, + szDate, + &hMMFeature.pRecords[0].pField[iField].nNumDinValue)) + return OGRERR_NOT_ENOUGH_MEMORY; + hMMFeature.pRecords[0].pField[iField].bIsValid = 1; + } + } + else if (eFType == OFTTime || eFType == OFTDateTime) + { + hMMFeature.nNumMRecords = max_function(hMMFeature.nNumMRecords, 1); + hMMFeature.pRecords[0].nNumField = nNumFields; + if (MMResizeMiraMonFieldValue(&(hMMFeature.pRecords[0].pField), + &hMMFeature.pRecords[0].nMaxField, + hMMFeature.pRecords[0].nNumField, + MM_INC_NUMBER_OF_FIELDS, + hMMFeature.pRecords[0].nNumField)) + return OGRERR_NOT_ENOUGH_MEMORY; + + if (MMIsEmptyString(pszRawValue)) + hMMFeature.pRecords[0].pField[iField].bIsValid = 0; + else + { + // MiraMon encoding is ISO 8859-1 (Latin1) -> Recode from UTF-8 + if (MM_SecureCopyStringFieldValue( + &hMMFeature.pRecords[0].pField[iField].pDinValue, + pszRawValue, + &hMMFeature.pRecords[0].pField[iField].nNumDinValue)) + return OGRERR_NOT_ENOUGH_MEMORY; + + hMMFeature.pRecords[0].pField[iField].bIsValid = 1; + } + } + else if (eFType == OFTInteger) + { + hMMFeature.nNumMRecords = max_function(hMMFeature.nNumMRecords, 1); + hMMFeature.pRecords[0].nNumField = nNumFields; + if (MMResizeMiraMonFieldValue(&(hMMFeature.pRecords[0].pField), + &hMMFeature.pRecords[0].nMaxField, + hMMFeature.pRecords[0].nNumField, + MM_INC_NUMBER_OF_FIELDS, + hMMFeature.pRecords[0].nNumField)) + return OGRERR_NOT_ENOUGH_MEMORY; + + if (MMIsEmptyString(pszRawValue)) + hMMFeature.pRecords[0].pField[iField].bIsValid = 0; + else + { + hMMFeature.pRecords[0].pField[iField].dValue = + poFeature->GetFieldAsInteger(iField); + + if (MM_SecureCopyStringFieldValue( + &hMMFeature.pRecords[0].pField[iField].pDinValue, + pszRawValue, + &hMMFeature.pRecords[0].pField[iField].nNumDinValue)) + return OGRERR_NOT_ENOUGH_MEMORY; + hMMFeature.pRecords[0].pField[iField].bIsValid = 1; + } + } + else if (eFType == OFTInteger64) + { + hMMFeature.nNumMRecords = max_function(hMMFeature.nNumMRecords, 1); + hMMFeature.pRecords[0].nNumField = nNumFields; + if (MMResizeMiraMonFieldValue(&(hMMFeature.pRecords[0].pField), + &hMMFeature.pRecords[0].nMaxField, + hMMFeature.pRecords[0].nNumField, + MM_INC_NUMBER_OF_FIELDS, + hMMFeature.pRecords[0].nNumField)) + return OGRERR_NOT_ENOUGH_MEMORY; + + if (MMIsEmptyString(pszRawValue)) + hMMFeature.pRecords[0].pField[iField].bIsValid = 0; + else + { + hMMFeature.pRecords[0].pField[iField].iValue = + poFeature->GetFieldAsInteger64(iField); + + if (MM_SecureCopyStringFieldValue( + &hMMFeature.pRecords[0].pField[iField].pDinValue, + pszRawValue, + &hMMFeature.pRecords[0].pField[iField].nNumDinValue)) + return OGRERR_NOT_ENOUGH_MEMORY; + hMMFeature.pRecords[0].pField[iField].bIsValid = 1; + } + } + else if (eFType == OFTReal) + { + hMMFeature.nNumMRecords = max_function(hMMFeature.nNumMRecords, 1); + hMMFeature.pRecords[0].nNumField = nNumFields; + if (MMResizeMiraMonFieldValue(&(hMMFeature.pRecords[0].pField), + &hMMFeature.pRecords[0].nMaxField, + hMMFeature.pRecords[0].nNumField, + MM_INC_NUMBER_OF_FIELDS, + hMMFeature.pRecords[0].nNumField)) + return OGRERR_NOT_ENOUGH_MEMORY; + + if (MMIsEmptyString(pszRawValue)) + hMMFeature.pRecords[0].pField[iField].bIsValid = 0; + else + { + hMMFeature.pRecords[0].pField[iField].dValue = + poFeature->GetFieldAsDouble(iField); + + if (MM_SecureCopyStringFieldValue( + &hMMFeature.pRecords[0].pField[iField].pDinValue, + pszRawValue, + &hMMFeature.pRecords[0].pField[iField].nNumDinValue)) + return OGRERR_NOT_ENOUGH_MEMORY; + hMMFeature.pRecords[0].pField[iField].bIsValid = 1; + } + } + else + { + CPLError(CE_Warning, CPLE_NotSupported, + "MiraMon: Field type %d not processed by MiraMon\n", + eFType); + hMMFeature.pRecords[0].pField[iField].bIsValid = 0; + } + } + + return OGRERR_NONE; +} + +/****************************************************************************/ +/* GetLayerDefn() */ +/* */ +/****************************************************************************/ +OGRFeatureDefn *OGRMiraMonLayer::GetLayerDefn() +{ + return m_poFeatureDefn; +} + +/****************************************************************************/ +/* GetExtent() */ +/* */ +/* Fetch extent of the data currently stored in the dataset. */ +/* The bForce flag has no effect on SHO files since that value */ +/* is always in the header. */ +/****************************************************************************/ + +OGRErr OGRMiraMonLayer::GetExtent(OGREnvelope *psExtent, int bForce) + +{ + if (phMiraMonLayer) + { + if (phMiraMonLayer->bIsDBF) + return OGRERR_FAILURE; + + // For polygons we need another polygon apart from the universal one + // to have a valid extension + if (phMiraMonLayer->bIsPolygon && + phMiraMonLayer->TopHeader.nElemCount < 1) + return OGRERR_FAILURE; + + if (phMiraMonLayer->TopHeader.nElemCount < 1) + return OGRERR_FAILURE; + + psExtent->MinX = phMiraMonLayer->TopHeader.hBB.dfMinX; + psExtent->MaxX = phMiraMonLayer->TopHeader.hBB.dfMaxX; + psExtent->MinY = phMiraMonLayer->TopHeader.hBB.dfMinY; + psExtent->MaxY = phMiraMonLayer->TopHeader.hBB.dfMaxY; + } + else + { + if (!bForce) + return OGRERR_FAILURE; + } + + return OGRERR_NONE; +} + +/****************************************************************************/ +/* TestCapability() */ +/****************************************************************************/ + +int OGRMiraMonLayer::TestCapability(const char *pszCap) + +{ + if (EQUAL(pszCap, OLCRandomRead)) + return TRUE; + + if (EQUAL(pszCap, OLCSequentialWrite)) + return m_bUpdate; + + if (EQUAL(pszCap, OLCFastFeatureCount)) + return !m_poFilterGeom && !m_poAttrQuery; + + if (EQUAL(pszCap, OLCFastGetExtent)) + return TRUE; + + if (EQUAL(pszCap, OLCCreateField)) + return m_bUpdate; + + if (EQUAL(pszCap, OLCZGeometries)) + return TRUE; + + if (EQUAL(pszCap, OLCStringsAsUTF8)) + return TRUE; + + return FALSE; +} + +/****************************************************************************/ +/* CreateField() */ +/****************************************************************************/ + +OGRErr OGRMiraMonLayer::CreateField(const OGRFieldDefn *poField, int bApproxOK) + +{ + if (!m_bUpdate) + { + CPLError(CE_Failure, CPLE_NoWriteAccess, + "Cannot create fields on a read-only dataset."); + return OGRERR_FAILURE; + } + + if (phMiraMonLayer && phMiraMonLayer->TopHeader.nElemCount > 0) + { + CPLError(CE_Failure, CPLE_NoWriteAccess, + "Cannot create fields to a layer with " + "already existing features in it."); + return OGRERR_FAILURE; + } + + switch (poField->GetType()) + { + case OFTInteger: + case OFTIntegerList: + case OFTInteger64: + case OFTInteger64List: + case OFTReal: + case OFTRealList: + case OFTString: + case OFTStringList: + case OFTDate: + m_poFeatureDefn->AddFieldDefn(poField); + return OGRERR_NONE; + default: + if (!bApproxOK) + { + CPLError(CE_Failure, CPLE_AppDefined, + "\nField %s is of an unsupported type: %s.", + poField->GetNameRef(), + poField->GetFieldTypeName(poField->GetType())); + return OGRERR_FAILURE; + } + else + { + OGRFieldDefn oModDef(poField); + oModDef.SetType(OFTString); + m_poFeatureDefn->AddFieldDefn(poField); + return OGRERR_NONE; + } + } +} + +/************************************************************************/ +/* AddToFileList() */ +/************************************************************************/ + +void OGRMiraMonLayer::AddToFileList(CPLStringList &oFileList) +{ + if (!phMiraMonLayer) + return; + + char szAuxFile[MM_CPL_PATH_BUF_SIZE]; + + oFileList.AddStringDirectly( + VSIGetCanonicalFilename(phMiraMonLayer->pszSrcLayerName)); + char *pszMMExt = + CPLStrdup(CPLGetExtension(phMiraMonLayer->pszSrcLayerName)); + + if (phMiraMonLayer->bIsPoint) + { + // As it's explicit on documentation a point has also two more files: + + // FILE_NAME_WITHOUT_EXTENSION.pnt --> FILE_NAME_WITHOUT_EXTENSION + T.rel + CPLStrlcpy(szAuxFile, CPLGetBasename(phMiraMonLayer->pszSrcLayerName), + MM_CPL_PATH_BUF_SIZE); + CPLStrlcat(szAuxFile, (pszMMExt[0] == 'p') ? "T.rel" : "T.REL", + MM_CPL_PATH_BUF_SIZE); + oFileList.AddStringDirectly(VSIGetCanonicalFilename( + CPLFormFilename(CPLGetDirname(phMiraMonLayer->pszSrcLayerName), + szAuxFile, nullptr))); + + // FILE_NAME_WITHOUT_EXTENSION.pnt --> FILE_NAME_WITHOUT_EXTENSION + T.dbf + CPLStrlcpy(szAuxFile, CPLGetBasename(phMiraMonLayer->pszSrcLayerName), + MM_CPL_PATH_BUF_SIZE); + CPLStrlcat(szAuxFile, (pszMMExt[0] == 'p') ? "T.dbf" : "T.DBF", + MM_CPL_PATH_BUF_SIZE); + oFileList.AddStringDirectly(VSIGetCanonicalFilename( + CPLFormFilename(CPLGetDirname(phMiraMonLayer->pszSrcLayerName), + szAuxFile, nullptr))); + } + else if (phMiraMonLayer->bIsArc && !phMiraMonLayer->bIsPolygon) + { + // As it's explicit on documentation a point has also five more files: + + // FILE_NAME_WITHOUT_EXTENSION.arc --> FILE_NAME_WITHOUT_EXTENSION + A.rel + CPLStrlcpy(szAuxFile, CPLGetBasename(phMiraMonLayer->pszSrcLayerName), + MM_CPL_PATH_BUF_SIZE); + CPLStrlcat(szAuxFile, (pszMMExt[0] == 'a') ? "A.rel" : "A.REL", + MM_CPL_PATH_BUF_SIZE); + oFileList.AddStringDirectly(VSIGetCanonicalFilename( + CPLFormFilename(CPLGetDirname(phMiraMonLayer->pszSrcLayerName), + szAuxFile, nullptr))); + + // FILE_NAME_WITHOUT_EXTENSION.arc --> FILE_NAME_WITHOUT_EXTENSION + A.dbf + CPLStrlcpy(szAuxFile, CPLGetBasename(phMiraMonLayer->pszSrcLayerName), + MM_CPL_PATH_BUF_SIZE); + CPLStrlcat(szAuxFile, (pszMMExt[0] == 'a') ? "A.dbf" : "A.DBF", + MM_CPL_PATH_BUF_SIZE); + oFileList.AddStringDirectly(VSIGetCanonicalFilename( + CPLFormFilename(CPLGetDirname(phMiraMonLayer->pszSrcLayerName), + szAuxFile, nullptr))); + + // FILE_NAME_WITHOUT_EXTENSION.arc --> FILE_NAME_WITHOUT_EXTENSION + .nod + CPLStrlcpy(szAuxFile, CPLGetBasename(phMiraMonLayer->pszSrcLayerName), + MM_CPL_PATH_BUF_SIZE); + CPLStrlcat(szAuxFile, (pszMMExt[0] == 'a') ? ".nod" : ".NOD", + MM_CPL_PATH_BUF_SIZE); + oFileList.AddStringDirectly(VSIGetCanonicalFilename( + CPLFormFilename(CPLGetDirname(phMiraMonLayer->pszSrcLayerName), + szAuxFile, nullptr))); + + // FILE_NAME_WITHOUT_EXTENSION.arc --> FILE_NAME_WITHOUT_EXTENSION + N.rel + CPLStrlcpy(szAuxFile, CPLGetBasename(phMiraMonLayer->pszSrcLayerName), + MM_CPL_PATH_BUF_SIZE); + CPLStrlcat(szAuxFile, (pszMMExt[0] == 'a') ? "N.rel" : "N.REL", + MM_CPL_PATH_BUF_SIZE); + oFileList.AddStringDirectly(VSIGetCanonicalFilename( + CPLFormFilename(CPLGetDirname(phMiraMonLayer->pszSrcLayerName), + szAuxFile, nullptr))); + + // FILE_NAME_WITHOUT_EXTENSION.arc --> FILE_NAME_WITHOUT_EXTENSION + N.dbf + CPLStrlcpy(szAuxFile, CPLGetBasename(phMiraMonLayer->pszSrcLayerName), + MM_CPL_PATH_BUF_SIZE); + CPLStrlcat(szAuxFile, (pszMMExt[0] == 'a') ? "N.dbf" : "N.DBF", + MM_CPL_PATH_BUF_SIZE); + oFileList.AddStringDirectly(VSIGetCanonicalFilename( + CPLFormFilename(CPLGetDirname(phMiraMonLayer->pszSrcLayerName), + szAuxFile, nullptr))); + } + else if (phMiraMonLayer->bIsPolygon) + { + // As it's explicit on documentation a point has also eight more files: + const char *szCompleteArcFileName; + char szArcFileName[MM_CPL_PATH_BUF_SIZE]; + + // FILE_NAME_WITHOUT_EXTENSION.pol --> FILE_NAME_WITHOUT_EXTENSION + P.rel + CPLStrlcpy(szAuxFile, CPLGetBasename(phMiraMonLayer->pszSrcLayerName), + MM_CPL_PATH_BUF_SIZE); + CPLStrlcat(szAuxFile, (pszMMExt[0] == 'p') ? "P.rel" : "P.REL", + MM_CPL_PATH_BUF_SIZE); + oFileList.AddStringDirectly(VSIGetCanonicalFilename( + CPLFormFilename(CPLGetDirname(phMiraMonLayer->pszSrcLayerName), + szAuxFile, nullptr))); + + // The name of the arc is in THIS metadata file + char *pszArcLayerName = MMReturnValueFromSectionINIFile( + CPLFormFilename(CPLGetDirname(phMiraMonLayer->pszSrcLayerName), + szAuxFile, nullptr), + SECTION_OVVW_ASPECTES_TECNICS, KEY_ArcSource); + if (!pszArcLayerName) + { + CPLFree(pszMMExt); + return; //Some files are missing + } + CPLStrlcpy(szArcFileName, pszArcLayerName, MM_CPL_PATH_BUF_SIZE); + + MM_RemoveInitial_and_FinalQuotationMarks(szArcFileName); + + // If extension is not specified ".arc" will be used + if (MMIsEmptyString(CPLGetExtension(pszArcLayerName))) + CPLStrlcat(szArcFileName, (pszMMExt[0] == 'p') ? ".arc" : ".ARC", + MM_CPL_PATH_BUF_SIZE); + + CPLFree(pszArcLayerName); + + szCompleteArcFileName = + CPLFormFilename(CPLGetDirname(phMiraMonLayer->pszSrcLayerName), + szArcFileName, nullptr); + + // The arc that has the coordinates of the polygon + oFileList.AddStringDirectly( + VSIGetCanonicalFilename(szCompleteArcFileName)); + + // FILE_NAME_WITHOUT_EXTENSION.pol --> FILE_NAME_WITHOUT_EXTENSION + P.dbf + CPLStrlcpy(szAuxFile, CPLGetBasename(phMiraMonLayer->pszSrcLayerName), + MM_CPL_PATH_BUF_SIZE); + CPLStrlcat(szAuxFile, (pszMMExt[0] == 'p') ? "P.dbf" : "P.DBF", + MM_CPL_PATH_BUF_SIZE); + oFileList.AddStringDirectly(VSIGetCanonicalFilename( + CPLFormFilename(CPLGetDirname(phMiraMonLayer->pszSrcLayerName), + szAuxFile, nullptr))); + + // FILE_NAME_WITHOUT_EXTENSION.arc --> FILE_NAME_WITHOUT_EXTENSION + A.rel + const char *pszBaseArcName = CPLGetBasename(szCompleteArcFileName); + CPLStrlcpy(szAuxFile, pszBaseArcName, MM_CPL_PATH_BUF_SIZE); + CPLStrlcat(szAuxFile, (pszMMExt[0] == 'p') ? "A.rel" : "A.REL", + MM_CPL_PATH_BUF_SIZE); + oFileList.AddStringDirectly(VSIGetCanonicalFilename(CPLFormFilename( + CPLGetDirname(szCompleteArcFileName), szAuxFile, nullptr))); + + // FILE_NAME_WITHOUT_EXTENSION.arc --> FILE_NAME_WITHOUT_EXTENSION + A.dbf + CPLStrlcpy(szAuxFile, pszBaseArcName, MM_CPL_PATH_BUF_SIZE); + CPLStrlcat(szAuxFile, (pszMMExt[0] == 'p') ? "A.dbf" : "A.DBF", + MM_CPL_PATH_BUF_SIZE); + oFileList.AddStringDirectly(VSIGetCanonicalFilename(CPLFormFilename( + CPLGetDirname(szCompleteArcFileName), szAuxFile, nullptr))); + + // FILE_NAME_WITHOUT_EXTENSION.arc --> FILE_NAME_WITHOUT_EXTENSION + .nod + CPLStrlcpy(szAuxFile, pszBaseArcName, MM_CPL_PATH_BUF_SIZE); + CPLStrlcat(szAuxFile, (pszMMExt[0] == 'p') ? ".nod" : ".NOD", + MM_CPL_PATH_BUF_SIZE); + oFileList.AddStringDirectly(VSIGetCanonicalFilename(CPLFormFilename( + CPLGetDirname(szCompleteArcFileName), szAuxFile, nullptr))); + + // FILE_NAME_WITHOUT_EXTENSION.arc --> FILE_NAME_WITHOUT_EXTENSION + N.rel + CPLStrlcpy(szAuxFile, pszBaseArcName, MM_CPL_PATH_BUF_SIZE); + CPLStrlcat(szAuxFile, (pszMMExt[0] == 'p') ? "N.rel" : "N.REL", + MM_CPL_PATH_BUF_SIZE); + oFileList.AddStringDirectly(VSIGetCanonicalFilename(CPLFormFilename( + CPLGetDirname(szCompleteArcFileName), szAuxFile, nullptr))); + + // FILE_NAME_WITHOUT_EXTENSION.arc --> FILE_NAME_WITHOUT_EXTENSION + N.dbf + CPLStrlcpy(szAuxFile, pszBaseArcName, MM_CPL_PATH_BUF_SIZE); + CPLStrlcat(szAuxFile, (pszMMExt[0] == 'p') ? "N.dbf" : "N.DBF", + MM_CPL_PATH_BUF_SIZE); + oFileList.AddStringDirectly(VSIGetCanonicalFilename(CPLFormFilename( + CPLGetDirname(szCompleteArcFileName), szAuxFile, nullptr))); + } + CPLFree(pszMMExt); +} diff --git a/ogr/ogrsf_frmts/ogrsf_frmts.h b/ogr/ogrsf_frmts/ogrsf_frmts.h index 309be765f8e3..b2902307fbd2 100644 --- a/ogr/ogrsf_frmts/ogrsf_frmts.h +++ b/ogr/ogrsf_frmts/ogrsf_frmts.h @@ -741,6 +741,7 @@ void DeclareDeferredOGRArrowPlugin(); void CPL_DLL RegisterOGRGTFS(); void CPL_DLL RegisterOGRPMTiles(); void CPL_DLL RegisterOGRJSONFG(); +void CPL_DLL RegisterOGRMiraMon(); // @endcond CPL_C_END diff --git a/scripts/fix_typos.sh b/scripts/fix_typos.sh index 2fb0aacf283b..f52f92c9c00e 100755 --- a/scripts/fix_typos.sh +++ b/scripts/fix_typos.sh @@ -126,6 +126,7 @@ AUTHORIZED_LIST="$AUTHORIZED_LIST,cJP2_Colorspace_RGBa,cJP2_Colorspace_Palette_R AUTHORIZED_LIST="$AUTHORIZED_LIST,CURLE_FILE_COULDNT_READ_FILE" AUTHORIZED_LIST="$AUTHORIZED_LIST,nParms,ProjParm,ProjParmId,GTIFFetchProjParms,gdal_GTIFFetchProjParms" # API of libgeotiff AUTHORIZED_LIST="$AUTHORIZED_LIST,lon,Lon,LON" +AUTHORIZED_LIST="$AUTHORIZED_LIST,MM_MARCA_VERSIO_1_DBF_ESTESA,MM_PERIMETRE_INIT_SIZE,MM_PERIMETRE_DECIMALS_SIZE,szMMNomCampPerimetreDefecte,MM_CAMP_ES_PERIMETRE,szMMNomCampNPoligonsDefecte,MM_CAMP_MOSTRABLE_QUAN_TE_CONTINGUT,MM_CAMP_ES_PERIMETRE_3D,SECTION_VERSIO" python3 fix_typos/codespell/codespell.py -w -i 3 -q 2 -S "$EXCLUDED_FILES,./autotest/*,./build*/*" \ -x scripts/typos_allowlist.txt --words-white-list=$AUTHORIZED_LIST \ diff --git a/scripts/typos_allowlist.txt b/scripts/typos_allowlist.txt index a2a7bf6390d7..be5ac1e0ee82 100644 --- a/scripts/typos_allowlist.txt +++ b/scripts/typos_allowlist.txt @@ -306,3 +306,25 @@ either 2 or 4 comma separated values. The same rules apply for the source and de * Esben Mose Hansen, Ange Optimization ApS SetLinearUnits("kilometre", 1000.0); // F(ixed) S(ize) L(ist) of (x,y[,z][,m]) values / Interleaved layout + * https://www.miramon.cat/eng/QuiSom.htm +#define SECTION_VERSIO "VERSIO" +#define szMMNomCampPerimetreDefecte "PERIMETRE" +#define szMMNomCampNPoligonsDefecte "N_POLIG" +#define MM_MIN_WIDTH_LONG 14 // For LONG_ARC and PERIMETRE +#define MM_MIN_WIDTH_AREA 19 // For LONG_ARC and PERIMETRE + CPLStrlcpy(szPerimeterOfThePolygonCat, "Perimetre del poligon", + CPLStrlcpy(szPerimeterOfThePolygonCat, "Perimetre del poligon", + CPLStrlcpy(szAreaOfThePolygonCat, "Area del poligon", + CPLStrlcpy(szNumberOfElementaryPolygonsCat, "Nombre de poligons elementals", + VSIFPrintfL(MMMap.fMMMap, "[VERSIO]\n"); +#define MM_IsDoubleInfinite(x) EsDoubleInfinit((x)) + https://www.miramon.cat/help/eng/GeMPlus/ClausREL.htm + fprintf_function(pF, "[%s]" LineReturn, SECTION_VERSIO); + fprintf_function(pF, "NomCampPerimetre=%s" LineReturn, + fprintf_function(pF, "NomCampNPoligons=%s" LineReturn, + MMReturnValueFromSectionINIFile(szREL_file, SECTION_VERSIO, nullptr); + MMReturnValueFromSectionINIFile(szREL_file, SECTION_VERSIO, KEY_Vers); + pszLine = MMReturnValueFromSectionINIFile(szREL_file, SECTION_VERSIO, + assert f.GetField("PERIMETRE") == pytest.approx(1289.866489495, abs=1e-5) + assert f.GetField("PERIMETRE") == pytest.approx(1123.514024, abs=1e-5) + assert f.GetField("PERIMETRE") == pytest.approx(680.544697, abs=1e-5)