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