From f9a03fb114d033083af8809dc94b298517051682 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Wed, 17 Apr 2024 11:05:38 +0200 Subject: [PATCH 1/5] Doc: OGR VRT: clarify GeometryField attributes (#9679) Fixes #9676 --- doc/source/drivers/vector/vrt.rst | 110 ++++++++++++++++++------------ 1 file changed, 67 insertions(+), 43 deletions(-) diff --git a/doc/source/drivers/vector/vrt.rst b/doc/source/drivers/vector/vrt.rst index 05a9102f40f8..a961bd2371ca 100644 --- a/doc/source/drivers/vector/vrt.rst +++ b/doc/source/drivers/vector/vrt.rst @@ -164,50 +164,78 @@ layer name, and may have the following subelements: - **GeometryField** (optional): This element is used to define how the geometry for features should be derived. - If not provided the geometry of the source feature is copied directly. - The type of geometry encoding is indicated with the **encoding** - attribute which may have the value "WKT", "WKB" or "PointFromColumns". - - If the encoding is "WKT" or "WKB" then the **field** attribute will - have the name of the field containing the WKT or WKB geometry. - - If the encoding is "PointFromColumns" then the **x**, **y**, **z** and - **m** attributes will have the names of the columns to be used for the - X, Y, Z and M coordinates. The **z** and **m** attributes are optional - (m only supported since OGR 2.1.1). - - The optional **reportSrcColumn** attribute can be used to specify - whether the source geometry fields (the fields set in the **field**, - **x**, **y**, **z**, **m** attributes) should be reported as fields of - the VRT layer. It defaults to TRUE. If set to FALSE, the source - geometry fields will only be used to build the geometry of the - features of the VRT layer. - - The GeometryField element can be repeated as - many times as necessary to create multiple geometry fields. It accepts a - **name** attribute (recommended) that will be used to define the VRT - geometry field name. When **encoding** is not specified, the **field** - attribute will be used to determine the corresponding geometry field - name in the source layer. If neither **encoding** nor **field** are - specified, it is assumed that the name of source geometry field is the - value of the **name** attribute. - - The optional **nullable** attribute can be used - to specify whether the geometry field is nullable. It defaults to - "true". - - When several geometry fields are used, the following child elements of - **GeometryField** can be defined to explicitly set the geometry type, - SRS, source region, or extent. + The GeometryField element can be repeated as many times as necessary to create + multiple geometry fields. + If no **GeometryField** element is specified, all the geometry fields of + the source layer will be exposed by the VRT layer. In order not to + expose any geometry field of the source layer, you need to specify + OGRVRTLayer-level **GeometryType** element to wkbNone. + + The following attributes can be defined: + + * **name** = string (recommended, and mandatory if the VRT will expose multiple geometry fields) + + Name that will be used to define the VRT geometry field name. If not set, + empty string is used. + + * **encoding** = Direct/WKT/WKB/PointFromColumns (optional) + + Type of geometry encoding. + + If the encoding is "Direct" or not specified, then the **field** attribute must + be set to the name of the source geometry field, if there are multiple source + geometry fields. If neither **encoding** nor **field** are + specified, it is assumed that the name of source geometry field is the + value of the **name** attribute. + + If the encoding is "WKT" or "WKB" then the **field** attribute must be set to + the name of the source field containing the WKT or WKB geometry. + + If the encoding is "PointFromColumns" then the **x**, **y**, **z** and + **m** attributes must be set to the names of the columns to be used for the + X, Y, Z and M coordinates. The **z** and **m** attributes are optional. + + * **field** = string (conditional) + + Name of the source field (or source geometry field for **encoding** = Direct) + from which this GeometryField should fetch geometries. This must be set + if **encoding** is WKT or WKB. + + * **x**, **y**, **z**, **m** = string (conditional) + + Name of the source fields for the X, Y, Z and M coordinates when + **encoding** = PointFromColumns + + * **reportSrcColumn** = true/false (optional) + + Specify whether the source geometry fields (the fields set in the **field**, + **x**, **y**, **z**, **m** attributes) should also be included as fields of + the VRT layer. It defaults to true. If set to false, the source + geometry fields will only be used to build the geometry of the + features of the VRT layer. + + Note that reportSrcColumn=true is taken into account only if no explicit + **Field** element is defined and when **encoding** is not "Direct". + If at least one field is explicitly defined, and reporting of the source + geometry field is desired, an explicit **Field** element for it must be defined. + + * **nullable** = true/false (optional) + + The optional **nullable** attribute can be used + to specify whether the geometry field is nullable. It defaults to + "true". + + The following child elements of **GeometryField** can be defined: * **GeometryType** (optional) : same syntax as OGRVRTLayer-level - **GeometryType**. + **GeometryType**. Useful when there are multiple geometry fields. * **SRS** (optional) : same syntax as OGRVRTLayer-level **LayerSRS** - (note SRS vs LayerSRS) + (note SRS vs LayerSRS). Useful when there are multiple geometry fields. * **SrcRegion** (optional) : same syntax as OGRVRTLayer-level - **SrcRegion** + **SrcRegion**. Useful when there are multiple geometry fields. * **ExtentXMin**, **ExtentYMin**, **ExtentXMax** and **ExtentXMax** - (optional) : same syntax as OGRVRTLayer-level elements of same name + (optional) : same syntax as OGRVRTLayer-level elements of same name. + Useful when there are multiple geometry fields. * **XYResolution** (optional, GDAL >= 3.9): Resolution for the coordinate precision of the X and Y coordinates. Expressed in the units of the X and Y axis of the SRS @@ -217,10 +245,6 @@ layer name, and may have the following subelements: * **MResolution** (optional, GDAL >= 3.9): Resolution for the coordinate precision of the M coordinates. - If no **GeometryField** element is specified, all the geometry fields of - the source layer will be exposed by the VRT layer. In order not to - expose any geometry field of the source layer, you need to specify - OGRVRTLayer-level **GeometryType** element to wkbNone. - **SrcRegion** (optional) : This element is used to define an initial spatial filter for the source features. This spatial From 901ae0369c5fa4e936b6619820924aed579b72a2 Mon Sep 17 00:00:00 2001 From: Alessandro Pasotti Date: Wed, 17 Apr 2024 17:25:36 +0200 Subject: [PATCH 2/5] gdalinfo: use argparser (#9687) --- apps/gdal_utils_priv.h | 12 +- apps/gdalargumentparser.cpp | 16 ++ apps/gdalargumentparser.h | 10 + apps/gdalinfo_bin.cpp | 147 ++++------ apps/gdalinfo_lib.cpp | 348 ++++++++++++++---------- autotest/utilities/test_gdalinfo.py | 4 +- autotest/utilities/test_gdalinfo_lib.py | 2 +- doc/source/programs/gdalinfo.rst | 8 +- 8 files changed, 295 insertions(+), 252 deletions(-) diff --git a/apps/gdal_utils_priv.h b/apps/gdal_utils_priv.h index 0490a4a35bd6..8127d5f0fac3 100644 --- a/apps/gdal_utils_priv.h +++ b/apps/gdal_utils_priv.h @@ -44,16 +44,16 @@ CPL_C_START struct GDALInfoOptionsForBinary { /* Filename to open. */ - char *pszFilename; + std::string osFilename{}; /* Open options. */ - char **papszOpenOptions; + CPLStringList aosOpenOptions{}; - /* > for reporting on a particular subdataset */ - int nSubdataset; + /* For reporting on a particular subdataset */ + int nSubdataset = 0; /* Allowed input drivers. */ - char **papszAllowInputDrivers; + CPLStringList aosAllowedInputDrivers{}; }; struct GDALDEMProcessingOptionsForBinary @@ -239,6 +239,8 @@ std::string CPL_DLL GDALVectorTranslateGetParserUsage(); std::string CPL_DLL GDALWarpAppGetParserUsage(); +std::string CPL_DLL GDALInfoAppGetParserUsage(); + #endif /* #ifndef DOXYGEN_SKIP */ #endif /* GDAL_UTILS_PRIV_H_INCLUDED */ diff --git a/apps/gdalargumentparser.cpp b/apps/gdalargumentparser.cpp index fef16fbc1f93..8870b03805d5 100644 --- a/apps/gdalargumentparser.cpp +++ b/apps/gdalargumentparser.cpp @@ -339,6 +339,22 @@ GDALArgumentParser::get_non_positional_arguments(const CPLStringList &aosArgs) return args; } +Argument &GDALArgumentParser::add_inverted_logic_flag(const std::string &name, + bool *store_into, + const std::string &help) +{ + return add_argument(name) + .default_value(true) + .implicit_value(false) + .action( + [store_into](const auto &) + { + if (store_into) + *store_into = false; + }) + .help(help); +} + /************************************************************************/ /* parse_args() */ /************************************************************************/ diff --git a/apps/gdalargumentparser.h b/apps/gdalargumentparser.h index 5310d0f464d5..c5b545748bf2 100644 --- a/apps/gdalargumentparser.h +++ b/apps/gdalargumentparser.h @@ -96,6 +96,16 @@ class GDALArgumentParser : public ArgumentParser //! Return the non positional arguments. CPLStringList get_non_positional_arguments(const CPLStringList &aosArgs); + /** + * Add an inverted logic (default true, false when set) flag + * @param name flag name + * @param store_into optional pointer to a bool variable where to store the value + * @param help optional help text + */ + Argument &add_inverted_logic_flag(const std::string &name, + bool *store_into = nullptr, + const std::string &help = ""); + private: std::map::iterator find_argument(const std::string &name); diff --git a/apps/gdalinfo_bin.cpp b/apps/gdalinfo_bin.cpp index 61a977ba03a2..0a72c73a35d4 100644 --- a/apps/gdalinfo_bin.cpp +++ b/apps/gdalinfo_bin.cpp @@ -35,53 +35,36 @@ #include "gdal_utils_priv.h" /************************************************************************/ -/* Usage() */ +/* GDALExit() */ +/* This function exits and cleans up GDAL and OGR resources */ +/* Perhaps it should be added to C api and used in all apps? */ /************************************************************************/ -static void Usage(bool bIsError, const char *pszErrorMsg = nullptr) - +static int GDALExit(int nCode) { - fprintf( - bIsError ? stderr : stdout, - "Usage: gdalinfo [--help] [--help-general]\n" - " [-json] [-mm] [-stats | -approx_stats] [-hist]\n" - " [-nogcp] [-nomd] [-norat] [-noct] [-nofl]\n" - " [-checksum] [-listmdd] [-mdd |all]\n" - " [-proj4] [-wkt_format {WKT1|WKT2|}]...\n" - " [-sd ] [-oo =]... [-if " - "]...\n" - " \n"); - - if (pszErrorMsg != nullptr) - fprintf(stderr, "\nFAILURE: %s\n", pszErrorMsg); - - exit(bIsError ? 1 : 0); -} + const char *pszDebug = CPLGetConfigOption("CPL_DEBUG", nullptr); + if (pszDebug && (EQUAL(pszDebug, "ON") || EQUAL(pszDebug, ""))) + { + GDALDumpOpenDatasets(stderr); + CPLDumpSharedList(nullptr); + } -/************************************************************************/ -/* GDALInfoOptionsForBinary() */ -/************************************************************************/ + GDALDestroyDriverManager(); -static GDALInfoOptionsForBinary *GDALInfoOptionsForBinaryNew(void) -{ - return static_cast( - CPLCalloc(1, sizeof(GDALInfoOptionsForBinary))); + OGRCleanupAll(); + + exit(nCode); } /************************************************************************/ -/* GDALInfoOptionsForBinaryFree() */ +/* Usage() */ /************************************************************************/ -static void -GDALInfoOptionsForBinaryFree(GDALInfoOptionsForBinary *psOptionsForBinary) +static void Usage() + { - if (psOptionsForBinary) - { - CPLFree(psOptionsForBinary->pszFilename); - CSLDestroy(psOptionsForBinary->papszOpenOptions); - CSLDestroy(psOptionsForBinary->papszAllowInputDrivers); - CPLFree(psOptionsForBinary); - } + fprintf(stderr, "%s\n", GDALInfoAppGetParserUsage().c_str()); + GDALExit(1); } /************************************************************************/ @@ -93,39 +76,30 @@ MAIN_START(argc, argv) { EarlySetConfigOptions(argc, argv); - GDALAllRegister(); + /* -------------------------------------------------------------------- */ + /* Register standard GDAL drivers, and process generic GDAL */ + /* command options. */ + /* -------------------------------------------------------------------- */ + GDALAllRegister(); argc = GDALGeneralCmdLineProcessor(argc, &argv, 0); if (argc < 1) - exit(-argc); + GDALExit(-argc); - for (int i = 0; argv != nullptr && argv[i] != nullptr; i++) - { - if (EQUAL(argv[i], "--utility_version")) - { - printf("%s was compiled against GDAL %s and is running against " - "GDAL %s\n", - argv[0], GDAL_RELEASE_NAME, GDALVersionInfo("RELEASE_NAME")); - CSLDestroy(argv); - return 0; - } - else if (EQUAL(argv[i], "--help")) - { - Usage(false); - } - } - argv = CSLAddString(argv, "-stdout"); + /* -------------------------------------------------------------------- */ + /* Parse command line */ + /* -------------------------------------------------------------------- */ - GDALInfoOptionsForBinary *psOptionsForBinary = - GDALInfoOptionsForBinaryNew(); + GDALInfoOptionsForBinary sOptionsForBinary; - GDALInfoOptions *psOptions = - GDALInfoOptionsNew(argv + 1, psOptionsForBinary); - if (psOptions == nullptr) - Usage(true); + std::unique_ptr psOptions{ + GDALInfoOptionsNew(argv + 1, &sOptionsForBinary), GDALInfoOptionsFree}; + CSLDestroy(argv); - if (psOptionsForBinary->pszFilename == nullptr) - Usage(true, "No datasource specified."); + if (!psOptions) + { + Usage(); + } /* -------------------------------------------------------------------- */ /* Open dataset. */ @@ -138,10 +112,10 @@ MAIN_START(argc, argv) #endif GDALDatasetH hDataset = GDALOpenEx( - psOptionsForBinary->pszFilename, + sOptionsForBinary.osFilename.c_str(), GDAL_OF_READONLY | GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR, - psOptionsForBinary->papszAllowInputDrivers, - psOptionsForBinary->papszOpenOptions, nullptr); + sOptionsForBinary.aosAllowedInputDrivers, + sOptionsForBinary.aosOpenOptions, nullptr); if (hDataset == nullptr) { @@ -151,11 +125,11 @@ MAIN_START(argc, argv) VSIStatBuf sStat; CPLString message; message.Printf("gdalinfo failed - unable to open '%s'.", - psOptionsForBinary->pszFilename); - if (VSIStat(psOptionsForBinary->pszFilename, &sStat) == 0) + sOptionsForBinary.osFilename.c_str()); + if (VSIStat(sOptionsForBinary.osFilename.c_str(), &sStat) == 0) { GDALDriverH drv = - GDALIdentifyDriverEx(psOptionsForBinary->pszFilename, + GDALIdentifyDriverEx(sOptionsForBinary.osFilename.c_str(), GDAL_OF_VECTOR, nullptr, nullptr); if (drv) { @@ -169,19 +143,19 @@ MAIN_START(argc, argv) /* If argument is a VSIFILE, then print its contents */ /* -------------------------------------------------------------------- */ - if (STARTS_WITH(psOptionsForBinary->pszFilename, "/vsizip/") || - STARTS_WITH(psOptionsForBinary->pszFilename, "/vsitar/")) + if (STARTS_WITH(sOptionsForBinary.osFilename.c_str(), "/vsizip/") || + STARTS_WITH(sOptionsForBinary.osFilename.c_str(), "/vsitar/")) { const char *const apszOptions[] = {"NAME_AND_TYPE_ONLY=YES", nullptr}; - VSIDIR *psDir = - VSIOpenDir(psOptionsForBinary->pszFilename, -1, apszOptions); + VSIDIR *psDir = VSIOpenDir(sOptionsForBinary.osFilename.c_str(), -1, + apszOptions); if (psDir) { fprintf(stdout, "Unable to open source `%s' directly.\n" "The archive contains several files:\n", - psOptionsForBinary->pszFilename); + sOptionsForBinary.osFilename.c_str()); int nCount = 0; while (auto psEntry = VSIGetNextDirEntry(psDir)) { @@ -189,13 +163,13 @@ MAIN_START(argc, argv) psEntry->pszName[strlen(psEntry->pszName) - 1] != '/') { fprintf(stdout, " %s/%s/\n", - psOptionsForBinary->pszFilename, + sOptionsForBinary.osFilename.c_str(), psEntry->pszName); } else { fprintf(stdout, " %s/%s\n", - psOptionsForBinary->pszFilename, + sOptionsForBinary.osFilename.c_str(), psEntry->pszName); } nCount++; @@ -209,12 +183,6 @@ MAIN_START(argc, argv) } } - CSLDestroy(argv); - - GDALInfoOptionsForBinaryFree(psOptionsForBinary); - - GDALInfoOptionsFree(psOptions); - GDALDumpOpenDatasets(stderr); GDALDestroyDriverManager(); @@ -230,19 +198,19 @@ MAIN_START(argc, argv) /* Read specified subdataset if requested. */ /* -------------------------------------------------------------------- */ - if (psOptionsForBinary->nSubdataset > 0) + if (sOptionsForBinary.nSubdataset > 0) { char **papszSubdatasets = GDALGetMetadata(hDataset, "SUBDATASETS"); int nSubdatasets = CSLCount(papszSubdatasets); if (nSubdatasets > 0 && - psOptionsForBinary->nSubdataset <= nSubdatasets) + sOptionsForBinary.nSubdataset <= nSubdatasets) { char szKeyName[1024]; char *pszSubdatasetName; snprintf(szKeyName, sizeof(szKeyName), "SUBDATASET_%d_NAME", - psOptionsForBinary->nSubdataset); + sOptionsForBinary.nSubdataset); szKeyName[sizeof(szKeyName) - 1] = '\0'; pszSubdatasetName = CPLStrdup(CSLFetchNameValue(papszSubdatasets, szKeyName)); @@ -255,11 +223,11 @@ MAIN_START(argc, argv) fprintf(stderr, "gdalinfo warning: subdataset %d of %d requested. " "Reading the main dataset.\n", - psOptionsForBinary->nSubdataset, nSubdatasets); + sOptionsForBinary.nSubdataset, nSubdatasets); } } - char *pszGDALInfoOutput = GDALInfo(hDataset, psOptions); + char *pszGDALInfoOutput = GDALInfo(hDataset, psOptions.get()); if (pszGDALInfoOutput) printf("%s", pszGDALInfoOutput); @@ -271,17 +239,12 @@ MAIN_START(argc, argv) } #endif - GDALInfoOptionsForBinaryFree(psOptionsForBinary); - - GDALInfoOptionsFree(psOptions); - - CSLDestroy(argv); - GDALDumpOpenDatasets(stderr); GDALDestroyDriverManager(); CPLDumpSharedList(nullptr); + GDALDestroy(); exit(0); diff --git a/apps/gdalinfo_lib.cpp b/apps/gdalinfo_lib.cpp index ef98681c0ca0..6ba776d08ba2 100644 --- a/apps/gdalinfo_lib.cpp +++ b/apps/gdalinfo_lib.cpp @@ -31,6 +31,7 @@ #include "cpl_port.h" #include "gdal_utils.h" #include "gdal_utils_priv.h" +#include "gdalargumentparser.h" #include #include @@ -79,61 +80,61 @@ typedef enum struct GDALInfoOptions { /*! output format */ - GDALInfoFormat eFormat; + GDALInfoFormat eFormat = GDALINFO_FORMAT_TEXT; - int bComputeMinMax; + bool bComputeMinMax = false; /*! report histogram information for all bands */ - int bReportHistograms; + bool bReportHistograms = false; /*! report a PROJ.4 string corresponding to the file's coordinate system */ - int bReportProj4; + bool bReportProj4 = false; /*! read and display image statistics. Force computation if no statistics are stored in an image */ - int bStats; + bool bStats = false; /*! read and display image statistics. Force computation if no statistics are stored in an image. However, they may be computed based on overviews or a subset of all tiles. Useful if you are in a hurry and don't want precise stats. */ - int bApproxStats; + bool bApproxStats = true; - int bSample; + bool bSample = false; /*! force computation of the checksum for each band in the dataset */ - int bComputeChecksum; + bool bComputeChecksum = false; /*! allow or suppress ground control points list printing. It may be useful for datasets with huge amount of GCPs, such as L1B AVHRR or HDF4 MODIS which contain thousands of them. */ - int bShowGCPs; + bool bShowGCPs = true; /*! allow or suppress metadata printing. Some datasets may contain a lot of metadata strings. */ - int bShowMetadata; + bool bShowMetadata = true; /*! allow or suppress printing of raster attribute table */ - int bShowRAT; + bool bShowRAT = true; /*! allow or suppress printing of color table */ - int bShowColorTable; + bool bShowColorTable = true; /*! list all metadata domains available for the dataset */ - int bListMDD; + bool bListMDD = false; /*! display the file list or the first file of the file list */ - int bShowFileList; + bool bShowFileList = true; /*! report metadata for the specified domains. "all" can be used to report metadata in all domains. */ - char **papszExtraMDDomains; + CPLStringList aosExtraMDDomains; /*! WKT format used for SRS */ - char *pszWKTFormat; + std::string osWKTFormat = "WKT2"; - bool bStdoutOutput; + bool bStdoutOutput = false; }; static int GDALInfoReportCorner(const GDALInfoOptions *psOptions, @@ -215,6 +216,160 @@ gdal_json_object_new_double_significant_digits(double dfVal, dfVal, nSignificantDigits); } +/************************************************************************/ +/* GDALWarpAppOptionsGetParser() */ +/************************************************************************/ + +static std::unique_ptr +GDALInfoAppOptionsGetParser(GDALInfoOptions *psOptions, + GDALInfoOptionsForBinary *psOptionsForBinary) +{ + auto argParser = std::make_unique( + "gdalinfo", /* bForBinary=*/psOptionsForBinary != nullptr); + + argParser->add_description(_("Raster dataset information utility.")); + + argParser->add_epilog( + _("For more details, consult https://gdal.org/programs/gdalinfo.html")); + + argParser->add_argument("-json") + .flag() + .action([psOptions](const auto &) + { psOptions->eFormat = GDALINFO_FORMAT_JSON; }) + .help(_("Display the output in json format.")); + + argParser->add_argument("-mm") + .store_into(psOptions->bComputeMinMax) + .help(_("Force computation of the actual min/max values for each band " + "in the dataset.")); + + { + auto &group = argParser->add_mutually_exclusive_group(); + group.add_argument("-stats") + .store_into(psOptions->bStats) + .help(_("Read and display image statistics computing exact values " + "if required.")); + + group.add_argument("-approx_stats") + .store_into(psOptions->bApproxStats) + .help( + _("Read and display image statistics computing approximated " + "values on overviews or a subset of all tiles if required.")); + } + + argParser->add_argument("-hist") + .store_into(psOptions->bReportHistograms) + .help(_("Report histogram information for all bands.")); + + argParser->add_inverted_logic_flag( + "-nogcp", &psOptions->bShowGCPs, + _("Suppress ground control points list printing.")); + + argParser->add_inverted_logic_flag("-nomd", &psOptions->bShowMetadata, + _("Suppress metadata printing.")); + + argParser->add_inverted_logic_flag( + "-norat", &psOptions->bShowRAT, + _("Suppress printing of raster attribute table.")); + + argParser->add_inverted_logic_flag("-noct", &psOptions->bShowColorTable, + _("Suppress printing of color table.")); + + argParser->add_inverted_logic_flag("-nofl", &psOptions->bShowFileList, + _("Suppress display of the file list.")); + + argParser->add_argument("-checksum") + .flag() + .store_into(psOptions->bComputeChecksum) + .help(_( + "Force computation of the checksum for each band in the dataset.")); + + argParser->add_argument("-listmdd") + .flag() + .store_into(psOptions->bListMDD) + .help(_("List all metadata domains available for the dataset.")); + + argParser->add_argument("-proj4") + .flag() + .store_into(psOptions->bReportProj4) + .help(_("Report a PROJ.4 string corresponding to the file's coordinate " + "system.")); + + argParser->add_argument("-wkt_format") + .metavar("") + .choices("WKT1", "WKT2", "WKT2_2015", "WKT2_2018", "WKT2_2019") + .store_into(psOptions->osWKTFormat) + .help(_("WKT format used for SRS.")); + + argParser->add_argument("-sd") + .metavar("") + .store_into(psOptionsForBinary->nSubdataset) + .help(_("Use subdataset of specified index (starting at 1), instead of " + "the source dataset itself.")); + + argParser->add_argument("-oo") + .metavar("=") + .append() + .action( + [psOptionsForBinary](const std::string &s) + { + if (psOptionsForBinary) + psOptionsForBinary->aosOpenOptions.AddString(s.c_str()); + }) + .help(_("Open option(s) for dataset.")); + + argParser->add_input_format_argument( + psOptionsForBinary ? &psOptionsForBinary->aosAllowedInputDrivers + : nullptr); + + argParser->add_argument("-mdd") + .metavar("|all") + .action( + [psOptions](const std::string &value) + { + psOptions->aosExtraMDDomains = + CSLAddString(psOptions->aosExtraMDDomains, value.c_str()); + }) + .help(_("Report metadata for the specified domains. 'all' can be used " + "to report metadata in all domains.")); + + /* Not documented: used by gdalinfo_bin.cpp only */ + argParser->add_argument("-stdout").flag().hidden().store_into( + psOptions->bStdoutOutput); + + if (psOptionsForBinary) + { + argParser->add_argument("dataset_name") + .metavar("") + .store_into(psOptionsForBinary->osFilename) + .help("Input dataset."); + } + + return argParser; +} + +/************************************************************************/ +/* GDALInfoAppGetParserUsage() */ +/************************************************************************/ + +std::string GDALInfoAppGetParserUsage() +{ + try + { + GDALInfoOptions sOptions; + GDALInfoOptionsForBinary sOptionsForBinary; + auto argParser = + GDALInfoAppOptionsGetParser(&sOptions, &sOptionsForBinary); + return argParser->usage(); + } + catch (const std::exception &err) + { + CPLError(CE_Failure, CPLE_AppDefined, "Unexpected exception: %s", + err.what()); + return std::string(); + } +} + /************************************************************************/ /* GDALInfo() */ /************************************************************************/ @@ -377,7 +532,7 @@ char *GDALInfo(GDALDatasetH hDataset, const GDALInfoOptions *psOptions) } CPLString osWKTFormat("FORMAT="); - osWKTFormat += psOptions->pszWKTFormat; + osWKTFormat += psOptions->osWKTFormat; const char *const apszWKTOptions[] = {osWKTFormat.c_str(), "MULTILINE=YES", nullptr}; @@ -404,7 +559,7 @@ char *GDALInfo(GDALDatasetH hDataset, const GDALInfoOptions *psOptions) if (bJson) { json_object *poWkt = json_object_new_string(pszPrettyWkt); - if (strcmp(psOptions->pszWKTFormat, "WKT2") == 0) + if (psOptions->osWKTFormat == "WKT2") { json_object *poStacWkt = nullptr; json_object_deep_copy(poWkt, &poStacWkt, nullptr); @@ -2021,12 +2176,12 @@ static void GDALInfoReportMetadata(const GDALInfoOptions *psOptions, /* -------------------------------------------------------------------- */ /* Report extra Metadata domains */ /* -------------------------------------------------------------------- */ - if (psOptions->papszExtraMDDomains != nullptr) + if (!psOptions->aosExtraMDDomains.empty()) { CPLStringList aosExtraMDDomainsExpanded; - if (EQUAL(psOptions->papszExtraMDDomains[0], "all") && - psOptions->papszExtraMDDomains[1] == nullptr) + if (EQUAL(psOptions->aosExtraMDDomains[0], "all") && + psOptions->aosExtraMDDomains.Count() == 1) { const CPLStringList aosMDDList(GDALGetMetadataDomainList(hObject)); for (const char *pszDomain : aosMDDList) @@ -2044,8 +2199,7 @@ static void GDALInfoReportMetadata(const GDALInfoOptions *psOptions, } else { - aosExtraMDDomainsExpanded = - CSLDuplicate(psOptions->papszExtraMDDomains); + aosExtraMDDomainsExpanded = psOptions->aosExtraMDDomains; } for (const char *pszDomain : aosExtraMDDomainsExpanded) @@ -2113,135 +2267,37 @@ GDALInfoOptions * GDALInfoOptionsNew(char **papszArgv, GDALInfoOptionsForBinary *psOptionsForBinary) { - bool bGotFilename = false; - GDALInfoOptions *psOptions = - static_cast(CPLCalloc(1, sizeof(GDALInfoOptions))); - - psOptions->eFormat = GDALINFO_FORMAT_TEXT; - psOptions->bComputeMinMax = FALSE; - psOptions->bReportHistograms = FALSE; - psOptions->bReportProj4 = FALSE; - psOptions->bStats = FALSE; - psOptions->bApproxStats = TRUE; - psOptions->bSample = FALSE; - psOptions->bComputeChecksum = FALSE; - psOptions->bShowGCPs = TRUE; - psOptions->bShowMetadata = TRUE; - psOptions->bShowRAT = TRUE; - psOptions->bShowColorTable = TRUE; - psOptions->bListMDD = FALSE; - psOptions->bShowFileList = TRUE; - psOptions->pszWKTFormat = CPLStrdup("WKT2"); + auto psOptions = std::make_unique(); /* -------------------------------------------------------------------- */ /* Parse arguments. */ /* -------------------------------------------------------------------- */ - for (int i = 0; papszArgv != nullptr && papszArgv[i] != nullptr; i++) + + CPLStringList aosArgv; + + if (papszArgv) { - if (EQUAL(papszArgv[i], "-json")) - psOptions->eFormat = GDALINFO_FORMAT_JSON; - else if (EQUAL(papszArgv[i], "-mm")) - psOptions->bComputeMinMax = TRUE; - else if (EQUAL(papszArgv[i], "-hist")) - psOptions->bReportHistograms = TRUE; - else if (EQUAL(papszArgv[i], "-proj4")) - psOptions->bReportProj4 = TRUE; - else if (EQUAL(papszArgv[i], "-stats")) - { - psOptions->bStats = TRUE; - psOptions->bApproxStats = FALSE; - } - else if (EQUAL(papszArgv[i], "-approx_stats")) - { - psOptions->bStats = TRUE; - psOptions->bApproxStats = TRUE; - } - else if (EQUAL(papszArgv[i], "-sample")) - psOptions->bSample = TRUE; - else if (EQUAL(papszArgv[i], "-checksum")) - psOptions->bComputeChecksum = TRUE; - else if (EQUAL(papszArgv[i], "-nogcp")) - psOptions->bShowGCPs = FALSE; - else if (EQUAL(papszArgv[i], "-nomd")) - psOptions->bShowMetadata = FALSE; - else if (EQUAL(papszArgv[i], "-norat")) - psOptions->bShowRAT = FALSE; - else if (EQUAL(papszArgv[i], "-noct")) - psOptions->bShowColorTable = FALSE; - else if (EQUAL(papszArgv[i], "-listmdd")) - psOptions->bListMDD = TRUE; - /* Not documented: used by gdalinfo_bin.cpp only */ - else if (EQUAL(papszArgv[i], "-stdout")) - psOptions->bStdoutOutput = true; - else if (EQUAL(papszArgv[i], "-mdd") && papszArgv[i + 1] != nullptr) + const int nArgc = CSLCount(papszArgv); + for (int i = 0; i < nArgc; i++) { - psOptions->papszExtraMDDomains = - CSLAddString(psOptions->papszExtraMDDomains, papszArgv[++i]); - } - else if (EQUAL(papszArgv[i], "-oo") && papszArgv[i + 1] != nullptr) - { - i++; - if (psOptionsForBinary) - { - psOptionsForBinary->papszOpenOptions = CSLAddString( - psOptionsForBinary->papszOpenOptions, papszArgv[i]); - } - } - else if (EQUAL(papszArgv[i], "-nofl")) - psOptions->bShowFileList = FALSE; - else if (EQUAL(papszArgv[i], "-sd") && papszArgv[i + 1] != nullptr) - { - i++; - if (psOptionsForBinary) - { - psOptionsForBinary->nSubdataset = atoi(papszArgv[i]); - } - } - else if (EQUAL(papszArgv[i], "-wkt_format") && - papszArgv[i + 1] != nullptr) - { - CPLFree(psOptions->pszWKTFormat); - psOptions->pszWKTFormat = CPLStrdup(papszArgv[++i]); + aosArgv.AddString(papszArgv[i]); } + } - else if (EQUAL(papszArgv[i], "-if") && papszArgv[i + 1] != nullptr) - { - i++; - if (psOptionsForBinary) - { - if (GDALGetDriverByName(papszArgv[i]) == nullptr) - { - CPLError(CE_Warning, CPLE_AppDefined, - "%s is not a recognized driver", papszArgv[i]); - } - psOptionsForBinary->papszAllowInputDrivers = CSLAddString( - psOptionsForBinary->papszAllowInputDrivers, papszArgv[i]); - } - } + try + { + auto argParser = + GDALInfoAppOptionsGetParser(psOptions.get(), psOptionsForBinary); - else if (papszArgv[i][0] == '-') - { - CPLError(CE_Failure, CPLE_NotSupported, "Unknown option name '%s'", - papszArgv[i]); - GDALInfoOptionsFree(psOptions); - return nullptr; - } - else if (!bGotFilename) - { - bGotFilename = true; - if (psOptionsForBinary) - psOptionsForBinary->pszFilename = CPLStrdup(papszArgv[i]); - } - else - { - CPLError(CE_Failure, CPLE_NotSupported, - "Too many command options '%s'", papszArgv[i]); - GDALInfoOptionsFree(psOptions); - return nullptr; - } + argParser->parse_args_without_binary_name(aosArgv.List()); + } + catch (const std::exception &error) + { + CPLError(CE_Failure, CPLE_AppDefined, "%s", error.what()); + return nullptr; } - return psOptions; + return psOptions.release(); } /************************************************************************/ @@ -2258,11 +2314,5 @@ GDALInfoOptionsNew(char **papszArgv, void GDALInfoOptionsFree(GDALInfoOptions *psOptions) { - if (psOptions != nullptr) - { - CSLDestroy(psOptions->papszExtraMDDomains); - CPLFree(psOptions->pszWKTFormat); - - CPLFree(psOptions); - } + delete psOptions; } diff --git a/autotest/utilities/test_gdalinfo.py b/autotest/utilities/test_gdalinfo.py index b8ddf982dbc2..6efd947f3d79 100755 --- a/autotest/utilities/test_gdalinfo.py +++ b/autotest/utilities/test_gdalinfo.py @@ -60,7 +60,7 @@ def test_gdalinfo_1(gdalinfo_path): gdalinfo_path + " ../gcore/data/byte.tif", encoding="UTF-8", ) - assert err is None or err == "", "got error/warning" + assert err is None or err == "", f"got error/warning {err}" assert ret.find("Driver: GTiff/GeoTIFF") != -1 @@ -460,7 +460,7 @@ def test_gdalinfo_28(gdalinfo_path): encoding="UTF-8", ) ret = json.loads(ret) - assert err is None or err == "", "got error/warning" + assert err is None or err == "", f"got error/warning {err}" assert ret["driverShortName"] == "GTiff" diff --git a/autotest/utilities/test_gdalinfo_lib.py b/autotest/utilities/test_gdalinfo_lib.py index 57358612d7f6..fe8dc55f5a92 100755 --- a/autotest/utilities/test_gdalinfo_lib.py +++ b/autotest/utilities/test_gdalinfo_lib.py @@ -121,7 +121,7 @@ def test_gdalinfo_lib_5(): computeMinMax=True, reportHistograms=True, reportProj4=True, - stats=True, + # stats=True, this is mutually exclusive with approxStats approxStats=True, computeChecksum=True, showGCPs=False, diff --git a/doc/source/programs/gdalinfo.rst b/doc/source/programs/gdalinfo.rst index 9a7dfa0894fd..5c55f2920e39 100644 --- a/doc/source/programs/gdalinfo.rst +++ b/doc/source/programs/gdalinfo.rst @@ -103,18 +103,20 @@ The following command line parameters can appear in any order Only display the first file of the file list. -.. option:: -wkt_format WKT1|WKT2|WKT2_2015|WKT2_2018 +.. option:: -wkt_format WKT1|WKT2|WKT2_2015|WKT2_2018|WKT2_2019 WKT format used to display the SRS. Currently the supported values are: ``WKT1`` - ``WKT2`` (latest WKT version, currently *WKT2_2018*) + ``WKT2`` (latest WKT version, currently *WKT2_2019*) ``WKT2_2015`` - ``WKT2_2018`` + ``WKT2_2018`` (deprecated) + + ``WKT2_2019`` .. versionadded:: 3.0.0 From 03f30b1052a9913352fb83d50d97970048e3435f Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Wed, 17 Apr 2024 17:44:50 +0200 Subject: [PATCH 3/5] typo fixes [ci skip] --- doc/source/development/building_from_source.rst | 2 +- doc/source/drivers/vector/gpkg.rst | 2 +- ogr/ogrsf_frmts/arrow_common/ograrrowlayer.hpp | 2 +- ogr/ogrsf_frmts/parquet/ogr_parquet.h | 2 +- scripts/typos_allowlist.txt | 1 + swig/include/python/docs/osr_spatialreference_docs.i | 2 +- 6 files changed, 6 insertions(+), 5 deletions(-) diff --git a/doc/source/development/building_from_source.rst b/doc/source/development/building_from_source.rst index ecbb5a0bf267..bed99ec09c82 100644 --- a/doc/source/development/building_from_source.rst +++ b/doc/source/development/building_from_source.rst @@ -1699,7 +1699,7 @@ PROJ .. versionadded:: 3.9 Control the mode used for find_package(PROJ). - Alters how the default CMake seach logic + Alters how the default CMake search logic (https://cmake.org/cmake/help/latest/command/find_package.html) applies. Defaults to CUSTOM, where the CONFIG mode is applied for PROJ >= 8, and fallbacks to default MODULE mode otherwise. diff --git a/doc/source/drivers/vector/gpkg.rst b/doc/source/drivers/vector/gpkg.rst index 319278304ad7..d568dcaca289 100644 --- a/doc/source/drivers/vector/gpkg.rst +++ b/doc/source/drivers/vector/gpkg.rst @@ -639,7 +639,7 @@ custom entry of srs_id=99999 with the following properties: Note that the use of a LOCAL_CS / EngineeringCRS is mostly to provide a valid CRS definition to comply with the requirements of the GeoPackage specification and to be compatible of other applications (or GDAL 3.8 or earlier), but the -semantics of that entry is intented to be "undefined SRS of any kind". +semantics of that entry is intended to be "undefined SRS of any kind". Level of support of GeoPackage Extensions ----------------------------------------- diff --git a/ogr/ogrsf_frmts/arrow_common/ograrrowlayer.hpp b/ogr/ogrsf_frmts/arrow_common/ograrrowlayer.hpp index 93c842250810..47854f431c41 100644 --- a/ogr/ogrsf_frmts/arrow_common/ograrrowlayer.hpp +++ b/ogr/ogrsf_frmts/arrow_common/ograrrowlayer.hpp @@ -5580,7 +5580,7 @@ inline int OGRArrowLayer::GetNextArrowArray(struct ArrowArrayStream *stream, const auto nFeatureIdxCur = m_nFeatureIdx; // TODO: We likely have an issue regarding FIDs based on m_nFeatureIdx // when m_iFIDArrowColumn < 0, only a subset of row groups is - // selected, and this batch goes accross non consecutive row groups. + // selected, and this batch goes across non consecutive row groups. for (int64_t i = 0; i < m_nIdxInBatch; ++i) IncrFeatureIdx(); diff --git a/ogr/ogrsf_frmts/parquet/ogr_parquet.h b/ogr/ogrsf_frmts/parquet/ogr_parquet.h index 0b36c7b914c1..7ebf99ee48f7 100644 --- a/ogr/ogrsf_frmts/parquet/ogr_parquet.h +++ b/ogr/ogrsf_frmts/parquet/ogr_parquet.h @@ -91,7 +91,7 @@ class OGRParquetLayer final : public OGRParquetLayerBase //! Iterator over m_asFeatureIdxRemapping std::vector>::iterator m_oFeatureIdxRemappingIter{}; - //! Feature index among the potentially restricted set of selected row gropus + //! Feature index among the potentially restricted set of selected row groups int64_t m_nFeatureIdxSelected = 0; std::vector m_anRequestedParquetColumns{}; // only valid when // m_bIgnoredFields is set diff --git a/scripts/typos_allowlist.txt b/scripts/typos_allowlist.txt index 5426c423eb50..a2a7bf6390d7 100644 --- a/scripts/typos_allowlist.txt +++ b/scripts/typos_allowlist.txt @@ -305,3 +305,4 @@ either 2 or 4 comma separated values. The same rules apply for the source and de FAIL_REGEX "[Uu]nknown switch" # PGI * Esben Mose Hansen, Ange Optimization ApS SetLinearUnits("kilometre", 1000.0); + // F(ixed) S(ize) L(ist) of (x,y[,z][,m]) values / Interleaved layout diff --git a/swig/include/python/docs/osr_spatialreference_docs.i b/swig/include/python/docs/osr_spatialreference_docs.i index 3940e0ecd4c6..876e2e2d11c6 100644 --- a/swig/include/python/docs/osr_spatialreference_docs.i +++ b/swig/include/python/docs/osr_spatialreference_docs.i @@ -502,7 +502,7 @@ Examples %feature("docstring") GetSemiMajor " -Get spheroid semi major axis (in metres starting with GDAL 3.0) +Get spheroid semi major axis (in meters starting with GDAL 3.0) See :cpp:func:`OGRSpatialReference::GetSemiMajor`. From fad106af996a464236ccb4c69dfbc93292cf56f9 Mon Sep 17 00:00:00 2001 From: AbelPau Date: Wed, 17 Apr 2024 17:32:20 +0200 Subject: [PATCH 4/5] Initial version of the OGR MiramonVector driver (read/write) --- .../miramon/Arcs/3dArcs/linies_3d_WGS84.arc | Bin 0 -> 944 bytes .../miramon/Arcs/3dArcs/linies_3d_WGS84.nod | Bin 0 -> 196 bytes .../miramon/Arcs/3dArcs/linies_3d_WGS84A.dbf | Bin 0 -> 987 bytes .../miramon/Arcs/3dArcs/linies_3d_WGS84A.rel | 161 + .../miramon/Arcs/3dArcs/linies_3d_WGS84N.dbf | Bin 0 -> 525 bytes .../miramon/Arcs/3dArcs/linies_3d_WGS84N.rel | 83 + .../data/miramon/Arcs/EmptyArcs/Empty_ARC.arc | Bin 0 -> 48 bytes .../data/miramon/Arcs/EmptyArcs/Empty_ARC.nod | Bin 0 -> 48 bytes .../miramon/Arcs/EmptyArcs/Empty_ARCA.dbf | Bin 0 -> 193 bytes .../miramon/Arcs/EmptyArcs/Empty_ARCA.rel | 41 + .../miramon/Arcs/EmptyArcs/Empty_ARCN.dbf | Bin 0 -> 129 bytes .../miramon/Arcs/EmptyArcs/Empty_ARCN.rel | 26 + .../miramon/Arcs/SimpleArcs/SimpleArcFile.arc | Bin 0 -> 592 bytes .../miramon/Arcs/SimpleArcs/SimpleArcFile.nod | Bin 0 -> 172 bytes .../Arcs/SimpleArcs/SimpleArcFileA.dbf | Bin 0 -> 789 bytes .../Arcs/SimpleArcs/SimpleArcFileA.rel | 102 + .../Arcs/SimpleArcs/SimpleArcFileN.dbf | Bin 0 -> 481 bytes .../Arcs/SimpleArcs/SimpleArcFileN.rel | 64 + .../miramon/Points/3dpoints/Some3dPoints.pnt | Bin 0 -> 1568 bytes .../miramon/Points/3dpoints/Some3dPointsT.dbf | Bin 0 -> 2710 bytes .../miramon/Points/3dpoints/Some3dPointsT.rel | 112 + .../miramon/Points/EmptyPoints/Empty_PNT.pnt | Bin 0 -> 128 bytes .../miramon/Points/EmptyPoints/Empty_PNTT.dbf | Bin 0 -> 97 bytes .../miramon/Points/EmptyPoints/Empty_PNTT.rel | 23 + .../Points/SimplePoints/SimplePointsFile.pnt | Bin 0 -> 96 bytes .../Points/SimplePoints/SimplePointsFileT.dbf | Bin 0 -> 254 bytes .../Points/SimplePoints/SimplePointsFileT.rel | 58 + .../miramon/Polygons/3dPolygons/tin_3d.arc | Bin 0 -> 1432 bytes .../miramon/Polygons/3dPolygons/tin_3d.nod | Bin 0 -> 192 bytes .../miramon/Polygons/3dPolygons/tin_3d.pol | Bin 0 -> 623 bytes .../miramon/Polygons/3dPolygons/tin_3dA.dbf | Bin 0 -> 1253 bytes .../miramon/Polygons/3dPolygons/tin_3dA.rel | 141 + .../miramon/Polygons/3dPolygons/tin_3dN.dbf | Bin 0 -> 171 bytes .../miramon/Polygons/3dPolygons/tin_3dN.rel | 108 + .../miramon/Polygons/3dPolygons/tin_3dP.dbf | Bin 0 -> 565 bytes .../miramon/Polygons/3dPolygons/tin_3dP.rel | 180 + .../Polygons/EmptyPolygons/Empty_POL.arc | Bin 0 -> 48 bytes .../Polygons/EmptyPolygons/Empty_POL.nod | Bin 0 -> 48 bytes .../Polygons/EmptyPolygons/Empty_POL.pol | Bin 0 -> 112 bytes .../Polygons/EmptyPolygons/Empty_POLA.dbf | Bin 0 -> 193 bytes .../Polygons/EmptyPolygons/Empty_POLA.rel | 44 + .../Polygons/EmptyPolygons/Empty_POLN.dbf | Bin 0 -> 129 bytes .../Polygons/EmptyPolygons/Empty_POLN.rel | 26 + .../Polygons/EmptyPolygons/Empty_POLP.dbf | Bin 0 -> 225 bytes .../Polygons/EmptyPolygons/Empty_POLP.rel | 52 + .../Polygons/SimplePolygons/SimplePolFile.arc | Bin 0 -> 536 bytes .../Polygons/SimplePolygons/SimplePolFile.nod | Bin 0 -> 92 bytes .../Polygons/SimplePolygons/SimplePolFile.pol | Bin 0 -> 349 bytes .../SimplePolygons/SimplePolFileA.dbf | Bin 0 -> 511 bytes .../SimplePolygons/SimplePolFileA.rel | 89 + .../SimplePolygons/SimplePolFileN.dbf | Bin 0 -> 261 bytes .../SimplePolygons/SimplePolFileN.rel | 64 + .../SimplePolygons/SimplePolFileP.dbf | Bin 0 -> 729 bytes .../SimplePolygons/SimplePolFileP.rel | 93 + autotest/ogr/ogr_miramon_vector.py | 677 ++ doc/source/drivers/vector/index.rst | 1 + doc/source/drivers/vector/miramon.rst | 374 + frmts/drivers.ini | 1 + fuzzers/CMakeLists.txt | 1 + fuzzers/build_google_oss_fuzzers.sh | 1 + fuzzers/build_seed_corpus.sh | 20 + fuzzers/ogr_fuzzer.cpp | 26 + ogr/ogrsf_frmts/CMakeLists.txt | 3 + ogr/ogrsf_frmts/generic/ogrregisterall.cpp | 3 + ogr/ogrsf_frmts/miramon/CMakeLists.txt | 14 + ogr/ogrsf_frmts/miramon/data/MM_m_idofic.csv | 233 + ogr/ogrsf_frmts/miramon/mm_constants.h | 173 + ogr/ogrsf_frmts/miramon/mm_gdal_constants.h | 97 + .../miramon/mm_gdal_driver_structs.h | 826 ++ ogr/ogrsf_frmts/miramon/mm_gdal_functions.c | 2906 +++++++ ogr/ogrsf_frmts/miramon/mm_gdal_functions.h | 164 + ogr/ogrsf_frmts/miramon/mm_gdal_structures.h | 116 + ogr/ogrsf_frmts/miramon/mm_rdlayr.c | 707 ++ ogr/ogrsf_frmts/miramon/mm_rdlayr.h | 22 + ogr/ogrsf_frmts/miramon/mm_wrlayr.c | 7459 +++++++++++++++++ ogr/ogrsf_frmts/miramon/mm_wrlayr.h | 222 + ogr/ogrsf_frmts/miramon/ogrmiramon.h | 168 + .../miramon/ogrmiramondatasource.cpp | 291 + ogr/ogrsf_frmts/miramon/ogrmiramondriver.cpp | 212 + ogr/ogrsf_frmts/miramon/ogrmiramonlayer.cpp | 2690 ++++++ ogr/ogrsf_frmts/ogrsf_frmts.h | 1 + scripts/fix_typos.sh | 1 + scripts/typos_allowlist.txt | 22 + 83 files changed, 18898 insertions(+) create mode 100644 autotest/ogr/data/miramon/Arcs/3dArcs/linies_3d_WGS84.arc create mode 100644 autotest/ogr/data/miramon/Arcs/3dArcs/linies_3d_WGS84.nod create mode 100644 autotest/ogr/data/miramon/Arcs/3dArcs/linies_3d_WGS84A.dbf create mode 100644 autotest/ogr/data/miramon/Arcs/3dArcs/linies_3d_WGS84A.rel create mode 100644 autotest/ogr/data/miramon/Arcs/3dArcs/linies_3d_WGS84N.dbf create mode 100644 autotest/ogr/data/miramon/Arcs/3dArcs/linies_3d_WGS84N.rel create mode 100644 autotest/ogr/data/miramon/Arcs/EmptyArcs/Empty_ARC.arc create mode 100644 autotest/ogr/data/miramon/Arcs/EmptyArcs/Empty_ARC.nod create mode 100644 autotest/ogr/data/miramon/Arcs/EmptyArcs/Empty_ARCA.dbf create mode 100644 autotest/ogr/data/miramon/Arcs/EmptyArcs/Empty_ARCA.rel create mode 100644 autotest/ogr/data/miramon/Arcs/EmptyArcs/Empty_ARCN.dbf create mode 100644 autotest/ogr/data/miramon/Arcs/EmptyArcs/Empty_ARCN.rel create mode 100644 autotest/ogr/data/miramon/Arcs/SimpleArcs/SimpleArcFile.arc create mode 100644 autotest/ogr/data/miramon/Arcs/SimpleArcs/SimpleArcFile.nod create mode 100644 autotest/ogr/data/miramon/Arcs/SimpleArcs/SimpleArcFileA.dbf create mode 100644 autotest/ogr/data/miramon/Arcs/SimpleArcs/SimpleArcFileA.rel create mode 100644 autotest/ogr/data/miramon/Arcs/SimpleArcs/SimpleArcFileN.dbf create mode 100644 autotest/ogr/data/miramon/Arcs/SimpleArcs/SimpleArcFileN.rel create mode 100644 autotest/ogr/data/miramon/Points/3dpoints/Some3dPoints.pnt create mode 100644 autotest/ogr/data/miramon/Points/3dpoints/Some3dPointsT.dbf create mode 100644 autotest/ogr/data/miramon/Points/3dpoints/Some3dPointsT.rel create mode 100644 autotest/ogr/data/miramon/Points/EmptyPoints/Empty_PNT.pnt create mode 100644 autotest/ogr/data/miramon/Points/EmptyPoints/Empty_PNTT.dbf create mode 100644 autotest/ogr/data/miramon/Points/EmptyPoints/Empty_PNTT.rel create mode 100644 autotest/ogr/data/miramon/Points/SimplePoints/SimplePointsFile.pnt create mode 100644 autotest/ogr/data/miramon/Points/SimplePoints/SimplePointsFileT.dbf create mode 100644 autotest/ogr/data/miramon/Points/SimplePoints/SimplePointsFileT.rel create mode 100644 autotest/ogr/data/miramon/Polygons/3dPolygons/tin_3d.arc create mode 100644 autotest/ogr/data/miramon/Polygons/3dPolygons/tin_3d.nod create mode 100644 autotest/ogr/data/miramon/Polygons/3dPolygons/tin_3d.pol create mode 100644 autotest/ogr/data/miramon/Polygons/3dPolygons/tin_3dA.dbf create mode 100644 autotest/ogr/data/miramon/Polygons/3dPolygons/tin_3dA.rel create mode 100644 autotest/ogr/data/miramon/Polygons/3dPolygons/tin_3dN.dbf create mode 100644 autotest/ogr/data/miramon/Polygons/3dPolygons/tin_3dN.rel create mode 100644 autotest/ogr/data/miramon/Polygons/3dPolygons/tin_3dP.dbf create mode 100644 autotest/ogr/data/miramon/Polygons/3dPolygons/tin_3dP.rel create mode 100644 autotest/ogr/data/miramon/Polygons/EmptyPolygons/Empty_POL.arc create mode 100644 autotest/ogr/data/miramon/Polygons/EmptyPolygons/Empty_POL.nod create mode 100644 autotest/ogr/data/miramon/Polygons/EmptyPolygons/Empty_POL.pol create mode 100644 autotest/ogr/data/miramon/Polygons/EmptyPolygons/Empty_POLA.dbf create mode 100644 autotest/ogr/data/miramon/Polygons/EmptyPolygons/Empty_POLA.rel create mode 100644 autotest/ogr/data/miramon/Polygons/EmptyPolygons/Empty_POLN.dbf create mode 100644 autotest/ogr/data/miramon/Polygons/EmptyPolygons/Empty_POLN.rel create mode 100644 autotest/ogr/data/miramon/Polygons/EmptyPolygons/Empty_POLP.dbf create mode 100644 autotest/ogr/data/miramon/Polygons/EmptyPolygons/Empty_POLP.rel create mode 100644 autotest/ogr/data/miramon/Polygons/SimplePolygons/SimplePolFile.arc create mode 100644 autotest/ogr/data/miramon/Polygons/SimplePolygons/SimplePolFile.nod create mode 100644 autotest/ogr/data/miramon/Polygons/SimplePolygons/SimplePolFile.pol create mode 100644 autotest/ogr/data/miramon/Polygons/SimplePolygons/SimplePolFileA.dbf create mode 100644 autotest/ogr/data/miramon/Polygons/SimplePolygons/SimplePolFileA.rel create mode 100644 autotest/ogr/data/miramon/Polygons/SimplePolygons/SimplePolFileN.dbf create mode 100644 autotest/ogr/data/miramon/Polygons/SimplePolygons/SimplePolFileN.rel create mode 100644 autotest/ogr/data/miramon/Polygons/SimplePolygons/SimplePolFileP.dbf create mode 100644 autotest/ogr/data/miramon/Polygons/SimplePolygons/SimplePolFileP.rel create mode 100644 autotest/ogr/ogr_miramon_vector.py create mode 100644 doc/source/drivers/vector/miramon.rst create mode 100644 ogr/ogrsf_frmts/miramon/CMakeLists.txt create mode 100644 ogr/ogrsf_frmts/miramon/data/MM_m_idofic.csv create mode 100644 ogr/ogrsf_frmts/miramon/mm_constants.h create mode 100644 ogr/ogrsf_frmts/miramon/mm_gdal_constants.h create mode 100644 ogr/ogrsf_frmts/miramon/mm_gdal_driver_structs.h create mode 100644 ogr/ogrsf_frmts/miramon/mm_gdal_functions.c create mode 100644 ogr/ogrsf_frmts/miramon/mm_gdal_functions.h create mode 100644 ogr/ogrsf_frmts/miramon/mm_gdal_structures.h create mode 100644 ogr/ogrsf_frmts/miramon/mm_rdlayr.c create mode 100644 ogr/ogrsf_frmts/miramon/mm_rdlayr.h create mode 100644 ogr/ogrsf_frmts/miramon/mm_wrlayr.c create mode 100644 ogr/ogrsf_frmts/miramon/mm_wrlayr.h create mode 100644 ogr/ogrsf_frmts/miramon/ogrmiramon.h create mode 100644 ogr/ogrsf_frmts/miramon/ogrmiramondatasource.cpp create mode 100644 ogr/ogrsf_frmts/miramon/ogrmiramondriver.cpp create mode 100644 ogr/ogrsf_frmts/miramon/ogrmiramonlayer.cpp 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 0000000000000000000000000000000000000000..e6e5b38820de94031de2f5e12a031ff23110f418 GIT binary patch literal 944 zcmZ<^a#k?ZGZfe>cwyV(wlDVa7nbyM@A_|_@H%W^)C?B~Pqo0Feb-zZ*cc!{;U~}1 zzdOF#A7^%nt1tO(|LI5!_qP=;4pJQ1(TTfV99V$L8WtMCU;?T=0Mr1YL3VMkf2s28+)4ZVhHq0m zuKt0$>zv=6r43759A3^je@Sesivu%I?E|1bkiD!xY_Yc4Ug*{ZxIGomr+?gVZkQs(sO{Z}69JYsr1I(SUaDcfNz3VY6AKn7Ot>xgt;5$URZp>!V4Cj7OHP=ep>6|u*qBX zNaDLc_ONh8*Dw8icS_*F-|+B*g(oZ=Vc`V}Cv^Sjd|3Fv!UY%<=-`2)a;szzGJgW9daye{;tQbaZ_a3P0CHjOI`AUC0>lTq4@8691EgW@JCNTE ib|=_9KyjG+VDe!10EH92_2fGMX%zPW`7m{0cK`r}%2>_- literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..de26c1fb8e0b6ef6acb5d499bd09b64fefcf4c99 GIT binary patch literal 196 zcmebCcTq6ZGZZ||>=IXB^4*>>&wFao)c^M9{O&AmSmNT~sTSC?@0yDPCj$g9GB7h# z0GS}V0ZMm3=?PGJ29#a^q*;J?1(d%7N*@5y=ztL_zzn6C&}fi6NDgEkE0hLOU^W1p C8x|-4 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..b1460b1b421650476b1adcddce7fd43c6c6cc856 GIT binary patch literal 987 zcmZ`#!D_=W3^f!6gYCB6^99DTEX6&RH7$YRLORB7`H&so>-)64O&`~P_}5E1uZJa<*ZMN7i^fll z-*se8)8#m=bDB$9m_PFie=4-jKMq0?MZJ*Z z@ebJUdk{GCXgNruRUykO$bI=OSEX{NJf0Cj?BoMc%(m#(s7q8&s&bH%Y7YrI3*<)- zcLMx9KB_yZpzPVqs!rueoTU&T#8y&X0_VUYi<30L*pmKLJq_wjw&T$Ov9RwWaLC73 Io2p>^0mRRI1^@s6 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..9376c48a6ae80cb46c99d4fdfb79940973d7247c GIT binary patch literal 525 zcmZRs=H%gIU|?uu&;gQYK!kz8(W<#7Ajver5%{STeb00jUR(iH0e literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..88197ddde3ebef69a634e4be9114d40b2cd0d79b GIT binary patch literal 48 ecmebCcTq6ZGh~>W<#7Ajver5%{STeb00jUT;}rG) literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..683995629988d9964fba1f3f4be70ce48cf2ac7c GIT binary patch literal 193 zcmZRsW*1=qf`bfMAPN-#WjtNt-Gdz6Je?UB{6LbTs45uz;=^2nLR=$)amf4l`?<$E i1_8B0^|PTG;OFn+8t>`n371EeW&q2(fuyn9&kF#pe-JMK literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..95689ae5c6954ec096059cfdcca4dea9e26d8711 GIT binary patch literal 129 zcmZRsW*1=qf<^`%5QPeWGM+B+?m>=jp3V#mejrIvR22-4LC(SPj`4o}F4*NmJOe_5 NLDH@ec}6t-ya4143pfA( literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..a5084dcc5b725485e9d8e3cb5f8cc69f76680ef6 GIT binary patch literal 592 zcmZ<^a#k?ZGh|3gDSH?GwbVht)#A2E>Qo1pplciC)G{4HUnXja$@e<2FhBshI=MM7 zn*v{o3DlhZSolylTJJosOwH@x? zwUq|e-@PX|z|_OS4;C(`o8wHluXQ+t@lBnwYgx6!%GnZ)t8|(j$`&5{FZ8X#!7ySu z^J9?~hm4C7c_nRG4wl@~hkM_3!NVs=^oZj(_X!S{+y!?t9It_gl_Y*K->)rU#B;<3r}xpb^rhvLhHEz literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..2b3fa6ee1e50370394723c3eb9280a167191edf6 GIT binary patch literal 172 zcmebCcTq6ZGh|3gDSH?GwbVht)#A2E>Qsj?zNu4oEvt42eVM2wCg1D8!2kh_49pA# pKqiQ;fYJ?6x&um2fYLLd^a3co0!X6+MyLRcW`gozG&7VBq5*xM6Pf@3 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..cb3e15a7209bbbe5b85fd7c7b68ae77740320221 GIT binary patch literal 789 zcmb7;y$*sf6h<%6Flk(L?-LN((gGv$7ZOtvB*v{T;_7<~q;Lx zggQ8A5C?f~QkN9kD?j!^MCi;bI(2Zyty;yrb@?42PYd_ z)!Sc>OrI|F*B+qQ`6u1#bfwV$`4pWDMnA8Y%jFMRe?1K24)L9K`)1zPYPC;!KRZ}D zsXUG+TPyG1^?KX##Esu0-=livZQfH>UB0%9Agazs3bx zc;5%!lRp+WpKJZrcz0$MPuL;lON)&yt#4P;tMZQLesAR2w|;jNpY=uS^4*z1C%;a) z74O$N^>6At@29R6dG%w3b?UgD_uTJe-cx2t`5gZ%jGK8+AHLh`9jWU}-gEx4yx$o; znqQ|s8$M6_>BANI(W~$L>wUWClW2kqFD)if<}dE(9zw&tL7@8=;8yVyaQa$oz^&q^ z{u3X;>3h3PyjAhD@D_6r!>!_T@PE}a57(UZiMJ|#0nWYC;-w_AiZ8;|=jMeZvWnN? zFPV=j+$w&^{LRD`P`Xk|p_Q5+^frO-bBcjn{@zNVh>-=wX-r5&Z}D zmrm9XD2g%BR2}3*U-_f&(|dli-?P_m5kh~CzUVm^ZxE^$-D+Jdt2si=puPyUR83np z8?$KiS*S0%d0m(dMt>IQUewF_2!!4RdeN-v(yp2Jp9lK7Y|qzC*Q}ktqd;%EwzGSz z|4`TQZRUkRoAX0`Sy!vit%26V9O`H1^``6=MO&i9puZfZN5krEbz0iV+a13CIn3{W4U3mseBIyN^e1{dr}V>;5)eR%0{Fy~;v!?zp#Z4F zbn~!t|EC@t;A4oAL}czA3M4z&)i_Ex6a#j6fYT@r?5SL3z*u5{(gi5uP}tgV<_%W3X2@GuiNk2+y3I}FaFkF-S_p(0Rw z)vl;;83YQPC!%tDr@$~H5!XKE%A-uRAD+P}RF>Eo6qUpUsH;8Z+V_e<<;-VLg8q4E z>l-Oijq({(aZP5-l}F_OF->Lyr8_l)!~m7OcB;P{mD1aD?lVYfVlikGI9Dw3!+C__ zKxtxt-c<(TsZav+wf%pIX1W4=1|`s#CKJ}xfO4IIsv(1Et~@G_%ix47rh9v$D-Qnv DL0U&Q literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..d06314c2b7ade160ee0e85c297c51342e3c571b8 GIT binary patch literal 128 ucmWIW3sErCGZdPd<#7Ajver6WGy@JWr<9??=85?~V(I@tPysXxrXB!P$21QB literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..82935df8b1a3be5791d5c7b1b20c1691da540a9c GIT binary patch literal 97 zcmZRsW*1=qf=jp3V#mejrIMR22-42tH5=0|O6&4`%TK04Pxe Avj6}9 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..f543cba80f3e4208d1212fbe4fdcdc6db387563e GIT binary patch literal 96 zcmWIW3sErCGh|xgZgJZrwbbG8>DU&wDNPQ!6DphwW7{02T$9KvY3p)eW`KbECRt0K Xb2q@nVftX|VfqE4bV9nOwmAR*uOuXR literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..57527da74671340fbf8db0f3f2debbbfe2da2835 GIT binary patch literal 254 zcmbPG%_hstz`)SMpavw-fCvMFr%SwhkfWQYGXsMkNR|stiDO8JAyf#c1PXXy3!f_ri|jkMTE2vvA{GF zF{D9JH?Gx1(cnM{iW||;MYu931sCHg7v1~*KaW2*eDMGM|GsnXJ@>rl#P8Y7jm@>| zqZ{QP8!abS6Qu(#5)d;m&-~fxH#*@T@D#OwjOD>tBWt~XoNM^G`&z+w%$ZCkSAK~3 z!kX-IE2fSfoVGq!@DV!UC;IYC4~fUU*we#Es!3H3n10{^kG{-Hd;OT(VAZtQfkDHA zukh2)d*|`E#@FGOpO+pzF@C~F$;TYfk9z{V)Cr$({&HW|m;185tmivFeQuJE^TOPc zFY$6d$(MM!FXtg^|67Tx8bwL_{5;Ar@Oazin4KG}Q+(ep_yTzU6f+}3^*XOF2k}DD zh@!maGd{!zPs^)g^0u4Tb!pq=!*1T5zFAmDd3B(V`*8hEH?MX0>!bvp=VAOvVP5Ov z{P;Eb&NAAh<@GiAteb~^%m*FF!^Z+&xSrn+`Zaj?=bxYP{<-NFbTNDO2aOr8bEW&{aB0f@~G R#2_<3G)OH-Kgb-AS^z^&6(j%v literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..5e690dfd30d47573654f4ee92c853ca5025c3860 GIT binary patch literal 623 zcmWIW_fas^Gi2V@u$^nRp1k8>ewH=QjpQ9C*8PrOdOXnaP2i3&ofCnMYzzp%1Z6S; zX;vU+24avH3lM|EVDcbw5CEwG0hoG_IuHf{n7tc<`RBYqvNunt`G5hEy&%0HgMUO6 zYr5)AamYHF>>*V8?LZJ)s(O!syrbOwwl%kq^aI^}?kdpzAUA+8NWTJ5{}UK^Q4}g=pzT1|6%TD3FDi)7ifQ4TH3Pf zXzmXH>i0_e*F3+m*`fd5jqvFvr!n-y>_7BX;|nJ+{E+>h0MtL>?y__LqBbg+<}_6 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..36aebaac2b495b3a78615d371955062395aa18f6 GIT binary patch literal 1253 zcma)4OA5j;5FO}Fa3i|U35NXIG|Qq@3KF4;xa;0CcsXxot3~Megk}-w`^=k}CVOP7 z1ra^z-rwS^iJDz^Xsdm*B}xOk5t7th>-JLL&VTshDIL11?d_vKn-)l?UEMXQ`SMem z-u!-`asE=FqY-GQpwW?YZ>Heq%OOzMM+x-*NK3ZSqoNRm@#?4OP)~q12KG*XpWGb9 z!|oN#7e%ep9PmsqN!Z6`;P^snUCA!Mo?sGGsyWg^8!mJ?dr`2aDv)2!)W5yx;Pmk6 r;{O!pBzeubi%1FEhdn8{6FF0`tnv2XOfX)-PDPTjhP{S6fl2lSj|`l% literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..d1f5c153999180028d278610a9a95a0dd5bbe138 GIT binary patch literal 171 zcmZRsVHRRzU|?uu0Fh`wgn_}+CEh*A(aqDDfx!%HF~~VM-Z9?K-vzsTh-W}( rFi6@JBF~7XpI1S_K*88RLBSAA8i7e;FlnM-WT>EEs$d3`H3PB%BC-*x literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..d2a443329d447ffff907483ed8d0629c57b183a1 GIT binary patch literal 565 zcmZ{e-AV*8428Q6h#=mG4^SV#&@}x?E{C-)13E5s5%1bo_n>Yqi3|Z{l>*3#n%k>8W`^vzcOl(@J|xlr9aNc{r0ynl zvk16=y_0AlWu+kP5|uibvhyul@36^-Hr;!OUIR&dTffTSz(?-cQQbKUP+oiDsOROF O6C|(MDusc(-u(sM=|{8x literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..e089cf12d0597d83a61e9e2099899f85764754c9 GIT binary patch literal 48 ecmZ<^a#k?ZGh~>W<#7Ajver5%{STeb00jUR(iH0e literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..88197ddde3ebef69a634e4be9114d40b2cd0d79b GIT binary patch literal 48 ecmebCcTq6ZGh~>W<#7Ajver5%{STeb00jUT;}rG) literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..255d114a0e0cb6dd2b7b5e5755c5c57ecaf4fc3a GIT binary patch literal 112 rcmWIW_fas^GgO$G<#7Ajver5%{STeb$N&N8;^=&+GB8zu%z?526fP+B literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..7481dceeefb0d1e45d8b4e5331e4b5bceb390a3d GIT binary patch literal 193 zcmZRc!T<&b8L}V@G}6;0-aW|C&C{8I!4D)QiYD(DALbeq;u;Z*L*B>V&pqBT2&f&Z dpAFRjKYth3cuzl1xIC&f16bY-B#qsEUI3q75E%df literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..95689ae5c6954ec096059cfdcca4dea9e26d8711 GIT binary patch literal 129 zcmZRsW*1=qf<^`%5QPeWGM+B+?m>=jp3V#mejrIvR22-4LC(SPj`4o}F4*NmJOe_5 NLDH@ec}6t-ya4143pfA( literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..a4ec5a35e3566f53df3371a1361b850670c5e5e7 GIT binary patch literal 225 zcmZRc!T<&j8R{VnG}6;0-aW|C&C{8I!4D)QiYD(DALbeq;u;Z*Lq5PY$kW#~B*+ye l&xUG%W00#OR0Lu^n!I1UV~{h@a3H{8e}KP_r#nu0UI4Xq5uE@4 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..431d702a3a3540257dfdffce360f123848d1eeb8 GIT binary patch literal 536 zcmZ<^a#k?ZGi2E98#`I@Yl*|Y+{;szD@=CS(bD|peQJe+oy>ifmSbHG%nT3!Qx`GM zUGJG~heHqB*RaOxb#QfTKxH?eYC%-nGFH!W^(hYSdv8Sre(Qvr|LB0;fx_u^4xaNr zCY1GcIdA~gS}-y&01+6weNBkr>znA{yGPA9OQp%dR8&BU|5*pzT}L-DR<}-Rasb)4 z1E`4!NQ1DHuuoUCOREFSoy(sY)!yRmaHu{}u-Mq85$;Zy`;T~ST9YW-=1{=1XUjX6 z7Px;txb6*opjzRu;BL<4DeCnY=C!#Uo5sIk0^I$V@=Mw{p7%H?Xf64*P_-TIU%uVd zKAU`+9d=kPniM47=K%Bfnxh;csREN6-p-uN_CLD;9xj3#+rAovwmOK1@3PEPnB>6k zX|B$DufqY|zE6uM1zmpvG#46Auy6`G`>G`2PoqP}O}kqSbK4wX;RFjmfwL6`*4t|x X(gN6zt#oa7FnhbkkNsGM1G;$t@$uhR literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..e5d310c3c0cee2ef29d314c8b4e269060b4f902e GIT binary patch literal 92 zcmebCcTq6ZGi2E98#`I@Yl%af+p%f<8zwmHXlZ`)KDENZbNifmSbHGEDQ+12xKxL zxD3odHX;2;${9RB2A~0${)l<*de3Y-9D3NkhBaQVgWC_%0>dEvZOd3a%hjhiG>W+N z7MK2ZaNm0?D)3t;-2IOZ=p87WUgzLB|6@W~UzY<+FNh8R5^i4;V)*(dIxIVRca6>b zuMWO@)Qq!KnjB0;1*G_&b-?|9bQ5EB>y#!G{SiR@Qo=r6(JrkHr@l^&Y`%Ke0Tc!x Nzz(ti2$(?u1OVn#qZAzevYYmk6ns|Kkt_dLW-d{ z!~QjI`;h9qJjcfU^T(+BZEnkfbdsCeeBBtkuqsrj)2F$q}WWOg2r+Fm~qrI@1e@R5?;CuU6bhEl`u`oUw{m p9#Kr@>{?Boj2&irAr)1#w=wGkH(Fa?5mv&O{Cf>5J&C literal 0 HcmV?d00001 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..3d54afff6a4a --- /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. +So, 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 +-------------------------- + +In MiraMon format structured vectors is the binary format of MiraMon 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 points 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 stringlines 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 polygons 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 access the rest to search for 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 type of file with the mentioned extensions: + +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 *polygons* or *multipolygons* 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 polygonal (or multipolygonal) 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 it 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 *stringlines*. +- *.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 knows to auto-extend string and integer fields to +dynamically accommodate for 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:: iMultiRecord + :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: *iMultiRecord=1* for first, *iMultiRecord=2* for second, etc + and *iMultiRecord=last* for the last element of the list. + *iMultiRecord=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 + + It is 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 iMultiRecord=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..fc76ee37fd3f --- /dev/null +++ b/ogr/ogrsf_frmts/miramon/mm_wrlayr.c @@ -0,0 +1,7459 @@ +/****************************************************************************** + * + * 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; + else + pCoord += pCurrentArcHeader->nElemCount; + + // If the ring is finished a jump to the next ring must be done. + if (hMiraMonLayer->bIsPolygon && VFG & MM_END_ARC_IN_RING) + pCoord++; + + 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; + } + 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..cbef1169a460 --- /dev/null +++ b/ogr/ogrsf_frmts/miramon/ogrmiramon.h @@ -0,0 +1,168 @@ +/****************************************************************************** + * $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; + + 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..26884ba0ae4c --- /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..4e2577f1b85f --- /dev/null +++ b/ogr/ogrsf_frmts/miramon/ogrmiramonlayer.cpp @@ -0,0 +1,2690 @@ +/****************************************************************************** + * + * 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), 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))); + } + + phMiraMonLayer->iMultiRecord = + MM_MULTIRECORD_NO_MULTIRECORD; // No option iMultiRecord + const char *szMultiRecord = + CSLFetchNameValue(papszOpenOptions, "iMultiRecord"); + 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); +} + +/****************************************************************************/ +/* 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; + } + MM_BOOLEAN IAmExternal; + + for (nIRing = 0; + nIRing < phMiraMonLayer->ReadFeature.nNRings; nIRing++) + { + auto poRing = std::make_unique(); + + IAmExternal = (MM_BOOLEAN)(phMiraMonLayer->ReadFeature + .flag_VFG[nIRing] & + MM_EXTERIOR_ARC_SIDE); + + 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 ((IAmExternal && + 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() == OFTInteger64List || + 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() == 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, 4); + pszDate_5[4] = '\0'; + Year = atoi(pszDate_5); + + CPLStrlcpy(pszDate_3, phMiraMonLayer->szStringToOperate + 4, + 2); + (pszDate_3)[2] = '\0'; + Month = atoi(pszDate_3); + + CPLStrlcpy(pszDate_3, phMiraMonLayer->szStringToOperate + 6, + 2); + (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 (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; + + 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; + + // 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; + + 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; + + hMMFeature.pRecords[0].pField[iField].iValue = + poFeature->GetFieldAsInteger64(iField); + if (MM_SecureCopyStringFieldValue( + &hMMFeature.pRecords[0].pField[iField].pDinValue, + poFeature->GetFieldAsString(iField), + &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; + + hMMFeature.pRecords[0].pField[iField].dValue = + poFeature->GetFieldAsDouble(iField); + if (MM_SecureCopyStringFieldValue( + &hMMFeature.pRecords[0].pField[iField].pDinValue, + poFeature->GetFieldAsString(iField), + &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) From 4b71cd21242680c50f096adee93c9da9ed5c7b61 Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Thu, 18 Apr 2024 00:23:57 +0200 Subject: [PATCH 5/5] Apply suggestions from code review Co-authored-by: Nyall Dawson --- doc/source/drivers/vector/miramon.rst | 32 +++++++++++++-------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/doc/source/drivers/vector/miramon.rst b/doc/source/drivers/vector/miramon.rst index 3d54afff6a4a..3b447acf6515 100644 --- a/doc/source/drivers/vector/miramon.rst +++ b/doc/source/drivers/vector/miramon.rst @@ -47,27 +47,27 @@ Driver capabilities Overview of MiraMon format -------------------------- -In MiraMon format structured vectors is the binary format of MiraMon for vector layer data, linked to +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 points layer, you must provide the name with the extension .pnt +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 stringlines layer, you must provide the name with the extension .arc +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 polygons layer, you must provide the name with the extension .pol +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 access the rest to search for the +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 type of file with the mentioned extensions: +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. @@ -129,14 +129,14 @@ of the layer file. the GDAL MiraMon vector driver because nodes contain topological information that is not transferred to other formats. -- **Polygon layers**: These layers contain *polygons* or *multipolygons* type features. +- **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 polygonal (or multipolygonal) vector features. + 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 `__, @@ -182,11 +182,11 @@ of the layer file. Encoding -------- -When reading MiraMon files, the code page setting in the header of the .dbf file +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 +When writing MiraMon files the codepage of *.dbf* files can be ANSI or UTF8 depending on the layer creation option DBFEncoding. Creation Issues @@ -195,7 +195,7 @@ 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 +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. @@ -209,7 +209,7 @@ folder or a set of files with the appropriate extension (*.pnt*, etc): 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 it the output is a **file** with extension all the translated layers in the origin dataset will be created with the specified name. +- 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*. @@ -233,7 +233,7 @@ When translating from a MiraMon vector format, the MiraMon vector driver input n described extensions: - *.pnt* for *points*. -- *.arc* for *stringlines*. +- *.arc* for *linestrings*. - *.pol* for *polygons* (or *multipolygons*). The extension *.nod* is not valid for translation. Take in consideration all auxiliary files described above. @@ -241,8 +241,8 @@ The extension *.nod* is not valid for translation. Take in consideration all aux Field sizes ----------- -The driver knows to auto-extend string and integer fields to -dynamically accommodate for the length of the data to be inserted. +The driver will automatically extend string and integer fields to +dynamically accommodate the length of the data to be inserted. Size Issues ----------- @@ -318,7 +318,7 @@ Layer creation options :choices: ENG, CAT, SPA :default: ENG - It is the language used in the metadata file (*.rel*) for the descriptors of + Sets the language used in the metadata file (*.rel*) for the descriptors of the *.dbf* fields. Examples