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