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