Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: S-57 Translator
4 : * Purpose: Implements S57ClassRegistrar class for keeping track of
5 : * information on S57 object classes.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 1999, Frank Warmerdam
10 : * Copyright (c) 2011-2013, Even Rouault <even dot rouault at spatialys.com>
11 : *
12 : * SPDX-License-Identifier: MIT
13 : ****************************************************************************/
14 :
15 : #include "cpl_conv.h"
16 : #include "cpl_string.h"
17 : #include "s57.h"
18 :
19 : #ifdef EMBED_RESOURCE_FILES
20 : #include "embedded_resources.h"
21 : #endif
22 :
23 : /************************************************************************/
24 : /* S57ClassRegistrar() */
25 : /************************************************************************/
26 :
27 3 : S57ClassRegistrar::S57ClassRegistrar()
28 3 : : nClasses(0), nAttrCount(0), papszNextLine(nullptr)
29 : {
30 3 : }
31 :
32 : /************************************************************************/
33 : /* ~S57ClassRegistrar() */
34 : /************************************************************************/
35 :
36 2 : S57ClassRegistrar::~S57ClassRegistrar()
37 :
38 : {
39 2 : nClasses = 0;
40 80004 : for (size_t i = 0; i < aoAttrInfos.size(); i++)
41 80002 : delete aoAttrInfos[i];
42 2 : aoAttrInfos.resize(0);
43 2 : nAttrCount = 0;
44 2 : }
45 :
46 : /************************************************************************/
47 : /* S57ClassContentExplorer() */
48 : /************************************************************************/
49 :
50 55 : S57ClassContentExplorer::S57ClassContentExplorer(
51 55 : S57ClassRegistrar *poRegistrarIn)
52 : : poRegistrar(poRegistrarIn), papapszClassesFields(nullptr),
53 55 : iCurrentClass(-1), papszCurrentFields(nullptr), papszTempResult(nullptr)
54 : {
55 55 : }
56 :
57 : /************************************************************************/
58 : /* ~S57ClassContentExplorer() */
59 : /************************************************************************/
60 :
61 110 : S57ClassContentExplorer::~S57ClassContentExplorer()
62 : {
63 55 : CSLDestroy(papszTempResult);
64 :
65 55 : if (papapszClassesFields != nullptr)
66 : {
67 15675 : for (int i = 0; i < poRegistrar->nClasses; i++)
68 15620 : CSLDestroy(papapszClassesFields[i]);
69 55 : CPLFree(papapszClassesFields);
70 : }
71 55 : }
72 :
73 : /************************************************************************/
74 : /* FindFile() */
75 : /************************************************************************/
76 :
77 6 : bool S57ClassRegistrar::FindFile(const char *pszTarget,
78 : const char *pszDirectory, bool bReportErr,
79 : VSILFILE **pfp)
80 :
81 : {
82 6 : const char *pszFilename = nullptr;
83 :
84 6 : *pfp = nullptr;
85 :
86 6 : if (pszDirectory == nullptr)
87 : {
88 : #if defined(USE_ONLY_EMBEDDED_RESOURCE_FILES)
89 : pszFilename = pszTarget;
90 : #else
91 6 : pszFilename = CPLFindFile("s57", pszTarget);
92 6 : if (pszFilename == nullptr)
93 0 : pszFilename = pszTarget;
94 : #endif
95 6 : if (EQUAL(pszFilename, pszTarget))
96 : {
97 : #ifdef EMBED_RESOURCE_FILES
98 : const char *pszContent = S57GetEmbeddedCSV(pszTarget);
99 : if (pszContent)
100 : {
101 : CPLDebug("S57", "Using embedded %s", pszTarget);
102 : *pfp = VSIFileFromMemBuffer(
103 : nullptr,
104 : const_cast<GByte *>(
105 : reinterpret_cast<const GByte *>(pszContent)),
106 : static_cast<int>(strlen(pszContent)),
107 : /* bTakeOwnership = */ false);
108 : }
109 : #endif
110 : }
111 : }
112 : else
113 : {
114 0 : pszFilename = CPLFormFilename(pszDirectory, pszTarget, nullptr);
115 : }
116 :
117 : #ifdef EMBED_RESOURCE_FILES
118 : if (!(*pfp))
119 : #endif
120 : {
121 6 : *pfp = VSIFOpenL(pszFilename, "rb");
122 : }
123 :
124 6 : if (*pfp == nullptr)
125 : {
126 0 : if (bReportErr)
127 0 : CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open %s.\n",
128 : pszFilename);
129 0 : return FALSE;
130 : }
131 :
132 6 : return TRUE;
133 : }
134 :
135 : /************************************************************************/
136 : /* ReadLine() */
137 : /* */
138 : /* Read a line from the provided file, or from the "built-in" */
139 : /* configuration file line list if the file is NULL. */
140 : /************************************************************************/
141 :
142 2319 : const char *S57ClassRegistrar::ReadLine(VSILFILE *fp)
143 :
144 : {
145 2319 : if (fp != nullptr)
146 2319 : return CPLReadLineL(fp);
147 :
148 0 : if (papszNextLine == nullptr)
149 0 : return nullptr;
150 :
151 0 : if (*papszNextLine == nullptr)
152 : {
153 0 : papszNextLine = nullptr;
154 0 : return nullptr;
155 : }
156 :
157 0 : return *(papszNextLine++);
158 : }
159 :
160 : /************************************************************************/
161 : /* LoadInfo() */
162 : /************************************************************************/
163 :
164 3 : bool S57ClassRegistrar::LoadInfo(const char *pszDirectory,
165 : const char *pszProfile, bool bReportErr)
166 :
167 : {
168 3 : if (pszDirectory == nullptr)
169 3 : pszDirectory = CPLGetConfigOption("S57_CSV", nullptr);
170 :
171 : /* ==================================================================== */
172 : /* Read the s57objectclasses file. */
173 : /* ==================================================================== */
174 3 : if (pszProfile == nullptr)
175 3 : pszProfile = CPLGetConfigOption("S57_PROFILE", "");
176 :
177 : char szTargetFile[1024]; // TODO: Get this off of the stack.
178 3 : if (EQUAL(pszProfile, "Additional_Military_Layers"))
179 : {
180 : // Has been suppressed in GDAL data/
181 0 : snprintf(szTargetFile, sizeof(szTargetFile), "s57objectclasses_%s.csv",
182 : "aml");
183 : }
184 3 : else if (EQUAL(pszProfile, "Inland_Waterways"))
185 : {
186 : // Has been suppressed in GDAL data/
187 0 : snprintf(szTargetFile, sizeof(szTargetFile), "s57objectclasses_%s.csv",
188 : "iw");
189 : }
190 3 : else if (strlen(pszProfile) > 0)
191 : {
192 0 : snprintf(szTargetFile, sizeof(szTargetFile), "s57objectclasses_%s.csv",
193 : pszProfile);
194 : }
195 : else
196 : {
197 3 : strcpy(szTargetFile, "s57objectclasses.csv");
198 : }
199 :
200 3 : VSILFILE *fp = nullptr;
201 3 : if (!FindFile(szTargetFile, pszDirectory, bReportErr, &fp))
202 : {
203 0 : if (EQUAL(pszProfile, "Additional_Military_Layers") ||
204 0 : EQUAL(pszProfile, "Inland_Waterways"))
205 : {
206 0 : strcpy(szTargetFile, "s57objectclasses.csv");
207 0 : if (!FindFile(szTargetFile, pszDirectory, bReportErr, &fp))
208 0 : return false;
209 : }
210 0 : return false;
211 : }
212 :
213 : /* -------------------------------------------------------------------- */
214 : /* Skip the line defining the column titles. */
215 : /* -------------------------------------------------------------------- */
216 3 : const char *pszLine = ReadLine(fp);
217 :
218 3 : if (!EQUAL(pszLine,
219 : "\"Code\",\"ObjectClass\",\"Acronym\",\"Attribute_A\","
220 : "\"Attribute_B\",\"Attribute_C\",\"Class\",\"Primitives\""))
221 : {
222 0 : CPLError(CE_Failure, CPLE_AppDefined,
223 : "s57objectclasses columns don't match expected format!\n");
224 0 : if (fp != nullptr)
225 0 : VSIFCloseL(fp);
226 0 : return false;
227 : }
228 :
229 : /* -------------------------------------------------------------------- */
230 : /* Read and form string list. */
231 : /* -------------------------------------------------------------------- */
232 3 : apszClassesInfo.Clear();
233 861 : while ((pszLine = ReadLine(fp)) != nullptr)
234 : {
235 858 : if (strstr(pszLine, "###") != nullptr)
236 6 : continue;
237 852 : apszClassesInfo.AddString(pszLine);
238 : }
239 :
240 : /* -------------------------------------------------------------------- */
241 : /* Cleanup, and establish state. */
242 : /* -------------------------------------------------------------------- */
243 3 : if (fp != nullptr)
244 3 : VSIFCloseL(fp);
245 :
246 3 : nClasses = apszClassesInfo.size();
247 3 : if (nClasses == 0)
248 0 : return false;
249 :
250 : /* ==================================================================== */
251 : /* Read the attributes list. */
252 : /* ==================================================================== */
253 :
254 3 : if (EQUAL(pszProfile, "Additional_Military_Layers"))
255 : {
256 : // Has been suppressed in GDAL data/
257 0 : snprintf(szTargetFile, sizeof(szTargetFile), "s57attributes_%s.csv",
258 : "aml");
259 : }
260 3 : else if (EQUAL(pszProfile, "Inland_Waterways"))
261 : {
262 : // Has been suppressed in GDAL data/
263 0 : snprintf(szTargetFile, sizeof(szTargetFile), "s57attributes_%s.csv",
264 : "iw");
265 : }
266 3 : else if (strlen(pszProfile) > 0)
267 : {
268 0 : snprintf(szTargetFile, sizeof(szTargetFile), "s57attributes_%s.csv",
269 : pszProfile);
270 : }
271 : else
272 : {
273 3 : strcpy(szTargetFile, "s57attributes.csv");
274 : }
275 :
276 3 : if (!FindFile(szTargetFile, pszDirectory, bReportErr, &fp))
277 : {
278 0 : if (EQUAL(pszProfile, "Additional_Military_Layers") ||
279 0 : EQUAL(pszProfile, "Inland_Waterways"))
280 : {
281 0 : strcpy(szTargetFile, "s57attributes.csv");
282 0 : if (!FindFile(szTargetFile, pszDirectory, bReportErr, &fp))
283 0 : return false;
284 : }
285 0 : return false;
286 : }
287 :
288 : /* -------------------------------------------------------------------- */
289 : /* Skip the line defining the column titles. */
290 : /* -------------------------------------------------------------------- */
291 3 : pszLine = ReadLine(fp);
292 :
293 3 : if (!EQUAL(
294 : pszLine,
295 : "\"Code\",\"Attribute\",\"Acronym\",\"Attributetype\",\"Class\""))
296 : {
297 0 : CPLError(CE_Failure, CPLE_AppDefined,
298 : "s57attributes columns don't match expected format!\n");
299 0 : if (fp != nullptr)
300 0 : VSIFCloseL(fp);
301 0 : return false;
302 : }
303 :
304 : /* -------------------------------------------------------------------- */
305 : /* Read and form string list. */
306 : /* -------------------------------------------------------------------- */
307 1452 : while ((pszLine = ReadLine(fp)) != nullptr)
308 : {
309 1449 : if (strstr(pszLine, "###") != nullptr)
310 9 : continue;
311 :
312 1440 : char **papszTokens = CSLTokenizeStringComplex(pszLine, ",", TRUE, TRUE);
313 :
314 1440 : if (CSLCount(papszTokens) < 5)
315 : {
316 0 : CSLDestroy(papszTokens);
317 0 : continue;
318 : }
319 :
320 1440 : int iAttr = atoi(papszTokens[0]);
321 1440 : if (iAttr >= (int)aoAttrInfos.size())
322 1440 : aoAttrInfos.resize(iAttr + 1);
323 :
324 1440 : if (iAttr < 0 || aoAttrInfos[iAttr] != nullptr)
325 : {
326 0 : CPLDebug("S57", "Duplicate/corrupt definition for attribute %d:%s",
327 0 : iAttr, papszTokens[2]);
328 0 : CSLDestroy(papszTokens);
329 0 : continue;
330 : }
331 :
332 1440 : aoAttrInfos[iAttr] = new S57AttrInfo();
333 1440 : aoAttrInfos[iAttr]->osName = papszTokens[1];
334 1440 : aoAttrInfos[iAttr]->osAcronym = papszTokens[2];
335 1440 : aoAttrInfos[iAttr]->chType = papszTokens[3][0];
336 1440 : aoAttrInfos[iAttr]->chClass = papszTokens[4][0];
337 1440 : anAttrIndex.push_back(iAttr);
338 1440 : CSLDestroy(papszTokens);
339 : }
340 :
341 3 : if (fp != nullptr)
342 3 : VSIFCloseL(fp);
343 :
344 3 : nAttrCount = static_cast<int>(anAttrIndex.size());
345 :
346 : /* -------------------------------------------------------------------- */
347 : /* Sort index by acronym. */
348 : /* -------------------------------------------------------------------- */
349 3 : bool bModified = false;
350 1050 : do
351 : {
352 1050 : bModified = false;
353 504000 : for (int iAttr = 0; iAttr < nAttrCount - 1; iAttr++)
354 : {
355 502950 : if (strcmp(aoAttrInfos[anAttrIndex[iAttr]]->osAcronym,
356 1005900 : aoAttrInfos[anAttrIndex[iAttr + 1]]->osAcronym) > 0)
357 : {
358 51513 : int nTemp = anAttrIndex[iAttr];
359 51513 : anAttrIndex[iAttr] = anAttrIndex[iAttr + 1];
360 51513 : anAttrIndex[iAttr + 1] = nTemp;
361 51513 : bModified = true;
362 : }
363 : }
364 : } while (bModified);
365 :
366 3 : return true;
367 : }
368 :
369 : /************************************************************************/
370 : /* SelectClassByIndex() */
371 : /************************************************************************/
372 :
373 46851 : bool S57ClassContentExplorer::SelectClassByIndex(int nNewIndex)
374 :
375 : {
376 46851 : if (nNewIndex < 0 || nNewIndex >= poRegistrar->nClasses)
377 18 : return false;
378 :
379 : /* -------------------------------------------------------------------- */
380 : /* Do we have our cache of class information field lists? */
381 : /* -------------------------------------------------------------------- */
382 46833 : if (papapszClassesFields == nullptr)
383 : {
384 55 : papapszClassesFields =
385 55 : (char ***)CPLCalloc(sizeof(void *), poRegistrar->nClasses);
386 : }
387 :
388 : /* -------------------------------------------------------------------- */
389 : /* Has this info been parsed yet? */
390 : /* -------------------------------------------------------------------- */
391 46833 : if (papapszClassesFields[nNewIndex] == nullptr)
392 15620 : papapszClassesFields[nNewIndex] = CSLTokenizeStringComplex(
393 15620 : poRegistrar->apszClassesInfo[nNewIndex], ",", TRUE, TRUE);
394 :
395 46833 : papszCurrentFields = papapszClassesFields[nNewIndex];
396 :
397 46833 : iCurrentClass = nNewIndex;
398 :
399 46833 : return true;
400 : }
401 :
402 : /************************************************************************/
403 : /* SelectClass() */
404 : /************************************************************************/
405 :
406 5281 : bool S57ClassContentExplorer::SelectClass(int nOBJL)
407 :
408 : {
409 748695 : for (int i = 0; i < poRegistrar->nClasses; i++)
410 : {
411 748695 : if (atoi(poRegistrar->apszClassesInfo[i]) == nOBJL)
412 5281 : return SelectClassByIndex(i);
413 : }
414 :
415 0 : return FALSE;
416 : }
417 :
418 : /************************************************************************/
419 : /* SelectClass() */
420 : /************************************************************************/
421 :
422 260 : bool S57ClassContentExplorer::SelectClass(const char *pszAcronym)
423 :
424 : {
425 36489 : for (int i = 0; i < poRegistrar->nClasses; i++)
426 : {
427 36440 : if (!SelectClassByIndex(i))
428 0 : continue;
429 :
430 36440 : const char *pszClassAcronym = GetAcronym();
431 36440 : if (pszClassAcronym != nullptr &&
432 36440 : strcmp(pszClassAcronym, pszAcronym) == 0)
433 211 : return true;
434 : }
435 :
436 49 : return false;
437 : }
438 :
439 : /************************************************************************/
440 : /* GetOBJL() */
441 : /************************************************************************/
442 :
443 5281 : int S57ClassContentExplorer::GetOBJL()
444 :
445 : {
446 5281 : if (iCurrentClass >= 0)
447 5281 : return atoi(poRegistrar->apszClassesInfo[iCurrentClass]);
448 :
449 0 : return -1;
450 : }
451 :
452 : /************************************************************************/
453 : /* GetDescription() */
454 : /************************************************************************/
455 :
456 54 : const char *S57ClassContentExplorer::GetDescription() const
457 :
458 : {
459 54 : if (iCurrentClass >= 0 && papszCurrentFields[0] != nullptr)
460 54 : return papszCurrentFields[1];
461 :
462 0 : return nullptr;
463 : }
464 :
465 : /************************************************************************/
466 : /* GetAcronym() */
467 : /************************************************************************/
468 :
469 48074 : const char *S57ClassContentExplorer::GetAcronym() const
470 :
471 : {
472 48074 : if (iCurrentClass >= 0 && papszCurrentFields[0] != nullptr &&
473 48074 : papszCurrentFields[1] != nullptr)
474 48074 : return papszCurrentFields[2];
475 :
476 0 : return nullptr;
477 : }
478 :
479 : /************************************************************************/
480 : /* GetAttributeList() */
481 : /* */
482 : /* The passed string can be "a", "b", "c" or NULL for all. The */
483 : /* returned list remained owned by this object, not the caller. */
484 : /************************************************************************/
485 :
486 5305 : char **S57ClassContentExplorer::GetAttributeList(const char *pszType)
487 :
488 : {
489 5305 : if (iCurrentClass < 0)
490 0 : return nullptr;
491 :
492 5305 : CSLDestroy(papszTempResult);
493 5305 : papszTempResult = nullptr;
494 :
495 21220 : for (int iColumn = 3; iColumn < 6; iColumn++)
496 : {
497 15915 : if (pszType != nullptr && iColumn == 3 && !EQUAL(pszType, "a"))
498 0 : continue;
499 :
500 15915 : if (pszType != nullptr && iColumn == 4 && !EQUAL(pszType, "b"))
501 0 : continue;
502 :
503 15915 : if (pszType != nullptr && iColumn == 5 && !EQUAL(pszType, "c"))
504 0 : continue;
505 :
506 31830 : char **papszTokens = CSLTokenizeStringComplex(
507 15915 : papszCurrentFields[iColumn], ";", TRUE, FALSE);
508 :
509 15915 : papszTempResult = CSLInsertStrings(papszTempResult, -1, papszTokens);
510 :
511 15915 : CSLDestroy(papszTokens);
512 : }
513 :
514 5305 : return papszTempResult;
515 : }
516 :
517 : /************************************************************************/
518 : /* GetClassCode() */
519 : /************************************************************************/
520 :
521 0 : char S57ClassContentExplorer::GetClassCode() const
522 :
523 : {
524 0 : if (iCurrentClass >= 0 && papszCurrentFields[0] != nullptr &&
525 0 : papszCurrentFields[1] != nullptr && papszCurrentFields[2] != nullptr &&
526 0 : papszCurrentFields[3] != nullptr && papszCurrentFields[4] != nullptr &&
527 0 : papszCurrentFields[5] != nullptr && papszCurrentFields[6] != nullptr)
528 0 : return papszCurrentFields[6][0];
529 :
530 0 : return '\0';
531 : }
532 :
533 : /************************************************************************/
534 : /* GetPrimitives() */
535 : /************************************************************************/
536 :
537 5281 : char **S57ClassContentExplorer::GetPrimitives()
538 :
539 : {
540 5281 : if (iCurrentClass >= 0 && CSLCount(papszCurrentFields) > 7)
541 : {
542 5281 : CSLDestroy(papszTempResult);
543 5281 : papszTempResult =
544 5281 : CSLTokenizeStringComplex(papszCurrentFields[7], ";", TRUE, FALSE);
545 5281 : return papszTempResult;
546 : }
547 :
548 0 : return nullptr;
549 : }
550 :
551 : /************************************************************************/
552 : /* GetAttrInfo() */
553 : /************************************************************************/
554 :
555 109511 : const S57AttrInfo *S57ClassRegistrar::GetAttrInfo(int iAttr)
556 : {
557 109511 : if (iAttr < 0 || iAttr >= (int)aoAttrInfos.size())
558 0 : return nullptr;
559 :
560 109511 : return aoAttrInfos[iAttr];
561 : }
562 :
563 : /************************************************************************/
564 : /* FindAttrByAcronym() */
565 : /************************************************************************/
566 :
567 108045 : int S57ClassRegistrar::FindAttrByAcronym(const char *pszName)
568 :
569 : {
570 108045 : int iStart = 0;
571 108045 : int iEnd = nAttrCount - 1;
572 :
573 842530 : while (iStart <= iEnd)
574 : {
575 842476 : const int iCandidate = (iStart + iEnd) / 2;
576 : int nCompareValue =
577 842476 : strcmp(pszName, aoAttrInfos[anAttrIndex[iCandidate]]->osAcronym);
578 :
579 842476 : if (nCompareValue < 0)
580 : {
581 393427 : iEnd = iCandidate - 1;
582 : }
583 449049 : else if (nCompareValue > 0)
584 : {
585 341058 : iStart = iCandidate + 1;
586 : }
587 : else
588 107991 : return anAttrIndex[iCandidate];
589 : }
590 :
591 54 : return -1;
592 : }
|