1 : /******************************************************************************
2 : * $Id: geo_normalize.c 2037 2011-05-24 01:45:24Z warmerdam $
3 : *
4 : * Project: libgeotiff
5 : * Purpose: Code to normalize PCS and other composite codes in a GeoTIFF file.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 1999, Frank Warmerdam
10 : *
11 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : *****************************************************************************/
29 :
30 : #include "cpl_serv.h"
31 : #include "geo_tiffp.h"
32 : #include "geovalues.h"
33 : #include "geo_normalize.h"
34 :
35 : #ifndef KvUserDefined
36 : # define KvUserDefined 32767
37 : #endif
38 :
39 : #ifndef PI
40 : # define PI 3.14159265358979323846
41 : #endif
42 :
43 : /* EPSG Codes for projection parameters. Unfortunately, these bear no
44 : relationship to the GeoTIFF codes even though the names are so similar. */
45 :
46 : #define EPSGNatOriginLat 8801
47 : #define EPSGNatOriginLong 8802
48 : #define EPSGNatOriginScaleFactor 8805
49 : #define EPSGFalseEasting 8806
50 : #define EPSGFalseNorthing 8807
51 : #define EPSGProjCenterLat 8811
52 : #define EPSGProjCenterLong 8812
53 : #define EPSGAzimuth 8813
54 : #define EPSGAngleRectifiedToSkewedGrid 8814
55 : #define EPSGInitialLineScaleFactor 8815
56 : #define EPSGProjCenterEasting 8816
57 : #define EPSGProjCenterNorthing 8817
58 : #define EPSGPseudoStdParallelLat 8818
59 : #define EPSGPseudoStdParallelScaleFactor 8819
60 : #define EPSGFalseOriginLat 8821
61 : #define EPSGFalseOriginLong 8822
62 : #define EPSGStdParallel1Lat 8823
63 : #define EPSGStdParallel2Lat 8824
64 : #define EPSGFalseOriginEasting 8826
65 : #define EPSGFalseOriginNorthing 8827
66 : #define EPSGSphericalOriginLat 8828
67 : #define EPSGSphericalOriginLong 8829
68 : #define EPSGInitialLongitude 8830
69 : #define EPSGZoneWidth 8831
70 : #define EPSGLatOfStdParallel 8832
71 : #define EPSGOriginLong 8833
72 : #define EPSGTopocentricOriginLat 8834
73 : #define EPSGTopocentricOriginLong 8835
74 : #define EPSGTopocentricOriginHeight 8836
75 :
76 : /************************************************************************/
77 : /* GTIFGetPCSInfo() */
78 : /************************************************************************/
79 :
80 2112 : int GTIFGetPCSInfo( int nPCSCode, char **ppszEPSGName,
81 : short *pnProjOp, short *pnUOMLengthCode,
82 : short *pnGeogCS )
83 :
84 : {
85 : char **papszRecord;
86 : char szSearchKey[24];
87 : const char *pszFilename;
88 : int nDatum;
89 : int nZone;
90 :
91 2112 : int Proj = GTIFPCSToMapSys( nPCSCode, &nDatum, &nZone );
92 4112 : if ((Proj == MapSys_UTM_North || Proj == MapSys_UTM_South) &&
93 2000 : nDatum != KvUserDefined)
94 : {
95 2000 : const char* pszDatumName = NULL;
96 2000 : switch (nDatum)
97 : {
98 1804 : case GCS_NAD27: pszDatumName = "NAD27"; break;
99 0 : case GCS_NAD83: pszDatumName = "NAD83"; break;
100 0 : case GCS_WGS_72: pszDatumName = "WGS 72"; break;
101 0 : case GCS_WGS_72BE: pszDatumName = "WGS 72BE"; break;
102 196 : case GCS_WGS_84: pszDatumName = "WGS 84"; break;
103 : default: break;
104 : }
105 :
106 2000 : if (pszDatumName)
107 : {
108 2000 : if (ppszEPSGName)
109 : {
110 : char szEPSGName[64];
111 1000 : sprintf(szEPSGName, "%s / UTM zone %d%c",
112 : pszDatumName, nZone, (Proj == MapSys_UTM_North) ? 'N' : 'S');
113 1000 : *ppszEPSGName = CPLStrdup(szEPSGName);
114 : }
115 :
116 2000 : if (pnProjOp)
117 1000 : *pnProjOp = (short) (((Proj == MapSys_UTM_North) ? Proj_UTM_zone_1N - 1 : Proj_UTM_zone_1S - 1) + nZone);
118 :
119 2000 : if (pnUOMLengthCode)
120 1000 : *pnUOMLengthCode = 9001; /* Linear_Meter */
121 :
122 2000 : if (pnGeogCS)
123 1000 : *pnGeogCS = (short) nDatum;
124 :
125 2000 : return TRUE;
126 : }
127 : }
128 :
129 : /* -------------------------------------------------------------------- */
130 : /* Search the pcs.override table for this PCS. */
131 : /* -------------------------------------------------------------------- */
132 112 : pszFilename = CSVFilename( "pcs.override.csv" );
133 112 : sprintf( szSearchKey, "%d", nPCSCode );
134 112 : papszRecord = CSVScanFileByName( pszFilename, "COORD_REF_SYS_CODE",
135 : szSearchKey, CC_Integer );
136 :
137 : /* -------------------------------------------------------------------- */
138 : /* If not found, search the EPSG PCS database. */
139 : /* -------------------------------------------------------------------- */
140 112 : if( papszRecord == NULL )
141 : {
142 112 : pszFilename = CSVFilename( "pcs.csv" );
143 :
144 112 : sprintf( szSearchKey, "%d", nPCSCode );
145 112 : papszRecord = CSVScanFileByName( pszFilename, "COORD_REF_SYS_CODE",
146 : szSearchKey, CC_Integer );
147 :
148 112 : if( papszRecord == NULL )
149 0 : return FALSE;
150 : }
151 :
152 : /* -------------------------------------------------------------------- */
153 : /* Get the name, if requested. */
154 : /* -------------------------------------------------------------------- */
155 112 : if( ppszEPSGName != NULL )
156 : {
157 56 : *ppszEPSGName =
158 56 : CPLStrdup( CSLGetField( papszRecord,
159 : CSVGetFileFieldId(pszFilename,
160 : "COORD_REF_SYS_NAME") ));
161 : }
162 :
163 : /* -------------------------------------------------------------------- */
164 : /* Get the UOM Length code, if requested. */
165 : /* -------------------------------------------------------------------- */
166 112 : if( pnUOMLengthCode != NULL )
167 : {
168 : const char *pszValue;
169 :
170 56 : pszValue =
171 56 : CSLGetField( papszRecord,
172 : CSVGetFileFieldId(pszFilename,"UOM_CODE"));
173 56 : if( atoi(pszValue) > 0 )
174 56 : *pnUOMLengthCode = (short) atoi(pszValue);
175 : else
176 0 : *pnUOMLengthCode = KvUserDefined;
177 : }
178 :
179 : /* -------------------------------------------------------------------- */
180 : /* Get the UOM Length code, if requested. */
181 : /* -------------------------------------------------------------------- */
182 112 : if( pnProjOp != NULL )
183 : {
184 : const char *pszValue;
185 :
186 56 : pszValue =
187 56 : CSLGetField( papszRecord,
188 : CSVGetFileFieldId(pszFilename,"COORD_OP_CODE"));
189 56 : if( atoi(pszValue) > 0 )
190 56 : *pnProjOp = (short) atoi(pszValue);
191 : else
192 0 : *pnUOMLengthCode = KvUserDefined;
193 : }
194 :
195 : /* -------------------------------------------------------------------- */
196 : /* Get the GeogCS (Datum with PM) code, if requested. */
197 : /* -------------------------------------------------------------------- */
198 112 : if( pnGeogCS != NULL )
199 : {
200 : const char *pszValue;
201 :
202 56 : pszValue =
203 56 : CSLGetField( papszRecord,
204 : CSVGetFileFieldId(pszFilename,"SOURCE_GEOGCRS_CODE"));
205 56 : if( atoi(pszValue) > 0 )
206 56 : *pnGeogCS = (short) atoi(pszValue);
207 : else
208 0 : *pnGeogCS = KvUserDefined;
209 : }
210 :
211 112 : return TRUE;
212 : }
213 :
214 : /************************************************************************/
215 : /* GTIFAngleToDD() */
216 : /* */
217 : /* Convert a numeric angle to decimal degress. */
218 : /************************************************************************/
219 :
220 88 : double GTIFAngleToDD( double dfAngle, int nUOMAngle )
221 :
222 : {
223 88 : if( nUOMAngle == 9110 ) /* DDD.MMSSsss */
224 : {
225 : char szAngleString[32];
226 :
227 0 : sprintf( szAngleString, "%12.7f", dfAngle );
228 0 : dfAngle = GTIFAngleStringToDD( szAngleString, nUOMAngle );
229 : }
230 88 : else if ( nUOMAngle != KvUserDefined )
231 : {
232 60 : double dfInDegrees = 1.0;
233 :
234 60 : GTIFGetUOMAngleInfo( nUOMAngle, NULL, &dfInDegrees );
235 60 : dfAngle = dfAngle * dfInDegrees;
236 : }
237 :
238 88 : return( dfAngle );
239 : }
240 :
241 : /************************************************************************/
242 : /* GTIFAngleStringToDD() */
243 : /* */
244 : /* Convert an angle in the specified units to decimal degrees. */
245 : /************************************************************************/
246 :
247 142 : double GTIFAngleStringToDD( const char * pszAngle, int nUOMAngle )
248 :
249 : {
250 : double dfAngle;
251 :
252 142 : if( nUOMAngle == 9110 ) /* DDD.MMSSsss */
253 : {
254 : char *pszDecimal;
255 :
256 54 : dfAngle = ABS(atoi(pszAngle));
257 54 : pszDecimal = strchr(pszAngle,'.');
258 54 : if( pszDecimal != NULL && strlen(pszDecimal) > 1 )
259 : {
260 : char szMinutes[3];
261 : char szSeconds[64];
262 :
263 48 : szMinutes[0] = pszDecimal[1];
264 78 : if( pszDecimal[2] >= '0' && pszDecimal[2] <= '9' )
265 30 : szMinutes[1] = pszDecimal[2];
266 : else
267 18 : szMinutes[1] = '0';
268 :
269 48 : szMinutes[2] = '\0';
270 48 : dfAngle += atoi(szMinutes) / 60.0;
271 :
272 48 : if( strlen(pszDecimal) > 3 )
273 : {
274 20 : szSeconds[0] = pszDecimal[3];
275 40 : if( pszDecimal[4] >= '0' && pszDecimal[4] <= '9' )
276 : {
277 20 : szSeconds[1] = pszDecimal[4];
278 20 : szSeconds[2] = '.';
279 20 : strncpy( szSeconds+3, pszDecimal + 5, sizeof(szSeconds) - 3 );
280 20 : szSeconds[sizeof(szSeconds) - 1] = 0;
281 : }
282 : else
283 : {
284 0 : szSeconds[1] = '0';
285 0 : szSeconds[2] = '\0';
286 : }
287 20 : dfAngle += GTIFAtof(szSeconds) / 3600.0;
288 : }
289 : }
290 :
291 54 : if( pszAngle[0] == '-' )
292 14 : dfAngle *= -1;
293 : }
294 106 : else if( nUOMAngle == 9105 || nUOMAngle == 9106 ) /* grad */
295 : {
296 18 : dfAngle = 180 * (GTIFAtof(pszAngle ) / 200);
297 : }
298 70 : else if( nUOMAngle == 9101 ) /* radians */
299 : {
300 0 : dfAngle = 180 * (GTIFAtof(pszAngle ) / PI);
301 : }
302 70 : else if( nUOMAngle == 9103 ) /* arc-minute */
303 : {
304 0 : dfAngle = GTIFAtof(pszAngle) / 60;
305 : }
306 70 : else if( nUOMAngle == 9104 ) /* arc-second */
307 : {
308 0 : dfAngle = GTIFAtof(pszAngle) / 3600;
309 : }
310 : else /* decimal degrees ... some cases missing but seeminly never used */
311 : {
312 70 : CPLAssert( nUOMAngle == 9102 || nUOMAngle == KvUserDefined
313 : || nUOMAngle == 0 );
314 :
315 70 : dfAngle = GTIFAtof(pszAngle );
316 : }
317 :
318 142 : return( dfAngle );
319 : }
320 :
321 : /************************************************************************/
322 : /* GTIFGetGCSInfo() */
323 : /* */
324 : /* Fetch the datum, and prime meridian related to a particular */
325 : /* GCS. */
326 : /************************************************************************/
327 :
328 3960 : int GTIFGetGCSInfo( int nGCSCode, char ** ppszName,
329 : short * pnDatum, short * pnPM, short *pnUOMAngle )
330 :
331 : {
332 : char szSearchKey[24];
333 3960 : int nDatum=0, nPM, nUOMAngle;
334 : const char *pszFilename;
335 :
336 : /* -------------------------------------------------------------------- */
337 : /* Handle some "well known" GCS codes directly */
338 : /* -------------------------------------------------------------------- */
339 3960 : const char * pszName = NULL;
340 3960 : nPM = PM_Greenwich;
341 3960 : nUOMAngle = Angular_DMS_Hemisphere;
342 3960 : if( nGCSCode == GCS_NAD27 )
343 : {
344 1878 : nDatum = Datum_North_American_Datum_1927;
345 1878 : pszName = "NAD27";
346 : }
347 2082 : else if( nGCSCode == GCS_NAD83 )
348 : {
349 6 : nDatum = Datum_North_American_Datum_1983;
350 6 : pszName = "NAD83";
351 : }
352 2076 : else if( nGCSCode == GCS_WGS_84 )
353 : {
354 1932 : nDatum = Datum_WGS84;
355 1932 : pszName = "WGS 84";
356 : }
357 144 : else if( nGCSCode == GCS_WGS_72 )
358 : {
359 0 : nDatum = Datum_WGS72;
360 0 : pszName = "WGS 72";
361 : }
362 144 : else if ( nGCSCode == KvUserDefined )
363 : {
364 54 : return FALSE;
365 : }
366 :
367 3906 : if (pszName != NULL)
368 : {
369 3816 : if( ppszName != NULL )
370 1906 : *ppszName = CPLStrdup( pszName );
371 3816 : if( pnDatum != NULL )
372 1910 : *pnDatum = (short) nDatum;
373 3816 : if( pnPM != NULL )
374 1910 : *pnPM = (short) nPM;
375 3816 : if( pnUOMAngle != NULL )
376 1910 : *pnUOMAngle = (short) nUOMAngle;
377 :
378 3816 : return TRUE;
379 : }
380 :
381 : /* -------------------------------------------------------------------- */
382 : /* Search the database for the corresponding datum code. */
383 : /* -------------------------------------------------------------------- */
384 90 : pszFilename = CSVFilename("gcs.override.csv");
385 90 : sprintf( szSearchKey, "%d", nGCSCode );
386 90 : nDatum = atoi(CSVGetField( pszFilename,
387 : "COORD_REF_SYS_CODE", szSearchKey,
388 : CC_Integer, "DATUM_CODE" ) );
389 :
390 90 : if( nDatum < 1 )
391 : {
392 90 : pszFilename = CSVFilename("gcs.csv");
393 90 : sprintf( szSearchKey, "%d", nGCSCode );
394 90 : nDatum = atoi(CSVGetField( pszFilename,
395 : "COORD_REF_SYS_CODE", szSearchKey,
396 : CC_Integer, "DATUM_CODE" ) );
397 : }
398 :
399 90 : if( nDatum < 1 )
400 : {
401 0 : return FALSE;
402 : }
403 :
404 90 : if( pnDatum != NULL )
405 46 : *pnDatum = (short) nDatum;
406 :
407 : /* -------------------------------------------------------------------- */
408 : /* Get the PM. */
409 : /* -------------------------------------------------------------------- */
410 90 : if( pnPM != NULL )
411 : {
412 46 : nPM = atoi(CSVGetField( pszFilename,
413 : "COORD_REF_SYS_CODE", szSearchKey, CC_Integer,
414 : "PRIME_MERIDIAN_CODE" ) );
415 :
416 46 : if( nPM < 1 )
417 0 : return FALSE;
418 :
419 46 : *pnPM = (short) nPM;
420 : }
421 :
422 : /* -------------------------------------------------------------------- */
423 : /* Get the angular units. */
424 : /* -------------------------------------------------------------------- */
425 90 : nUOMAngle = atoi(CSVGetField( pszFilename,
426 : "COORD_REF_SYS_CODE",szSearchKey, CC_Integer,
427 : "UOM_CODE" ) );
428 :
429 90 : if( nUOMAngle < 1 )
430 0 : return FALSE;
431 :
432 90 : if( pnUOMAngle != NULL )
433 46 : *pnUOMAngle = (short) nUOMAngle;
434 :
435 : /* -------------------------------------------------------------------- */
436 : /* Get the name, if requested. */
437 : /* -------------------------------------------------------------------- */
438 90 : if( ppszName != NULL )
439 44 : *ppszName =
440 44 : CPLStrdup(CSVGetField( pszFilename,
441 : "COORD_REF_SYS_CODE",szSearchKey,CC_Integer,
442 : "COORD_REF_SYS_NAME" ));
443 :
444 90 : return( TRUE );
445 : }
446 :
447 : /************************************************************************/
448 : /* GTIFGetEllipsoidInfo() */
449 : /* */
450 : /* Fetch info about an ellipsoid. Axes are always returned in */
451 : /* meters. SemiMajor computed based on inverse flattening */
452 : /* where that is provided. */
453 : /************************************************************************/
454 :
455 3916 : int GTIFGetEllipsoidInfo( int nEllipseCode, char ** ppszName,
456 : double * pdfSemiMajor, double * pdfSemiMinor )
457 :
458 : {
459 : char szSearchKey[24];
460 3916 : double dfSemiMajor=0.0, dfToMeters = 1.0;
461 : int nUOMLength;
462 : const char* pszFilename;
463 :
464 : /* -------------------------------------------------------------------- */
465 : /* Try some well known ellipsoids. */
466 : /* -------------------------------------------------------------------- */
467 3916 : double dfInvFlattening=0.0, dfSemiMinor=0.0;
468 3916 : const char *pszName = NULL;
469 :
470 3916 : if( nEllipseCode == Ellipse_Clarke_1866 )
471 : {
472 1884 : pszName = "Clarke 1866";
473 1884 : dfSemiMajor = 6378206.4;
474 1884 : dfSemiMinor = 6356583.8;
475 1884 : dfInvFlattening = 0.0;
476 : }
477 2032 : else if( nEllipseCode == Ellipse_GRS_1980 )
478 : {
479 32 : pszName = "GRS 1980";
480 32 : dfSemiMajor = 6378137.0;
481 32 : dfSemiMinor = 0.0;
482 32 : dfInvFlattening = 298.257222101;
483 : }
484 2000 : else if( nEllipseCode == Ellipse_WGS_84 )
485 : {
486 1936 : pszName = "WGS 84";
487 1936 : dfSemiMajor = 6378137.0;
488 1936 : dfSemiMinor = 0.0;
489 1936 : dfInvFlattening = 298.257223563;
490 : }
491 64 : else if( nEllipseCode == 7043 )
492 : {
493 8 : pszName = "WGS 72";
494 8 : dfSemiMajor = 6378135.0;
495 8 : dfSemiMinor = 0.0;
496 8 : dfInvFlattening = 298.26;
497 : }
498 :
499 3916 : if (pszName != NULL)
500 : {
501 3860 : if( dfSemiMinor == 0.0 )
502 1976 : dfSemiMinor = dfSemiMajor * (1 - 1.0/dfInvFlattening);
503 :
504 3860 : if( pdfSemiMinor != NULL )
505 1932 : *pdfSemiMinor = dfSemiMinor;
506 3860 : if( pdfSemiMajor != NULL )
507 1932 : *pdfSemiMajor = dfSemiMajor;
508 3860 : if( ppszName != NULL )
509 1928 : *ppszName = CPLStrdup( pszName );
510 :
511 3860 : return TRUE;
512 : }
513 :
514 : /* -------------------------------------------------------------------- */
515 : /* Get the semi major axis. */
516 : /* -------------------------------------------------------------------- */
517 56 : sprintf( szSearchKey, "%d", nEllipseCode );
518 56 : pszFilename = CSVFilename("ellipsoid.csv" );
519 :
520 56 : dfSemiMajor =
521 56 : GTIFAtof(CSVGetField( pszFilename,
522 : "ELLIPSOID_CODE", szSearchKey, CC_Integer,
523 : "SEMI_MAJOR_AXIS" ) );
524 :
525 56 : if( dfSemiMajor == 0.0 )
526 : {
527 0 : return FALSE;
528 : }
529 :
530 : /* -------------------------------------------------------------------- */
531 : /* Get the translation factor into meters. */
532 : /* -------------------------------------------------------------------- */
533 56 : nUOMLength = atoi(CSVGetField( pszFilename,
534 : "ELLIPSOID_CODE", szSearchKey, CC_Integer,
535 : "UOM_CODE" ));
536 56 : GTIFGetUOMLengthInfo( nUOMLength, NULL, &dfToMeters );
537 :
538 56 : dfSemiMajor *= dfToMeters;
539 :
540 56 : if( pdfSemiMajor != NULL )
541 28 : *pdfSemiMajor = dfSemiMajor;
542 :
543 : /* -------------------------------------------------------------------- */
544 : /* Get the semi-minor if requested. If the Semi-minor axis */
545 : /* isn't available, compute it based on the inverse flattening. */
546 : /* -------------------------------------------------------------------- */
547 56 : if( pdfSemiMinor != NULL )
548 : {
549 28 : *pdfSemiMinor =
550 28 : GTIFAtof(CSVGetField( pszFilename,
551 : "ELLIPSOID_CODE", szSearchKey, CC_Integer,
552 : "SEMI_MINOR_AXIS" )) * dfToMeters;
553 :
554 28 : if( *pdfSemiMinor == 0.0 )
555 : {
556 : double dfInvFlattening;
557 :
558 16 : dfInvFlattening =
559 16 : GTIFAtof(CSVGetField( pszFilename,
560 : "ELLIPSOID_CODE", szSearchKey, CC_Integer,
561 : "INV_FLATTENING" ));
562 16 : *pdfSemiMinor = dfSemiMajor * (1 - 1.0/dfInvFlattening);
563 : }
564 : }
565 :
566 : /* -------------------------------------------------------------------- */
567 : /* Get the name, if requested. */
568 : /* -------------------------------------------------------------------- */
569 56 : if( ppszName != NULL )
570 28 : *ppszName =
571 28 : CPLStrdup(CSVGetField( pszFilename,
572 : "ELLIPSOID_CODE", szSearchKey, CC_Integer,
573 : "ELLIPSOID_NAME" ));
574 :
575 56 : return( TRUE );
576 : }
577 :
578 : /************************************************************************/
579 : /* GTIFGetPMInfo() */
580 : /* */
581 : /* Get the offset between a given prime meridian and Greenwich */
582 : /* in degrees. */
583 : /************************************************************************/
584 :
585 3912 : int GTIFGetPMInfo( int nPMCode, char ** ppszName, double *pdfOffset )
586 :
587 : {
588 : char szSearchKey[24];
589 : int nUOMAngle;
590 : const char *pszFilename;
591 :
592 : /* -------------------------------------------------------------------- */
593 : /* Use a special short cut for Greenwich, since it is so common. */
594 : /* -------------------------------------------------------------------- */
595 3912 : if( nPMCode == PM_Greenwich )
596 : {
597 3884 : if( pdfOffset != NULL )
598 1944 : *pdfOffset = 0.0;
599 3884 : if( ppszName != NULL )
600 1940 : *ppszName = CPLStrdup( "Greenwich" );
601 3884 : return TRUE;
602 : }
603 :
604 : /* -------------------------------------------------------------------- */
605 : /* Search the database for the corresponding datum code. */
606 : /* -------------------------------------------------------------------- */
607 28 : pszFilename = CSVFilename("prime_meridian.csv");
608 28 : sprintf( szSearchKey, "%d", nPMCode );
609 :
610 28 : nUOMAngle =
611 28 : atoi(CSVGetField( pszFilename,
612 : "PRIME_MERIDIAN_CODE", szSearchKey, CC_Integer,
613 : "UOM_CODE" ) );
614 28 : if( nUOMAngle < 1 )
615 4 : return FALSE;
616 :
617 : /* -------------------------------------------------------------------- */
618 : /* Get the PM offset. */
619 : /* -------------------------------------------------------------------- */
620 24 : if( pdfOffset != NULL )
621 : {
622 12 : *pdfOffset =
623 12 : GTIFAngleStringToDD(
624 : CSVGetField( pszFilename,
625 : "PRIME_MERIDIAN_CODE", szSearchKey, CC_Integer,
626 : "GREENWICH_LONGITUDE" ),
627 : nUOMAngle );
628 : }
629 :
630 : /* -------------------------------------------------------------------- */
631 : /* Get the name, if requested. */
632 : /* -------------------------------------------------------------------- */
633 24 : if( ppszName != NULL )
634 12 : *ppszName =
635 12 : CPLStrdup(
636 : CSVGetField( pszFilename,
637 : "PRIME_MERIDIAN_CODE", szSearchKey, CC_Integer,
638 : "PRIME_MERIDIAN_NAME" ));
639 :
640 24 : return( TRUE );
641 : }
642 :
643 : /************************************************************************/
644 : /* GTIFGetDatumInfo() */
645 : /* */
646 : /* Fetch the ellipsoid, and name for a datum. */
647 : /************************************************************************/
648 :
649 3916 : int GTIFGetDatumInfo( int nDatumCode, char ** ppszName, short * pnEllipsoid )
650 :
651 : {
652 : char szSearchKey[24];
653 3916 : int nEllipsoid = 0;
654 : const char *pszFilename;
655 : FILE *fp;
656 3916 : const char *pszName = NULL;
657 :
658 : /* -------------------------------------------------------------------- */
659 : /* Handle a few built-in datums. */
660 : /* -------------------------------------------------------------------- */
661 3916 : if( nDatumCode == Datum_North_American_Datum_1927 )
662 : {
663 1880 : nEllipsoid = Ellipse_Clarke_1866;
664 1880 : pszName = "North American Datum 1927";
665 : }
666 2036 : else if( nDatumCode == Datum_North_American_Datum_1983 )
667 : {
668 6 : nEllipsoid = Ellipse_GRS_1980;
669 6 : pszName = "North American Datum 1983";
670 : }
671 2030 : else if( nDatumCode == Datum_WGS84 )
672 : {
673 1932 : nEllipsoid = Ellipse_WGS_84;
674 1932 : pszName = "World Geodetic System 1984";
675 : }
676 98 : else if( nDatumCode == Datum_WGS72 )
677 : {
678 8 : nEllipsoid = 7043; /* WGS72 */
679 8 : pszName = "World Geodetic System 1972";
680 : }
681 :
682 3916 : if (pszName != NULL)
683 : {
684 3826 : if( pnEllipsoid != NULL )
685 1914 : *pnEllipsoid = (short) nEllipsoid;
686 :
687 3826 : if( ppszName != NULL )
688 1912 : *ppszName = CPLStrdup( pszName );
689 :
690 3826 : return TRUE;
691 : }
692 :
693 : /* -------------------------------------------------------------------- */
694 : /* If we can't find datum.csv then gdal_datum.csv is an */
695 : /* acceptable fallback. Mostly this is for GDAL. */
696 : /* -------------------------------------------------------------------- */
697 90 : pszFilename = CSVFilename( "datum.csv" );
698 90 : if( (fp = VSIFOpen(pszFilename,"r")) == NULL )
699 : {
700 90 : if( (fp = VSIFOpen(CSVFilename("gdal_datum.csv"), "r")) != NULL )
701 : {
702 90 : pszFilename = CSVFilename( "gdal_datum.csv" );
703 90 : VSIFClose( fp );
704 : }
705 : }
706 : else
707 0 : VSIFClose( fp );
708 :
709 : /* -------------------------------------------------------------------- */
710 : /* Search the database for the corresponding datum code. */
711 : /* -------------------------------------------------------------------- */
712 90 : sprintf( szSearchKey, "%d", nDatumCode );
713 :
714 90 : nEllipsoid = atoi(CSVGetField( pszFilename,
715 : "DATUM_CODE", szSearchKey, CC_Integer,
716 : "ELLIPSOID_CODE" ) );
717 :
718 90 : if( pnEllipsoid != NULL )
719 46 : *pnEllipsoid = (short) nEllipsoid;
720 :
721 90 : if( nEllipsoid < 1 )
722 : {
723 0 : return FALSE;
724 : }
725 :
726 : /* -------------------------------------------------------------------- */
727 : /* Get the name, if requested. */
728 : /* -------------------------------------------------------------------- */
729 90 : if( ppszName != NULL )
730 44 : *ppszName =
731 44 : CPLStrdup(CSVGetField( pszFilename,
732 : "DATUM_CODE", szSearchKey, CC_Integer,
733 : "DATUM_NAME" ));
734 :
735 90 : return( TRUE );
736 : }
737 :
738 :
739 : /************************************************************************/
740 : /* GTIFGetUOMLengthInfo() */
741 : /* */
742 : /* Note: This function should eventually also know how to */
743 : /* lookup length aliases in the UOM_LE_ALIAS table. */
744 : /************************************************************************/
745 :
746 3636 : int GTIFGetUOMLengthInfo( int nUOMLengthCode,
747 : char **ppszUOMName,
748 : double * pdfInMeters )
749 :
750 : {
751 : char **papszUnitsRecord;
752 : char szSearchKey[24];
753 : int iNameField;
754 : const char *pszFilename;
755 :
756 : /* -------------------------------------------------------------------- */
757 : /* We short cut meter to save work and avoid failure for missing */
758 : /* in the most common cases. */
759 : /* -------------------------------------------------------------------- */
760 3636 : if( nUOMLengthCode == 9001 )
761 : {
762 3536 : if( ppszUOMName != NULL )
763 2258 : *ppszUOMName = CPLStrdup( "metre" );
764 3536 : if( pdfInMeters != NULL )
765 1278 : *pdfInMeters = 1.0;
766 :
767 3536 : return TRUE;
768 : }
769 :
770 100 : if( nUOMLengthCode == 9002 )
771 : {
772 0 : if( ppszUOMName != NULL )
773 0 : *ppszUOMName = CPLStrdup( "foot" );
774 0 : if( pdfInMeters != NULL )
775 0 : *pdfInMeters = 0.3048;
776 :
777 0 : return TRUE;
778 : }
779 :
780 100 : if( nUOMLengthCode == 9003 )
781 : {
782 80 : if( ppszUOMName != NULL )
783 48 : *ppszUOMName = CPLStrdup( "US survey foot" );
784 80 : if( pdfInMeters != NULL )
785 32 : *pdfInMeters = 12.0 / 39.37;
786 :
787 80 : return TRUE;
788 : }
789 :
790 : /* -------------------------------------------------------------------- */
791 : /* Search the units database for this unit. If we don't find */
792 : /* it return failure. */
793 : /* -------------------------------------------------------------------- */
794 20 : pszFilename = CSVFilename( "unit_of_measure.csv" );
795 :
796 20 : sprintf( szSearchKey, "%d", nUOMLengthCode );
797 20 : papszUnitsRecord =
798 20 : CSVScanFileByName( pszFilename,
799 : "UOM_CODE", szSearchKey, CC_Integer );
800 :
801 20 : if( papszUnitsRecord == NULL )
802 12 : return FALSE;
803 :
804 : /* -------------------------------------------------------------------- */
805 : /* Get the name, if requested. */
806 : /* -------------------------------------------------------------------- */
807 8 : if( ppszUOMName != NULL )
808 : {
809 0 : iNameField = CSVGetFileFieldId( pszFilename,
810 : "UNIT_OF_MEAS_NAME" );
811 0 : *ppszUOMName = CPLStrdup( CSLGetField(papszUnitsRecord, iNameField) );
812 : }
813 :
814 : /* -------------------------------------------------------------------- */
815 : /* Get the A and B factor fields, and create the multiplicative */
816 : /* factor. */
817 : /* -------------------------------------------------------------------- */
818 8 : if( pdfInMeters != NULL )
819 : {
820 : int iBFactorField, iCFactorField;
821 :
822 8 : iBFactorField = CSVGetFileFieldId( pszFilename, "FACTOR_B" );
823 8 : iCFactorField = CSVGetFileFieldId( pszFilename, "FACTOR_C" );
824 :
825 8 : if( GTIFAtof(CSLGetField(papszUnitsRecord, iCFactorField)) > 0.0 )
826 8 : *pdfInMeters = GTIFAtof(CSLGetField(papszUnitsRecord, iBFactorField))
827 : / GTIFAtof(CSLGetField(papszUnitsRecord, iCFactorField));
828 : else
829 0 : *pdfInMeters = 0.0;
830 : }
831 :
832 8 : return( TRUE );
833 : }
834 :
835 : /************************************************************************/
836 : /* GTIFGetUOMAngleInfo() */
837 : /************************************************************************/
838 :
839 4082 : int GTIFGetUOMAngleInfo( int nUOMAngleCode,
840 : char **ppszUOMName,
841 : double * pdfInDegrees )
842 :
843 : {
844 4082 : const char *pszUOMName = NULL;
845 4082 : double dfInDegrees = 1.0;
846 : const char *pszFilename;
847 : char szSearchKey[24];
848 :
849 4082 : switch( nUOMAngleCode )
850 : {
851 : case 9101:
852 0 : pszUOMName = "radian";
853 0 : dfInDegrees = 180.0 / PI;
854 0 : break;
855 :
856 : case 9102:
857 : case 9107:
858 : case 9108:
859 : case 9110:
860 : case 9122:
861 4070 : pszUOMName = "degree";
862 4070 : dfInDegrees = 1.0;
863 4070 : break;
864 :
865 : case 9103:
866 0 : pszUOMName = "arc-minute";
867 0 : dfInDegrees = 1 / 60.0;
868 0 : break;
869 :
870 : case 9104:
871 0 : pszUOMName = "arc-second";
872 0 : dfInDegrees = 1 / 3600.0;
873 0 : break;
874 :
875 : case 9105:
876 12 : pszUOMName = "grad";
877 12 : dfInDegrees = 180.0 / 200.0;
878 12 : break;
879 :
880 : case 9106:
881 0 : pszUOMName = "gon";
882 0 : dfInDegrees = 180.0 / 200.0;
883 0 : break;
884 :
885 : case 9109:
886 0 : pszUOMName = "microradian";
887 0 : dfInDegrees = 180.0 / (PI * 1000000.0);
888 : break;
889 :
890 : default:
891 : break;
892 : }
893 :
894 4082 : if (pszUOMName)
895 : {
896 4082 : if( ppszUOMName != NULL )
897 : {
898 2006 : if( pszUOMName != NULL )
899 2006 : *ppszUOMName = CPLStrdup( pszUOMName );
900 : else
901 0 : *ppszUOMName = NULL;
902 : }
903 :
904 4082 : if( pdfInDegrees != NULL )
905 2076 : *pdfInDegrees = dfInDegrees;
906 :
907 4082 : return TRUE;
908 : }
909 :
910 0 : pszFilename = CSVFilename( "unit_of_measure.csv" );
911 0 : sprintf( szSearchKey, "%d", nUOMAngleCode );
912 0 : pszUOMName = CSVGetField( pszFilename,
913 : "UOM_CODE", szSearchKey, CC_Integer,
914 : "UNIT_OF_MEAS_NAME" );
915 :
916 : /* -------------------------------------------------------------------- */
917 : /* If the file is found, read from there. Note that FactorC is */
918 : /* an empty field for any of the DMS style formats, and in this */
919 : /* case we really want to return the default InDegrees value */
920 : /* (1.0) from above. */
921 : /* -------------------------------------------------------------------- */
922 0 : if( pszUOMName != NULL )
923 : {
924 : double dfFactorB, dfFactorC, dfInRadians;
925 :
926 0 : dfFactorB =
927 0 : GTIFAtof(CSVGetField( pszFilename,
928 : "UOM_CODE", szSearchKey, CC_Integer,
929 : "FACTOR_B" ));
930 :
931 0 : dfFactorC =
932 0 : GTIFAtof(CSVGetField( pszFilename,
933 : "UOM_CODE", szSearchKey, CC_Integer,
934 : "FACTOR_C" ));
935 :
936 0 : if( dfFactorC != 0.0 )
937 : {
938 0 : dfInRadians = (dfFactorB / dfFactorC);
939 0 : dfInDegrees = dfInRadians * 180.0 / PI;
940 : }
941 : }
942 : else
943 : {
944 0 : return FALSE;
945 : }
946 :
947 : /* -------------------------------------------------------------------- */
948 : /* Return to caller. */
949 : /* -------------------------------------------------------------------- */
950 0 : if( ppszUOMName != NULL )
951 : {
952 0 : if( pszUOMName != NULL )
953 0 : *ppszUOMName = CPLStrdup( pszUOMName );
954 : else
955 0 : *ppszUOMName = NULL;
956 : }
957 :
958 0 : if( pdfInDegrees != NULL )
959 0 : *pdfInDegrees = dfInDegrees;
960 :
961 0 : return( TRUE );
962 : }
963 :
964 : /************************************************************************/
965 : /* EPSGProjMethodToCTProjMethod() */
966 : /* */
967 : /* Convert between the EPSG enumeration for projection methods, */
968 : /* and the GeoTIFF CT codes. */
969 : /************************************************************************/
970 :
971 1106 : static int EPSGProjMethodToCTProjMethod( int nEPSG )
972 :
973 : {
974 : /* see trf_method.csv for list of EPSG codes */
975 :
976 1106 : switch( nEPSG )
977 : {
978 : case 9801:
979 16 : return( CT_LambertConfConic_1SP );
980 :
981 : case 9802:
982 20 : return( CT_LambertConfConic_2SP );
983 :
984 : case 9803:
985 0 : return( CT_LambertConfConic_2SP ); /* Belgian variant not supported */
986 :
987 : case 9804:
988 4 : return( CT_Mercator ); /* 1SP and 2SP not differentiated */
989 :
990 : case 9805:
991 0 : return( CT_Mercator ); /* 1SP and 2SP not differentiated */
992 :
993 : /* Mercator 1SP (Spherical) For EPSG:3785 */
994 : case 9841:
995 0 : return( CT_Mercator ); /* 1SP and 2SP not differentiated */
996 :
997 : /* Google Mercator For EPSG:3857 */
998 : case 1024:
999 0 : return( CT_Mercator ); /* 1SP and 2SP not differentiated */
1000 :
1001 : case 9806:
1002 4 : return( CT_CassiniSoldner );
1003 :
1004 : case 9807:
1005 1002 : return( CT_TransverseMercator );
1006 :
1007 : case 9808:
1008 4 : return( CT_TransvMercator_SouthOriented );
1009 :
1010 : case 9809:
1011 4 : return( CT_ObliqueStereographic );
1012 :
1013 : case 9810:
1014 : /* case 9829: variant B not quite the same */
1015 4 : return( CT_PolarStereographic );
1016 :
1017 : case 9811:
1018 4 : return( CT_NewZealandMapGrid );
1019 :
1020 : case 9812:
1021 0 : return( CT_ObliqueMercator ); /* is hotine actually different? */
1022 :
1023 : case 9813:
1024 0 : return( CT_ObliqueMercator_Laborde );
1025 :
1026 : case 9814:
1027 0 : return( CT_ObliqueMercator_Rosenmund ); /* swiss */
1028 :
1029 : case 9815:
1030 8 : return( CT_ObliqueMercator );
1031 :
1032 : case 9816: /* tunesia mining grid has no counterpart */
1033 0 : return( KvUserDefined );
1034 :
1035 : case 9820:
1036 : case 1027:
1037 4 : return( CT_LambertAzimEqualArea );
1038 :
1039 : case 9822:
1040 12 : return( CT_AlbersEqualArea );
1041 :
1042 : case 9834:
1043 4 : return( CT_CylindricalEqualArea );
1044 : }
1045 :
1046 16 : return( KvUserDefined );
1047 : }
1048 :
1049 : /************************************************************************/
1050 : /* SetGTParmIds() */
1051 : /* */
1052 : /* This is hardcoded logic to set the GeoTIFF parmaeter */
1053 : /* identifiers for all the EPSG supported projections. As the */
1054 : /* trf_method.csv table grows with new projections, this code */
1055 : /* will need to be updated. */
1056 : /************************************************************************/
1057 :
1058 1106 : static int SetGTParmIds( int nCTProjection,
1059 : int *panProjParmId,
1060 : int *panEPSGCodes )
1061 :
1062 : {
1063 : int anWorkingDummy[7];
1064 :
1065 1106 : if( panEPSGCodes == NULL )
1066 1052 : panEPSGCodes = anWorkingDummy;
1067 1106 : if( panProjParmId == NULL )
1068 54 : panProjParmId = anWorkingDummy;
1069 :
1070 1106 : memset( panEPSGCodes, 0, sizeof(int) * 7 );
1071 :
1072 : /* psDefn->nParms = 7; */
1073 :
1074 1106 : switch( nCTProjection )
1075 : {
1076 : case CT_CassiniSoldner:
1077 : case CT_NewZealandMapGrid:
1078 8 : panProjParmId[0] = ProjNatOriginLatGeoKey;
1079 8 : panProjParmId[1] = ProjNatOriginLongGeoKey;
1080 8 : panProjParmId[5] = ProjFalseEastingGeoKey;
1081 8 : panProjParmId[6] = ProjFalseNorthingGeoKey;
1082 :
1083 8 : panEPSGCodes[0] = EPSGNatOriginLat;
1084 8 : panEPSGCodes[1] = EPSGNatOriginLong;
1085 8 : panEPSGCodes[5] = EPSGFalseEasting;
1086 8 : panEPSGCodes[6] = EPSGFalseNorthing;
1087 8 : return TRUE;
1088 :
1089 : case CT_ObliqueMercator:
1090 8 : panProjParmId[0] = ProjCenterLatGeoKey;
1091 8 : panProjParmId[1] = ProjCenterLongGeoKey;
1092 8 : panProjParmId[2] = ProjAzimuthAngleGeoKey;
1093 8 : panProjParmId[3] = ProjRectifiedGridAngleGeoKey;
1094 8 : panProjParmId[4] = ProjScaleAtCenterGeoKey;
1095 8 : panProjParmId[5] = ProjFalseEastingGeoKey;
1096 8 : panProjParmId[6] = ProjFalseNorthingGeoKey;
1097 :
1098 8 : panEPSGCodes[0] = EPSGProjCenterLat;
1099 8 : panEPSGCodes[1] = EPSGProjCenterLong;
1100 8 : panEPSGCodes[2] = EPSGAzimuth;
1101 8 : panEPSGCodes[3] = EPSGAngleRectifiedToSkewedGrid;
1102 8 : panEPSGCodes[4] = EPSGInitialLineScaleFactor;
1103 8 : panEPSGCodes[5] = EPSGProjCenterEasting; /* EPSG proj method 9812 uses EPSGFalseEasting, but 9815 uses EPSGProjCenterEasting */
1104 8 : panEPSGCodes[6] = EPSGProjCenterNorthing; /* EPSG proj method 9812 uses EPSGFalseNorthing, but 9815 uses EPSGProjCenterNorthing */
1105 8 : return TRUE;
1106 :
1107 : case CT_ObliqueMercator_Laborde:
1108 0 : panProjParmId[0] = ProjCenterLatGeoKey;
1109 0 : panProjParmId[1] = ProjCenterLongGeoKey;
1110 0 : panProjParmId[2] = ProjAzimuthAngleGeoKey;
1111 0 : panProjParmId[4] = ProjScaleAtCenterGeoKey;
1112 0 : panProjParmId[5] = ProjFalseEastingGeoKey;
1113 0 : panProjParmId[6] = ProjFalseNorthingGeoKey;
1114 :
1115 0 : panEPSGCodes[0] = EPSGProjCenterLat;
1116 0 : panEPSGCodes[1] = EPSGProjCenterLong;
1117 0 : panEPSGCodes[2] = EPSGAzimuth;
1118 0 : panEPSGCodes[4] = EPSGInitialLineScaleFactor;
1119 0 : panEPSGCodes[5] = EPSGProjCenterEasting;
1120 0 : panEPSGCodes[6] = EPSGProjCenterNorthing;
1121 0 : return TRUE;
1122 :
1123 : case CT_LambertConfConic_1SP:
1124 : case CT_Mercator:
1125 : case CT_ObliqueStereographic:
1126 : case CT_PolarStereographic:
1127 : case CT_TransverseMercator:
1128 : case CT_TransvMercator_SouthOriented:
1129 1034 : panProjParmId[0] = ProjNatOriginLatGeoKey;
1130 1034 : panProjParmId[1] = ProjNatOriginLongGeoKey;
1131 1034 : panProjParmId[4] = ProjScaleAtNatOriginGeoKey;
1132 1034 : panProjParmId[5] = ProjFalseEastingGeoKey;
1133 1034 : panProjParmId[6] = ProjFalseNorthingGeoKey;
1134 :
1135 1034 : panEPSGCodes[0] = EPSGNatOriginLat;
1136 1034 : panEPSGCodes[1] = EPSGNatOriginLong;
1137 1034 : panEPSGCodes[4] = EPSGNatOriginScaleFactor;
1138 1034 : panEPSGCodes[5] = EPSGFalseEasting;
1139 1034 : panEPSGCodes[6] = EPSGFalseNorthing;
1140 1034 : return TRUE;
1141 :
1142 : case CT_LambertConfConic_2SP:
1143 20 : panProjParmId[0] = ProjFalseOriginLatGeoKey;
1144 20 : panProjParmId[1] = ProjFalseOriginLongGeoKey;
1145 20 : panProjParmId[2] = ProjStdParallel1GeoKey;
1146 20 : panProjParmId[3] = ProjStdParallel2GeoKey;
1147 20 : panProjParmId[5] = ProjFalseEastingGeoKey;
1148 20 : panProjParmId[6] = ProjFalseNorthingGeoKey;
1149 :
1150 20 : panEPSGCodes[0] = EPSGFalseOriginLat;
1151 20 : panEPSGCodes[1] = EPSGFalseOriginLong;
1152 20 : panEPSGCodes[2] = EPSGStdParallel1Lat;
1153 20 : panEPSGCodes[3] = EPSGStdParallel2Lat;
1154 20 : panEPSGCodes[5] = EPSGFalseOriginEasting;
1155 20 : panEPSGCodes[6] = EPSGFalseOriginNorthing;
1156 20 : return TRUE;
1157 :
1158 : case CT_AlbersEqualArea:
1159 12 : panProjParmId[0] = ProjStdParallel1GeoKey;
1160 12 : panProjParmId[1] = ProjStdParallel2GeoKey;
1161 12 : panProjParmId[2] = ProjNatOriginLatGeoKey;
1162 12 : panProjParmId[3] = ProjNatOriginLongGeoKey;
1163 12 : panProjParmId[5] = ProjFalseEastingGeoKey;
1164 12 : panProjParmId[6] = ProjFalseNorthingGeoKey;
1165 :
1166 12 : panEPSGCodes[0] = EPSGStdParallel1Lat;
1167 12 : panEPSGCodes[1] = EPSGStdParallel2Lat;
1168 12 : panEPSGCodes[2] = EPSGFalseOriginLat;
1169 12 : panEPSGCodes[3] = EPSGFalseOriginLong;
1170 12 : panEPSGCodes[5] = EPSGFalseOriginEasting;
1171 12 : panEPSGCodes[6] = EPSGFalseOriginNorthing;
1172 12 : return TRUE;
1173 :
1174 : case CT_SwissObliqueCylindrical:
1175 0 : panProjParmId[0] = ProjCenterLatGeoKey;
1176 0 : panProjParmId[1] = ProjCenterLongGeoKey;
1177 0 : panProjParmId[5] = ProjFalseEastingGeoKey;
1178 0 : panProjParmId[6] = ProjFalseNorthingGeoKey;
1179 :
1180 : /* EPSG codes? */
1181 0 : return TRUE;
1182 :
1183 : case CT_LambertAzimEqualArea:
1184 4 : panProjParmId[0] = ProjCenterLatGeoKey;
1185 4 : panProjParmId[1] = ProjCenterLongGeoKey;
1186 4 : panProjParmId[5] = ProjFalseEastingGeoKey;
1187 4 : panProjParmId[6] = ProjFalseNorthingGeoKey;
1188 :
1189 4 : panEPSGCodes[0] = EPSGNatOriginLat;
1190 4 : panEPSGCodes[1] = EPSGNatOriginLong;
1191 4 : panEPSGCodes[5] = EPSGFalseEasting;
1192 4 : panEPSGCodes[6] = EPSGFalseNorthing;
1193 4 : return TRUE;
1194 :
1195 : case CT_CylindricalEqualArea:
1196 4 : panProjParmId[0] = ProjStdParallel1GeoKey;
1197 4 : panProjParmId[1] = ProjNatOriginLongGeoKey;
1198 4 : panProjParmId[5] = ProjFalseEastingGeoKey;
1199 4 : panProjParmId[6] = ProjFalseNorthingGeoKey;
1200 :
1201 4 : panEPSGCodes[0] = EPSGStdParallel1Lat;
1202 4 : panEPSGCodes[1] = EPSGFalseOriginLong;
1203 4 : panEPSGCodes[5] = EPSGFalseOriginEasting;
1204 4 : panEPSGCodes[6] = EPSGFalseOriginNorthing;
1205 4 : return TRUE;
1206 :
1207 : default:
1208 16 : return( FALSE );
1209 : }
1210 : }
1211 :
1212 : /************************************************************************/
1213 : /* GTIFGetProjTRFInfo() */
1214 : /* */
1215 : /* Transform a PROJECTION_TRF_CODE into a projection method, */
1216 : /* and a set of parameters. The parameters identify will */
1217 : /* depend on the returned method, but they will all have been */
1218 : /* normalized into degrees and meters. */
1219 : /************************************************************************/
1220 :
1221 1060 : int GTIFGetProjTRFInfo( /* COORD_OP_CODE from coordinate_operation.csv */
1222 : int nProjTRFCode,
1223 : char **ppszProjTRFName,
1224 : short * pnProjMethod,
1225 : double * padfProjParms )
1226 :
1227 : {
1228 : int nProjMethod, i, anEPSGCodes[7];
1229 : double adfProjParms[7];
1230 : char szTRFCode[16];
1231 : int nCTProjMethod;
1232 : char *pszFilename;
1233 :
1234 1060 : if ((nProjTRFCode >= Proj_UTM_zone_1N && nProjTRFCode <= Proj_UTM_zone_60N) ||
1235 : (nProjTRFCode >= Proj_UTM_zone_1S && nProjTRFCode <= Proj_UTM_zone_60S))
1236 : {
1237 : int bNorth;
1238 : int nZone;
1239 1006 : if (nProjTRFCode <= Proj_UTM_zone_60N)
1240 : {
1241 1006 : bNorth = TRUE;
1242 1006 : nZone = nProjTRFCode - Proj_UTM_zone_1N + 1;
1243 : }
1244 : else
1245 : {
1246 0 : bNorth = FALSE;
1247 0 : nZone = nProjTRFCode - Proj_UTM_zone_1S + 1;
1248 : }
1249 :
1250 1006 : if (ppszProjTRFName)
1251 : {
1252 : char szProjTRFName[64];
1253 0 : sprintf(szProjTRFName, "UTM zone %d%c",
1254 : nZone, (bNorth) ? 'N' : 'S');
1255 0 : *ppszProjTRFName = CPLStrdup(szProjTRFName);
1256 : }
1257 :
1258 1006 : if (pnProjMethod)
1259 1006 : *pnProjMethod = 9807;
1260 :
1261 1006 : if (padfProjParms)
1262 : {
1263 1006 : padfProjParms[0] = 0;
1264 1006 : padfProjParms[1] = -183 + 6 * nZone;
1265 1006 : padfProjParms[2] = 0;
1266 1006 : padfProjParms[3] = 0;
1267 1006 : padfProjParms[4] = 0.9996;
1268 1006 : padfProjParms[5] = 500000;
1269 1006 : padfProjParms[6] = (bNorth) ? 0 : 10000000;
1270 : }
1271 :
1272 1006 : return TRUE;
1273 : }
1274 :
1275 : /* -------------------------------------------------------------------- */
1276 : /* Get the proj method. If this fails to return a meaningful */
1277 : /* number, then the whole function fails. */
1278 : /* -------------------------------------------------------------------- */
1279 54 : pszFilename = CPLStrdup(CSVFilename("projop_wparm.csv"));
1280 54 : sprintf( szTRFCode, "%d", nProjTRFCode );
1281 54 : nProjMethod =
1282 54 : atoi( CSVGetField( pszFilename,
1283 : "COORD_OP_CODE", szTRFCode, CC_Integer,
1284 : "COORD_OP_METHOD_CODE" ) );
1285 54 : if( nProjMethod == 0 )
1286 : {
1287 0 : CPLFree( pszFilename );
1288 0 : return FALSE;
1289 : }
1290 :
1291 : /* -------------------------------------------------------------------- */
1292 : /* Initialize a definition of what EPSG codes need to be loaded */
1293 : /* into what fields in adfProjParms. */
1294 : /* -------------------------------------------------------------------- */
1295 54 : nCTProjMethod = EPSGProjMethodToCTProjMethod( nProjMethod );
1296 54 : SetGTParmIds( nCTProjMethod, NULL, anEPSGCodes );
1297 :
1298 : /* -------------------------------------------------------------------- */
1299 : /* Get the parameters for this projection. For the time being */
1300 : /* I am assuming the first four parameters are angles, the */
1301 : /* fifth is unitless (normally scale), and the remainder are */
1302 : /* linear measures. This works fine for the existing */
1303 : /* projections, but is a pretty fragile approach. */
1304 : /* -------------------------------------------------------------------- */
1305 :
1306 432 : for( i = 0; i < 7; i++ )
1307 : {
1308 : char szParamUOMID[32], szParamValueID[32], szParamCodeID[32];
1309 : const char *pszValue;
1310 : int nUOM;
1311 378 : int nEPSGCode = anEPSGCodes[i];
1312 : int iEPSG;
1313 :
1314 : /* Establish default */
1315 378 : if( nEPSGCode == EPSGAngleRectifiedToSkewedGrid )
1316 4 : adfProjParms[i] = 90.0;
1317 396 : else if( nEPSGCode == EPSGNatOriginScaleFactor
1318 : || nEPSGCode == EPSGInitialLineScaleFactor
1319 : || nEPSGCode == EPSGPseudoStdParallelScaleFactor )
1320 22 : adfProjParms[i] = 1.0;
1321 : else
1322 352 : adfProjParms[i] = 0.0;
1323 :
1324 : /* If there is no parameter, skip */
1325 378 : if( nEPSGCode == 0 )
1326 132 : continue;
1327 :
1328 : /* Find the matching parameter */
1329 834 : for( iEPSG = 0; iEPSG < 7; iEPSG++ )
1330 : {
1331 828 : sprintf( szParamCodeID, "PARAMETER_CODE_%d", iEPSG+1 );
1332 :
1333 828 : if( atoi(CSVGetField( pszFilename,
1334 : "COORD_OP_CODE", szTRFCode, CC_Integer,
1335 : szParamCodeID )) == nEPSGCode )
1336 240 : break;
1337 : }
1338 :
1339 : /* not found, accept the default */
1340 246 : if( iEPSG == 7 )
1341 : {
1342 : /* for CT_ObliqueMercator try alternate parameter codes first */
1343 : /* because EPSG proj method 9812 uses EPSGFalseXXXXX, but 9815 uses EPSGProjCenterXXXXX */
1344 6 : if ( nCTProjMethod == CT_ObliqueMercator && nEPSGCode == EPSGProjCenterEasting )
1345 0 : nEPSGCode = EPSGFalseEasting;
1346 6 : else if ( nCTProjMethod == CT_ObliqueMercator && nEPSGCode == EPSGProjCenterNorthing )
1347 0 : nEPSGCode = EPSGFalseNorthing;
1348 : else
1349 6 : continue;
1350 :
1351 0 : for( iEPSG = 0; iEPSG < 7; iEPSG++ )
1352 : {
1353 0 : sprintf( szParamCodeID, "PARAMETER_CODE_%d", iEPSG+1 );
1354 :
1355 0 : if( atoi(CSVGetField( pszFilename,
1356 : "COORD_OP_CODE", szTRFCode, CC_Integer,
1357 : szParamCodeID )) == nEPSGCode )
1358 0 : break;
1359 : }
1360 :
1361 0 : if( iEPSG == 7 )
1362 0 : continue;
1363 : }
1364 :
1365 : /* Get the value, and UOM */
1366 240 : sprintf( szParamUOMID, "PARAMETER_UOM_%d", iEPSG+1 );
1367 240 : sprintf( szParamValueID, "PARAMETER_VALUE_%d", iEPSG+1 );
1368 :
1369 240 : nUOM = atoi(CSVGetField( pszFilename,
1370 : "COORD_OP_CODE", szTRFCode, CC_Integer,
1371 : szParamUOMID ));
1372 240 : pszValue = CSVGetField( pszFilename,
1373 : "COORD_OP_CODE", szTRFCode, CC_Integer,
1374 : szParamValueID );
1375 :
1376 : /* Transform according to the UOM */
1377 370 : if( nUOM >= 9100 && nUOM < 9200 )
1378 130 : adfProjParms[i] = GTIFAngleStringToDD( pszValue, nUOM );
1379 198 : else if( nUOM > 9000 && nUOM < 9100 )
1380 : {
1381 : double dfInMeters;
1382 :
1383 88 : if( !GTIFGetUOMLengthInfo( nUOM, NULL, &dfInMeters ) )
1384 0 : dfInMeters = 1.0;
1385 88 : adfProjParms[i] = GTIFAtof(pszValue) * dfInMeters;
1386 : }
1387 : else
1388 22 : adfProjParms[i] = GTIFAtof(pszValue);
1389 : }
1390 :
1391 : /* -------------------------------------------------------------------- */
1392 : /* Get the name, if requested. */
1393 : /* -------------------------------------------------------------------- */
1394 54 : if( ppszProjTRFName != NULL )
1395 : {
1396 0 : *ppszProjTRFName =
1397 0 : CPLStrdup(CSVGetField( pszFilename,
1398 : "COORD_OP_CODE", szTRFCode, CC_Integer,
1399 : "COORD_OP_NAME" ));
1400 : }
1401 :
1402 : /* -------------------------------------------------------------------- */
1403 : /* Transfer requested data into passed variables. */
1404 : /* -------------------------------------------------------------------- */
1405 54 : if( pnProjMethod != NULL )
1406 54 : *pnProjMethod = (short) nProjMethod;
1407 :
1408 54 : if( padfProjParms != NULL )
1409 : {
1410 432 : for( i = 0; i < 7; i++ )
1411 378 : padfProjParms[i] = adfProjParms[i];
1412 : }
1413 :
1414 54 : CPLFree( pszFilename );
1415 :
1416 54 : return TRUE;
1417 : }
1418 :
1419 : /************************************************************************/
1420 : /* GTIFFetchProjParms() */
1421 : /* */
1422 : /* Fetch the projection parameters for a particular projection */
1423 : /* from a GeoTIFF file, and fill the GTIFDefn structure out */
1424 : /* with them. */
1425 : /************************************************************************/
1426 :
1427 96 : static void GTIFFetchProjParms( GTIF * psGTIF, GTIFDefn * psDefn )
1428 :
1429 : {
1430 96 : double dfNatOriginLong = 0.0, dfNatOriginLat = 0.0, dfRectGridAngle = 0.0;
1431 96 : double dfFalseEasting = 0.0, dfFalseNorthing = 0.0, dfNatOriginScale = 1.0;
1432 96 : double dfStdParallel1 = 0.0, dfStdParallel2 = 0.0, dfAzimuth = 0.0;
1433 : int iParm;
1434 :
1435 : /* -------------------------------------------------------------------- */
1436 : /* Get the false easting, and northing if available. */
1437 : /* -------------------------------------------------------------------- */
1438 136 : if( !GTIFKeyGet(psGTIF, ProjFalseEastingGeoKey, &dfFalseEasting, 0, 1)
1439 20 : && !GTIFKeyGet(psGTIF, ProjCenterEastingGeoKey,
1440 : &dfFalseEasting, 0, 1)
1441 20 : && !GTIFKeyGet(psGTIF, ProjFalseOriginEastingGeoKey,
1442 : &dfFalseEasting, 0, 1) )
1443 0 : dfFalseEasting = 0.0;
1444 :
1445 136 : if( !GTIFKeyGet(psGTIF, ProjFalseNorthingGeoKey, &dfFalseNorthing,0,1)
1446 20 : && !GTIFKeyGet(psGTIF, ProjCenterNorthingGeoKey,
1447 : &dfFalseNorthing, 0, 1)
1448 20 : && !GTIFKeyGet(psGTIF, ProjFalseOriginNorthingGeoKey,
1449 : &dfFalseNorthing, 0, 1) )
1450 0 : dfFalseNorthing = 0.0;
1451 :
1452 96 : switch( psDefn->CTProjection )
1453 : {
1454 : /* -------------------------------------------------------------------- */
1455 : case CT_Stereographic:
1456 : /* -------------------------------------------------------------------- */
1457 12 : if( GTIFKeyGet(psGTIF, ProjNatOriginLongGeoKey,
1458 : &dfNatOriginLong, 0, 1 ) == 0
1459 4 : && GTIFKeyGet(psGTIF, ProjFalseOriginLongGeoKey,
1460 : &dfNatOriginLong, 0, 1 ) == 0
1461 4 : && GTIFKeyGet(psGTIF, ProjCenterLongGeoKey,
1462 4 : &dfNatOriginLong, 0, 1 ) == 0 )
1463 0 : dfNatOriginLong = 0.0;
1464 :
1465 12 : if( GTIFKeyGet(psGTIF, ProjNatOriginLatGeoKey,
1466 : &dfNatOriginLat, 0, 1 ) == 0
1467 4 : && GTIFKeyGet(psGTIF, ProjFalseOriginLatGeoKey,
1468 : &dfNatOriginLat, 0, 1 ) == 0
1469 4 : && GTIFKeyGet(psGTIF, ProjCenterLatGeoKey,
1470 4 : &dfNatOriginLat, 0, 1 ) == 0 )
1471 0 : dfNatOriginLat = 0.0;
1472 :
1473 4 : if( GTIFKeyGet(psGTIF, ProjScaleAtNatOriginGeoKey,
1474 : &dfNatOriginScale, 0, 1 ) == 0 )
1475 0 : dfNatOriginScale = 1.0;
1476 :
1477 : /* notdef: should transform to decimal degrees at this point */
1478 :
1479 4 : psDefn->ProjParm[0] = dfNatOriginLat;
1480 4 : psDefn->ProjParmId[0] = ProjCenterLatGeoKey;
1481 4 : psDefn->ProjParm[1] = dfNatOriginLong;
1482 4 : psDefn->ProjParmId[1] = ProjCenterLongGeoKey;
1483 4 : psDefn->ProjParm[4] = dfNatOriginScale;
1484 4 : psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey;
1485 4 : psDefn->ProjParm[5] = dfFalseEasting;
1486 4 : psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
1487 4 : psDefn->ProjParm[6] = dfFalseNorthing;
1488 4 : psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
1489 :
1490 4 : psDefn->nParms = 7;
1491 4 : break;
1492 :
1493 : /* -------------------------------------------------------------------- */
1494 : case CT_LambertConfConic_1SP:
1495 : case CT_Mercator:
1496 : case CT_ObliqueStereographic:
1497 : case CT_TransverseMercator:
1498 : case CT_TransvMercator_SouthOriented:
1499 : /* -------------------------------------------------------------------- */
1500 14 : if( GTIFKeyGet(psGTIF, ProjNatOriginLongGeoKey,
1501 : &dfNatOriginLong, 0, 1 ) == 0
1502 14 : && GTIFKeyGet(psGTIF, ProjFalseOriginLongGeoKey,
1503 : &dfNatOriginLong, 0, 1 ) == 0
1504 0 : && GTIFKeyGet(psGTIF, ProjCenterLongGeoKey,
1505 0 : &dfNatOriginLong, 0, 1 ) == 0 )
1506 0 : dfNatOriginLong = 0.0;
1507 :
1508 14 : if( GTIFKeyGet(psGTIF, ProjNatOriginLatGeoKey,
1509 : &dfNatOriginLat, 0, 1 ) == 0
1510 14 : && GTIFKeyGet(psGTIF, ProjFalseOriginLatGeoKey,
1511 : &dfNatOriginLat, 0, 1 ) == 0
1512 0 : && GTIFKeyGet(psGTIF, ProjCenterLatGeoKey,
1513 0 : &dfNatOriginLat, 0, 1 ) == 0 )
1514 0 : dfNatOriginLat = 0.0;
1515 :
1516 14 : if( GTIFKeyGet(psGTIF, ProjScaleAtNatOriginGeoKey,
1517 : &dfNatOriginScale, 0, 1 ) == 0 )
1518 0 : dfNatOriginScale = 1.0;
1519 :
1520 : /* notdef: should transform to decimal degrees at this point */
1521 :
1522 14 : psDefn->ProjParm[0] = dfNatOriginLat;
1523 14 : psDefn->ProjParmId[0] = ProjNatOriginLatGeoKey;
1524 14 : psDefn->ProjParm[1] = dfNatOriginLong;
1525 14 : psDefn->ProjParmId[1] = ProjNatOriginLongGeoKey;
1526 14 : psDefn->ProjParm[4] = dfNatOriginScale;
1527 14 : psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey;
1528 14 : psDefn->ProjParm[5] = dfFalseEasting;
1529 14 : psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
1530 14 : psDefn->ProjParm[6] = dfFalseNorthing;
1531 14 : psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
1532 :
1533 14 : psDefn->nParms = 7;
1534 14 : break;
1535 :
1536 : /* -------------------------------------------------------------------- */
1537 : case CT_ObliqueMercator: /* hotine */
1538 : /* -------------------------------------------------------------------- */
1539 0 : if( GTIFKeyGet(psGTIF, ProjNatOriginLongGeoKey,
1540 : &dfNatOriginLong, 0, 1 ) == 0
1541 0 : && GTIFKeyGet(psGTIF, ProjFalseOriginLongGeoKey,
1542 : &dfNatOriginLong, 0, 1 ) == 0
1543 0 : && GTIFKeyGet(psGTIF, ProjCenterLongGeoKey,
1544 0 : &dfNatOriginLong, 0, 1 ) == 0 )
1545 0 : dfNatOriginLong = 0.0;
1546 :
1547 0 : if( GTIFKeyGet(psGTIF, ProjNatOriginLatGeoKey,
1548 : &dfNatOriginLat, 0, 1 ) == 0
1549 0 : && GTIFKeyGet(psGTIF, ProjFalseOriginLatGeoKey,
1550 : &dfNatOriginLat, 0, 1 ) == 0
1551 0 : && GTIFKeyGet(psGTIF, ProjCenterLatGeoKey,
1552 0 : &dfNatOriginLat, 0, 1 ) == 0 )
1553 0 : dfNatOriginLat = 0.0;
1554 :
1555 0 : if( GTIFKeyGet(psGTIF, ProjAzimuthAngleGeoKey,
1556 : &dfAzimuth, 0, 1 ) == 0 )
1557 0 : dfAzimuth = 0.0;
1558 :
1559 0 : if( GTIFKeyGet(psGTIF, ProjRectifiedGridAngleGeoKey,
1560 : &dfRectGridAngle, 0, 1 ) == 0 )
1561 0 : dfRectGridAngle = 90.0;
1562 :
1563 0 : if( GTIFKeyGet(psGTIF, ProjScaleAtNatOriginGeoKey,
1564 : &dfNatOriginScale, 0, 1 ) == 0
1565 0 : && GTIFKeyGet(psGTIF, ProjScaleAtCenterGeoKey,
1566 0 : &dfNatOriginScale, 0, 1 ) == 0 )
1567 0 : dfNatOriginScale = 1.0;
1568 :
1569 : /* notdef: should transform to decimal degrees at this point */
1570 :
1571 0 : psDefn->ProjParm[0] = dfNatOriginLat;
1572 0 : psDefn->ProjParmId[0] = ProjCenterLatGeoKey;
1573 0 : psDefn->ProjParm[1] = dfNatOriginLong;
1574 0 : psDefn->ProjParmId[1] = ProjCenterLongGeoKey;
1575 0 : psDefn->ProjParm[2] = dfAzimuth;
1576 0 : psDefn->ProjParmId[2] = ProjAzimuthAngleGeoKey;
1577 0 : psDefn->ProjParm[3] = dfRectGridAngle;
1578 0 : psDefn->ProjParmId[3] = ProjRectifiedGridAngleGeoKey;
1579 0 : psDefn->ProjParm[4] = dfNatOriginScale;
1580 0 : psDefn->ProjParmId[4] = ProjScaleAtCenterGeoKey;
1581 0 : psDefn->ProjParm[5] = dfFalseEasting;
1582 0 : psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
1583 0 : psDefn->ProjParm[6] = dfFalseNorthing;
1584 0 : psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
1585 :
1586 0 : psDefn->nParms = 7;
1587 0 : break;
1588 :
1589 : /* -------------------------------------------------------------------- */
1590 : case CT_CassiniSoldner:
1591 : case CT_Polyconic:
1592 : /* -------------------------------------------------------------------- */
1593 4 : if( GTIFKeyGet(psGTIF, ProjNatOriginLongGeoKey,
1594 : &dfNatOriginLong, 0, 1 ) == 0
1595 4 : && GTIFKeyGet(psGTIF, ProjFalseOriginLongGeoKey,
1596 : &dfNatOriginLong, 0, 1 ) == 0
1597 0 : && GTIFKeyGet(psGTIF, ProjCenterLongGeoKey,
1598 0 : &dfNatOriginLong, 0, 1 ) == 0 )
1599 0 : dfNatOriginLong = 0.0;
1600 :
1601 4 : if( GTIFKeyGet(psGTIF, ProjNatOriginLatGeoKey,
1602 : &dfNatOriginLat, 0, 1 ) == 0
1603 4 : && GTIFKeyGet(psGTIF, ProjFalseOriginLatGeoKey,
1604 : &dfNatOriginLat, 0, 1 ) == 0
1605 0 : && GTIFKeyGet(psGTIF, ProjCenterLatGeoKey,
1606 0 : &dfNatOriginLat, 0, 1 ) == 0 )
1607 0 : dfNatOriginLat = 0.0;
1608 :
1609 6 : if( GTIFKeyGet(psGTIF, ProjScaleAtNatOriginGeoKey,
1610 : &dfNatOriginScale, 0, 1 ) == 0
1611 4 : && GTIFKeyGet(psGTIF, ProjScaleAtCenterGeoKey,
1612 2 : &dfNatOriginScale, 0, 1 ) == 0 )
1613 2 : dfNatOriginScale = 1.0;
1614 :
1615 : /* notdef: should transform to decimal degrees at this point */
1616 :
1617 4 : psDefn->ProjParm[0] = dfNatOriginLat;
1618 4 : psDefn->ProjParmId[0] = ProjNatOriginLatGeoKey;
1619 4 : psDefn->ProjParm[1] = dfNatOriginLong;
1620 4 : psDefn->ProjParmId[1] = ProjNatOriginLongGeoKey;
1621 4 : psDefn->ProjParm[4] = dfNatOriginScale;
1622 4 : psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey;
1623 4 : psDefn->ProjParm[5] = dfFalseEasting;
1624 4 : psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
1625 4 : psDefn->ProjParm[6] = dfFalseNorthing;
1626 4 : psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
1627 :
1628 4 : psDefn->nParms = 7;
1629 4 : break;
1630 :
1631 : /* -------------------------------------------------------------------- */
1632 : case CT_AzimuthalEquidistant:
1633 : case CT_MillerCylindrical:
1634 : case CT_Gnomonic:
1635 : case CT_LambertAzimEqualArea:
1636 : case CT_Orthographic:
1637 : case CT_NewZealandMapGrid:
1638 : /* -------------------------------------------------------------------- */
1639 98 : if( GTIFKeyGet(psGTIF, ProjNatOriginLongGeoKey,
1640 : &dfNatOriginLong, 0, 1 ) == 0
1641 34 : && GTIFKeyGet(psGTIF, ProjFalseOriginLongGeoKey,
1642 : &dfNatOriginLong, 0, 1 ) == 0
1643 32 : && GTIFKeyGet(psGTIF, ProjCenterLongGeoKey,
1644 32 : &dfNatOriginLong, 0, 1 ) == 0 )
1645 0 : dfNatOriginLong = 0.0;
1646 :
1647 98 : if( GTIFKeyGet(psGTIF, ProjNatOriginLatGeoKey,
1648 : &dfNatOriginLat, 0, 1 ) == 0
1649 34 : && GTIFKeyGet(psGTIF, ProjFalseOriginLatGeoKey,
1650 : &dfNatOriginLat, 0, 1 ) == 0
1651 32 : && GTIFKeyGet(psGTIF, ProjCenterLatGeoKey,
1652 32 : &dfNatOriginLat, 0, 1 ) == 0 )
1653 0 : dfNatOriginLat = 0.0;
1654 :
1655 : /* notdef: should transform to decimal degrees at this point */
1656 :
1657 34 : psDefn->ProjParm[0] = dfNatOriginLat;
1658 34 : psDefn->ProjParmId[0] = ProjCenterLatGeoKey;
1659 34 : psDefn->ProjParm[1] = dfNatOriginLong;
1660 34 : psDefn->ProjParmId[1] = ProjCenterLongGeoKey;
1661 34 : psDefn->ProjParm[5] = dfFalseEasting;
1662 34 : psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
1663 34 : psDefn->ProjParm[6] = dfFalseNorthing;
1664 34 : psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
1665 :
1666 34 : psDefn->nParms = 7;
1667 34 : break;
1668 :
1669 : /* -------------------------------------------------------------------- */
1670 : case CT_Equirectangular:
1671 : /* -------------------------------------------------------------------- */
1672 12 : if( GTIFKeyGet(psGTIF, ProjNatOriginLongGeoKey,
1673 : &dfNatOriginLong, 0, 1 ) == 0
1674 4 : && GTIFKeyGet(psGTIF, ProjFalseOriginLongGeoKey,
1675 : &dfNatOriginLong, 0, 1 ) == 0
1676 4 : && GTIFKeyGet(psGTIF, ProjCenterLongGeoKey,
1677 4 : &dfNatOriginLong, 0, 1 ) == 0 )
1678 0 : dfNatOriginLong = 0.0;
1679 :
1680 12 : if( GTIFKeyGet(psGTIF, ProjNatOriginLatGeoKey,
1681 : &dfNatOriginLat, 0, 1 ) == 0
1682 4 : && GTIFKeyGet(psGTIF, ProjFalseOriginLatGeoKey,
1683 : &dfNatOriginLat, 0, 1 ) == 0
1684 4 : && GTIFKeyGet(psGTIF, ProjCenterLatGeoKey,
1685 4 : &dfNatOriginLat, 0, 1 ) == 0 )
1686 0 : dfNatOriginLat = 0.0;
1687 :
1688 4 : if( GTIFKeyGet(psGTIF, ProjStdParallel1GeoKey,
1689 : &dfStdParallel1, 0, 1 ) == 0 )
1690 0 : dfStdParallel1 = 0.0;
1691 :
1692 : /* notdef: should transform to decimal degrees at this point */
1693 :
1694 4 : psDefn->ProjParm[0] = dfNatOriginLat;
1695 4 : psDefn->ProjParmId[0] = ProjCenterLatGeoKey;
1696 4 : psDefn->ProjParm[1] = dfNatOriginLong;
1697 4 : psDefn->ProjParmId[1] = ProjCenterLongGeoKey;
1698 4 : psDefn->ProjParm[2] = dfStdParallel1;
1699 4 : psDefn->ProjParmId[2] = ProjStdParallel1GeoKey;
1700 4 : psDefn->ProjParm[5] = dfFalseEasting;
1701 4 : psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
1702 4 : psDefn->ProjParm[6] = dfFalseNorthing;
1703 4 : psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
1704 :
1705 4 : psDefn->nParms = 7;
1706 4 : break;
1707 :
1708 : /* -------------------------------------------------------------------- */
1709 : case CT_Robinson:
1710 : case CT_Sinusoidal:
1711 : case CT_VanDerGrinten:
1712 : /* -------------------------------------------------------------------- */
1713 0 : if( GTIFKeyGet(psGTIF, ProjNatOriginLongGeoKey,
1714 : &dfNatOriginLong, 0, 1 ) == 0
1715 0 : && GTIFKeyGet(psGTIF, ProjFalseOriginLongGeoKey,
1716 : &dfNatOriginLong, 0, 1 ) == 0
1717 0 : && GTIFKeyGet(psGTIF, ProjCenterLongGeoKey,
1718 0 : &dfNatOriginLong, 0, 1 ) == 0 )
1719 0 : dfNatOriginLong = 0.0;
1720 :
1721 : /* notdef: should transform to decimal degrees at this point */
1722 :
1723 0 : psDefn->ProjParm[1] = dfNatOriginLong;
1724 0 : psDefn->ProjParmId[1] = ProjCenterLongGeoKey;
1725 0 : psDefn->ProjParm[5] = dfFalseEasting;
1726 0 : psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
1727 0 : psDefn->ProjParm[6] = dfFalseNorthing;
1728 0 : psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
1729 :
1730 0 : psDefn->nParms = 7;
1731 0 : break;
1732 :
1733 : /* -------------------------------------------------------------------- */
1734 : case CT_PolarStereographic:
1735 : /* -------------------------------------------------------------------- */
1736 8 : if( GTIFKeyGet(psGTIF, ProjStraightVertPoleLongGeoKey,
1737 : &dfNatOriginLong, 0, 1 ) == 0
1738 8 : && GTIFKeyGet(psGTIF, ProjNatOriginLongGeoKey,
1739 : &dfNatOriginLong, 0, 1 ) == 0
1740 0 : && GTIFKeyGet(psGTIF, ProjFalseOriginLongGeoKey,
1741 : &dfNatOriginLong, 0, 1 ) == 0
1742 0 : && GTIFKeyGet(psGTIF, ProjCenterLongGeoKey,
1743 0 : &dfNatOriginLong, 0, 1 ) == 0 )
1744 0 : dfNatOriginLong = 0.0;
1745 :
1746 8 : if( GTIFKeyGet(psGTIF, ProjNatOriginLatGeoKey,
1747 : &dfNatOriginLat, 0, 1 ) == 0
1748 8 : && GTIFKeyGet(psGTIF, ProjFalseOriginLatGeoKey,
1749 : &dfNatOriginLat, 0, 1 ) == 0
1750 0 : && GTIFKeyGet(psGTIF, ProjCenterLatGeoKey,
1751 0 : &dfNatOriginLat, 0, 1 ) == 0 )
1752 0 : dfNatOriginLat = 0.0;
1753 :
1754 8 : if( GTIFKeyGet(psGTIF, ProjScaleAtNatOriginGeoKey,
1755 : &dfNatOriginScale, 0, 1 ) == 0
1756 8 : && GTIFKeyGet(psGTIF, ProjScaleAtCenterGeoKey,
1757 0 : &dfNatOriginScale, 0, 1 ) == 0 )
1758 0 : dfNatOriginScale = 1.0;
1759 :
1760 : /* notdef: should transform to decimal degrees at this point */
1761 :
1762 8 : psDefn->ProjParm[0] = dfNatOriginLat;
1763 8 : psDefn->ProjParmId[0] = ProjNatOriginLatGeoKey;;
1764 8 : psDefn->ProjParm[1] = dfNatOriginLong;
1765 8 : psDefn->ProjParmId[1] = ProjStraightVertPoleLongGeoKey;
1766 8 : psDefn->ProjParm[4] = dfNatOriginScale;
1767 8 : psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey;
1768 8 : psDefn->ProjParm[5] = dfFalseEasting;
1769 8 : psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
1770 8 : psDefn->ProjParm[6] = dfFalseNorthing;
1771 8 : psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
1772 :
1773 8 : psDefn->nParms = 7;
1774 8 : break;
1775 :
1776 : /* -------------------------------------------------------------------- */
1777 : case CT_LambertConfConic_2SP:
1778 : /* -------------------------------------------------------------------- */
1779 20 : if( GTIFKeyGet(psGTIF, ProjStdParallel1GeoKey,
1780 : &dfStdParallel1, 0, 1 ) == 0 )
1781 0 : dfStdParallel1 = 0.0;
1782 :
1783 20 : if( GTIFKeyGet(psGTIF, ProjStdParallel2GeoKey,
1784 : &dfStdParallel2, 0, 1 ) == 0 )
1785 0 : dfStdParallel1 = 0.0;
1786 :
1787 40 : if( GTIFKeyGet(psGTIF, ProjNatOriginLongGeoKey,
1788 : &dfNatOriginLong, 0, 1 ) == 0
1789 20 : && GTIFKeyGet(psGTIF, ProjFalseOriginLongGeoKey,
1790 : &dfNatOriginLong, 0, 1 ) == 0
1791 20 : && GTIFKeyGet(psGTIF, ProjCenterLongGeoKey,
1792 0 : &dfNatOriginLong, 0, 1 ) == 0 )
1793 0 : dfNatOriginLong = 0.0;
1794 :
1795 40 : if( GTIFKeyGet(psGTIF, ProjNatOriginLatGeoKey,
1796 : &dfNatOriginLat, 0, 1 ) == 0
1797 20 : && GTIFKeyGet(psGTIF, ProjFalseOriginLatGeoKey,
1798 : &dfNatOriginLat, 0, 1 ) == 0
1799 20 : && GTIFKeyGet(psGTIF, ProjCenterLatGeoKey,
1800 0 : &dfNatOriginLat, 0, 1 ) == 0 )
1801 0 : dfNatOriginLat = 0.0;
1802 :
1803 : /* notdef: should transform to decimal degrees at this point */
1804 :
1805 20 : psDefn->ProjParm[0] = dfNatOriginLat;
1806 20 : psDefn->ProjParmId[0] = ProjFalseOriginLatGeoKey;
1807 20 : psDefn->ProjParm[1] = dfNatOriginLong;
1808 20 : psDefn->ProjParmId[1] = ProjFalseOriginLongGeoKey;
1809 20 : psDefn->ProjParm[2] = dfStdParallel1;
1810 20 : psDefn->ProjParmId[2] = ProjStdParallel1GeoKey;
1811 20 : psDefn->ProjParm[3] = dfStdParallel2;
1812 20 : psDefn->ProjParmId[3] = ProjStdParallel2GeoKey;
1813 20 : psDefn->ProjParm[5] = dfFalseEasting;
1814 20 : psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
1815 20 : psDefn->ProjParm[6] = dfFalseNorthing;
1816 20 : psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
1817 :
1818 20 : psDefn->nParms = 7;
1819 20 : break;
1820 :
1821 : /* -------------------------------------------------------------------- */
1822 : case CT_AlbersEqualArea:
1823 : case CT_EquidistantConic:
1824 : /* -------------------------------------------------------------------- */
1825 2 : if( GTIFKeyGet(psGTIF, ProjStdParallel1GeoKey,
1826 : &dfStdParallel1, 0, 1 ) == 0 )
1827 0 : dfStdParallel1 = 0.0;
1828 :
1829 2 : if( GTIFKeyGet(psGTIF, ProjStdParallel2GeoKey,
1830 : &dfStdParallel2, 0, 1 ) == 0 )
1831 0 : dfStdParallel2 = 0.0;
1832 :
1833 2 : if( GTIFKeyGet(psGTIF, ProjNatOriginLongGeoKey,
1834 : &dfNatOriginLong, 0, 1 ) == 0
1835 2 : && GTIFKeyGet(psGTIF, ProjFalseOriginLongGeoKey,
1836 : &dfNatOriginLong, 0, 1 ) == 0
1837 0 : && GTIFKeyGet(psGTIF, ProjCenterLongGeoKey,
1838 0 : &dfNatOriginLong, 0, 1 ) == 0 )
1839 0 : dfNatOriginLong = 0.0;
1840 :
1841 2 : if( GTIFKeyGet(psGTIF, ProjNatOriginLatGeoKey,
1842 : &dfNatOriginLat, 0, 1 ) == 0
1843 2 : && GTIFKeyGet(psGTIF, ProjFalseOriginLatGeoKey,
1844 : &dfNatOriginLat, 0, 1 ) == 0
1845 0 : && GTIFKeyGet(psGTIF, ProjCenterLatGeoKey,
1846 0 : &dfNatOriginLat, 0, 1 ) == 0 )
1847 0 : dfNatOriginLat = 0.0;
1848 :
1849 : /* notdef: should transform to decimal degrees at this point */
1850 :
1851 2 : psDefn->ProjParm[0] = dfStdParallel1;
1852 2 : psDefn->ProjParmId[0] = ProjStdParallel1GeoKey;
1853 2 : psDefn->ProjParm[1] = dfStdParallel2;
1854 2 : psDefn->ProjParmId[1] = ProjStdParallel2GeoKey;
1855 2 : psDefn->ProjParm[2] = dfNatOriginLat;
1856 2 : psDefn->ProjParmId[2] = ProjNatOriginLatGeoKey;
1857 2 : psDefn->ProjParm[3] = dfNatOriginLong;
1858 2 : psDefn->ProjParmId[3] = ProjNatOriginLongGeoKey;
1859 2 : psDefn->ProjParm[5] = dfFalseEasting;
1860 2 : psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
1861 2 : psDefn->ProjParm[6] = dfFalseNorthing;
1862 2 : psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
1863 :
1864 2 : psDefn->nParms = 7;
1865 2 : break;
1866 :
1867 : /* -------------------------------------------------------------------- */
1868 : case CT_CylindricalEqualArea:
1869 : /* -------------------------------------------------------------------- */
1870 6 : if( GTIFKeyGet(psGTIF, ProjStdParallel1GeoKey,
1871 : &dfStdParallel1, 0, 1 ) == 0 )
1872 0 : dfStdParallel1 = 0.0;
1873 :
1874 6 : if( GTIFKeyGet(psGTIF, ProjNatOriginLongGeoKey,
1875 : &dfNatOriginLong, 0, 1 ) == 0
1876 6 : && GTIFKeyGet(psGTIF, ProjFalseOriginLongGeoKey,
1877 : &dfNatOriginLong, 0, 1 ) == 0
1878 0 : && GTIFKeyGet(psGTIF, ProjCenterLongGeoKey,
1879 0 : &dfNatOriginLong, 0, 1 ) == 0 )
1880 0 : dfNatOriginLong = 0.0;
1881 :
1882 : /* notdef: should transform to decimal degrees at this point */
1883 :
1884 6 : psDefn->ProjParm[0] = dfStdParallel1;
1885 6 : psDefn->ProjParmId[0] = ProjStdParallel1GeoKey;
1886 6 : psDefn->ProjParm[1] = dfNatOriginLong;
1887 6 : psDefn->ProjParmId[1] = ProjNatOriginLongGeoKey;
1888 6 : psDefn->ProjParm[5] = dfFalseEasting;
1889 6 : psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
1890 6 : psDefn->ProjParm[6] = dfFalseNorthing;
1891 6 : psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
1892 :
1893 6 : psDefn->nParms = 7;
1894 : break;
1895 : }
1896 :
1897 : /* -------------------------------------------------------------------- */
1898 : /* Normalize any linear parameters into meters. In GeoTIFF */
1899 : /* the linear projection parameter tags are normally in the */
1900 : /* units of the coordinate system described. */
1901 : /* -------------------------------------------------------------------- */
1902 768 : for( iParm = 0; iParm < psDefn->nParms; iParm++ )
1903 : {
1904 672 : switch( psDefn->ProjParmId[iParm] )
1905 : {
1906 : case ProjFalseEastingGeoKey:
1907 : case ProjFalseNorthingGeoKey:
1908 : case ProjFalseOriginEastingGeoKey:
1909 : case ProjFalseOriginNorthingGeoKey:
1910 : case ProjCenterEastingGeoKey:
1911 : case ProjCenterNorthingGeoKey:
1912 384 : if( psDefn->UOMLengthInMeters != 0
1913 384 : && psDefn->UOMLengthInMeters != 1.0 )
1914 : {
1915 40 : psDefn->ProjParm[iParm] *= psDefn->UOMLengthInMeters;
1916 : }
1917 : break;
1918 :
1919 : default:
1920 : break;
1921 : }
1922 : }
1923 96 : }
1924 :
1925 : /************************************************************************/
1926 : /* GTIFGetDefn() */
1927 : /************************************************************************/
1928 :
1929 : /**
1930 : @param psGTIF GeoTIFF information handle as returned by GTIFNew.
1931 : @param psDefn Pointer to an existing GTIFDefn structure. This structure
1932 : does not need to have been pre-initialized at all.
1933 :
1934 : @return TRUE if the function has been successful, otherwise FALSE.
1935 :
1936 : This function reads the coordinate system definition from a GeoTIFF file,
1937 : and <i>normalizes</i> it into a set of component information using
1938 : definitions from CSV (Comma Seperated Value ASCII) files derived from
1939 : EPSG tables. This function is intended to simplify correct support for
1940 : reading files with defined PCS (Projected Coordinate System) codes that
1941 : wouldn't otherwise be directly known by application software by reducing
1942 : it to the underlying projection method, parameters, datum, ellipsoid,
1943 : prime meridian and units.<p>
1944 :
1945 : The application should pass a pointer to an existing uninitialized
1946 : GTIFDefn structure, and GTIFGetDefn() will fill it in. The fuction
1947 : currently always returns TRUE but in the future will return FALSE if
1948 : CSV files are not found. In any event, all geokeys actually found in the
1949 : file will be copied into the GTIFDefn. However, if the CSV files aren't
1950 : found codes implied by other codes will not be set properly.<p>
1951 :
1952 : GTIFGetDefn() will not generally work if the EPSG derived CSV files cannot
1953 : be found. By default a modest attempt will be made to find them, but
1954 : in general it is necessary for the calling application to override the
1955 : logic to find them. This can be done by calling the
1956 : SetCSVFilenameHook() function to
1957 : override the search method based on application knowledge of where they are
1958 : found.<p>
1959 :
1960 : The normalization methodology operates by fetching tags from the GeoTIFF
1961 : file, and then setting all other tags implied by them in the structure. The
1962 : implied relationships are worked out by reading definitions from the
1963 : various EPSG derived CSV tables.<p>
1964 :
1965 : For instance, if a PCS (ProjectedCSTypeGeoKey) is found in the GeoTIFF file
1966 : this code is used to lookup a record in the <tt>horiz_cs.csv</tt> CSV
1967 : file. For example given the PCS 26746 we can find the name
1968 : (NAD27 / California zone VI), the GCS 4257 (NAD27), and the ProjectionCode
1969 : 10406 (California CS27 zone VI). The GCS, and ProjectionCode can in turn
1970 : be looked up in other tables until all the details of units, ellipsoid,
1971 : prime meridian, datum, projection (LambertConfConic_2SP) and projection
1972 : parameters are established. A full listgeo dump of a file
1973 : for this result might look like the following, all based on a single PCS
1974 : value:<p>
1975 :
1976 : <pre>
1977 : % listgeo -norm ~/data/geotiff/pci_eg/spaf27.tif
1978 : Geotiff_Information:
1979 : Version: 1
1980 : Key_Revision: 1.0
1981 : Tagged_Information:
1982 : ModelTiepointTag (2,3):
1983 : 0 0 0
1984 : 1577139.71 634349.176 0
1985 : ModelPixelScaleTag (1,3):
1986 : 195.509321 198.32184 0
1987 : End_Of_Tags.
1988 : Keyed_Information:
1989 : GTModelTypeGeoKey (Short,1): ModelTypeProjected
1990 : GTRasterTypeGeoKey (Short,1): RasterPixelIsArea
1991 : ProjectedCSTypeGeoKey (Short,1): PCS_NAD27_California_VI
1992 : End_Of_Keys.
1993 : End_Of_Geotiff.
1994 :
1995 : PCS = 26746 (NAD27 / California zone VI)
1996 : Projection = 10406 (California CS27 zone VI)
1997 : Projection Method: CT_LambertConfConic_2SP
1998 : ProjStdParallel1GeoKey: 33.883333
1999 : ProjStdParallel2GeoKey: 32.766667
2000 : ProjFalseOriginLatGeoKey: 32.166667
2001 : ProjFalseOriginLongGeoKey: -116.233333
2002 : ProjFalseEastingGeoKey: 609601.219202
2003 : ProjFalseNorthingGeoKey: 0.000000
2004 : GCS: 4267/NAD27
2005 : Datum: 6267/North American Datum 1927
2006 : Ellipsoid: 7008/Clarke 1866 (6378206.40,6356583.80)
2007 : Prime Meridian: 8901/Greenwich (0.000000)
2008 : Projection Linear Units: 9003/US survey foot (0.304801m)
2009 : </pre>
2010 :
2011 : Note that GTIFGetDefn() does not inspect or return the tiepoints and scale.
2012 : This must be handled seperately as it normally would. It is intended to
2013 : simplify capture and normalization of the coordinate system definition.
2014 : Note that GTIFGetDefn() also does the following things:
2015 :
2016 : <ol>
2017 : <li> Convert all angular values to decimal degrees.
2018 : <li> Convert all linear values to meters.
2019 : <li> Return the linear units and conversion to meters for the tiepoints and
2020 : scale (though the tiepoints and scale remain in their native units).
2021 : <li> When reading projection parameters a variety of differences between
2022 : different GeoTIFF generators are handled, and a normalized set of parameters
2023 : for each projection are always returned.
2024 : </ol>
2025 :
2026 : Code fields in the GTIFDefn are filled with KvUserDefined if there is not
2027 : value to assign. The parameter lists for each of the underlying projection
2028 : transform methods can be found at the
2029 : <a href="http://www.remotesensing.org/geotiff/proj_list">Projections</a>
2030 : page. Note that nParms will be set based on the maximum parameter used.
2031 : Some of the parameters may not be used in which case the
2032 : GTIFDefn::ProjParmId[] will
2033 : be zero. This is done to retain correspondence to the EPSG parameter
2034 : numbering scheme.<p>
2035 :
2036 : The
2037 : <a href="http://www.remotesensing.org/cgi-bin/cvsweb.cgi/~checkout~/osrs/geotiff/libgeotiff/geotiff_proj4.c">geotiff_proj4.c</a> module distributed with libgeotiff can
2038 : be used as an example of code that converts a GTIFDefn into another projection
2039 : system.<p>
2040 :
2041 : @see GTIFKeySet(), SetCSVFilenameHook()
2042 :
2043 : */
2044 :
2045 2074 : int GTIFGetDefn( GTIF * psGTIF, GTIFDefn * psDefn )
2046 :
2047 : {
2048 : int i;
2049 : short nGeogUOMLinear;
2050 : double dfInvFlattening;
2051 :
2052 : /* -------------------------------------------------------------------- */
2053 : /* Initially we default all the information we can. */
2054 : /* -------------------------------------------------------------------- */
2055 2074 : psDefn->DefnSet = 1;
2056 2074 : psDefn->Model = KvUserDefined;
2057 2074 : psDefn->PCS = KvUserDefined;
2058 2074 : psDefn->GCS = KvUserDefined;
2059 2074 : psDefn->UOMLength = KvUserDefined;
2060 2074 : psDefn->UOMLengthInMeters = 1.0;
2061 2074 : psDefn->UOMAngle = KvUserDefined;
2062 2074 : psDefn->UOMAngleInDegrees = 1.0;
2063 2074 : psDefn->Datum = KvUserDefined;
2064 2074 : psDefn->Ellipsoid = KvUserDefined;
2065 2074 : psDefn->SemiMajor = 0.0;
2066 2074 : psDefn->SemiMinor = 0.0;
2067 2074 : psDefn->PM = KvUserDefined;
2068 2074 : psDefn->PMLongToGreenwich = 0.0;
2069 2074 : psDefn->TOWGS84Count = 0;
2070 2074 : memset( psDefn->TOWGS84, 0, sizeof(psDefn->TOWGS84) );
2071 :
2072 2074 : psDefn->ProjCode = KvUserDefined;
2073 2074 : psDefn->Projection = KvUserDefined;
2074 2074 : psDefn->CTProjection = KvUserDefined;
2075 :
2076 2074 : psDefn->nParms = 0;
2077 22814 : for( i = 0; i < MAX_GTIF_PROJPARMS; i++ )
2078 : {
2079 20740 : psDefn->ProjParm[i] = 0.0;
2080 20740 : psDefn->ProjParmId[i] = 0;
2081 : }
2082 :
2083 2074 : psDefn->MapSys = KvUserDefined;
2084 2074 : psDefn->Zone = 0;
2085 :
2086 : /* -------------------------------------------------------------------- */
2087 : /* Do we have any geokeys? */
2088 : /* -------------------------------------------------------------------- */
2089 : {
2090 2074 : int nKeyCount = 0;
2091 : int anVersion[3];
2092 2074 : GTIFDirectoryInfo( psGTIF, anVersion, &nKeyCount );
2093 :
2094 2074 : if( nKeyCount == 0 )
2095 : {
2096 80 : psDefn->DefnSet = 0;
2097 80 : return FALSE;
2098 : }
2099 : }
2100 :
2101 : /* -------------------------------------------------------------------- */
2102 : /* Try to get the overall model type. */
2103 : /* -------------------------------------------------------------------- */
2104 1994 : GTIFKeyGet(psGTIF,GTModelTypeGeoKey,&(psDefn->Model),0,1);
2105 :
2106 : /* -------------------------------------------------------------------- */
2107 : /* Extract the Geog units. */
2108 : /* -------------------------------------------------------------------- */
2109 1994 : nGeogUOMLinear = 9001; /* Linear_Meter */
2110 1994 : GTIFKeyGet(psGTIF, GeogLinearUnitsGeoKey, &nGeogUOMLinear, 0, 1 );
2111 :
2112 : /* -------------------------------------------------------------------- */
2113 : /* Try to get a PCS. */
2114 : /* -------------------------------------------------------------------- */
2115 5136 : if( GTIFKeyGet(psGTIF,ProjectedCSTypeGeoKey, &(psDefn->PCS),0,1) == 1
2116 3142 : && psDefn->PCS != KvUserDefined )
2117 : {
2118 : /*
2119 : * Translate this into useful information.
2120 : */
2121 1048 : GTIFGetPCSInfo( psDefn->PCS, NULL, &(psDefn->ProjCode),
2122 : &(psDefn->UOMLength), &(psDefn->GCS) );
2123 : }
2124 :
2125 : /* -------------------------------------------------------------------- */
2126 : /* If we have the PCS code, but didn't find it in the CSV files */
2127 : /* (likely because we can't find them) we will try some ``jiffy */
2128 : /* rules'' for UTM and state plane. */
2129 : /* -------------------------------------------------------------------- */
2130 1994 : if( psDefn->PCS != KvUserDefined && psDefn->ProjCode == KvUserDefined )
2131 : {
2132 : int nMapSys, nZone;
2133 0 : int nGCS = psDefn->GCS;
2134 :
2135 0 : nMapSys = GTIFPCSToMapSys( psDefn->PCS, &nGCS, &nZone );
2136 0 : if( nMapSys != KvUserDefined )
2137 : {
2138 0 : psDefn->ProjCode = (short) GTIFMapSysToProj( nMapSys, nZone );
2139 0 : psDefn->GCS = (short) nGCS;
2140 : }
2141 : }
2142 :
2143 : /* -------------------------------------------------------------------- */
2144 : /* If the Proj_ code is specified directly, use that. */
2145 : /* -------------------------------------------------------------------- */
2146 1994 : if( psDefn->ProjCode == KvUserDefined )
2147 946 : GTIFKeyGet(psGTIF, ProjectionGeoKey, &(psDefn->ProjCode), 0, 1 );
2148 :
2149 1994 : if( psDefn->ProjCode != KvUserDefined )
2150 : {
2151 : /*
2152 : * We have an underlying projection transformation value. Look
2153 : * this up. For a PCS of ``WGS 84 / UTM 11'' the transformation
2154 : * would be Transverse Mercator, with a particular set of options.
2155 : * The nProjTRFCode itself would correspond to the name
2156 : * ``UTM zone 11N'', and doesn't include datum info.
2157 : */
2158 1052 : GTIFGetProjTRFInfo( psDefn->ProjCode, NULL, &(psDefn->Projection),
2159 : psDefn->ProjParm );
2160 :
2161 : /*
2162 : * Set the GeoTIFF identity of the parameters.
2163 : */
2164 1052 : psDefn->CTProjection = (short)
2165 1052 : EPSGProjMethodToCTProjMethod( psDefn->Projection );
2166 :
2167 1052 : SetGTParmIds( psDefn->CTProjection, psDefn->ProjParmId, NULL);
2168 1052 : psDefn->nParms = 7;
2169 : }
2170 :
2171 : /* -------------------------------------------------------------------- */
2172 : /* Try to get a GCS. If found, it will override any implied by */
2173 : /* the PCS. */
2174 : /* -------------------------------------------------------------------- */
2175 1994 : GTIFKeyGet(psGTIF, GeographicTypeGeoKey, &(psDefn->GCS), 0, 1 );
2176 1994 : if( psDefn->GCS < 1 || psDefn->GCS >= KvUserDefined )
2177 66 : psDefn->GCS = KvUserDefined;
2178 :
2179 : /* -------------------------------------------------------------------- */
2180 : /* Derive the datum, and prime meridian from the GCS. */
2181 : /* -------------------------------------------------------------------- */
2182 1994 : if( psDefn->GCS != KvUserDefined )
2183 : {
2184 1928 : GTIFGetGCSInfo( psDefn->GCS, NULL, &(psDefn->Datum), &(psDefn->PM),
2185 : &(psDefn->UOMAngle) );
2186 : }
2187 :
2188 : /* -------------------------------------------------------------------- */
2189 : /* Handle the GCS angular units. GeogAngularUnitsGeoKey */
2190 : /* overrides the GCS or PCS setting. */
2191 : /* -------------------------------------------------------------------- */
2192 1994 : GTIFKeyGet(psGTIF, GeogAngularUnitsGeoKey, &(psDefn->UOMAngle), 0, 1 );
2193 1994 : if( psDefn->UOMAngle != KvUserDefined )
2194 : {
2195 1988 : GTIFGetUOMAngleInfo( psDefn->UOMAngle, NULL,
2196 : &(psDefn->UOMAngleInDegrees) );
2197 : }
2198 :
2199 : /* -------------------------------------------------------------------- */
2200 : /* Check for a datum setting, and then use the datum to derive */
2201 : /* an ellipsoid. */
2202 : /* -------------------------------------------------------------------- */
2203 1994 : GTIFKeyGet(psGTIF, GeogGeodeticDatumGeoKey, &(psDefn->Datum), 0, 1 );
2204 :
2205 1994 : if( psDefn->Datum != KvUserDefined )
2206 : {
2207 1932 : GTIFGetDatumInfo( psDefn->Datum, NULL, &(psDefn->Ellipsoid) );
2208 : }
2209 :
2210 : /* -------------------------------------------------------------------- */
2211 : /* Check for an explicit ellipsoid. Use the ellipsoid to */
2212 : /* derive the ellipsoid characteristics, if possible. */
2213 : /* -------------------------------------------------------------------- */
2214 1994 : GTIFKeyGet(psGTIF, GeogEllipsoidGeoKey, &(psDefn->Ellipsoid), 0, 1 );
2215 :
2216 1994 : if( psDefn->Ellipsoid != KvUserDefined )
2217 : {
2218 1932 : GTIFGetEllipsoidInfo( psDefn->Ellipsoid, NULL,
2219 : &(psDefn->SemiMajor), &(psDefn->SemiMinor) );
2220 : }
2221 :
2222 : /* -------------------------------------------------------------------- */
2223 : /* Check for overridden ellipsoid parameters. It would be nice */
2224 : /* to warn if they conflict with provided information, but for */
2225 : /* now we just override. */
2226 : /* -------------------------------------------------------------------- */
2227 1994 : GTIFKeyGet(psGTIF, GeogSemiMajorAxisGeoKey, &(psDefn->SemiMajor), 0, 1 );
2228 1994 : GTIFKeyGet(psGTIF, GeogSemiMinorAxisGeoKey, &(psDefn->SemiMinor), 0, 1 );
2229 :
2230 1994 : if( GTIFKeyGet(psGTIF, GeogInvFlatteningGeoKey, &dfInvFlattening,
2231 : 0, 1 ) == 1 )
2232 : {
2233 860 : if( dfInvFlattening != 0.0 )
2234 860 : psDefn->SemiMinor =
2235 860 : psDefn->SemiMajor * (1 - 1.0/dfInvFlattening);
2236 : else
2237 0 : psDefn->SemiMinor = psDefn->SemiMajor;
2238 : }
2239 :
2240 : /* -------------------------------------------------------------------- */
2241 : /* Get the prime meridian info. */
2242 : /* -------------------------------------------------------------------- */
2243 1994 : GTIFKeyGet(psGTIF, GeogPrimeMeridianGeoKey, &(psDefn->PM), 0, 1 );
2244 :
2245 1994 : if( psDefn->PM != KvUserDefined )
2246 : {
2247 1928 : GTIFGetPMInfo( psDefn->PM, NULL, &(psDefn->PMLongToGreenwich) );
2248 : }
2249 : else
2250 : {
2251 66 : GTIFKeyGet(psGTIF, GeogPrimeMeridianLongGeoKey,
2252 66 : &(psDefn->PMLongToGreenwich), 0, 1 );
2253 :
2254 66 : psDefn->PMLongToGreenwich =
2255 66 : GTIFAngleToDD( psDefn->PMLongToGreenwich,
2256 66 : psDefn->UOMAngle );
2257 : }
2258 :
2259 : /* -------------------------------------------------------------------- */
2260 : /* Get the TOWGS84 parameters. */
2261 : /* -------------------------------------------------------------------- */
2262 1994 : psDefn->TOWGS84Count =
2263 1994 : GTIFKeyGet(psGTIF, GeogTOWGS84GeoKey, &(psDefn->TOWGS84), 0, 7 );
2264 :
2265 : /* -------------------------------------------------------------------- */
2266 : /* Have the projection units of measure been overridden? We */
2267 : /* should likely be doing something about angular units too, */
2268 : /* but these are very rarely not decimal degrees for actual */
2269 : /* file coordinates. */
2270 : /* -------------------------------------------------------------------- */
2271 1994 : GTIFKeyGet(psGTIF,ProjLinearUnitsGeoKey,&(psDefn->UOMLength),0,1);
2272 :
2273 1994 : if( psDefn->UOMLength != KvUserDefined )
2274 : {
2275 1150 : GTIFGetUOMLengthInfo( psDefn->UOMLength, NULL,
2276 : &(psDefn->UOMLengthInMeters) );
2277 : }
2278 :
2279 : /* -------------------------------------------------------------------- */
2280 : /* Handle a variety of user defined transform types. */
2281 : /* -------------------------------------------------------------------- */
2282 1994 : if( GTIFKeyGet(psGTIF,ProjCoordTransGeoKey,
2283 1994 : &(psDefn->CTProjection),0,1) == 1)
2284 : {
2285 96 : GTIFFetchProjParms( psGTIF, psDefn );
2286 : }
2287 :
2288 : /* -------------------------------------------------------------------- */
2289 : /* Try to set the zoned map system information. */
2290 : /* -------------------------------------------------------------------- */
2291 1994 : psDefn->MapSys = GTIFProjToMapSys( psDefn->ProjCode, &(psDefn->Zone) );
2292 :
2293 : /* -------------------------------------------------------------------- */
2294 : /* If this is UTM, and we were unable to extract the projection */
2295 : /* parameters from the CSV file, just set them directly now, */
2296 : /* since it's pretty easy, and a common case. */
2297 : /* -------------------------------------------------------------------- */
2298 3988 : if( (psDefn->MapSys == MapSys_UTM_North
2299 2990 : || psDefn->MapSys == MapSys_UTM_South)
2300 998 : && psDefn->CTProjection == KvUserDefined )
2301 : {
2302 0 : psDefn->CTProjection = CT_TransverseMercator;
2303 0 : psDefn->nParms = 7;
2304 0 : psDefn->ProjParmId[0] = ProjNatOriginLatGeoKey;
2305 0 : psDefn->ProjParm[0] = 0.0;
2306 :
2307 0 : psDefn->ProjParmId[1] = ProjNatOriginLongGeoKey;
2308 0 : psDefn->ProjParm[1] = psDefn->Zone*6 - 183.0;
2309 :
2310 0 : psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey;
2311 0 : psDefn->ProjParm[4] = 0.9996;
2312 :
2313 0 : psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
2314 0 : psDefn->ProjParm[5] = 500000.0;
2315 :
2316 0 : psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
2317 :
2318 0 : if( psDefn->MapSys == MapSys_UTM_North )
2319 0 : psDefn->ProjParm[6] = 0.0;
2320 : else
2321 0 : psDefn->ProjParm[6] = 10000000.0;
2322 : }
2323 :
2324 1994 : return TRUE;
2325 : }
2326 :
2327 : /************************************************************************/
2328 : /* GTIFDecToDMS() */
2329 : /* */
2330 : /* Convenient function to translate decimal degrees to DMS */
2331 : /* format for reporting to a user. */
2332 : /************************************************************************/
2333 :
2334 0 : const char *GTIFDecToDMS( double dfAngle, const char * pszAxis,
2335 : int nPrecision )
2336 :
2337 : {
2338 : int nDegrees, nMinutes;
2339 : double dfSeconds;
2340 : char szFormat[30];
2341 : static char szBuffer[50];
2342 0 : const char *pszHemisphere = NULL;
2343 : double dfRound;
2344 : int i;
2345 :
2346 0 : dfRound = 0.5/60;
2347 0 : for( i = 0; i < nPrecision; i++ )
2348 0 : dfRound = dfRound * 0.1;
2349 :
2350 0 : nDegrees = (int) ABS(dfAngle);
2351 0 : nMinutes = (int) ((ABS(dfAngle) - nDegrees) * 60 + dfRound);
2352 0 : dfSeconds = ABS((ABS(dfAngle) * 3600 - nDegrees*3600 - nMinutes*60));
2353 :
2354 0 : if( EQUAL(pszAxis,"Long") && dfAngle < 0.0 )
2355 0 : pszHemisphere = "W";
2356 0 : else if( EQUAL(pszAxis,"Long") )
2357 0 : pszHemisphere = "E";
2358 0 : else if( dfAngle < 0.0 )
2359 0 : pszHemisphere = "S";
2360 : else
2361 0 : pszHemisphere = "N";
2362 :
2363 0 : sprintf( szFormat, "%%3dd%%2d\'%%%d.%df\"%s",
2364 : nPrecision+3, nPrecision, pszHemisphere );
2365 0 : sprintf( szBuffer, szFormat, nDegrees, nMinutes, dfSeconds );
2366 :
2367 0 : return( szBuffer );
2368 : }
2369 :
2370 : /************************************************************************/
2371 : /* GTIFPrintDefn() */
2372 : /* */
2373 : /* Report the contents of a GTIFDefn structure ... mostly for */
2374 : /* debugging. */
2375 : /************************************************************************/
2376 :
2377 0 : void GTIFPrintDefn( GTIFDefn * psDefn, FILE * fp )
2378 :
2379 : {
2380 : /* -------------------------------------------------------------------- */
2381 : /* Do we have anything to report? */
2382 : /* -------------------------------------------------------------------- */
2383 0 : if( !psDefn->DefnSet )
2384 : {
2385 0 : fprintf( fp, "No GeoKeys found.\n" );
2386 0 : return;
2387 : }
2388 :
2389 : /* -------------------------------------------------------------------- */
2390 : /* Get the PCS name if possible. */
2391 : /* -------------------------------------------------------------------- */
2392 0 : if( psDefn->PCS != KvUserDefined )
2393 : {
2394 0 : char *pszPCSName = NULL;
2395 :
2396 0 : GTIFGetPCSInfo( psDefn->PCS, &pszPCSName, NULL, NULL, NULL );
2397 0 : if( pszPCSName == NULL )
2398 0 : pszPCSName = CPLStrdup("name unknown");
2399 :
2400 0 : fprintf( fp, "PCS = %d (%s)\n", psDefn->PCS, pszPCSName );
2401 0 : CPLFree( pszPCSName );
2402 : }
2403 :
2404 : /* -------------------------------------------------------------------- */
2405 : /* Dump the projection code if possible. */
2406 : /* -------------------------------------------------------------------- */
2407 0 : if( psDefn->ProjCode != KvUserDefined )
2408 : {
2409 0 : char *pszTRFName = NULL;
2410 :
2411 0 : GTIFGetProjTRFInfo( psDefn->ProjCode, &pszTRFName, NULL, NULL );
2412 0 : if( pszTRFName == NULL )
2413 0 : pszTRFName = CPLStrdup("");
2414 :
2415 0 : fprintf( fp, "Projection = %d (%s)\n",
2416 0 : psDefn->ProjCode, pszTRFName );
2417 :
2418 0 : CPLFree( pszTRFName );
2419 : }
2420 :
2421 : /* -------------------------------------------------------------------- */
2422 : /* Try to dump the projection method name, and parameters if possible.*/
2423 : /* -------------------------------------------------------------------- */
2424 0 : if( psDefn->CTProjection != KvUserDefined )
2425 : {
2426 0 : char *pszName = GTIFValueName(ProjCoordTransGeoKey,
2427 0 : psDefn->CTProjection);
2428 : int i;
2429 :
2430 0 : if( pszName == NULL )
2431 0 : pszName = "(unknown)";
2432 :
2433 0 : fprintf( fp, "Projection Method: %s\n", pszName );
2434 :
2435 0 : for( i = 0; i < psDefn->nParms; i++ )
2436 : {
2437 0 : if( psDefn->ProjParmId[i] == 0 )
2438 0 : continue;
2439 :
2440 0 : pszName = GTIFKeyName((geokey_t) psDefn->ProjParmId[i]);
2441 0 : if( pszName == NULL )
2442 0 : pszName = "(unknown)";
2443 :
2444 0 : if( i < 4 )
2445 : {
2446 : char *pszAxisName;
2447 :
2448 0 : if( strstr(pszName,"Long") != NULL )
2449 0 : pszAxisName = "Long";
2450 0 : else if( strstr(pszName,"Lat") != NULL )
2451 0 : pszAxisName = "Lat";
2452 : else
2453 0 : pszAxisName = "?";
2454 :
2455 0 : fprintf( fp, " %s: %f (%s)\n",
2456 : pszName, psDefn->ProjParm[i],
2457 : GTIFDecToDMS( psDefn->ProjParm[i], pszAxisName, 2 ) );
2458 : }
2459 0 : else if( i == 4 )
2460 0 : fprintf( fp, " %s: %f\n", pszName, psDefn->ProjParm[i] );
2461 : else
2462 0 : fprintf( fp, " %s: %f m\n", pszName, psDefn->ProjParm[i] );
2463 : }
2464 : }
2465 :
2466 : /* -------------------------------------------------------------------- */
2467 : /* Report the GCS name, and number. */
2468 : /* -------------------------------------------------------------------- */
2469 0 : if( psDefn->GCS != KvUserDefined )
2470 : {
2471 0 : char *pszName = NULL;
2472 :
2473 0 : GTIFGetGCSInfo( psDefn->GCS, &pszName, NULL, NULL, NULL );
2474 0 : if( pszName == NULL )
2475 0 : pszName = CPLStrdup("(unknown)");
2476 :
2477 0 : fprintf( fp, "GCS: %d/%s\n", psDefn->GCS, pszName );
2478 0 : CPLFree( pszName );
2479 : }
2480 :
2481 : /* -------------------------------------------------------------------- */
2482 : /* Report the datum name. */
2483 : /* -------------------------------------------------------------------- */
2484 0 : if( psDefn->Datum != KvUserDefined )
2485 : {
2486 0 : char *pszName = NULL;
2487 :
2488 0 : GTIFGetDatumInfo( psDefn->Datum, &pszName, NULL );
2489 0 : if( pszName == NULL )
2490 0 : pszName = CPLStrdup("(unknown)");
2491 :
2492 0 : fprintf( fp, "Datum: %d/%s\n", psDefn->Datum, pszName );
2493 0 : CPLFree( pszName );
2494 : }
2495 :
2496 : /* -------------------------------------------------------------------- */
2497 : /* Report the ellipsoid. */
2498 : /* -------------------------------------------------------------------- */
2499 0 : if( psDefn->Ellipsoid != KvUserDefined )
2500 : {
2501 0 : char *pszName = NULL;
2502 :
2503 0 : GTIFGetEllipsoidInfo( psDefn->Ellipsoid, &pszName, NULL, NULL );
2504 0 : if( pszName == NULL )
2505 0 : pszName = CPLStrdup("(unknown)");
2506 :
2507 0 : fprintf( fp, "Ellipsoid: %d/%s (%.2f,%.2f)\n",
2508 0 : psDefn->Ellipsoid, pszName,
2509 : psDefn->SemiMajor, psDefn->SemiMinor );
2510 0 : CPLFree( pszName );
2511 : }
2512 :
2513 : /* -------------------------------------------------------------------- */
2514 : /* Report the prime meridian. */
2515 : /* -------------------------------------------------------------------- */
2516 0 : if( psDefn->PM != KvUserDefined )
2517 : {
2518 0 : char *pszName = NULL;
2519 :
2520 0 : GTIFGetPMInfo( psDefn->PM, &pszName, NULL );
2521 :
2522 0 : if( pszName == NULL )
2523 0 : pszName = CPLStrdup("(unknown)");
2524 :
2525 0 : fprintf( fp, "Prime Meridian: %d/%s (%f/%s)\n",
2526 0 : psDefn->PM, pszName,
2527 : psDefn->PMLongToGreenwich,
2528 : GTIFDecToDMS( psDefn->PMLongToGreenwich, "Long", 2 ) );
2529 0 : CPLFree( pszName );
2530 : }
2531 :
2532 : /* -------------------------------------------------------------------- */
2533 : /* Report TOWGS84 parameters. */
2534 : /* -------------------------------------------------------------------- */
2535 0 : if( psDefn->TOWGS84Count > 0 )
2536 : {
2537 : int i;
2538 :
2539 0 : fprintf( fp, "TOWGS84: " );
2540 :
2541 0 : for( i = 0; i < psDefn->TOWGS84Count; i++ )
2542 : {
2543 0 : if( i > 0 )
2544 0 : fprintf( fp, "," );
2545 0 : fprintf( fp, "%g", psDefn->TOWGS84[i] );
2546 : }
2547 :
2548 0 : fprintf( fp, "\n" );
2549 : }
2550 :
2551 : /* -------------------------------------------------------------------- */
2552 : /* Report the projection units of measure (currently just */
2553 : /* linear). */
2554 : /* -------------------------------------------------------------------- */
2555 0 : if( psDefn->UOMLength != KvUserDefined )
2556 : {
2557 0 : char *pszName = NULL;
2558 :
2559 0 : GTIFGetUOMLengthInfo( psDefn->UOMLength, &pszName, NULL );
2560 0 : if( pszName == NULL )
2561 0 : pszName = CPLStrdup( "(unknown)" );
2562 :
2563 0 : fprintf( fp, "Projection Linear Units: %d/%s (%fm)\n",
2564 0 : psDefn->UOMLength, pszName, psDefn->UOMLengthInMeters );
2565 0 : CPLFree( pszName );
2566 : }
2567 : }
2568 :
2569 : /************************************************************************/
2570 : /* GTIFFreeMemory() */
2571 : /* */
2572 : /* Externally visible function to free memory allocated within */
2573 : /* geo_normalize.c. */
2574 : /************************************************************************/
2575 :
2576 13190 : void GTIFFreeMemory( char * pMemory )
2577 :
2578 : {
2579 13190 : if( pMemory != NULL )
2580 13182 : VSIFree( pMemory );
2581 13190 : }
2582 :
2583 : /************************************************************************/
2584 : /* GTIFDeaccessCSV() */
2585 : /* */
2586 : /* Free all cached CSV info. */
2587 : /************************************************************************/
2588 :
2589 1057 : void GTIFDeaccessCSV()
2590 :
2591 : {
2592 1057 : CSVDeaccess( NULL );
2593 1057 : }
|