LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/selafin - ogrselafindatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 210 395 53.2 %
Date: 2024-11-21 22:18:42 Functions: 15 16 93.8 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  * Project:  Selafin importer
       3             :  * Purpose:  Implementation of OGRSelafinDataSource class.
       4             :  * Author:   François Hissel, francois.hissel@gmail.com
       5             :  *
       6             :  ******************************************************************************
       7             :  * Copyright (c) 2014,  François Hissel <francois.hissel@gmail.com>
       8             :  *
       9             :  * SPDX-License-Identifier: MIT
      10             :  ****************************************************************************/
      11             : 
      12             : #include "ogr_selafin.h"
      13             : #include "cpl_conv.h"
      14             : #include "cpl_string.h"
      15             : #include "cpl_vsi_virtual.h"
      16             : #include "cpl_vsi.h"
      17             : #include "io_selafin.h"
      18             : 
      19             : #include <algorithm>
      20             : #include <ctime>
      21             : 
      22             : /************************************************************************/
      23             : /*                          Range                                       */
      24             : /************************************************************************/
      25       43116 : Range::~Range()
      26             : {
      27       21558 :     deleteList(poVals);
      28       21558 :     deleteList(poActual);
      29       21558 : }
      30             : 
      31       43125 : void Range::deleteList(Range::List *poList)
      32             : {
      33       43125 :     if (poList == nullptr)
      34       43125 :         return;
      35           0 :     Range::List *pol = poList;
      36           0 :     while (pol != nullptr)
      37             :     {
      38           0 :         poList = poList->poNext;
      39           0 :         delete pol;
      40           0 :         pol = poList;
      41             :     }
      42             : }
      43             : 
      44           3 : void Range::setRange(const char *pszStr)
      45             : {
      46           3 :     deleteList(poVals);
      47           3 :     deleteList(poActual);
      48           3 :     poVals = nullptr;
      49           3 :     Range::List *poEnd = nullptr;
      50           3 :     if (pszStr == nullptr || pszStr[0] != '[')
      51             :     {
      52           0 :         CPLError(CE_Warning, CPLE_IllegalArg, "Invalid range specified\n");
      53           3 :         return;
      54             :     }
      55           3 :     const char *pszc = pszStr;
      56           3 :     char *psze = nullptr;
      57             :     SelafinTypeDef eType;
      58           3 :     while (*pszc != 0 && *pszc != ']')
      59             :     {
      60           3 :         pszc++;
      61           3 :         if (*pszc == 'p' || *pszc == 'P')
      62             :         {
      63           0 :             eType = POINTS;
      64           0 :             pszc++;
      65             :         }
      66           3 :         else if (*pszc == 'e' || *pszc == 'E')
      67             :         {
      68           0 :             eType = ELEMENTS;
      69           0 :             pszc++;
      70             :         }
      71             :         else
      72           3 :             eType = ALL;
      73             : 
      74           3 :         int nMin = 0;
      75           3 :         if (*pszc != ':')
      76             :         {
      77           3 :             nMin = (int)strtol(pszc, &psze, 10);
      78           3 :             if (*psze != ':' && *psze != ',' && *psze != ']')
      79             :             {
      80           3 :                 CPLError(CE_Warning, CPLE_IllegalArg,
      81             :                          "Invalid range specified\n");
      82           3 :                 deleteList(poVals);
      83           3 :                 poVals = nullptr;
      84           3 :                 return;
      85             :             }
      86           0 :             pszc = psze;
      87             :         }
      88           0 :         int nMax = -1;
      89           0 :         if (*pszc == ':')
      90             :         {
      91           0 :             ++pszc;
      92           0 :             if (*pszc != ',' && *pszc != ']')
      93             :             {
      94           0 :                 nMax = (int)strtol(pszc, &psze, 10);
      95           0 :                 if (*psze != ',' && *psze != ']')
      96             :                 {
      97           0 :                     CPLError(CE_Warning, CPLE_IllegalArg,
      98             :                              "Invalid range specified\n");
      99           0 :                     deleteList(poVals);
     100           0 :                     poVals = nullptr;
     101           0 :                     return;
     102             :                 }
     103           0 :                 pszc = psze;
     104             :             }
     105             :         }
     106             :         else
     107           0 :             nMax = nMin;
     108           0 :         Range::List *poNew = nullptr;
     109           0 :         if (eType != ALL)
     110           0 :             poNew = new Range::List(eType, nMin, nMax, nullptr);
     111             :         else
     112           0 :             poNew =
     113             :                 new Range::List(POINTS, nMin, nMax,
     114           0 :                                 new Range::List(ELEMENTS, nMin, nMax, nullptr));
     115           0 :         if (poVals == nullptr)
     116             :         {
     117           0 :             poVals = poNew;
     118           0 :             poEnd = poNew;
     119             :         }
     120             :         else
     121             :         {
     122           0 :             poEnd->poNext = poNew;
     123           0 :             poEnd = poNew;
     124             :         }
     125           0 :         if (poEnd->poNext != nullptr)
     126           0 :             poEnd = poEnd->poNext;
     127             :     }
     128           0 :     if (*pszc != ']')
     129             :     {
     130           0 :         CPLError(CE_Warning, CPLE_IllegalArg, "Invalid range specified\n");
     131           0 :         deleteList(poVals);
     132           0 :         poVals = nullptr;
     133             :     }
     134             : }
     135             : 
     136           8 : bool Range::contains(SelafinTypeDef eType, int nValue) const
     137             : {
     138           8 :     if (poVals == nullptr)
     139           8 :         return true;
     140           0 :     Range::List *poCur = poActual;
     141           0 :     while (poCur != nullptr)
     142             :     {
     143           0 :         if (poCur->eType == eType && nValue >= poCur->nMin &&
     144           0 :             nValue <= poCur->nMax)
     145           0 :             return true;
     146           0 :         poCur = poCur->poNext;
     147             :     }
     148           0 :     return false;
     149             : }
     150             : 
     151           0 : void Range::sortList(Range::List *&poList, Range::List *poEnd)
     152             : {
     153           0 :     if (poList == nullptr || poList == poEnd)
     154           0 :         return;
     155           0 :     Range::List *pol = poList;
     156           0 :     Range::List *poBefore = nullptr;
     157           0 :     Range::List *poBeforeEnd = nullptr;
     158             :     // poList plays the role of the pivot value. Values greater and smaller are
     159             :     // sorted on each side of it. The order relation here is POINTS ranges
     160             :     // first, then sorted by nMin value.
     161           0 :     while (pol->poNext != poEnd)
     162             :     {
     163           0 :         if ((pol->eType == ELEMENTS &&
     164           0 :              (pol->poNext->eType == POINTS || pol->poNext->nMin < pol->nMin)) ||
     165           0 :             (pol->eType == POINTS && pol->poNext->eType == POINTS &&
     166           0 :              pol->poNext->nMin < pol->nMin))
     167             :         {
     168           0 :             if (poBefore == nullptr)
     169             :             {
     170           0 :                 poBefore = pol->poNext;
     171           0 :                 poBeforeEnd = poBefore;
     172             :             }
     173             :             else
     174             :             {
     175           0 :                 poBeforeEnd->poNext = pol->poNext;
     176           0 :                 poBeforeEnd = poBeforeEnd->poNext;
     177             :             }
     178           0 :             pol->poNext = pol->poNext->poNext;
     179             :         }
     180             :         else
     181           0 :             pol = pol->poNext;
     182             :     }
     183           0 :     if (poBefore != nullptr)
     184           0 :         poBeforeEnd->poNext = poList;
     185             :     // Now, poList is well placed. We do the same for the sublists before and
     186             :     // after poList
     187           0 :     Range::sortList(poBefore, poList);
     188           0 :     Range::sortList(poList->poNext, poEnd);
     189             :     // Finally, we restore the right starting point of the list
     190           0 :     if (poBefore != nullptr)
     191           0 :         poList = poBefore;
     192             : }
     193             : 
     194          21 : void Range::setMaxValue(int nMaxValueP)
     195             : {
     196          21 :     nMaxValue = nMaxValueP;
     197          21 :     if (poVals == nullptr)
     198          21 :         return;
     199             :     // We keep an internal private copy of the list where the range is
     200             :     // "resolved", that is simplified to a union of disjoint intervals
     201           0 :     deleteList(poActual);
     202           0 :     poActual = nullptr;
     203           0 :     Range::List *pol = poVals;
     204           0 :     Range::List *poActualEnd = nullptr;
     205             :     int nMinT, nMaxT;
     206           0 :     while (pol != nullptr)
     207             :     {
     208           0 :         if (pol->nMin < 0)
     209           0 :             nMinT = pol->nMin + nMaxValue;
     210             :         else
     211           0 :             nMinT = pol->nMin;
     212           0 :         if (pol->nMin < 0)
     213           0 :             pol->nMin = 0;
     214           0 :         if (pol->nMin >= nMaxValue)
     215           0 :             pol->nMin = nMaxValue - 1;
     216           0 :         if (pol->nMax < 0)
     217           0 :             nMaxT = pol->nMax + nMaxValue;
     218             :         else
     219           0 :             nMaxT = pol->nMax;
     220           0 :         if (pol->nMax < 0)
     221           0 :             pol->nMax = 0;
     222           0 :         if (pol->nMax >= nMaxValue)
     223           0 :             pol->nMax = nMaxValue - 1;
     224           0 :         if (nMaxT < nMinT)
     225           0 :             continue;
     226           0 :         if (poActual == nullptr)
     227             :         {
     228           0 :             poActual = new Range::List(pol->eType, nMinT, nMaxT, nullptr);
     229           0 :             poActualEnd = poActual;
     230             :         }
     231             :         else
     232             :         {
     233           0 :             poActualEnd->poNext =
     234           0 :                 new Range::List(pol->eType, nMinT, nMaxT, nullptr);
     235           0 :             poActualEnd = poActualEnd->poNext;
     236             :         }
     237           0 :         pol = pol->poNext;
     238             :     }
     239           0 :     sortList(poActual);
     240             :     // Now we merge successive ranges when they intersect or are consecutive
     241           0 :     if (poActual != nullptr)
     242             :     {
     243           0 :         pol = poActual;
     244           0 :         while (pol->poNext != nullptr)
     245             :         {
     246           0 :             if (pol->poNext->eType == pol->eType &&
     247           0 :                 pol->poNext->nMin <= pol->nMax + 1)
     248             :             {
     249           0 :                 if (pol->poNext->nMax > pol->nMax)
     250           0 :                     pol->nMax = pol->poNext->nMax;
     251           0 :                 poActualEnd = pol->poNext->poNext;
     252           0 :                 delete pol->poNext;
     253           0 :                 pol->poNext = poActualEnd;
     254             :             }
     255             :             else
     256           0 :                 pol = pol->poNext;
     257             :         }
     258             :     }
     259             : }
     260             : 
     261          21 : size_t Range::getSize() const
     262             : {
     263          21 :     if (poVals == nullptr)
     264          21 :         return nMaxValue * 2;
     265           0 :     Range::List *pol = poActual;
     266           0 :     size_t nSize = 0;
     267           0 :     while (pol != nullptr)
     268             :     {
     269           0 :         nSize += (pol->nMax - pol->nMin + 1);
     270           0 :         pol = pol->poNext;
     271             :     }
     272           0 :     return nSize;
     273             : }
     274             : 
     275             : /************************************************************************/
     276             : /*                          OGRSelafinDataSource()                      */
     277             : /************************************************************************/
     278             : 
     279       21558 : OGRSelafinDataSource::OGRSelafinDataSource()
     280             :     : pszName(nullptr), papoLayers(nullptr), nLayers(0), bUpdate(false),
     281       21558 :       poHeader(nullptr), poSpatialRef(nullptr)
     282             : {
     283       21558 : }
     284             : 
     285             : /************************************************************************/
     286             : /*                         ~OGRSelafinDataSource()                      */
     287             : /************************************************************************/
     288             : 
     289       43116 : OGRSelafinDataSource::~OGRSelafinDataSource()
     290             : {
     291       21572 :     for (int i = 0; i < nLayers; i++)
     292          14 :         delete papoLayers[i];
     293       21558 :     CPLFree(papoLayers);
     294       21558 :     CPLFree(pszName);
     295       21558 :     delete poHeader;
     296       21558 :     if (poSpatialRef != nullptr)
     297           4 :         poSpatialRef->Release();
     298       43116 : }
     299             : 
     300             : /************************************************************************/
     301             : /*                           TestCapability()                           */
     302             : /************************************************************************/
     303             : 
     304          18 : int OGRSelafinDataSource::TestCapability(const char *pszCap)
     305             : {
     306          18 :     if (EQUAL(pszCap, ODsCCreateLayer))
     307          17 :         return TRUE;
     308           1 :     else if (EQUAL(pszCap, ODsCDeleteLayer))
     309           1 :         return TRUE;
     310           0 :     else if (EQUAL(pszCap, ODsCCreateGeomFieldAfterCreateLayer))
     311           0 :         return FALSE;
     312             :     else
     313           0 :         return FALSE;
     314             : }
     315             : 
     316             : /************************************************************************/
     317             : /*                              GetLayer()                              */
     318             : /************************************************************************/
     319             : 
     320          20 : OGRLayer *OGRSelafinDataSource::GetLayer(int iLayer)
     321             : {
     322          20 :     if (iLayer < 0 || iLayer >= nLayers)
     323           0 :         return nullptr;
     324             :     else
     325          20 :         return papoLayers[iLayer];
     326             : }
     327             : 
     328             : /************************************************************************/
     329             : /*                                Open()                                */
     330             : /************************************************************************/
     331       21558 : int OGRSelafinDataSource::Open(const char *pszFilename, int bUpdateIn,
     332             :                                int bCreate)
     333             : {
     334             :     // Check if a range is set and extract it and the filename.
     335       21558 :     const char *pszc = pszFilename;
     336       21558 :     if (*pszFilename == 0)
     337         417 :         return FALSE;
     338      917317 :     while (*pszc)
     339      896176 :         ++pszc;
     340       21141 :     if (*(pszc - 1) == ']')
     341             :     {
     342           3 :         --pszc;
     343          37 :         while (pszc != pszFilename && *pszc != '[')
     344          34 :             pszc--;
     345           3 :         if (pszc == pszFilename)
     346           0 :             return FALSE;
     347           3 :         poRange.setRange(pszc);
     348             :     }
     349       21141 :     pszName = CPLStrdup(pszFilename);
     350       21141 :     pszName[pszc - pszFilename] = 0;
     351       21141 :     bUpdate = CPL_TO_BOOL(bUpdateIn);
     352       21141 :     if (bCreate && EQUAL(pszName, "/vsistdout/"))
     353           0 :         return TRUE;
     354             :     /* For writable /vsizip/, do nothing more */
     355       21141 :     if (bCreate && STARTS_WITH(pszName, "/vsizip/"))
     356           0 :         return TRUE;
     357       42282 :     CPLString osFilename(pszName);
     358             :     // Determine what sort of object this is.
     359             :     VSIStatBufL sStatBuf;
     360       21141 :     if (VSIStatExL(osFilename, &sStatBuf, VSI_STAT_NATURE_FLAG) != 0)
     361       21120 :         return FALSE;
     362             : 
     363             :     // Is this a single Selafin file?
     364          21 :     if (VSI_ISREG(sStatBuf.st_mode))
     365          21 :         return OpenTable(pszName);
     366             : 
     367             :     // Is this a single a ZIP file with only a Selafin file inside ?
     368           0 :     if (STARTS_WITH(osFilename, "/vsizip/") && VSI_ISREG(sStatBuf.st_mode))
     369             :     {
     370           0 :         char **papszFiles = VSIReadDir(osFilename);
     371           0 :         if (CSLCount(papszFiles) != 1)
     372             :         {
     373           0 :             CSLDestroy(papszFiles);
     374           0 :             return FALSE;
     375             :         }
     376           0 :         osFilename = CPLFormFilename(osFilename, papszFiles[0], nullptr);
     377           0 :         CSLDestroy(papszFiles);
     378           0 :         return OpenTable(osFilename);
     379             :     }
     380             : 
     381             : #ifdef notdef
     382             :     // Otherwise it has to be a directory.
     383             :     if (!VSI_ISDIR(sStatBuf.st_mode))
     384             :         return FALSE;
     385             : 
     386             :     // Scan through for entries which look like Selafin files
     387             :     int nNotSelafinCount = 0, i;
     388             :     char **papszNames = VSIReadDir(osFilename);
     389             :     for (i = 0; papszNames != NULL && papszNames[i] != NULL; i++)
     390             :     {
     391             :         CPLString oSubFilename =
     392             :             CPLFormFilename(osFilename, papszNames[i], NULL);
     393             :         if (EQUAL(papszNames[i], ".") || EQUAL(papszNames[i], ".."))
     394             :             continue;
     395             :         if (VSIStatL(oSubFilename, &sStatBuf) != 0 ||
     396             :             !VSI_ISREG(sStatBuf.st_mode))
     397             :         {
     398             :             nNotSelafinCount++;
     399             :             continue;
     400             :         }
     401             :         if (!OpenTable(oSubFilename))
     402             :         {
     403             :             CPLDebug("Selafin", "Cannot open %s", oSubFilename.c_str());
     404             :             nNotSelafinCount++;
     405             :             continue;
     406             :         }
     407             :     }
     408             :     CSLDestroy(papszNames);
     409             : 
     410             :     // We presume that this is indeed intended to be a Selafin datasource if
     411             :     // over half the files were Selafin files.
     412             :     return nNotSelafinCount < nLayers;
     413             : #else
     414           0 :     return FALSE;
     415             : #endif
     416             : }
     417             : 
     418             : /************************************************************************/
     419             : /*                              OpenTable()                             */
     420             : /************************************************************************/
     421          21 : int OGRSelafinDataSource::OpenTable(const char *pszFilename)
     422             : {
     423             : #ifdef DEBUG_VERBOSE
     424             :     CPLDebug("Selafin", "OpenTable(%s,%i)", pszFilename,
     425             :              static_cast<int>(bUpdate));
     426             : #endif
     427             :     // Open the file
     428          21 :     VSILFILE *fp = nullptr;
     429          21 :     if (bUpdate)
     430             :     {
     431          20 :         fp = VSIFOpenExL(pszFilename, "rb+", true);
     432             :     }
     433             :     else
     434             :     {
     435           1 :         fp = VSIFOpenExL(pszFilename, "rb", true);
     436             :     }
     437             : 
     438          21 :     if (fp == nullptr)
     439             :     {
     440           0 :         CPLError(CE_Warning, CPLE_OpenFailed, "Failed to open %s.",
     441             :                  VSIGetLastErrorMsg());
     442           0 :         return FALSE;
     443             :     }
     444          21 :     if (!bUpdate && strstr(pszFilename, "/vsigzip/") == nullptr &&
     445           1 :         strstr(pszFilename, "/vsizip/") == nullptr)
     446           1 :         fp = (VSILFILE *)VSICreateBufferedReaderHandle((VSIVirtualHandle *)fp);
     447             : 
     448             :     // Quickly check if the file is in Selafin format, before actually starting
     449             :     // to read to make it faster
     450             :     char szBuf[9];
     451          21 :     VSIFReadL(szBuf, 1, 4, fp);
     452          21 :     if (szBuf[0] != 0 || szBuf[1] != 0 || szBuf[2] != 0 || szBuf[3] != 0x50)
     453             :     {
     454           0 :         VSIFCloseL(fp);
     455           0 :         return FALSE;
     456             :     }
     457          21 :     VSIFSeekL(fp, 84, SEEK_SET);
     458          21 :     VSIFReadL(szBuf, 1, 8, fp);
     459          21 :     if (szBuf[0] != 0 || szBuf[1] != 0 || szBuf[2] != 0 || szBuf[3] != 0x50 ||
     460          21 :         szBuf[4] != 0 || szBuf[5] != 0 || szBuf[6] != 0 || szBuf[7] != 8)
     461             :     {
     462           0 :         VSIFCloseL(fp);
     463           0 :         return FALSE;
     464             :     }
     465             :     /* VSIFSeekL(fp,76,SEEK_SET);
     466             :     VSIFReadL(szBuf,1,8,fp);
     467             :     if (STRNCASECMP(szBuf,"Seraphin",8)!=0 && STRNCASECMP(szBuf,"Serafin",7)!=0)
     468             :     { VSIFCloseL(fp); return FALSE;
     469             :     } */
     470             : 
     471             :     // Get layer base name
     472          42 :     CPLString osBaseLayerName = CPLGetBasename(pszFilename);
     473             : 
     474             :     // Read header of file to get common information for all layers
     475             :     // poHeader now owns fp
     476          21 :     poHeader = Selafin::read_header(fp, pszFilename);
     477          21 :     if (poHeader == nullptr)
     478             :     {
     479           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     480             :                  "Failed to open %s, wrong format.\n", pszFilename);
     481           0 :         return FALSE;
     482             :     }
     483          21 :     if (poHeader->nEpsg != 0)
     484             :     {
     485           2 :         poSpatialRef = new OGRSpatialReference();
     486           2 :         poSpatialRef->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     487           2 :         if (poSpatialRef->importFromEPSG(poHeader->nEpsg) != OGRERR_NONE)
     488             :         {
     489           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     490             :                      "EPSG %d not found. Could not set datasource SRS.\n",
     491           0 :                      poHeader->nEpsg);
     492           0 :             delete poSpatialRef;
     493           0 :             poSpatialRef = nullptr;
     494             :         }
     495             :     }
     496             : 
     497             :     // To prevent int overflow in poRange.getSize() call where we do
     498             :     // nSteps * 2
     499          21 :     if (poHeader->nSteps >= INT_MAX / 2)
     500             :     {
     501           0 :         CPLError(CE_Failure, CPLE_OpenFailed, "Invalid nSteps value");
     502           0 :         return FALSE;
     503             :     }
     504             : 
     505             :     // Create two layers for each selected time step: one for points, the other
     506             :     // for elements
     507          21 :     poRange.setMaxValue(poHeader->nSteps);
     508          21 :     size_t size = poRange.getSize();
     509          21 :     if (size > INT32_MAX)
     510             :     {
     511           0 :         CPLError(CE_Failure, CPLE_OpenFailed, "Invalid size");
     512           0 :         return FALSE;
     513             :     }
     514          21 :     const int nNewLayers = static_cast<int>(size);
     515          21 :     if (EQUAL(pszFilename, "/vsistdin/"))
     516           0 :         osBaseLayerName = "layer";
     517          42 :     CPLString osLayerName;
     518          42 :     papoLayers = (OGRSelafinLayer **)CPLRealloc(
     519          21 :         papoLayers, sizeof(void *) * (nLayers + nNewLayers));
     520          63 :     for (size_t j = 0; j < 2; ++j)
     521             :     {
     522          42 :         SelafinTypeDef eType = (j == 0) ? POINTS : ELEMENTS;
     523          50 :         for (int i = 0; i < poHeader->nSteps; ++i)
     524             :         {
     525           8 :             if (poRange.contains(eType, i))
     526             :             {
     527           8 :                 char szTemp[30] = {};
     528           8 :                 double dfTime = 0.0;
     529           8 :                 if (VSIFSeekL(fp, poHeader->getPosition(i) + 4, SEEK_SET) !=
     530          16 :                         0 ||
     531           8 :                     Selafin::read_float(fp, dfTime) == 0)
     532             :                 {
     533           0 :                     CPLError(CE_Failure, CPLE_OpenFailed,
     534             :                              "Failed to open %s, wrong format.\n", pszFilename);
     535           0 :                     return FALSE;
     536             :                 }
     537           8 :                 if (poHeader->panStartDate == nullptr)
     538           8 :                     snprintf(szTemp, 29, "%d", i);
     539             :                 else
     540             :                 {
     541             :                     struct tm sDate;
     542           0 :                     memset(&sDate, 0, sizeof(sDate));
     543           0 :                     sDate.tm_year =
     544           0 :                         std::max(poHeader->panStartDate[0], 0) - 1900;
     545           0 :                     sDate.tm_mon = std::max(poHeader->panStartDate[1], 1) - 1;
     546           0 :                     sDate.tm_mday = poHeader->panStartDate[2];
     547           0 :                     sDate.tm_hour = poHeader->panStartDate[3];
     548           0 :                     sDate.tm_min = poHeader->panStartDate[4];
     549           0 :                     double dfSec = poHeader->panStartDate[5] + dfTime;
     550           0 :                     if (dfSec >= 0 && dfSec < 60)
     551           0 :                         sDate.tm_sec = static_cast<int>(dfSec);
     552           0 :                     mktime(&sDate);
     553           0 :                     strftime(szTemp, 29, "%Y_%m_%d_%H_%M_%S", &sDate);
     554             :                 }
     555           8 :                 if (eType == POINTS)
     556           4 :                     osLayerName = osBaseLayerName + "_p" + szTemp;
     557             :                 else
     558           4 :                     osLayerName = osBaseLayerName + "_e" + szTemp;
     559           8 :                 papoLayers[nLayers++] =
     560           8 :                     new OGRSelafinLayer(this, osLayerName, bUpdate,
     561           8 :                                         poSpatialRef, poHeader, i, eType);
     562             :                 // poHeader->nRefCount++;
     563             :             }
     564             :         }
     565             :     }
     566             : 
     567             :     // Free allocated variables and exit
     568          21 :     return TRUE;
     569             : }
     570             : 
     571             : /************************************************************************/
     572             : /*                           ICreateLayer()                             */
     573             : /************************************************************************/
     574             : 
     575             : OGRLayer *
     576          19 : OGRSelafinDataSource::ICreateLayer(const char *pszLayerName,
     577             :                                    const OGRGeomFieldDefn *poGeomFieldDefn,
     578             :                                    CSLConstList papszOptions)
     579             : 
     580             : {
     581          19 :     auto eGType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
     582             :     const auto poSpatialRefP =
     583          19 :         poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
     584             : 
     585          19 :     CPLDebug("Selafin", "CreateLayer(%s,%s)", pszLayerName,
     586             :              (eGType == wkbPoint) ? "wkbPoint" : "wkbPolygon");
     587             :     // Verify we are in update mode.
     588          19 :     if (!bUpdate)
     589             :     {
     590           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
     591             :                  "Data source %s opened read-only.  "
     592             :                  "New layer %s cannot be created.",
     593             :                  pszName, pszLayerName);
     594           0 :         return nullptr;
     595             :     }
     596             : 
     597             :     // Check that new layer is a point or polygon layer
     598          19 :     if (eGType != wkbPoint)
     599             :     {
     600          15 :         CPLError(
     601             :             CE_Failure, CPLE_NoWriteAccess,
     602             :             "Selafin format can only handle %s layers whereas input is %s\n.",
     603             :             OGRGeometryTypeToName(wkbPoint), OGRGeometryTypeToName(eGType));
     604          15 :         return nullptr;
     605             :     }
     606             :     // Parse options
     607           4 :     const char *pszTemp = CSLFetchNameValue(papszOptions, "DATE");
     608           4 :     const double dfDate = pszTemp != nullptr ? CPLAtof(pszTemp) : 0.0;
     609             :     // Set the SRS of the datasource if this is the first layer
     610           4 :     if (nLayers == 0 && poSpatialRefP != nullptr)
     611             :     {
     612           2 :         poSpatialRef = poSpatialRefP->Clone();
     613           2 :         const char *szEpsg = poSpatialRef->GetAttrValue("GEOGCS|AUTHORITY", 1);
     614           2 :         int nEpsg = 0;
     615           2 :         if (szEpsg != nullptr)
     616           2 :             nEpsg = (int)strtol(szEpsg, nullptr, 10);
     617           2 :         if (nEpsg == 0)
     618             :         {
     619           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     620             :                      "Could not find EPSG code for SRS. The SRS won't be saved "
     621             :                      "in the datasource.");
     622             :         }
     623             :         else
     624             :         {
     625           2 :             poHeader->nEpsg = nEpsg;
     626             :         }
     627             :     }
     628             :     // Create the new layer in the Selafin file by adding a "time step" at the
     629             :     // end Beware, as the new layer shares the same header, it automatically
     630             :     // contains the same number of features and fields as the existing ones.
     631             :     // This may not be intuitive for the user.
     632           4 :     if (VSIFSeekL(poHeader->fp, 0, SEEK_END) != 0)
     633           0 :         return nullptr;
     634           4 :     if (Selafin::write_integer(poHeader->fp, 4) == 0 ||
     635           8 :         Selafin::write_float(poHeader->fp, dfDate) == 0 ||
     636           4 :         Selafin::write_integer(poHeader->fp, 4) == 0)
     637             :     {
     638           0 :         CPLError(CE_Failure, CPLE_FileIO,
     639             :                  "Could not write to Selafin file %s.\n", pszName);
     640           0 :         return nullptr;
     641             :     }
     642           4 :     double *pdfValues = nullptr;
     643           4 :     if (poHeader->nPoints > 0)
     644             :     {
     645             :         pdfValues =
     646           1 :             (double *)VSI_MALLOC2_VERBOSE(sizeof(double), poHeader->nPoints);
     647           1 :         if (pdfValues == nullptr)
     648           0 :             return nullptr;
     649             :     }
     650           5 :     for (int i = 0; i < poHeader->nVar; ++i)
     651             :     {
     652           2 :         if (Selafin::write_floatarray(poHeader->fp, pdfValues,
     653           1 :                                       poHeader->nPoints) == 0)
     654             :         {
     655           0 :             CPLError(CE_Failure, CPLE_FileIO,
     656             :                      "Could not write to Selafin file %s.\n", pszName);
     657           0 :             CPLFree(pdfValues);
     658           0 :             return nullptr;
     659             :         }
     660             :     }
     661           4 :     CPLFree(pdfValues);
     662           4 :     VSIFFlushL(poHeader->fp);
     663           4 :     poHeader->nSteps++;
     664             :     // Create two layers as usual, one for points and one for elements
     665           4 :     nLayers += 2;
     666           4 :     papoLayers =
     667           4 :         (OGRSelafinLayer **)CPLRealloc(papoLayers, sizeof(void *) * nLayers);
     668           8 :     CPLString szName = pszLayerName;
     669           4 :     CPLString szNewLayerName = szName + "_p";
     670           4 :     papoLayers[nLayers - 2] =
     671           4 :         new OGRSelafinLayer(this, szNewLayerName, bUpdate, poSpatialRef,
     672           4 :                             poHeader, poHeader->nSteps - 1, POINTS);
     673           4 :     szNewLayerName = szName + "_e";
     674           4 :     papoLayers[nLayers - 1] =
     675           4 :         new OGRSelafinLayer(this, szNewLayerName, bUpdate, poSpatialRef,
     676           4 :                             poHeader, poHeader->nSteps - 1, ELEMENTS);
     677           4 :     return papoLayers[nLayers - 2];
     678             : }
     679             : 
     680             : /************************************************************************/
     681             : /*                            DeleteLayer()                             */
     682             : /************************************************************************/
     683           1 : OGRErr OGRSelafinDataSource::DeleteLayer(int iLayer)
     684             : {
     685             :     // Verify we are in update mode.
     686           1 :     if (!bUpdate)
     687             :     {
     688           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
     689             :                  "Data source %s opened read-only.  "
     690             :                  "Layer %d cannot be deleted.\n",
     691             :                  pszName, iLayer);
     692           0 :         return OGRERR_FAILURE;
     693             :     }
     694           1 :     if (iLayer < 0 || iLayer >= nLayers)
     695             :     {
     696           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     697             :                  "Layer %d not in legal range of 0 to %d.", iLayer,
     698           0 :                  nLayers - 1);
     699           0 :         return OGRERR_FAILURE;
     700             :     }
     701             :     // Delete layer in file. Here we don't need to create a copy of the file
     702             :     // because we only update values and it can't get corrupted even if the
     703             :     // system crashes during the operation
     704           1 :     const int nNum = papoLayers[iLayer]->GetStepNumber();
     705           1 :     double *dfValues = nullptr;
     706           2 :     for (int i = nNum; i < poHeader->nSteps - 1; ++i)
     707             :     {
     708           1 :         double dfTime = 0.0;
     709           1 :         if (VSIFSeekL(poHeader->fp, poHeader->getPosition(i + 1) + 4,
     710           1 :                       SEEK_SET) != 0 ||
     711           1 :             Selafin::read_float(poHeader->fp, dfTime) == 0 ||
     712           1 :             VSIFSeekL(poHeader->fp, poHeader->getPosition(i) + 4, SEEK_SET) !=
     713           2 :                 0 ||
     714           1 :             Selafin::write_float(poHeader->fp, dfTime) == 0)
     715             :         {
     716           0 :             CPLError(CE_Failure, CPLE_FileIO,
     717             :                      "Could not update Selafin file %s.\n", pszName);
     718           0 :             return OGRERR_FAILURE;
     719             :         }
     720           2 :         for (int j = 0; j < poHeader->nVar; ++j)
     721             :         {
     722           1 :             bool ok = true;
     723           1 :             if (VSIFSeekL(poHeader->fp, poHeader->getPosition(i + 1) + 12,
     724           1 :                           SEEK_SET) != 0)
     725             :             {
     726           0 :                 ok = false;
     727             :             }
     728             :             else
     729             :             {
     730           2 :                 int ret = Selafin::read_floatarray(poHeader->fp, &dfValues,
     731           1 :                                                    poHeader->nFileSize);
     732           1 :                 if (ret < 0 || ret != poHeader->nPoints ||
     733           1 :                     VSIFSeekL(poHeader->fp, poHeader->getPosition(i) + 12,
     734           2 :                               SEEK_SET) != 0 ||
     735           1 :                     Selafin::write_floatarray(poHeader->fp, dfValues,
     736           1 :                                               poHeader->nPoints) == 0)
     737             :                 {
     738           0 :                     ok = false;
     739             :                 }
     740             :             }
     741           1 :             if (!ok)
     742             :             {
     743           0 :                 CPLError(CE_Failure, CPLE_FileIO,
     744             :                          "Could not update Selafin file %s.\n", pszName);
     745           0 :                 CPLFree(dfValues);
     746           0 :                 return OGRERR_FAILURE;
     747             :             }
     748           1 :             CPLFree(dfValues);
     749           1 :             dfValues = nullptr;
     750             :         }
     751             :     }
     752             :     // Delete all layers with the same step number in layer list. Usually there
     753             :     // are two of them: one for points and one for elements, but we can't rely
     754             :     // on that because of possible layer filtering specifications
     755           5 :     for (int i = 0; i < nLayers; ++i)
     756             :     {
     757           4 :         if (papoLayers[i]->GetStepNumber() == nNum)
     758             :         {
     759           2 :             delete papoLayers[i];
     760           2 :             nLayers--;
     761           7 :             for (int j = i; j < nLayers; ++j)
     762           5 :                 papoLayers[j] = papoLayers[j + 1];
     763           2 :             --i;
     764             :         }
     765             :     }
     766           1 :     return OGRERR_NONE;
     767             : }

Generated by: LCOV version 1.14