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