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

Generated by: LCOV version 1.14