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