/src/gdal/ogr/ogrsf_frmts/selafin/ogrselafindatasource.cpp
Line | Count | Source |
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 | | Range::~Range() |
26 | 36.8k | { |
27 | 36.8k | deleteList(poVals); |
28 | 36.8k | deleteList(poActual); |
29 | 36.8k | } |
30 | | |
31 | | void Range::deleteList(Range::List *poList) |
32 | 79.3k | { |
33 | 79.3k | if (poList == nullptr) |
34 | 78.6k | return; |
35 | 724 | Range::List *pol = poList; |
36 | 8.46k | while (pol != nullptr) |
37 | 7.74k | { |
38 | 7.74k | poList = poList->poNext; |
39 | 7.74k | delete pol; |
40 | 7.74k | pol = poList; |
41 | 7.74k | } |
42 | 724 | } |
43 | | |
44 | | void Range::setRange(const char *pszStr) |
45 | 1.94k | { |
46 | 1.94k | deleteList(poVals); |
47 | 1.94k | deleteList(poActual); |
48 | 1.94k | poVals = nullptr; |
49 | 1.94k | Range::List *poEnd = nullptr; |
50 | 1.94k | if (pszStr == nullptr || pszStr[0] != '[') |
51 | 0 | { |
52 | 0 | CPLError(CE_Warning, CPLE_IllegalArg, "Invalid range specified"); |
53 | 0 | return; |
54 | 0 | } |
55 | 1.94k | const char *pszc = pszStr; |
56 | 1.94k | char *psze = nullptr; |
57 | 1.94k | SelafinTypeDef eType; |
58 | 6.05k | while (*pszc != 0 && *pszc != ']') |
59 | 6.01k | { |
60 | 6.01k | pszc++; |
61 | 6.01k | if (*pszc == 'p' || *pszc == 'P') |
62 | 532 | { |
63 | 532 | eType = POINTS; |
64 | 532 | pszc++; |
65 | 532 | } |
66 | 5.48k | else if (*pszc == 'e' || *pszc == 'E') |
67 | 347 | { |
68 | 347 | eType = ELEMENTS; |
69 | 347 | pszc++; |
70 | 347 | } |
71 | 5.13k | else |
72 | 5.13k | eType = ALL; |
73 | | |
74 | 6.01k | int nMin = 0; |
75 | 6.01k | if (*pszc != ':') |
76 | 5.94k | { |
77 | 5.94k | nMin = (int)strtol(pszc, &psze, 10); |
78 | 5.94k | if (*psze != ':' && *psze != ',' && *psze != ']') |
79 | 1.80k | { |
80 | 1.80k | CPLError(CE_Warning, CPLE_IllegalArg, |
81 | 1.80k | "Invalid range specified"); |
82 | 1.80k | deleteList(poVals); |
83 | 1.80k | poVals = nullptr; |
84 | 1.80k | return; |
85 | 1.80k | } |
86 | 4.14k | pszc = psze; |
87 | 4.14k | } |
88 | 4.20k | int nMax = -1; |
89 | 4.20k | if (*pszc == ':') |
90 | 200 | { |
91 | 200 | ++pszc; |
92 | 200 | if (*pszc != ',' && *pszc != ']') |
93 | 162 | { |
94 | 162 | nMax = (int)strtol(pszc, &psze, 10); |
95 | 162 | if (*psze != ',' && *psze != ']') |
96 | 91 | { |
97 | 91 | CPLError(CE_Warning, CPLE_IllegalArg, |
98 | 91 | "Invalid range specified"); |
99 | 91 | deleteList(poVals); |
100 | 91 | poVals = nullptr; |
101 | 91 | return; |
102 | 91 | } |
103 | 71 | pszc = psze; |
104 | 71 | } |
105 | 200 | } |
106 | 4.00k | else |
107 | 4.00k | nMax = nMin; |
108 | 4.11k | Range::List *poNew = nullptr; |
109 | 4.11k | if (eType != ALL) |
110 | 490 | poNew = new Range::List(eType, nMin, nMax, nullptr); |
111 | 3.62k | else |
112 | 3.62k | poNew = |
113 | 3.62k | new Range::List(POINTS, nMin, nMax, |
114 | 3.62k | new Range::List(ELEMENTS, nMin, nMax, nullptr)); |
115 | 4.11k | if (poVals == nullptr) |
116 | 724 | { |
117 | 724 | poVals = poNew; |
118 | 724 | poEnd = poNew; |
119 | 724 | } |
120 | 3.39k | else |
121 | 3.39k | { |
122 | 3.39k | poEnd->poNext = poNew; |
123 | 3.39k | poEnd = poNew; |
124 | 3.39k | } |
125 | 4.11k | if (poEnd->poNext != nullptr) |
126 | 3.62k | poEnd = poEnd->poNext; |
127 | 4.11k | } |
128 | 43 | if (*pszc != ']') |
129 | 0 | { |
130 | 0 | CPLError(CE_Warning, CPLE_IllegalArg, "Invalid range specified"); |
131 | 0 | deleteList(poVals); |
132 | 0 | poVals = nullptr; |
133 | 0 | } |
134 | 43 | } |
135 | | |
136 | | bool Range::contains(SelafinTypeDef eType, int nValue) const |
137 | 21.3k | { |
138 | 21.3k | if (poVals == nullptr) |
139 | 21.3k | return true; |
140 | 0 | Range::List *poCur = poActual; |
141 | 0 | while (poCur != nullptr) |
142 | 0 | { |
143 | 0 | if (poCur->eType == eType && nValue >= poCur->nMin && |
144 | 0 | nValue <= poCur->nMax) |
145 | 0 | return true; |
146 | 0 | poCur = poCur->poNext; |
147 | 0 | } |
148 | 0 | return false; |
149 | 0 | } |
150 | | |
151 | | void Range::sortList(Range::List *&poList, Range::List *poEnd) |
152 | 0 | { |
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 | 0 | { |
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 | 0 | { |
168 | 0 | if (poBefore == nullptr) |
169 | 0 | { |
170 | 0 | poBefore = pol->poNext; |
171 | 0 | poBeforeEnd = poBefore; |
172 | 0 | } |
173 | 0 | else |
174 | 0 | { |
175 | 0 | poBeforeEnd->poNext = pol->poNext; |
176 | 0 | poBeforeEnd = poBeforeEnd->poNext; |
177 | 0 | } |
178 | 0 | pol->poNext = pol->poNext->poNext; |
179 | 0 | } |
180 | 0 | else |
181 | 0 | pol = pol->poNext; |
182 | 0 | } |
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 | 0 | } |
193 | | |
194 | | void Range::setMaxValue(int nMaxValueP) |
195 | 48 | { |
196 | 48 | nMaxValue = nMaxValueP; |
197 | 48 | if (poVals == nullptr) |
198 | 48 | 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 | 0 | int nMinT, nMaxT; |
206 | 0 | while (pol != nullptr) |
207 | 0 | { |
208 | 0 | if (pol->nMin < 0) |
209 | 0 | nMinT = pol->nMin + nMaxValue; |
210 | 0 | 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 | 0 | 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 | 0 | { |
228 | 0 | poActual = new Range::List(pol->eType, nMinT, nMaxT, nullptr); |
229 | 0 | poActualEnd = poActual; |
230 | 0 | } |
231 | 0 | else |
232 | 0 | { |
233 | 0 | poActualEnd->poNext = |
234 | 0 | new Range::List(pol->eType, nMinT, nMaxT, nullptr); |
235 | 0 | poActualEnd = poActualEnd->poNext; |
236 | 0 | } |
237 | 0 | pol = pol->poNext; |
238 | 0 | } |
239 | 0 | sortList(poActual); |
240 | | // Now we merge successive ranges when they intersect or are consecutive |
241 | 0 | if (poActual != nullptr) |
242 | 0 | { |
243 | 0 | pol = poActual; |
244 | 0 | while (pol->poNext != nullptr) |
245 | 0 | { |
246 | 0 | if (pol->poNext->eType == pol->eType && |
247 | 0 | pol->poNext->nMin <= pol->nMax + 1) |
248 | 0 | { |
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 | 0 | } |
255 | 0 | else |
256 | 0 | pol = pol->poNext; |
257 | 0 | } |
258 | 0 | } |
259 | 0 | } |
260 | | |
261 | | size_t Range::getSize() const |
262 | 48 | { |
263 | 48 | if (poVals == nullptr) |
264 | 48 | return nMaxValue * 2; |
265 | 0 | Range::List *pol = poActual; |
266 | 0 | size_t nSize = 0; |
267 | 0 | while (pol != nullptr) |
268 | 0 | { |
269 | 0 | nSize += (pol->nMax - pol->nMin + 1); |
270 | 0 | pol = pol->poNext; |
271 | 0 | } |
272 | 0 | return nSize; |
273 | 48 | } |
274 | | |
275 | | /************************************************************************/ |
276 | | /* OGRSelafinDataSource() */ |
277 | | /************************************************************************/ |
278 | | |
279 | | OGRSelafinDataSource::OGRSelafinDataSource() |
280 | 36.8k | : pszName(nullptr), papoLayers(nullptr), nLayers(0), bUpdate(false), |
281 | 36.8k | poHeader(nullptr), poSpatialRef(nullptr) |
282 | 36.8k | { |
283 | 36.8k | } |
284 | | |
285 | | /************************************************************************/ |
286 | | /* ~OGRSelafinDataSource() */ |
287 | | /************************************************************************/ |
288 | | |
289 | | OGRSelafinDataSource::~OGRSelafinDataSource() |
290 | 36.8k | { |
291 | 58.1k | for (int i = 0; i < nLayers; i++) |
292 | 21.3k | delete papoLayers[i]; |
293 | 36.8k | CPLFree(papoLayers); |
294 | 36.8k | CPLFree(pszName); |
295 | 36.8k | delete poHeader; |
296 | 36.8k | if (poSpatialRef != nullptr) |
297 | 10 | poSpatialRef->Release(); |
298 | 36.8k | } |
299 | | |
300 | | /************************************************************************/ |
301 | | /* TestCapability() */ |
302 | | /************************************************************************/ |
303 | | |
304 | | int OGRSelafinDataSource::TestCapability(const char *pszCap) const |
305 | 0 | { |
306 | 0 | if (EQUAL(pszCap, ODsCCreateLayer)) |
307 | 0 | return TRUE; |
308 | 0 | else if (EQUAL(pszCap, ODsCDeleteLayer)) |
309 | 0 | return TRUE; |
310 | 0 | else if (EQUAL(pszCap, ODsCCreateGeomFieldAfterCreateLayer)) |
311 | 0 | return FALSE; |
312 | 0 | else |
313 | 0 | return FALSE; |
314 | 0 | } |
315 | | |
316 | | /************************************************************************/ |
317 | | /* GetLayer() */ |
318 | | /************************************************************************/ |
319 | | |
320 | | const OGRLayer *OGRSelafinDataSource::GetLayer(int iLayer) const |
321 | 464 | { |
322 | 464 | if (iLayer < 0 || iLayer >= nLayers) |
323 | 0 | return nullptr; |
324 | 464 | else |
325 | 464 | return papoLayers[iLayer]; |
326 | 464 | } |
327 | | |
328 | | /************************************************************************/ |
329 | | /* Open() */ |
330 | | /************************************************************************/ |
331 | | int OGRSelafinDataSource::Open(const char *pszFilename, int bUpdateIn, |
332 | | int bCreate) |
333 | 36.8k | { |
334 | | // Check if a range is set and extract it and the filename. |
335 | 36.8k | const char *pszc = pszFilename; |
336 | 36.8k | if (*pszFilename == 0) |
337 | 0 | return FALSE; |
338 | 62.6M | while (*pszc) |
339 | 62.6M | ++pszc; |
340 | 36.8k | if (*(pszc - 1) == ']') |
341 | 2.90k | { |
342 | 2.90k | --pszc; |
343 | 2.49M | while (pszc != pszFilename && *pszc != '[') |
344 | 2.49M | pszc--; |
345 | 2.90k | if (pszc == pszFilename) |
346 | 961 | return FALSE; |
347 | 1.94k | poRange.setRange(pszc); |
348 | 1.94k | } |
349 | 35.8k | pszName = CPLStrdup(pszFilename); |
350 | 35.8k | pszName[pszc - pszFilename] = 0; |
351 | 35.8k | bUpdate = CPL_TO_BOOL(bUpdateIn); |
352 | 35.8k | if (bCreate && EQUAL(pszName, "/vsistdout/")) |
353 | 0 | return TRUE; |
354 | | /* For writable /vsizip/, do nothing more */ |
355 | 35.8k | if (bCreate && STARTS_WITH(pszName, "/vsizip/")) |
356 | 0 | return TRUE; |
357 | 35.8k | CPLString osFilename(pszName); |
358 | | // Determine what sort of object this is. |
359 | 35.8k | VSIStatBufL sStatBuf; |
360 | 35.8k | if (VSIStatExL(osFilename, &sStatBuf, VSI_STAT_NATURE_FLAG) != 0) |
361 | 35.4k | return FALSE; |
362 | | |
363 | | // Is this a single Selafin file? |
364 | 408 | if (VSI_ISREG(sStatBuf.st_mode)) |
365 | 408 | 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 | 0 | { |
370 | 0 | char **papszFiles = VSIReadDir(osFilename); |
371 | 0 | if (CSLCount(papszFiles) != 1) |
372 | 0 | { |
373 | 0 | CSLDestroy(papszFiles); |
374 | 0 | return FALSE; |
375 | 0 | } |
376 | 0 | osFilename = CPLFormFilenameSafe(osFilename, papszFiles[0], nullptr); |
377 | 0 | CSLDestroy(papszFiles); |
378 | 0 | return OpenTable(osFilename); |
379 | 0 | } |
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 | | const CPLString oSubFilename = |
392 | | CPLFormFilenameSafe(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 | 0 | #endif |
416 | 0 | } |
417 | | |
418 | | /************************************************************************/ |
419 | | /* OpenTable() */ |
420 | | /************************************************************************/ |
421 | | int OGRSelafinDataSource::OpenTable(const char *pszFilename) |
422 | 408 | { |
423 | | #ifdef DEBUG_VERBOSE |
424 | | CPLDebug("Selafin", "OpenTable(%s,%i)", pszFilename, |
425 | | static_cast<int>(bUpdate)); |
426 | | #endif |
427 | | // Open the file |
428 | 408 | VSILFILE *fp = nullptr; |
429 | 408 | if (bUpdate) |
430 | 0 | { |
431 | 0 | fp = VSIFOpenExL(pszFilename, "rb+", true); |
432 | 0 | } |
433 | 408 | else |
434 | 408 | { |
435 | 408 | fp = VSIFOpenExL(pszFilename, "rb", true); |
436 | 408 | } |
437 | | |
438 | 408 | if (fp == nullptr) |
439 | 17 | { |
440 | 17 | CPLError(CE_Warning, CPLE_OpenFailed, "Failed to open %s.", |
441 | 17 | VSIGetLastErrorMsg()); |
442 | 17 | return FALSE; |
443 | 17 | } |
444 | 391 | if (!bUpdate && strstr(pszFilename, "/vsigzip/") == nullptr && |
445 | 344 | strstr(pszFilename, "/vsizip/") == nullptr) |
446 | 339 | fp = VSICreateBufferedReaderHandle(fp); |
447 | | |
448 | | // Quickly check if the file is in Selafin format, before actually starting |
449 | | // to read to make it faster |
450 | 391 | char szBuf[9]; |
451 | 391 | VSIFReadL(szBuf, 1, 4, fp); |
452 | 391 | if (szBuf[0] != 0 || szBuf[1] != 0 || szBuf[2] != 0 || szBuf[3] != 0x50) |
453 | 129 | { |
454 | 129 | VSIFCloseL(fp); |
455 | 129 | return FALSE; |
456 | 129 | } |
457 | 262 | VSIFSeekL(fp, 84, SEEK_SET); |
458 | 262 | VSIFReadL(szBuf, 1, 8, fp); |
459 | 262 | if (szBuf[0] != 0 || szBuf[1] != 0 || szBuf[2] != 0 || szBuf[3] != 0x50 || |
460 | 262 | szBuf[4] != 0 || szBuf[5] != 0 || szBuf[6] != 0 || szBuf[7] != 8) |
461 | 0 | { |
462 | 0 | VSIFCloseL(fp); |
463 | 0 | return FALSE; |
464 | 0 | } |
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 | 262 | CPLString osBaseLayerName = CPLGetBasenameSafe(pszFilename); |
473 | | |
474 | | // Read header of file to get common information for all layers |
475 | | // poHeader now owns fp |
476 | 262 | poHeader = Selafin::read_header(fp, pszFilename); |
477 | 262 | if (poHeader == nullptr) |
478 | 214 | { |
479 | 214 | CPLError(CE_Failure, CPLE_OpenFailed, |
480 | 214 | "Failed to open %s, wrong format.\n", pszFilename); |
481 | 214 | return FALSE; |
482 | 214 | } |
483 | 48 | if (poHeader->nEpsg != 0) |
484 | 48 | { |
485 | 48 | poSpatialRef = new OGRSpatialReference(); |
486 | 48 | poSpatialRef->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); |
487 | 48 | if (poSpatialRef->importFromEPSG(poHeader->nEpsg) != OGRERR_NONE) |
488 | 38 | { |
489 | 38 | CPLError(CE_Warning, CPLE_AppDefined, |
490 | 38 | "EPSG %d not found. Could not set datasource SRS.\n", |
491 | 38 | poHeader->nEpsg); |
492 | 38 | delete poSpatialRef; |
493 | 38 | poSpatialRef = nullptr; |
494 | 38 | } |
495 | 48 | } |
496 | | |
497 | | // To prevent int overflow in poRange.getSize() call where we do |
498 | | // nSteps * 2 |
499 | 48 | if (poHeader->nSteps >= INT_MAX / 2) |
500 | 0 | { |
501 | 0 | CPLError(CE_Failure, CPLE_OpenFailed, "Invalid nSteps value"); |
502 | 0 | return FALSE; |
503 | 0 | } |
504 | | |
505 | | // Create two layers for each selected time step: one for points, the other |
506 | | // for elements |
507 | 48 | poRange.setMaxValue(poHeader->nSteps); |
508 | 48 | size_t size = poRange.getSize(); |
509 | 48 | if (size > INT32_MAX) |
510 | 0 | { |
511 | 0 | CPLError(CE_Failure, CPLE_OpenFailed, "Invalid size"); |
512 | 0 | return FALSE; |
513 | 0 | } |
514 | 48 | const int nNewLayers = static_cast<int>(size); |
515 | 48 | if (EQUAL(pszFilename, "/vsistdin/")) |
516 | 0 | osBaseLayerName = "layer"; |
517 | 48 | CPLString osLayerName; |
518 | 48 | papoLayers = (OGRSelafinLayer **)CPLRealloc( |
519 | 48 | papoLayers, sizeof(void *) * (nLayers + nNewLayers)); |
520 | 144 | for (size_t j = 0; j < 2; ++j) |
521 | 96 | { |
522 | 96 | SelafinTypeDef eType = (j == 0) ? POINTS : ELEMENTS; |
523 | 21.4k | for (int i = 0; i < poHeader->nSteps; ++i) |
524 | 21.3k | { |
525 | 21.3k | if (poRange.contains(eType, i)) |
526 | 21.3k | { |
527 | 21.3k | char szTemp[30] = {}; |
528 | 21.3k | double dfTime = 0.0; |
529 | 21.3k | if (VSIFSeekL(fp, poHeader->getPosition(i) + 4, SEEK_SET) != |
530 | 21.3k | 0 || |
531 | 21.3k | Selafin::read_float(fp, dfTime) == 0) |
532 | 0 | { |
533 | 0 | CPLError(CE_Failure, CPLE_OpenFailed, |
534 | 0 | "Failed to open %s, wrong format.\n", pszFilename); |
535 | 0 | return FALSE; |
536 | 0 | } |
537 | 21.3k | if (poHeader->panStartDate == nullptr) |
538 | 2.78k | snprintf(szTemp, 29, "%d", i); |
539 | 18.5k | else |
540 | 18.5k | { |
541 | 18.5k | struct tm sDate; |
542 | 18.5k | memset(&sDate, 0, sizeof(sDate)); |
543 | 18.5k | sDate.tm_year = |
544 | 18.5k | std::max(poHeader->panStartDate[0], 0) - 1900; |
545 | 18.5k | sDate.tm_mon = std::max(poHeader->panStartDate[1], 1) - 1; |
546 | 18.5k | sDate.tm_mday = poHeader->panStartDate[2]; |
547 | 18.5k | sDate.tm_hour = poHeader->panStartDate[3]; |
548 | 18.5k | sDate.tm_min = poHeader->panStartDate[4]; |
549 | 18.5k | double dfSec = poHeader->panStartDate[5] + dfTime; |
550 | 18.5k | if (dfSec >= 0 && dfSec < 60) |
551 | 6.99k | sDate.tm_sec = static_cast<int>(dfSec); |
552 | 18.5k | mktime(&sDate); |
553 | 18.5k | strftime(szTemp, 29, "%Y_%m_%d_%H_%M_%S", &sDate); |
554 | 18.5k | } |
555 | 21.3k | if (eType == POINTS) |
556 | 10.6k | osLayerName = osBaseLayerName + "_p" + szTemp; |
557 | 10.6k | else |
558 | 10.6k | osLayerName = osBaseLayerName + "_e" + szTemp; |
559 | 21.3k | papoLayers[nLayers++] = |
560 | 21.3k | new OGRSelafinLayer(this, osLayerName, bUpdate, |
561 | 21.3k | poSpatialRef, poHeader, i, eType); |
562 | | // poHeader->nRefCount++; |
563 | 21.3k | } |
564 | 21.3k | } |
565 | 96 | } |
566 | | |
567 | | // Free allocated variables and exit |
568 | 48 | return TRUE; |
569 | 48 | } |
570 | | |
571 | | /************************************************************************/ |
572 | | /* ICreateLayer() */ |
573 | | /************************************************************************/ |
574 | | |
575 | | OGRLayer * |
576 | | OGRSelafinDataSource::ICreateLayer(const char *pszLayerName, |
577 | | const OGRGeomFieldDefn *poGeomFieldDefn, |
578 | | CSLConstList papszOptions) |
579 | | |
580 | 0 | { |
581 | 0 | auto eGType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone; |
582 | 0 | const auto poSpatialRefP = |
583 | 0 | poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr; |
584 | |
|
585 | 0 | CPLDebug("Selafin", "CreateLayer(%s,%s)", pszLayerName, |
586 | 0 | (eGType == wkbPoint) ? "wkbPoint" : "wkbPolygon"); |
587 | | // Verify we are in update mode. |
588 | 0 | if (!bUpdate) |
589 | 0 | { |
590 | 0 | CPLError(CE_Failure, CPLE_NoWriteAccess, |
591 | 0 | "Data source %s opened read-only. " |
592 | 0 | "New layer %s cannot be created.", |
593 | 0 | pszName, pszLayerName); |
594 | 0 | return nullptr; |
595 | 0 | } |
596 | | |
597 | | // Check that new layer is a point or polygon layer |
598 | 0 | if (eGType != wkbPoint) |
599 | 0 | { |
600 | 0 | CPLError( |
601 | 0 | CE_Failure, CPLE_NoWriteAccess, |
602 | 0 | "Selafin format can only handle %s layers whereas input is %s\n.", |
603 | 0 | OGRGeometryTypeToName(wkbPoint), OGRGeometryTypeToName(eGType)); |
604 | 0 | return nullptr; |
605 | 0 | } |
606 | | // Parse options |
607 | 0 | const char *pszTemp = CSLFetchNameValue(papszOptions, "DATE"); |
608 | 0 | const double dfDate = pszTemp != nullptr ? CPLAtof(pszTemp) : 0.0; |
609 | | // Set the SRS of the datasource if this is the first layer |
610 | 0 | if (nLayers == 0 && poSpatialRefP != nullptr) |
611 | 0 | { |
612 | 0 | poSpatialRef = poSpatialRefP->Clone(); |
613 | 0 | const char *szEpsg = poSpatialRef->GetAttrValue("GEOGCS|AUTHORITY", 1); |
614 | 0 | int nEpsg = 0; |
615 | 0 | if (szEpsg != nullptr) |
616 | 0 | nEpsg = (int)strtol(szEpsg, nullptr, 10); |
617 | 0 | if (nEpsg == 0) |
618 | 0 | { |
619 | 0 | CPLError(CE_Warning, CPLE_AppDefined, |
620 | 0 | "Could not find EPSG code for SRS. The SRS won't be saved " |
621 | 0 | "in the datasource."); |
622 | 0 | } |
623 | 0 | else |
624 | 0 | { |
625 | 0 | poHeader->nEpsg = nEpsg; |
626 | 0 | } |
627 | 0 | } |
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 | 0 | if (VSIFSeekL(poHeader->fp, 0, SEEK_END) != 0) |
633 | 0 | return nullptr; |
634 | 0 | if (Selafin::write_integer(poHeader->fp, 4) == 0 || |
635 | 0 | Selafin::write_float(poHeader->fp, dfDate) == 0 || |
636 | 0 | Selafin::write_integer(poHeader->fp, 4) == 0) |
637 | 0 | { |
638 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
639 | 0 | "Could not write to Selafin file %s.\n", pszName); |
640 | 0 | return nullptr; |
641 | 0 | } |
642 | 0 | double *pdfValues = nullptr; |
643 | 0 | if (poHeader->nPoints > 0) |
644 | 0 | { |
645 | 0 | pdfValues = |
646 | 0 | (double *)VSI_MALLOC2_VERBOSE(sizeof(double), poHeader->nPoints); |
647 | 0 | if (pdfValues == nullptr) |
648 | 0 | return nullptr; |
649 | 0 | } |
650 | 0 | for (int i = 0; i < poHeader->nVar; ++i) |
651 | 0 | { |
652 | 0 | if (Selafin::write_floatarray(poHeader->fp, pdfValues, |
653 | 0 | poHeader->nPoints) == 0) |
654 | 0 | { |
655 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
656 | 0 | "Could not write to Selafin file %s.\n", pszName); |
657 | 0 | CPLFree(pdfValues); |
658 | 0 | return nullptr; |
659 | 0 | } |
660 | 0 | } |
661 | 0 | CPLFree(pdfValues); |
662 | 0 | VSIFFlushL(poHeader->fp); |
663 | 0 | poHeader->nSteps++; |
664 | | // Create two layers as usual, one for points and one for elements |
665 | 0 | nLayers += 2; |
666 | 0 | papoLayers = |
667 | 0 | (OGRSelafinLayer **)CPLRealloc(papoLayers, sizeof(void *) * nLayers); |
668 | 0 | const CPLString osName(pszLayerName); |
669 | 0 | CPLString osNewLayerName(osName + "_p"); |
670 | 0 | papoLayers[nLayers - 2] = |
671 | 0 | new OGRSelafinLayer(this, osNewLayerName, bUpdate, poSpatialRef, |
672 | 0 | poHeader, poHeader->nSteps - 1, POINTS); |
673 | 0 | osNewLayerName = osName + "_e"; |
674 | 0 | papoLayers[nLayers - 1] = |
675 | 0 | new OGRSelafinLayer(this, osNewLayerName, bUpdate, poSpatialRef, |
676 | 0 | poHeader, poHeader->nSteps - 1, ELEMENTS); |
677 | 0 | return papoLayers[nLayers - 2]; |
678 | 0 | } |
679 | | |
680 | | /************************************************************************/ |
681 | | /* DeleteLayer() */ |
682 | | /************************************************************************/ |
683 | | OGRErr OGRSelafinDataSource::DeleteLayer(int iLayer) |
684 | 0 | { |
685 | | // Verify we are in update mode. |
686 | 0 | if (!bUpdate) |
687 | 0 | { |
688 | 0 | CPLError(CE_Failure, CPLE_NoWriteAccess, |
689 | 0 | "Data source %s opened read-only. " |
690 | 0 | "Layer %d cannot be deleted.\n", |
691 | 0 | pszName, iLayer); |
692 | 0 | return OGRERR_FAILURE; |
693 | 0 | } |
694 | 0 | if (iLayer < 0 || iLayer >= nLayers) |
695 | 0 | { |
696 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
697 | 0 | "Layer %d not in legal range of 0 to %d.", iLayer, |
698 | 0 | nLayers - 1); |
699 | 0 | return OGRERR_FAILURE; |
700 | 0 | } |
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 | 0 | const int nNum = papoLayers[iLayer]->GetStepNumber(); |
705 | 0 | double *dfValues = nullptr; |
706 | 0 | for (int i = nNum; i < poHeader->nSteps - 1; ++i) |
707 | 0 | { |
708 | 0 | double dfTime = 0.0; |
709 | 0 | if (VSIFSeekL(poHeader->fp, poHeader->getPosition(i + 1) + 4, |
710 | 0 | SEEK_SET) != 0 || |
711 | 0 | Selafin::read_float(poHeader->fp, dfTime) == 0 || |
712 | 0 | VSIFSeekL(poHeader->fp, poHeader->getPosition(i) + 4, SEEK_SET) != |
713 | 0 | 0 || |
714 | 0 | Selafin::write_float(poHeader->fp, dfTime) == 0) |
715 | 0 | { |
716 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
717 | 0 | "Could not update Selafin file %s.\n", pszName); |
718 | 0 | return OGRERR_FAILURE; |
719 | 0 | } |
720 | 0 | for (int j = 0; j < poHeader->nVar; ++j) |
721 | 0 | { |
722 | 0 | bool ok = true; |
723 | 0 | if (VSIFSeekL(poHeader->fp, poHeader->getPosition(i + 1) + 12, |
724 | 0 | SEEK_SET) != 0) |
725 | 0 | { |
726 | 0 | ok = false; |
727 | 0 | } |
728 | 0 | else |
729 | 0 | { |
730 | 0 | int ret = Selafin::read_floatarray(poHeader->fp, &dfValues, |
731 | 0 | poHeader->nFileSize); |
732 | 0 | if (ret < 0 || ret != poHeader->nPoints || |
733 | 0 | VSIFSeekL(poHeader->fp, poHeader->getPosition(i) + 12, |
734 | 0 | SEEK_SET) != 0 || |
735 | 0 | Selafin::write_floatarray(poHeader->fp, dfValues, |
736 | 0 | poHeader->nPoints) == 0) |
737 | 0 | { |
738 | 0 | ok = false; |
739 | 0 | } |
740 | 0 | } |
741 | 0 | if (!ok) |
742 | 0 | { |
743 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
744 | 0 | "Could not update Selafin file %s.\n", pszName); |
745 | 0 | CPLFree(dfValues); |
746 | 0 | return OGRERR_FAILURE; |
747 | 0 | } |
748 | 0 | CPLFree(dfValues); |
749 | 0 | dfValues = nullptr; |
750 | 0 | } |
751 | 0 | } |
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 | 0 | for (int i = 0; i < nLayers; ++i) |
756 | 0 | { |
757 | 0 | if (papoLayers[i]->GetStepNumber() == nNum) |
758 | 0 | { |
759 | 0 | delete papoLayers[i]; |
760 | 0 | nLayers--; |
761 | 0 | for (int j = i; j < nLayers; ++j) |
762 | 0 | papoLayers[j] = papoLayers[j + 1]; |
763 | 0 | --i; |
764 | 0 | } |
765 | 0 | } |
766 | 0 | return OGRERR_NONE; |
767 | 0 | } |