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 : }
|