Line data Source code
1 : /******************************************************************************
2 : * $Id$
3 : *
4 : * Project: libgeotiff
5 : * Purpose: Code to normalize PCS and other composite codes in a GeoTIFF file.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 1999, Frank Warmerdam
10 : * Copyright (c) 2018, Even Rouault <even.rouault at spatialys.com>
11 : *
12 : * Permission is hereby granted, free of charge, to any person obtaining a
13 : * copy of this software and associated documentation files (the "Software"),
14 : * to deal in the Software without restriction, including without limitation
15 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16 : * and/or sell copies of the Software, and to permit persons to whom the
17 : * Software is furnished to do so, subject to the following conditions:
18 : *
19 : * The above copyright notice and this permission notice shall be included
20 : * in all copies or substantial portions of the Software.
21 : *
22 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28 : * DEALINGS IN THE SOFTWARE.
29 : *****************************************************************************/
30 :
31 : #include <assert.h>
32 : #include <stdio.h>
33 : #include <stdlib.h>
34 : #include <string.h>
35 :
36 : #include "cpl_serv.h"
37 : #include "geo_tiffp.h"
38 : #include "geovalues.h"
39 : #include "geo_normalize.h"
40 : #include "geo_keyp.h"
41 :
42 : #include "proj.h"
43 :
44 : #ifndef KvUserDefined
45 : # define KvUserDefined 32767
46 : #endif
47 :
48 : #ifndef M_PI
49 : # define M_PI 3.14159265358979323846
50 : #endif
51 :
52 : /* EPSG Codes for projection parameters. Unfortunately, these bear no
53 : relationship to the GeoTIFF codes even though the names are so similar. */
54 :
55 : #define EPSGNatOriginLat 8801
56 : #define EPSGNatOriginLong 8802
57 : #define EPSGNatOriginScaleFactor 8805
58 : #define EPSGFalseEasting 8806
59 : #define EPSGFalseNorthing 8807
60 : #define EPSGProjCenterLat 8811
61 : #define EPSGProjCenterLong 8812
62 : #define EPSGAzimuth 8813
63 : #define EPSGAngleRectifiedToSkewedGrid 8814
64 : #define EPSGInitialLineScaleFactor 8815
65 : #define EPSGProjCenterEasting 8816
66 : #define EPSGProjCenterNorthing 8817
67 : #define EPSGPseudoStdParallelLat 8818
68 : #define EPSGPseudoStdParallelScaleFactor 8819
69 : #define EPSGFalseOriginLat 8821
70 : #define EPSGFalseOriginLong 8822
71 : #define EPSGStdParallel1Lat 8823
72 : #define EPSGStdParallel2Lat 8824
73 : #define EPSGFalseOriginEasting 8826
74 : #define EPSGFalseOriginNorthing 8827
75 : #define EPSGSphericalOriginLat 8828
76 : #define EPSGSphericalOriginLong 8829
77 : #define EPSGInitialLongitude 8830
78 : #define EPSGZoneWidth 8831
79 : #define EPSGLatOfStdParallel 8832
80 : #define EPSGOriginLong 8833
81 : #define EPSGTopocentricOriginLat 8834
82 : #define EPSGTopocentricOriginLong 8835
83 : #define EPSGTopocentricOriginHeight 8836
84 :
85 : #define CT_Ext_Mercator_2SP -CT_Mercator
86 :
87 : #ifndef CPL_INLINE
88 : # if (defined(__GNUC__) && !defined(__NO_INLINE__)) || defined(_MSC_VER)
89 : # define HAS_CPL_INLINE 1
90 : # define CPL_INLINE __inline
91 : # elif defined(__SUNPRO_CC)
92 : # define HAS_CPL_INLINE 1
93 : # define CPL_INLINE inline
94 : # else
95 : # define CPL_INLINE
96 : # endif
97 : #endif
98 :
99 : #ifndef CPL_UNUSED
100 : #if defined(__GNUC__) && __GNUC__ >= 4
101 : # define CPL_UNUSED __attribute((__unused__))
102 : #else
103 : /* TODO: add cases for other compilers */
104 : # define CPL_UNUSED
105 : #endif
106 : #endif
107 :
108 13556 : CPL_INLINE static void CPL_IGNORE_RET_VAL_INT(CPL_UNUSED int unused) {}
109 :
110 : /************************************************************************/
111 : /* GTIFKeyGetSSHORT() */
112 : /************************************************************************/
113 :
114 : // Geotiff SHORT keys are supposed to be unsigned, but geo_normalize interface
115 : // uses signed short...
116 57597 : static int GTIFKeyGetSSHORT( GTIF *gtif, geokey_t key, short* pnVal )
117 : {
118 : unsigned short sVal;
119 57597 : if( GTIFKeyGetSHORT(gtif, key, &sVal, 0, 1) == 1 )
120 : {
121 17696 : memcpy(pnVal, &sVal, 2);
122 17696 : return 1;
123 : }
124 39901 : return 0;
125 : }
126 :
127 : /************************************************************************/
128 : /* GTIFGetPCSInfo() */
129 : /************************************************************************/
130 :
131 5894 : int GTIFGetPCSInfoEx( void* ctxIn,
132 : int nPCSCode, char **ppszEPSGName,
133 : short *pnProjOp, short *pnUOMLengthCode,
134 : short *pnGeogCS )
135 :
136 : {
137 : int nDatum;
138 : int nZone;
139 :
140 : /* Deal with a few well known CRS */
141 5894 : int Proj = GTIFPCSToMapSys( nPCSCode, &nDatum, &nZone );
142 5894 : if ((Proj == MapSys_UTM_North || Proj == MapSys_UTM_South) &&
143 5570 : nDatum != KvUserDefined)
144 : {
145 5570 : const char* pszDatumName = NULL;
146 5570 : switch (nDatum)
147 : {
148 5264 : case GCS_NAD27: pszDatumName = "NAD27"; break;
149 10 : case GCS_NAD83: pszDatumName = "NAD83"; break;
150 0 : case GCS_WGS_72: pszDatumName = "WGS 72"; break;
151 0 : case GCS_WGS_72BE: pszDatumName = "WGS 72BE"; break;
152 296 : case GCS_WGS_84: pszDatumName = "WGS 84"; break;
153 0 : default: break;
154 : }
155 :
156 5570 : if (pszDatumName)
157 : {
158 5570 : if (ppszEPSGName)
159 : {
160 : char szEPSGName[64];
161 2785 : snprintf(
162 : szEPSGName, sizeof(szEPSGName), "%s / UTM zone %d%c",
163 : pszDatumName, nZone,
164 : (Proj == MapSys_UTM_North) ? 'N' : 'S');
165 2785 : *ppszEPSGName = CPLStrdup(szEPSGName);
166 : }
167 :
168 5570 : if (pnProjOp)
169 2785 : *pnProjOp = (short) (((Proj == MapSys_UTM_North) ? Proj_UTM_zone_1N - 1 : Proj_UTM_zone_1S - 1) + nZone);
170 :
171 5570 : if (pnUOMLengthCode)
172 2785 : *pnUOMLengthCode = 9001; /* Linear_Meter */
173 :
174 5570 : if (pnGeogCS)
175 2785 : *pnGeogCS = (short) nDatum;
176 :
177 5570 : return TRUE;
178 : }
179 : }
180 :
181 324 : if( nPCSCode == KvUserDefined )
182 0 : return FALSE;
183 :
184 324 : PJ_CONTEXT* ctx = (PJ_CONTEXT*)ctxIn;
185 : {
186 : char szCode[12];
187 :
188 324 : snprintf(szCode, sizeof(szCode), "%d", nPCSCode);
189 324 : PJ* proj_crs = proj_create_from_database(
190 : ctx, "EPSG", szCode, PJ_CATEGORY_CRS, 0, NULL);
191 324 : if( !proj_crs )
192 : {
193 0 : return FALSE;
194 : }
195 :
196 324 : if( proj_get_type(proj_crs) != PJ_TYPE_PROJECTED_CRS )
197 : {
198 0 : proj_destroy(proj_crs);
199 0 : return FALSE;
200 : }
201 :
202 324 : if( ppszEPSGName )
203 : {
204 162 : const char* pszName = proj_get_name(proj_crs);
205 162 : if( !pszName )
206 : {
207 : // shouldn't happen
208 0 : proj_destroy(proj_crs);
209 0 : return FALSE;
210 : }
211 162 : *ppszEPSGName = CPLStrdup(pszName);
212 : }
213 :
214 324 : if( pnProjOp )
215 : {
216 162 : PJ* conversion = proj_crs_get_coordoperation(
217 : ctx, proj_crs);
218 162 : if( !conversion )
219 : {
220 : // shouldn't happen except out of memory
221 0 : proj_destroy(proj_crs);
222 0 : return FALSE;
223 : }
224 :
225 : {
226 162 : const char* pszConvCode = proj_get_id_code(conversion, 0);
227 162 : assert( pszConvCode );
228 162 : *pnProjOp = (short) atoi(pszConvCode);
229 : }
230 :
231 162 : proj_destroy(conversion);
232 : }
233 :
234 324 : if( pnUOMLengthCode )
235 : {
236 162 : PJ* coordSys = proj_crs_get_coordinate_system(
237 : ctx, proj_crs);
238 162 : if( !coordSys )
239 : {
240 : // shouldn't happen except out of memory
241 0 : proj_destroy(proj_crs);
242 0 : return FALSE;
243 : }
244 :
245 : {
246 162 : const char* pszUnitCode = NULL;
247 162 : if( !proj_cs_get_axis_info(
248 : ctx, coordSys, 0,
249 : NULL, /* name */
250 : NULL, /* abbreviation*/
251 : NULL, /* direction */
252 : NULL, /* conversion factor */
253 : NULL, /* unit name */
254 : NULL, /* unit auth name (should be EPSG) */
255 162 : &pszUnitCode) || pszUnitCode == NULL )
256 : {
257 0 : proj_destroy(coordSys);
258 0 : return FALSE;
259 : }
260 162 : *pnUOMLengthCode = (short) atoi(pszUnitCode);
261 162 : proj_destroy(coordSys);
262 : }
263 : }
264 :
265 324 : if( pnGeogCS )
266 : {
267 162 : PJ* geod_crs = proj_crs_get_geodetic_crs(ctx, proj_crs);
268 162 : if( !geod_crs )
269 : {
270 : // shouldn't happen except out of memory
271 0 : proj_destroy(proj_crs);
272 0 : return FALSE;
273 : }
274 :
275 : {
276 162 : const char* pszGeodCode = proj_get_id_code(geod_crs, 0);
277 162 : assert( pszGeodCode );
278 162 : *pnGeogCS = (short) atoi(pszGeodCode);
279 : }
280 :
281 162 : proj_destroy(geod_crs);
282 : }
283 :
284 :
285 324 : proj_destroy(proj_crs);
286 324 : return TRUE;
287 : }
288 : }
289 :
290 :
291 0 : int GTIFGetPCSInfo( int nPCSCode, char **ppszEPSGName,
292 : short *pnProjOp, short *pnUOMLengthCode,
293 : short *pnGeogCS )
294 :
295 : {
296 0 : PJ_CONTEXT* ctx = proj_context_create();
297 0 : int ret = GTIFGetPCSInfoEx(ctx, nPCSCode, ppszEPSGName, pnProjOp,
298 : pnUOMLengthCode, pnGeogCS);
299 0 : proj_context_destroy(ctx);
300 0 : return ret;
301 : }
302 :
303 : /************************************************************************/
304 : /* GTIFAngleToDD() */
305 : /* */
306 : /* Convert a numeric angle to decimal degrees. */
307 : /************************************************************************/
308 :
309 406 : double GTIFAngleToDD( double dfAngle, int nUOMAngle )
310 :
311 : {
312 406 : if( nUOMAngle == 9110 ) /* DDD.MMSSsss */
313 : {
314 0 : if( dfAngle > -999.9 && dfAngle < 999.9 )
315 : {
316 : char szAngleString[32];
317 :
318 0 : snprintf(szAngleString, sizeof(szAngleString), "%12.7f", dfAngle);
319 0 : dfAngle = GTIFAngleStringToDD( szAngleString, nUOMAngle );
320 : }
321 : }
322 406 : else if ( nUOMAngle != KvUserDefined )
323 : {
324 378 : double dfInDegrees = 1.0;
325 :
326 378 : GTIFGetUOMAngleInfo( nUOMAngle, NULL, &dfInDegrees );
327 378 : dfAngle = dfAngle * dfInDegrees;
328 : }
329 :
330 406 : return dfAngle;
331 : }
332 :
333 : /************************************************************************/
334 : /* GTIFAngleStringToDD() */
335 : /* */
336 : /* Convert an angle in the specified units to decimal degrees. */
337 : /************************************************************************/
338 :
339 0 : double GTIFAngleStringToDD( const char * pszAngle, int nUOMAngle )
340 :
341 : {
342 : double dfAngle;
343 :
344 0 : if( nUOMAngle == 9110 ) /* DDD.MMSSsss */
345 : {
346 : char *pszDecimal;
347 :
348 0 : dfAngle = ABS(atoi(pszAngle));
349 0 : pszDecimal = strchr(pszAngle,'.');
350 0 : if( pszDecimal != NULL && strlen(pszDecimal) > 1 )
351 : {
352 : char szMinutes[3];
353 : char szSeconds[64];
354 :
355 0 : szMinutes[0] = pszDecimal[1];
356 0 : if( pszDecimal[2] >= '0' && pszDecimal[2] <= '9' )
357 0 : szMinutes[1] = pszDecimal[2];
358 : else
359 0 : szMinutes[1] = '0';
360 :
361 0 : szMinutes[2] = '\0';
362 0 : dfAngle += atoi(szMinutes) / 60.0;
363 :
364 0 : if( strlen(pszDecimal) > 3 )
365 : {
366 0 : szSeconds[0] = pszDecimal[3];
367 0 : if( pszDecimal[4] >= '0' && pszDecimal[4] <= '9' )
368 : {
369 0 : szSeconds[1] = pszDecimal[4];
370 0 : szSeconds[2] = '.';
371 0 : strncpy( szSeconds+3, pszDecimal + 5, sizeof(szSeconds) - 3 );
372 0 : szSeconds[sizeof(szSeconds) - 1] = 0;
373 : }
374 : else
375 : {
376 0 : szSeconds[1] = '0';
377 0 : szSeconds[2] = '\0';
378 : }
379 0 : dfAngle += GTIFAtof(szSeconds) / 3600.0;
380 : }
381 : }
382 :
383 0 : if( pszAngle[0] == '-' )
384 0 : dfAngle *= -1;
385 : }
386 0 : else if( nUOMAngle == 9105 || nUOMAngle == 9106 ) /* grad */
387 : {
388 0 : dfAngle = 180 * (GTIFAtof(pszAngle ) / 200);
389 : }
390 0 : else if( nUOMAngle == 9101 ) /* radians */
391 : {
392 0 : dfAngle = 180 * (GTIFAtof(pszAngle ) / M_PI);
393 : }
394 0 : else if( nUOMAngle == 9103 ) /* arc-minute */
395 : {
396 0 : dfAngle = GTIFAtof(pszAngle) / 60;
397 : }
398 0 : else if( nUOMAngle == 9104 ) /* arc-second */
399 : {
400 0 : dfAngle = GTIFAtof(pszAngle) / 3600;
401 : }
402 : else /* decimal degrees ... some cases missing but seemingly never used */
403 : {
404 0 : CPLAssert( nUOMAngle == 9102 || nUOMAngle == KvUserDefined
405 : || nUOMAngle == 0 );
406 :
407 0 : dfAngle = GTIFAtof(pszAngle );
408 : }
409 :
410 0 : return dfAngle;
411 : }
412 :
413 : /************************************************************************/
414 : /* GTIFGetGCSInfo() */
415 : /* */
416 : /* Fetch the datum, and prime meridian related to a particular */
417 : /* GCS. */
418 : /************************************************************************/
419 :
420 10283 : int GTIFGetGCSInfoEx( void* ctxIn,
421 : int nGCSCode, char ** ppszName,
422 : short * pnDatum, short * pnPM, short *pnUOMAngle )
423 :
424 : {
425 10283 : int nDatum=0;
426 :
427 : /* -------------------------------------------------------------------- */
428 : /* Handle some "well known" GCS codes directly */
429 : /* -------------------------------------------------------------------- */
430 10283 : const char * pszName = NULL;
431 10283 : const int nPM = PM_Greenwich;
432 10283 : const int nUOMAngle = Angular_DMS_Hemisphere;
433 10283 : if( nGCSCode == GCS_NAD27 )
434 : {
435 5314 : nDatum = Datum_North_American_Datum_1927;
436 5314 : pszName = "NAD27";
437 : }
438 4969 : else if( nGCSCode == GCS_NAD83 )
439 : {
440 26 : nDatum = Datum_North_American_Datum_1983;
441 26 : pszName = "NAD83";
442 : }
443 4943 : else if( nGCSCode == GCS_WGS_84 )
444 : {
445 4670 : nDatum = Datum_WGS84;
446 4670 : pszName = "WGS 84";
447 : }
448 273 : else if( nGCSCode == GCS_WGS_72 )
449 : {
450 10 : nDatum = Datum_WGS72;
451 10 : pszName = "WGS 72";
452 : }
453 263 : else if ( nGCSCode == KvUserDefined )
454 : {
455 73 : return FALSE;
456 : }
457 :
458 10210 : if (pszName != NULL)
459 : {
460 10020 : if( ppszName != NULL )
461 5006 : *ppszName = CPLStrdup( pszName );
462 10020 : if( pnDatum != NULL )
463 5014 : *pnDatum = (short) nDatum;
464 10020 : if( pnPM != NULL )
465 5014 : *pnPM = (short) nPM;
466 10020 : if( pnUOMAngle != NULL )
467 5014 : *pnUOMAngle = (short) nUOMAngle;
468 :
469 10020 : return TRUE;
470 : }
471 :
472 : /* -------------------------------------------------------------------- */
473 : /* Search the database. */
474 : /* -------------------------------------------------------------------- */
475 :
476 190 : PJ_CONTEXT* ctx = (PJ_CONTEXT*)ctxIn;
477 : {
478 : char szCode[12];
479 :
480 190 : snprintf(szCode, sizeof(szCode), "%d", nGCSCode);
481 190 : PJ* geod_crs = proj_create_from_database(
482 : ctx, "EPSG", szCode, PJ_CATEGORY_CRS, 0, NULL);
483 190 : if( !geod_crs )
484 : {
485 0 : return FALSE;
486 : }
487 :
488 : {
489 190 : const int objType = proj_get_type(geod_crs);
490 190 : if( objType != PJ_TYPE_GEODETIC_CRS &&
491 190 : objType != PJ_TYPE_GEOCENTRIC_CRS &&
492 2 : objType != PJ_TYPE_GEOGRAPHIC_2D_CRS &&
493 : objType != PJ_TYPE_GEOGRAPHIC_3D_CRS )
494 : {
495 0 : proj_destroy(geod_crs);
496 0 : return FALSE;
497 : }
498 : }
499 :
500 190 : if( ppszName )
501 : {
502 95 : pszName = proj_get_name(geod_crs);
503 95 : if( !pszName )
504 : {
505 : // shouldn't happen
506 0 : proj_destroy(geod_crs);
507 0 : return FALSE;
508 : }
509 95 : *ppszName = CPLStrdup(pszName);
510 : }
511 :
512 190 : if( pnDatum )
513 : {
514 : #if PROJ_VERSION_MAJOR >= 8
515 : PJ* datum = proj_crs_get_datum_forced(ctx, geod_crs);
516 : #else
517 95 : PJ* datum = proj_crs_get_datum(ctx, geod_crs);
518 : #endif
519 95 : if( !datum )
520 : {
521 0 : proj_destroy(geod_crs);
522 0 : return FALSE;
523 : }
524 :
525 : {
526 95 : const char* pszDatumCode = proj_get_id_code(datum, 0);
527 95 : assert( pszDatumCode );
528 95 : *pnDatum = (short) atoi(pszDatumCode);
529 : }
530 :
531 95 : proj_destroy(datum);
532 : }
533 :
534 190 : if( pnPM )
535 : {
536 95 : PJ* pm = proj_get_prime_meridian(ctx, geod_crs);
537 95 : if( !pm )
538 : {
539 0 : proj_destroy(geod_crs);
540 0 : return FALSE;
541 : }
542 :
543 : {
544 95 : const char* pszPMCode = proj_get_id_code(pm, 0);
545 95 : assert( pszPMCode );
546 95 : *pnPM = (short) atoi(pszPMCode);
547 : }
548 :
549 95 : proj_destroy(pm);
550 : }
551 :
552 190 : if( pnUOMAngle )
553 : {
554 95 : PJ* coordSys = proj_crs_get_coordinate_system(
555 : ctx, geod_crs);
556 95 : if( !coordSys )
557 : {
558 : // shouldn't happen except out of memory
559 0 : proj_destroy(geod_crs);
560 0 : return FALSE;
561 : }
562 :
563 : {
564 95 : const char* pszUnitCode = NULL;
565 95 : if( !proj_cs_get_axis_info(
566 : ctx, coordSys, 0,
567 : NULL, /* name */
568 : NULL, /* abbreviation*/
569 : NULL, /* direction */
570 : NULL, /* conversion factor */
571 : NULL, /* unit name */
572 : NULL, /* unit auth name (should be EPSG) */
573 95 : &pszUnitCode) || pszUnitCode == NULL )
574 : {
575 0 : proj_destroy(coordSys);
576 0 : return FALSE;
577 : }
578 95 : *pnUOMAngle = (short) atoi(pszUnitCode);
579 95 : proj_destroy(coordSys);
580 : }
581 : }
582 :
583 190 : proj_destroy(geod_crs);
584 190 : return TRUE;
585 : }
586 : }
587 :
588 20 : int GTIFGetGCSInfo( int nGCSCode, char ** ppszName,
589 : short * pnDatum, short * pnPM, short *pnUOMAngle )
590 :
591 : {
592 20 : PJ_CONTEXT* ctx = proj_context_create();
593 20 : const int ret = GTIFGetGCSInfoEx(ctx, nGCSCode, ppszName, pnDatum,
594 : pnPM, pnUOMAngle);
595 20 : proj_context_destroy(ctx);
596 20 : return ret;
597 : }
598 :
599 : /************************************************************************/
600 : /* GTIFGetEllipsoidInfo() */
601 : /* */
602 : /* Fetch info about an ellipsoid. Axes are always returned in */
603 : /* meters. SemiMajor computed based on inverse flattening */
604 : /* where that is provided. */
605 : /************************************************************************/
606 :
607 10226 : int GTIFGetEllipsoidInfoEx( void* ctxIn,
608 : int nEllipseCode, char ** ppszName,
609 : double * pdfSemiMajor, double * pdfSemiMinor )
610 :
611 : {
612 10226 : PJ_CONTEXT* ctx = (PJ_CONTEXT*)ctxIn;
613 : /* -------------------------------------------------------------------- */
614 : /* Try some well known ellipsoids. */
615 : /* -------------------------------------------------------------------- */
616 10226 : double dfSemiMajor=0.0;
617 10226 : double dfInvFlattening=0.0, dfSemiMinor=0.0;
618 10226 : const char *pszName = NULL;
619 :
620 10226 : if( nEllipseCode == Ellipse_Clarke_1866 )
621 : {
622 5316 : pszName = "Clarke 1866";
623 5316 : dfSemiMajor = 6378206.4;
624 5316 : dfSemiMinor = 6356583.8;
625 5316 : dfInvFlattening = 0.0;
626 : }
627 4910 : else if( nEllipseCode == Ellipse_GRS_1980 )
628 : {
629 135 : pszName = "GRS 1980";
630 135 : dfSemiMajor = 6378137.0;
631 135 : dfSemiMinor = 0.0;
632 135 : dfInvFlattening = 298.257222101;
633 : }
634 4775 : else if( nEllipseCode == Ellipse_WGS_84 )
635 : {
636 4669 : pszName = "WGS 84";
637 4669 : dfSemiMajor = 6378137.0;
638 4669 : dfSemiMinor = 0.0;
639 4669 : dfInvFlattening = 298.257223563;
640 : }
641 106 : else if( nEllipseCode == 7043 )
642 : {
643 10 : pszName = "WGS 72";
644 10 : dfSemiMajor = 6378135.0;
645 10 : dfSemiMinor = 0.0;
646 10 : dfInvFlattening = 298.26;
647 : }
648 :
649 10226 : if (pszName != NULL)
650 : {
651 10130 : if( dfSemiMinor == 0.0 )
652 4814 : dfSemiMinor = dfSemiMajor * (1 - 1.0/dfInvFlattening);
653 :
654 10130 : if( pdfSemiMinor != NULL )
655 5069 : *pdfSemiMinor = dfSemiMinor;
656 10130 : if( pdfSemiMajor != NULL )
657 5069 : *pdfSemiMajor = dfSemiMajor;
658 10130 : if( ppszName != NULL )
659 5061 : *ppszName = CPLStrdup( pszName );
660 :
661 10130 : return TRUE;
662 : }
663 :
664 96 : if( nEllipseCode == KvUserDefined )
665 8 : return FALSE;
666 :
667 : /* -------------------------------------------------------------------- */
668 : /* Search the database. */
669 : /* -------------------------------------------------------------------- */
670 : {
671 : char szCode[12];
672 :
673 88 : snprintf(szCode, sizeof(szCode), "%d", nEllipseCode);
674 88 : PJ* ellipsoid = proj_create_from_database(
675 : ctx, "EPSG", szCode, PJ_CATEGORY_ELLIPSOID, 0, NULL);
676 88 : if( !ellipsoid )
677 : {
678 3 : return FALSE;
679 : }
680 :
681 85 : if( ppszName )
682 : {
683 42 : pszName = proj_get_name(ellipsoid);
684 42 : if( !pszName )
685 : {
686 : // shouldn't happen
687 0 : proj_destroy(ellipsoid);
688 0 : return FALSE;
689 : }
690 42 : *ppszName = CPLStrdup(pszName);
691 : }
692 :
693 85 : proj_ellipsoid_get_parameters(
694 : ctx, ellipsoid, pdfSemiMajor, pdfSemiMinor, NULL, NULL);
695 :
696 85 : proj_destroy(ellipsoid);
697 :
698 85 : return TRUE;
699 : }
700 : }
701 :
702 20 : int GTIFGetEllipsoidInfo( int nEllipseCode, char ** ppszName,
703 : double * pdfSemiMajor, double * pdfSemiMinor )
704 :
705 : {
706 20 : PJ_CONTEXT* ctx = proj_context_create();
707 20 : const int ret = GTIFGetEllipsoidInfoEx(ctx, nEllipseCode, ppszName, pdfSemiMajor,
708 : pdfSemiMinor);
709 20 : proj_context_destroy(ctx);
710 20 : return ret;
711 : }
712 :
713 : /************************************************************************/
714 : /* GTIFGetPMInfo() */
715 : /* */
716 : /* Get the offset between a given prime meridian and Greenwich */
717 : /* in degrees. */
718 : /************************************************************************/
719 :
720 10219 : int GTIFGetPMInfoEx( void* ctxIn,
721 : int nPMCode, char ** ppszName, double *pdfOffset )
722 :
723 : {
724 : /* -------------------------------------------------------------------- */
725 : /* Use a special short cut for Greenwich, since it is so common. */
726 : /* -------------------------------------------------------------------- */
727 10219 : if( nPMCode == PM_Greenwich )
728 : {
729 10198 : if( pdfOffset != NULL )
730 5103 : *pdfOffset = 0.0;
731 10198 : if( ppszName != NULL )
732 5095 : *ppszName = CPLStrdup( "Greenwich" );
733 10198 : return TRUE;
734 : }
735 :
736 :
737 21 : if( nPMCode == KvUserDefined )
738 7 : return FALSE;
739 :
740 : /* -------------------------------------------------------------------- */
741 : /* Search the database. */
742 : /* -------------------------------------------------------------------- */
743 14 : PJ_CONTEXT* ctx = (PJ_CONTEXT*)ctxIn;
744 : {
745 : char szCode[12];
746 :
747 14 : snprintf(szCode, sizeof(szCode), "%d", nPMCode);
748 14 : PJ* pm = proj_create_from_database(
749 : ctx, "EPSG", szCode, PJ_CATEGORY_PRIME_MERIDIAN, 0, NULL);
750 14 : if( !pm )
751 : {
752 0 : return FALSE;
753 : }
754 :
755 14 : if( ppszName )
756 : {
757 7 : const char* pszName = proj_get_name(pm);
758 7 : if( !pszName )
759 : {
760 : // shouldn't happen
761 0 : proj_destroy(pm);
762 0 : return FALSE;
763 : }
764 7 : *ppszName = CPLStrdup(pszName);
765 : }
766 :
767 14 : if( pdfOffset )
768 : {
769 7 : double conv_factor = 0;
770 7 : proj_prime_meridian_get_parameters(
771 : ctx, pm, pdfOffset, &conv_factor, NULL);
772 7 : *pdfOffset *= conv_factor * 180.0 / M_PI;
773 : }
774 :
775 14 : proj_destroy(pm);
776 :
777 14 : return TRUE;
778 : }
779 : }
780 :
781 20 : int GTIFGetPMInfo( int nPMCode, char ** ppszName, double *pdfOffset )
782 :
783 : {
784 20 : PJ_CONTEXT* ctx = proj_context_create();
785 20 : const int ret = GTIFGetPMInfoEx(ctx, nPMCode, ppszName, pdfOffset);
786 20 : proj_context_destroy(ctx);
787 20 : return ret;
788 : }
789 :
790 : /************************************************************************/
791 : /* GTIFGetDatumInfo() */
792 : /* */
793 : /* Fetch the ellipsoid, and name for a datum. */
794 : /************************************************************************/
795 :
796 10222 : int GTIFGetDatumInfoEx( void* ctxIn,
797 : int nDatumCode, char ** ppszName, short * pnEllipsoid )
798 :
799 : {
800 10222 : const char* pszName = NULL;
801 10222 : int nEllipsoid = 0;
802 :
803 : /* -------------------------------------------------------------------- */
804 : /* Handle a few built-in datums. */
805 : /* -------------------------------------------------------------------- */
806 10222 : if( nDatumCode == Datum_North_American_Datum_1927 )
807 : {
808 5314 : nEllipsoid = Ellipse_Clarke_1866;
809 5314 : pszName = "North American Datum 1927";
810 : }
811 4908 : else if( nDatumCode == Datum_North_American_Datum_1983 )
812 : {
813 26 : nEllipsoid = Ellipse_GRS_1980;
814 26 : pszName = "North American Datum 1983";
815 : }
816 4882 : else if( nDatumCode == Datum_WGS84 )
817 : {
818 4668 : nEllipsoid = Ellipse_WGS_84;
819 4668 : pszName = "World Geodetic System 1984";
820 : }
821 214 : else if( nDatumCode == Datum_WGS72 )
822 : {
823 10 : nEllipsoid = 7043; /* WGS72 */
824 10 : pszName = "World Geodetic System 1972";
825 : }
826 :
827 10222 : if (pszName != NULL)
828 : {
829 10018 : if( pnEllipsoid != NULL )
830 5013 : *pnEllipsoid = (short) nEllipsoid;
831 :
832 10018 : if( ppszName != NULL )
833 5005 : *ppszName = CPLStrdup( pszName );
834 :
835 10018 : return TRUE;
836 : }
837 :
838 204 : if( nDatumCode == KvUserDefined )
839 8 : return FALSE;
840 :
841 : /* -------------------------------------------------------------------- */
842 : /* Search the database. */
843 : /* -------------------------------------------------------------------- */
844 196 : PJ_CONTEXT* ctx = (PJ_CONTEXT*)ctxIn;
845 : {
846 : char szCode[12];
847 :
848 196 : snprintf(szCode, sizeof(szCode), "%d", nDatumCode);
849 196 : PJ* datum = proj_create_from_database(
850 : ctx, "EPSG", szCode, PJ_CATEGORY_DATUM, 0, NULL);
851 196 : if( !datum )
852 : {
853 2 : return FALSE;
854 : }
855 :
856 194 : const PJ_TYPE pjType = proj_get_type(datum);
857 194 : if( pjType != PJ_TYPE_GEODETIC_REFERENCE_FRAME &&
858 : pjType != PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME )
859 : {
860 0 : proj_destroy(datum);
861 0 : return FALSE;
862 : }
863 :
864 194 : if( ppszName )
865 : {
866 97 : pszName = proj_get_name(datum);
867 97 : if( !pszName )
868 : {
869 : // shouldn't happen
870 0 : proj_destroy(datum);
871 0 : return FALSE;
872 : }
873 97 : *ppszName = CPLStrdup(pszName);
874 : }
875 :
876 194 : if( pnEllipsoid )
877 : {
878 97 : PJ* ellipsoid = proj_get_ellipsoid(ctx, datum);
879 97 : if( !ellipsoid )
880 : {
881 0 : proj_destroy(datum);
882 0 : return FALSE;
883 : }
884 :
885 : {
886 97 : const char* pszEllipsoidCode = proj_get_id_code(
887 : ellipsoid, 0);
888 97 : assert( pszEllipsoidCode );
889 97 : *pnEllipsoid = (short) atoi(pszEllipsoidCode);
890 : }
891 :
892 97 : proj_destroy(ellipsoid);
893 : }
894 :
895 194 : proj_destroy(datum);
896 :
897 194 : return TRUE;
898 : }
899 : }
900 :
901 20 : int GTIFGetDatumInfo( int nDatumCode, char ** ppszName, short * pnEllipsoid )
902 :
903 : {
904 20 : PJ_CONTEXT* ctx = proj_context_create();
905 20 : const int ret = GTIFGetDatumInfoEx(ctx, nDatumCode, ppszName, pnEllipsoid);
906 20 : proj_context_destroy(ctx);
907 20 : return ret;
908 : }
909 :
910 : /************************************************************************/
911 : /* GTIFGetUOMLengthInfo() */
912 : /* */
913 : /* Note: This function should eventually also know how to */
914 : /* lookup length aliases in the UOM_LE_ALIAS table. */
915 : /************************************************************************/
916 :
917 6810 : int GTIFGetUOMLengthInfoEx( void* ctxIn,
918 : int nUOMLengthCode,
919 : char **ppszUOMName,
920 : double * pdfInMeters )
921 :
922 : {
923 : /* -------------------------------------------------------------------- */
924 : /* We short cut meter to save work and avoid failure for missing */
925 : /* in the most common cases. */
926 : /* -------------------------------------------------------------------- */
927 6810 : if( nUOMLengthCode == 9001 )
928 : {
929 6757 : if( ppszUOMName != NULL )
930 3406 : *ppszUOMName = CPLStrdup( "metre" );
931 6757 : if( pdfInMeters != NULL )
932 3351 : *pdfInMeters = 1.0;
933 :
934 6757 : return TRUE;
935 : }
936 :
937 53 : if( nUOMLengthCode == 9002 )
938 : {
939 0 : if( ppszUOMName != NULL )
940 0 : *ppszUOMName = CPLStrdup( "foot" );
941 0 : if( pdfInMeters != NULL )
942 0 : *pdfInMeters = 0.3048;
943 :
944 0 : return TRUE;
945 : }
946 :
947 53 : if( nUOMLengthCode == 9003 )
948 : {
949 46 : if( ppszUOMName != NULL )
950 28 : *ppszUOMName = CPLStrdup( "US survey foot" );
951 46 : if( pdfInMeters != NULL )
952 18 : *pdfInMeters = 12.0 / 39.37;
953 :
954 46 : return TRUE;
955 : }
956 :
957 7 : if( nUOMLengthCode == KvUserDefined )
958 5 : return FALSE;
959 :
960 : /* -------------------------------------------------------------------- */
961 : /* Search the units database for this unit. If we don't find */
962 : /* it return failure. */
963 : /* -------------------------------------------------------------------- */
964 : char szCode[12];
965 2 : const char* pszName = NULL;
966 :
967 2 : snprintf(szCode, sizeof(szCode), "%d", nUOMLengthCode);
968 2 : PJ_CONTEXT* ctx = (PJ_CONTEXT*)ctxIn;
969 2 : if( !proj_uom_get_info_from_database(
970 : ctx, "EPSG", szCode, &pszName, pdfInMeters, NULL) )
971 : {
972 0 : return FALSE;
973 : }
974 2 : if( ppszUOMName )
975 : {
976 1 : *ppszUOMName = CPLStrdup(pszName);
977 : }
978 2 : return TRUE;
979 : }
980 :
981 105 : int GTIFGetUOMLengthInfo( int nUOMLengthCode,
982 : char **ppszUOMName,
983 : double * pdfInMeters )
984 :
985 : {
986 105 : PJ_CONTEXT* ctx = proj_context_create();
987 105 : const int ret = GTIFGetUOMLengthInfoEx(
988 : ctx, nUOMLengthCode, ppszUOMName, pdfInMeters);
989 105 : proj_context_destroy(ctx);
990 105 : return ret;
991 : }
992 :
993 : /************************************************************************/
994 : /* GTIFGetUOMAngleInfo() */
995 : /************************************************************************/
996 :
997 11026 : int GTIFGetUOMAngleInfoEx( void* ctxIn,
998 : int nUOMAngleCode,
999 : char **ppszUOMName,
1000 : double * pdfInDegrees )
1001 :
1002 : {
1003 11026 : const char *pszUOMName = NULL;
1004 11026 : double dfInDegrees = 1.0;
1005 :
1006 11026 : switch( nUOMAngleCode )
1007 : {
1008 4 : case 9101:
1009 4 : pszUOMName = "radian";
1010 4 : dfInDegrees = 180.0 / M_PI;
1011 4 : break;
1012 :
1013 10989 : case 9102:
1014 : case 9107:
1015 : case 9108:
1016 : case 9110:
1017 : case 9122:
1018 10989 : pszUOMName = "degree";
1019 10989 : dfInDegrees = 1.0;
1020 10989 : break;
1021 :
1022 4 : case 9103:
1023 4 : pszUOMName = "arc-minute";
1024 4 : dfInDegrees = 1 / 60.0;
1025 4 : break;
1026 :
1027 4 : case 9104:
1028 4 : pszUOMName = "arc-second";
1029 4 : dfInDegrees = 1 / 3600.0;
1030 4 : break;
1031 :
1032 14 : case 9105:
1033 14 : pszUOMName = "grad";
1034 14 : dfInDegrees = 180.0 / 200.0;
1035 14 : break;
1036 :
1037 4 : case 9106:
1038 4 : pszUOMName = "gon";
1039 4 : dfInDegrees = 180.0 / 200.0;
1040 4 : break;
1041 :
1042 0 : case 9109:
1043 0 : pszUOMName = "microradian";
1044 0 : dfInDegrees = 180.0 / (M_PI * 1000000.0);
1045 0 : break;
1046 :
1047 7 : default:
1048 7 : break;
1049 : }
1050 :
1051 11026 : if (pszUOMName)
1052 : {
1053 11019 : if( ppszUOMName != NULL )
1054 : {
1055 5153 : *ppszUOMName = CPLStrdup( pszUOMName );
1056 : }
1057 :
1058 11019 : if( pdfInDegrees != NULL )
1059 11009 : *pdfInDegrees = dfInDegrees;
1060 :
1061 11019 : return TRUE;
1062 : }
1063 :
1064 7 : if( nUOMAngleCode == KvUserDefined )
1065 7 : return FALSE;
1066 :
1067 : /* -------------------------------------------------------------------- */
1068 : /* Search the units database for this unit. If we don't find */
1069 : /* it return failure. */
1070 : /* -------------------------------------------------------------------- */
1071 : char szCode[12];
1072 0 : const char* pszName = NULL;
1073 0 : double dfConvFactorToRadians = 0;
1074 :
1075 0 : snprintf(szCode, sizeof(szCode), "%d", nUOMAngleCode);
1076 0 : PJ_CONTEXT* ctx = (PJ_CONTEXT*)ctxIn;
1077 0 : if( !proj_uom_get_info_from_database(
1078 : ctx, "EPSG", szCode, &pszName, &dfConvFactorToRadians, NULL) )
1079 : {
1080 0 : return FALSE;
1081 : }
1082 0 : if( ppszUOMName )
1083 : {
1084 0 : *ppszUOMName = CPLStrdup(pszName);
1085 : }
1086 0 : if( pdfInDegrees )
1087 : {
1088 0 : *pdfInDegrees = dfConvFactorToRadians * 180.0 / M_PI;
1089 : }
1090 0 : return TRUE;
1091 : }
1092 :
1093 398 : int GTIFGetUOMAngleInfo( int nUOMAngleCode,
1094 : char **ppszUOMName,
1095 : double * pdfInDegrees )
1096 :
1097 : {
1098 398 : PJ_CONTEXT* ctx = proj_context_create();
1099 398 : const int ret = GTIFGetUOMAngleInfoEx(
1100 : ctx, nUOMAngleCode, ppszUOMName, pdfInDegrees);
1101 398 : proj_context_destroy(ctx);
1102 398 : return ret;
1103 : }
1104 :
1105 : /************************************************************************/
1106 : /* EPSGProjMethodToCTProjMethod() */
1107 : /* */
1108 : /* Convert between the EPSG enumeration for projection methods, */
1109 : /* and the GeoTIFF CT codes. */
1110 : /************************************************************************/
1111 :
1112 6070 : static int EPSGProjMethodToCTProjMethod( int nEPSG, int bReturnExtendedCTCode )
1113 :
1114 : {
1115 6070 : switch( nEPSG )
1116 : {
1117 15 : case 9801:
1118 15 : return CT_LambertConfConic_1SP;
1119 :
1120 12 : case 9802:
1121 12 : return CT_LambertConfConic_2SP;
1122 :
1123 0 : case 9803:
1124 0 : return CT_LambertConfConic_2SP; /* Belgian variant not supported */
1125 :
1126 3 : case 9804:
1127 3 : return CT_Mercator; /* 1SP and 2SP not differentiated */
1128 :
1129 3 : case 9805:
1130 3 : if( bReturnExtendedCTCode )
1131 2 : return CT_Ext_Mercator_2SP;
1132 : else
1133 1 : return CT_Mercator; /* 1SP and 2SP not differentiated */
1134 :
1135 : /* Mercator 1SP (Spherical) For EPSG:3785 */
1136 0 : case 9841:
1137 0 : return CT_Mercator; /* 1SP and 2SP not differentiated */
1138 :
1139 : /* Google Mercator For EPSG:3857 */
1140 171 : case 1024:
1141 171 : return CT_Mercator; /* 1SP and 2SP not differentiated */
1142 :
1143 3 : case 9806:
1144 3 : return CT_CassiniSoldner;
1145 :
1146 5788 : case 9807:
1147 5788 : return CT_TransverseMercator;
1148 :
1149 0 : case 9808:
1150 0 : return CT_TransvMercator_SouthOriented;
1151 :
1152 12 : case 9809:
1153 12 : return CT_ObliqueStereographic;
1154 :
1155 18 : case 9810:
1156 : case 9829: /* variant B not quite the same - not sure how to handle */
1157 18 : return CT_PolarStereographic;
1158 :
1159 3 : case 9811:
1160 3 : return CT_NewZealandMapGrid;
1161 :
1162 3 : case 9812:
1163 3 : return CT_ObliqueMercator; /* is hotine actually different? */
1164 :
1165 0 : case 9813:
1166 0 : return CT_ObliqueMercator_Laborde;
1167 :
1168 0 : case 9814:
1169 0 : return CT_ObliqueMercator_Rosenmund; /* swiss */
1170 :
1171 6 : case 9815:
1172 6 : return CT_HotineObliqueMercatorAzimuthCenter;
1173 :
1174 0 : case 9816: /* tunesia mining grid has no counterpart */
1175 0 : return KvUserDefined;
1176 :
1177 3 : case 9818:
1178 3 : return CT_Polyconic;
1179 :
1180 3 : case 9820:
1181 : case 1027:
1182 3 : return CT_LambertAzimEqualArea;
1183 :
1184 3 : case 9822:
1185 3 : return CT_AlbersEqualArea;
1186 :
1187 0 : case 9834:
1188 0 : return CT_CylindricalEqualArea;
1189 :
1190 9 : case 1028:
1191 : case 1029:
1192 : case 9823: /* spherical */
1193 : case 9842: /* elliptical */
1194 9 : return CT_Equirectangular;
1195 :
1196 15 : default: /* use the EPSG code for other methods */
1197 15 : return nEPSG;
1198 : }
1199 : }
1200 :
1201 : /************************************************************************/
1202 : /* SetGTParamIds() */
1203 : /* */
1204 : /* This is hardcoded logic to set the GeoTIFF parameter */
1205 : /* identifiers for all the EPSG supported projections. As new */
1206 : /* projection methods are added, this code will need to be updated */
1207 : /************************************************************************/
1208 :
1209 3110 : static int SetGTParamIds( int nCTProjection,
1210 : int nEPSGProjMethod,
1211 : int *panProjParamId,
1212 : int *panEPSGCodes )
1213 :
1214 : {
1215 : int anWorkingDummy[7];
1216 :
1217 3110 : if( panEPSGCodes == NULL )
1218 2960 : panEPSGCodes = anWorkingDummy;
1219 3110 : if( panProjParamId == NULL )
1220 150 : panProjParamId = anWorkingDummy;
1221 :
1222 3110 : memset( panEPSGCodes, 0, sizeof(int) * 7 );
1223 :
1224 : /* psDefn->nParms = 7; */
1225 :
1226 3110 : switch( nCTProjection )
1227 : {
1228 6 : case CT_CassiniSoldner:
1229 : case CT_NewZealandMapGrid:
1230 : case CT_Polyconic:
1231 6 : panProjParamId[0] = ProjNatOriginLatGeoKey;
1232 6 : panProjParamId[1] = ProjNatOriginLongGeoKey;
1233 6 : panProjParamId[5] = ProjFalseEastingGeoKey;
1234 6 : panProjParamId[6] = ProjFalseNorthingGeoKey;
1235 :
1236 6 : panEPSGCodes[0] = EPSGNatOriginLat;
1237 6 : panEPSGCodes[1] = EPSGNatOriginLong;
1238 6 : panEPSGCodes[5] = EPSGFalseEasting;
1239 6 : panEPSGCodes[6] = EPSGFalseNorthing;
1240 6 : return TRUE;
1241 :
1242 6 : case CT_ObliqueMercator:
1243 : case CT_HotineObliqueMercatorAzimuthCenter:
1244 6 : panProjParamId[0] = ProjCenterLatGeoKey;
1245 6 : panProjParamId[1] = ProjCenterLongGeoKey;
1246 6 : panProjParamId[2] = ProjAzimuthAngleGeoKey;
1247 6 : panProjParamId[3] = ProjRectifiedGridAngleGeoKey;
1248 6 : panProjParamId[4] = ProjScaleAtCenterGeoKey;
1249 6 : panProjParamId[5] = ProjFalseEastingGeoKey;
1250 6 : panProjParamId[6] = ProjFalseNorthingGeoKey;
1251 :
1252 6 : panEPSGCodes[0] = EPSGProjCenterLat;
1253 6 : panEPSGCodes[1] = EPSGProjCenterLong;
1254 6 : panEPSGCodes[2] = EPSGAzimuth;
1255 6 : panEPSGCodes[3] = EPSGAngleRectifiedToSkewedGrid;
1256 6 : panEPSGCodes[4] = EPSGInitialLineScaleFactor;
1257 6 : panEPSGCodes[5] = EPSGProjCenterEasting; /* EPSG proj method 9812 uses EPSGFalseEasting, but 9815 uses EPSGProjCenterEasting */
1258 6 : panEPSGCodes[6] = EPSGProjCenterNorthing; /* EPSG proj method 9812 uses EPSGFalseNorthing, but 9815 uses EPSGProjCenterNorthing */
1259 6 : return TRUE;
1260 :
1261 0 : case CT_ObliqueMercator_Laborde:
1262 0 : panProjParamId[0] = ProjCenterLatGeoKey;
1263 0 : panProjParamId[1] = ProjCenterLongGeoKey;
1264 0 : panProjParamId[2] = ProjAzimuthAngleGeoKey;
1265 0 : panProjParamId[4] = ProjScaleAtCenterGeoKey;
1266 0 : panProjParamId[5] = ProjFalseEastingGeoKey;
1267 0 : panProjParamId[6] = ProjFalseNorthingGeoKey;
1268 :
1269 0 : panEPSGCodes[0] = EPSGProjCenterLat;
1270 0 : panEPSGCodes[1] = EPSGProjCenterLong;
1271 0 : panEPSGCodes[2] = EPSGAzimuth;
1272 0 : panEPSGCodes[4] = EPSGInitialLineScaleFactor;
1273 0 : panEPSGCodes[5] = EPSGFalseEasting;
1274 0 : panEPSGCodes[6] = EPSGFalseNorthing;
1275 0 : return TRUE;
1276 :
1277 3068 : case CT_LambertConfConic_1SP:
1278 : case CT_Mercator:
1279 : case CT_ObliqueStereographic:
1280 : case CT_PolarStereographic:
1281 : case CT_TransverseMercator:
1282 : case CT_TransvMercator_SouthOriented:
1283 3068 : panProjParamId[0] = ProjNatOriginLatGeoKey;
1284 3068 : if( nCTProjection == CT_PolarStereographic )
1285 : {
1286 12 : panProjParamId[1] = ProjStraightVertPoleLongGeoKey;
1287 : }
1288 : else
1289 : {
1290 3056 : panProjParamId[1] = ProjNatOriginLongGeoKey;
1291 : }
1292 3068 : if( nEPSGProjMethod == 9805 ) /* Mercator_2SP */
1293 : {
1294 0 : panProjParamId[2] = ProjStdParallel1GeoKey;
1295 : }
1296 3068 : panProjParamId[4] = ProjScaleAtNatOriginGeoKey;
1297 3068 : panProjParamId[5] = ProjFalseEastingGeoKey;
1298 3068 : panProjParamId[6] = ProjFalseNorthingGeoKey;
1299 :
1300 3068 : panEPSGCodes[0] = EPSGNatOriginLat;
1301 3068 : panEPSGCodes[1] = EPSGNatOriginLong;
1302 3068 : if( nEPSGProjMethod == 9805 ) /* Mercator_2SP */
1303 : {
1304 0 : panEPSGCodes[2] = EPSGStdParallel1Lat;
1305 : }
1306 3068 : panEPSGCodes[4] = EPSGNatOriginScaleFactor;
1307 3068 : panEPSGCodes[5] = EPSGFalseEasting;
1308 3068 : panEPSGCodes[6] = EPSGFalseNorthing;
1309 3068 : return TRUE;
1310 :
1311 8 : case CT_LambertConfConic_2SP:
1312 8 : panProjParamId[0] = ProjFalseOriginLatGeoKey;
1313 8 : panProjParamId[1] = ProjFalseOriginLongGeoKey;
1314 8 : panProjParamId[2] = ProjStdParallel1GeoKey;
1315 8 : panProjParamId[3] = ProjStdParallel2GeoKey;
1316 8 : panProjParamId[5] = ProjFalseEastingGeoKey;
1317 8 : panProjParamId[6] = ProjFalseNorthingGeoKey;
1318 :
1319 8 : panEPSGCodes[0] = EPSGFalseOriginLat;
1320 8 : panEPSGCodes[1] = EPSGFalseOriginLong;
1321 8 : panEPSGCodes[2] = EPSGStdParallel1Lat;
1322 8 : panEPSGCodes[3] = EPSGStdParallel2Lat;
1323 8 : panEPSGCodes[5] = EPSGFalseOriginEasting;
1324 8 : panEPSGCodes[6] = EPSGFalseOriginNorthing;
1325 8 : return TRUE;
1326 :
1327 2 : case CT_AlbersEqualArea:
1328 2 : panProjParamId[0] = ProjStdParallel1GeoKey;
1329 2 : panProjParamId[1] = ProjStdParallel2GeoKey;
1330 2 : panProjParamId[2] = ProjNatOriginLatGeoKey;
1331 2 : panProjParamId[3] = ProjNatOriginLongGeoKey;
1332 2 : panProjParamId[5] = ProjFalseEastingGeoKey;
1333 2 : panProjParamId[6] = ProjFalseNorthingGeoKey;
1334 :
1335 2 : panEPSGCodes[0] = EPSGStdParallel1Lat;
1336 2 : panEPSGCodes[1] = EPSGStdParallel2Lat;
1337 2 : panEPSGCodes[2] = EPSGFalseOriginLat;
1338 2 : panEPSGCodes[3] = EPSGFalseOriginLong;
1339 2 : panEPSGCodes[5] = EPSGFalseOriginEasting;
1340 2 : panEPSGCodes[6] = EPSGFalseOriginNorthing;
1341 2 : return TRUE;
1342 :
1343 0 : case CT_SwissObliqueCylindrical:
1344 0 : panProjParamId[0] = ProjCenterLatGeoKey;
1345 0 : panProjParamId[1] = ProjCenterLongGeoKey;
1346 0 : panProjParamId[5] = ProjFalseEastingGeoKey;
1347 0 : panProjParamId[6] = ProjFalseNorthingGeoKey;
1348 :
1349 : /* EPSG codes? */
1350 0 : return TRUE;
1351 :
1352 2 : case CT_LambertAzimEqualArea:
1353 2 : panProjParamId[0] = ProjCenterLatGeoKey;
1354 2 : panProjParamId[1] = ProjCenterLongGeoKey;
1355 2 : panProjParamId[5] = ProjFalseEastingGeoKey;
1356 2 : panProjParamId[6] = ProjFalseNorthingGeoKey;
1357 :
1358 2 : panEPSGCodes[0] = EPSGNatOriginLat;
1359 2 : panEPSGCodes[1] = EPSGNatOriginLong;
1360 2 : panEPSGCodes[5] = EPSGFalseEasting;
1361 2 : panEPSGCodes[6] = EPSGFalseNorthing;
1362 2 : return TRUE;
1363 :
1364 0 : case CT_CylindricalEqualArea:
1365 0 : panProjParamId[0] = ProjStdParallel1GeoKey;
1366 0 : panProjParamId[1] = ProjNatOriginLongGeoKey;
1367 0 : panProjParamId[5] = ProjFalseEastingGeoKey;
1368 0 : panProjParamId[6] = ProjFalseNorthingGeoKey;
1369 :
1370 0 : panEPSGCodes[0] = EPSGStdParallel1Lat;
1371 0 : panEPSGCodes[1] = EPSGFalseOriginLong;
1372 0 : panEPSGCodes[5] = EPSGFalseOriginEasting;
1373 0 : panEPSGCodes[6] = EPSGFalseOriginNorthing;
1374 0 : return TRUE;
1375 :
1376 6 : case CT_Equirectangular:
1377 6 : panProjParamId[0] = ProjCenterLatGeoKey;
1378 6 : panProjParamId[1] = ProjCenterLongGeoKey;
1379 6 : panProjParamId[2] = ProjStdParallel1GeoKey;
1380 6 : panProjParamId[5] = ProjFalseEastingGeoKey;
1381 6 : panProjParamId[6] = ProjFalseNorthingGeoKey;
1382 :
1383 6 : panEPSGCodes[0] = EPSGNatOriginLat;
1384 6 : panEPSGCodes[1] = EPSGNatOriginLong;
1385 6 : panEPSGCodes[2] = EPSGStdParallel1Lat;
1386 6 : panEPSGCodes[5] = EPSGFalseEasting;
1387 6 : panEPSGCodes[6] = EPSGFalseNorthing;
1388 6 : return TRUE;
1389 :
1390 2 : case CT_Ext_Mercator_2SP:
1391 2 : panProjParamId[0] = ProjNatOriginLatGeoKey;
1392 2 : panProjParamId[1] = ProjNatOriginLongGeoKey;
1393 2 : panProjParamId[2] = ProjStdParallel1GeoKey;
1394 2 : panProjParamId[5] = ProjFalseEastingGeoKey;
1395 2 : panProjParamId[6] = ProjFalseNorthingGeoKey;
1396 :
1397 2 : panEPSGCodes[0] = EPSGNatOriginLat;
1398 2 : panEPSGCodes[1] = EPSGNatOriginLong;
1399 2 : panEPSGCodes[2] = EPSGStdParallel1Lat;
1400 2 : panEPSGCodes[5] = EPSGFalseEasting;
1401 2 : panEPSGCodes[6] = EPSGFalseNorthing;
1402 2 : return TRUE;
1403 :
1404 10 : default:
1405 10 : return FALSE;
1406 : }
1407 : }
1408 :
1409 : /************************************************************************/
1410 : /* GTIFGetProjTRFInfo() */
1411 : /* */
1412 : /* Transform a PROJECTION_TRF_CODE into a projection method, */
1413 : /* and a set of parameters. The parameters identify will */
1414 : /* depend on the returned method, but they will all have been */
1415 : /* normalized into degrees and meters. */
1416 : /************************************************************************/
1417 :
1418 2960 : int GTIFGetProjTRFInfoEx( void* ctxIn,
1419 : int nProjTRFCode,
1420 : char **ppszProjTRFName,
1421 : short * pnProjMethod,
1422 : double * padfProjParams )
1423 :
1424 : {
1425 2960 : if ((nProjTRFCode >= Proj_UTM_zone_1N && nProjTRFCode <= Proj_UTM_zone_60N) ||
1426 48 : (nProjTRFCode >= Proj_UTM_zone_1S && nProjTRFCode <= Proj_UTM_zone_60S))
1427 : {
1428 : int bNorth;
1429 : int nZone;
1430 2810 : if (nProjTRFCode <= Proj_UTM_zone_60N)
1431 : {
1432 2807 : bNorth = TRUE;
1433 2807 : nZone = nProjTRFCode - Proj_UTM_zone_1N + 1;
1434 : }
1435 : else
1436 : {
1437 3 : bNorth = FALSE;
1438 3 : nZone = nProjTRFCode - Proj_UTM_zone_1S + 1;
1439 : }
1440 :
1441 2810 : if (ppszProjTRFName)
1442 : {
1443 : char szProjTRFName[64];
1444 0 : snprintf(szProjTRFName, sizeof(szProjTRFName), "UTM zone %d%c",
1445 : nZone, (bNorth) ? 'N' : 'S');
1446 0 : *ppszProjTRFName = CPLStrdup(szProjTRFName);
1447 : }
1448 :
1449 2810 : if (pnProjMethod)
1450 2810 : *pnProjMethod = 9807;
1451 :
1452 2810 : if (padfProjParams)
1453 : {
1454 2810 : padfProjParams[0] = 0;
1455 2810 : padfProjParams[1] = -183 + 6 * nZone;
1456 2810 : padfProjParams[2] = 0;
1457 2810 : padfProjParams[3] = 0;
1458 2810 : padfProjParams[4] = 0.9996;
1459 2810 : padfProjParams[5] = 500000;
1460 2810 : padfProjParams[6] = (bNorth) ? 0 : 10000000;
1461 : }
1462 :
1463 2810 : return TRUE;
1464 : }
1465 :
1466 150 : if( nProjTRFCode == KvUserDefined )
1467 0 : return FALSE;
1468 :
1469 : {
1470 : char szCode[12];
1471 150 : snprintf(szCode, sizeof(szCode), "%d", nProjTRFCode);
1472 150 : PJ_CONTEXT* ctx = (PJ_CONTEXT*)ctxIn;
1473 150 : PJ *transf = proj_create_from_database(
1474 : ctx, "EPSG", szCode, PJ_CATEGORY_COORDINATE_OPERATION, 0, NULL);
1475 150 : if( !transf )
1476 : {
1477 0 : return FALSE;
1478 : }
1479 :
1480 150 : if( proj_get_type(transf) != PJ_TYPE_CONVERSION )
1481 : {
1482 0 : proj_destroy(transf);
1483 0 : return FALSE;
1484 : }
1485 :
1486 : /* Get the projection method code */
1487 150 : const char* pszMethodCode = NULL;
1488 150 : proj_coordoperation_get_method_info(ctx, transf,
1489 : NULL, /* method name */
1490 : NULL, /* method auth name (should be EPSG) */
1491 : &pszMethodCode);
1492 150 : assert( pszMethodCode );
1493 150 : const int nProjMethod = atoi(pszMethodCode);
1494 :
1495 : /* -------------------------------------------------------------------- */
1496 : /* Initialize a definition of what EPSG codes need to be loaded */
1497 : /* into what fields in adfProjParams. */
1498 : /* -------------------------------------------------------------------- */
1499 150 : const int nCTProjMethod = EPSGProjMethodToCTProjMethod( nProjMethod, TRUE );
1500 : int anEPSGCodes[7];
1501 150 : SetGTParamIds( nCTProjMethod, nProjMethod, NULL, anEPSGCodes );
1502 :
1503 :
1504 : /* -------------------------------------------------------------------- */
1505 : /* Get the parameters for this projection. */
1506 : /* -------------------------------------------------------------------- */
1507 :
1508 : double adfProjParams[7];
1509 :
1510 1200 : for( int i = 0; i < 7; i++ )
1511 : {
1512 1050 : int nEPSGCode = anEPSGCodes[i];
1513 :
1514 : /* Establish default */
1515 1050 : if( nEPSGCode == EPSGAngleRectifiedToSkewedGrid )
1516 3 : adfProjParams[i] = 90.0;
1517 1047 : else if( nEPSGCode == EPSGNatOriginScaleFactor
1518 918 : || nEPSGCode == EPSGInitialLineScaleFactor
1519 915 : || nEPSGCode == EPSGPseudoStdParallelScaleFactor )
1520 132 : adfProjParams[i] = 1.0;
1521 : else
1522 915 : adfProjParams[i] = 0.0;
1523 :
1524 : /* If there is no parameter, skip */
1525 1050 : if( nEPSGCode == 0 )
1526 381 : continue;
1527 :
1528 732 : const int nParamCount = proj_coordoperation_get_param_count(ctx, transf);
1529 :
1530 : /* Find the matching parameter */
1531 732 : const char *pszUOMCategory = NULL;
1532 732 : double dfValue = 0.0;
1533 732 : double dfUnitConvFactor = 0.0;
1534 : int iEPSG; /* Used after for */
1535 2241 : for( iEPSG = 0; iEPSG < nParamCount; iEPSG++ )
1536 : {
1537 2172 : const char* pszParamCode = NULL;
1538 2172 : proj_coordoperation_get_param(
1539 : ctx, transf, iEPSG,
1540 : NULL, /* name */
1541 : NULL, /* auth name */
1542 : &pszParamCode,
1543 : &dfValue,
1544 : NULL, /* value (string) */
1545 : &dfUnitConvFactor, /* unit conv factor */
1546 : NULL, /* unit name */
1547 : NULL, /* unit auth name */
1548 : NULL, /* unit code */
1549 : &pszUOMCategory /* unit category */);
1550 2172 : assert(pszParamCode);
1551 2172 : if( atoi(pszParamCode) == nEPSGCode )
1552 : {
1553 663 : break;
1554 : }
1555 : }
1556 :
1557 : /* not found, accept the default */
1558 732 : if( iEPSG == nParamCount )
1559 : {
1560 : /* for CT_ObliqueMercator try alternate parameter codes first */
1561 : /* because EPSG proj method 9812 uses EPSGFalseXXXXX, but 9815 uses EPSGProjCenterXXXXX */
1562 69 : if ( nCTProjMethod == CT_ObliqueMercator && nEPSGCode == EPSGProjCenterEasting )
1563 1 : nEPSGCode = EPSGFalseEasting;
1564 68 : else if ( nCTProjMethod == CT_ObliqueMercator && nEPSGCode == EPSGProjCenterNorthing )
1565 1 : nEPSGCode = EPSGFalseNorthing;
1566 : /* for CT_PolarStereographic try alternate parameter codes first */
1567 : /* because EPSG proj method 9829 uses EPSGLatOfStdParallel instead of EPSGNatOriginLat */
1568 : /* and EPSGOriginLong instead of EPSGNatOriginLong */
1569 67 : else if( nCTProjMethod == CT_PolarStereographic && nEPSGCode == EPSGNatOriginLat )
1570 2 : nEPSGCode = EPSGLatOfStdParallel;
1571 65 : else if( nCTProjMethod == CT_PolarStereographic && nEPSGCode == EPSGNatOriginLong )
1572 2 : nEPSGCode = EPSGOriginLong;
1573 : else
1574 63 : continue;
1575 :
1576 19 : for( iEPSG = 0; iEPSG < nParamCount; iEPSG++ )
1577 : {
1578 19 : const char* pszParamCode = NULL;
1579 19 : proj_coordoperation_get_param(
1580 : ctx, transf, iEPSG,
1581 : NULL, /* name */
1582 : NULL, /* auth name */
1583 : &pszParamCode,
1584 : &dfValue,
1585 : NULL, /* value (string) */
1586 : &dfUnitConvFactor, /* unit conv factor */
1587 : NULL, /* unit name */
1588 : NULL, /* unit auth name */
1589 : NULL, /* unit code */
1590 : &pszUOMCategory /* unit category */);
1591 19 : assert(pszParamCode);
1592 19 : if( atoi(pszParamCode) == nEPSGCode )
1593 : {
1594 6 : break;
1595 : }
1596 : }
1597 :
1598 6 : if( iEPSG == nParamCount )
1599 0 : continue;
1600 : }
1601 :
1602 669 : assert(pszUOMCategory);
1603 :
1604 669 : adfProjParams[i] = dfValue * dfUnitConvFactor;
1605 669 : if( strcmp(pszUOMCategory, "angular") == 0.0 )
1606 : {
1607 : /* Convert from radians to degrees */
1608 306 : adfProjParams[i] *= 180 / M_PI;
1609 : }
1610 : }
1611 :
1612 : /* -------------------------------------------------------------------- */
1613 : /* Get the name, if requested. */
1614 : /* -------------------------------------------------------------------- */
1615 150 : if( ppszProjTRFName != NULL )
1616 : {
1617 0 : const char* pszName = proj_get_name(transf);
1618 0 : if( !pszName )
1619 : {
1620 : // shouldn't happen
1621 0 : proj_destroy(transf);
1622 0 : return FALSE;
1623 : }
1624 0 : *ppszProjTRFName = CPLStrdup(pszName);
1625 : }
1626 :
1627 : /* -------------------------------------------------------------------- */
1628 : /* Transfer requested data into passed variables. */
1629 : /* -------------------------------------------------------------------- */
1630 150 : if( pnProjMethod != NULL )
1631 150 : *pnProjMethod = (short) nProjMethod;
1632 :
1633 150 : if( padfProjParams != NULL )
1634 : {
1635 1200 : for( int i = 0; i < 7; i++ )
1636 1050 : padfProjParams[i] = adfProjParams[i];
1637 : }
1638 :
1639 150 : proj_destroy(transf);
1640 :
1641 150 : return TRUE;
1642 : }
1643 : }
1644 :
1645 0 : int GTIFGetProjTRFInfo( /* Conversion code */
1646 : int nProjTRFCode,
1647 : char **ppszProjTRFName,
1648 : short * pnProjMethod,
1649 : double * padfProjParams )
1650 : {
1651 0 : PJ_CONTEXT* ctx = proj_context_create();
1652 0 : const int ret = GTIFGetProjTRFInfoEx(
1653 : ctx, nProjTRFCode, ppszProjTRFName, pnProjMethod, padfProjParams);
1654 0 : proj_context_destroy(ctx);
1655 0 : return ret;
1656 : }
1657 :
1658 : /************************************************************************/
1659 : /* GTIFFetchProjParms() */
1660 : /* */
1661 : /* Fetch the projection parameters for a particular projection */
1662 : /* from a GeoTIFF file, and fill the GTIFDefn structure out */
1663 : /* with them. */
1664 : /************************************************************************/
1665 :
1666 74 : static void GTIFFetchProjParms( GTIF * psGTIF, GTIFDefn * psDefn )
1667 : {
1668 :
1669 : /* -------------------------------------------------------------------- */
1670 : /* Get the false easting, and northing if available. */
1671 : /* -------------------------------------------------------------------- */
1672 74 : double dfFalseEasting = 0.0;
1673 74 : if( !GTIFKeyGetDOUBLE(psGTIF, ProjFalseEastingGeoKey, &dfFalseEasting, 0, 1)
1674 12 : && !GTIFKeyGetDOUBLE(psGTIF, ProjCenterEastingGeoKey,
1675 : &dfFalseEasting, 0, 1)
1676 12 : && !GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginEastingGeoKey,
1677 : &dfFalseEasting, 0, 1) )
1678 0 : dfFalseEasting = 0.0;
1679 :
1680 74 : double dfFalseNorthing = 0.0;
1681 74 : if( !GTIFKeyGetDOUBLE(psGTIF, ProjFalseNorthingGeoKey, &dfFalseNorthing,0,1)
1682 12 : && !GTIFKeyGetDOUBLE(psGTIF, ProjCenterNorthingGeoKey,
1683 : &dfFalseNorthing, 0, 1)
1684 12 : && !GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginNorthingGeoKey,
1685 : &dfFalseNorthing, 0, 1) )
1686 0 : dfFalseNorthing = 0.0;
1687 :
1688 74 : double dfNatOriginLong = 0.0, dfNatOriginLat = 0.0, dfRectGridAngle = 0.0;
1689 74 : double dfNatOriginScale = 1.0;
1690 74 : double dfStdParallel1 = 0.0, dfStdParallel2 = 0.0, dfAzimuth = 0.0;
1691 :
1692 74 : switch( psDefn->CTProjection )
1693 : {
1694 : /* -------------------------------------------------------------------- */
1695 0 : case CT_Stereographic:
1696 : /* -------------------------------------------------------------------- */
1697 0 : if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey,
1698 : &dfNatOriginLong, 0, 1 ) == 0
1699 0 : && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey,
1700 : &dfNatOriginLong, 0, 1 ) == 0
1701 0 : && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey,
1702 : &dfNatOriginLong, 0, 1 ) == 0 )
1703 0 : dfNatOriginLong = 0.0;
1704 :
1705 0 : if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey,
1706 : &dfNatOriginLat, 0, 1 ) == 0
1707 0 : && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey,
1708 : &dfNatOriginLat, 0, 1 ) == 0
1709 0 : && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey,
1710 : &dfNatOriginLat, 0, 1 ) == 0 )
1711 0 : dfNatOriginLat = 0.0;
1712 :
1713 0 : if( GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtNatOriginGeoKey,
1714 : &dfNatOriginScale, 0, 1 ) == 0 )
1715 0 : dfNatOriginScale = 1.0;
1716 :
1717 : /* notdef: should transform to decimal degrees at this point */
1718 :
1719 0 : psDefn->ProjParm[0] = dfNatOriginLat;
1720 0 : psDefn->ProjParmId[0] = ProjCenterLatGeoKey;
1721 0 : psDefn->ProjParm[1] = dfNatOriginLong;
1722 0 : psDefn->ProjParmId[1] = ProjCenterLongGeoKey;
1723 0 : psDefn->ProjParm[4] = dfNatOriginScale;
1724 0 : psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey;
1725 0 : psDefn->ProjParm[5] = dfFalseEasting;
1726 0 : psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
1727 0 : psDefn->ProjParm[6] = dfFalseNorthing;
1728 0 : psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
1729 :
1730 0 : psDefn->nParms = 7;
1731 0 : break;
1732 :
1733 : /* -------------------------------------------------------------------- */
1734 8 : case CT_Mercator:
1735 : /* -------------------------------------------------------------------- */
1736 8 : if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey,
1737 : &dfNatOriginLong, 0, 1 ) == 0
1738 0 : && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey,
1739 : &dfNatOriginLong, 0, 1 ) == 0
1740 0 : && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey,
1741 : &dfNatOriginLong, 0, 1 ) == 0 )
1742 0 : dfNatOriginLong = 0.0;
1743 :
1744 8 : if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey,
1745 : &dfNatOriginLat, 0, 1 ) == 0
1746 0 : && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey,
1747 : &dfNatOriginLat, 0, 1 ) == 0
1748 0 : && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey,
1749 : &dfNatOriginLat, 0, 1 ) == 0 )
1750 0 : dfNatOriginLat = 0.0;
1751 :
1752 :
1753 8 : const int bHaveSP1 = GTIFKeyGetDOUBLE(psGTIF, ProjStdParallel1GeoKey,
1754 : &dfStdParallel1, 0, 1 );
1755 :
1756 8 : int bHaveNOS = GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtNatOriginGeoKey,
1757 : &dfNatOriginScale, 0, 1 );
1758 :
1759 : /* Default scale only if dfStdParallel1 isn't defined either */
1760 8 : if( !bHaveNOS && !bHaveSP1)
1761 : {
1762 0 : bHaveNOS = TRUE;
1763 0 : dfNatOriginScale = 1.0;
1764 : }
1765 :
1766 : /* notdef: should transform to decimal degrees at this point */
1767 :
1768 8 : psDefn->ProjParm[0] = dfNatOriginLat;
1769 8 : psDefn->ProjParmId[0] = ProjNatOriginLatGeoKey;
1770 8 : psDefn->ProjParm[1] = dfNatOriginLong;
1771 8 : psDefn->ProjParmId[1] = ProjNatOriginLongGeoKey;
1772 8 : if( bHaveSP1 )
1773 : {
1774 2 : psDefn->ProjParm[2] = dfStdParallel1;
1775 2 : psDefn->ProjParmId[2] = ProjStdParallel1GeoKey;
1776 : }
1777 8 : if( bHaveNOS )
1778 : {
1779 7 : psDefn->ProjParm[4] = dfNatOriginScale;
1780 7 : psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey;
1781 : }
1782 8 : psDefn->ProjParm[5] = dfFalseEasting;
1783 8 : psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
1784 8 : psDefn->ProjParm[6] = dfFalseNorthing;
1785 8 : psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
1786 :
1787 8 : psDefn->nParms = 7;
1788 8 : break;
1789 :
1790 : /* -------------------------------------------------------------------- */
1791 6 : case CT_LambertConfConic_1SP:
1792 : case CT_ObliqueStereographic:
1793 : case CT_TransverseMercator:
1794 : case CT_TransvMercator_SouthOriented:
1795 : /* -------------------------------------------------------------------- */
1796 6 : if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey,
1797 : &dfNatOriginLong, 0, 1 ) == 0
1798 0 : && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey,
1799 : &dfNatOriginLong, 0, 1 ) == 0
1800 0 : && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey,
1801 : &dfNatOriginLong, 0, 1 ) == 0 )
1802 0 : dfNatOriginLong = 0.0;
1803 :
1804 6 : if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey,
1805 : &dfNatOriginLat, 0, 1 ) == 0
1806 0 : && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey,
1807 : &dfNatOriginLat, 0, 1 ) == 0
1808 0 : && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey,
1809 : &dfNatOriginLat, 0, 1 ) == 0 )
1810 0 : dfNatOriginLat = 0.0;
1811 :
1812 6 : if( GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtNatOriginGeoKey,
1813 : &dfNatOriginScale, 0, 1 ) == 0
1814 : /* See https://github.com/OSGeo/gdal/files/1665718/lasinfo.txt */
1815 0 : && GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtCenterGeoKey,
1816 : &dfNatOriginScale, 0, 1 ) == 0 )
1817 0 : dfNatOriginScale = 1.0;
1818 :
1819 : /* notdef: should transform to decimal degrees at this point */
1820 :
1821 6 : psDefn->ProjParm[0] = dfNatOriginLat;
1822 6 : psDefn->ProjParmId[0] = ProjNatOriginLatGeoKey;
1823 6 : psDefn->ProjParm[1] = dfNatOriginLong;
1824 6 : psDefn->ProjParmId[1] = ProjNatOriginLongGeoKey;
1825 6 : psDefn->ProjParm[4] = dfNatOriginScale;
1826 6 : psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey;
1827 6 : psDefn->ProjParm[5] = dfFalseEasting;
1828 6 : psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
1829 6 : psDefn->ProjParm[6] = dfFalseNorthing;
1830 6 : psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
1831 :
1832 6 : psDefn->nParms = 7;
1833 6 : break;
1834 :
1835 : /* -------------------------------------------------------------------- */
1836 3 : case CT_ObliqueMercator: /* hotine */
1837 : case CT_HotineObliqueMercatorAzimuthCenter:
1838 : /* -------------------------------------------------------------------- */
1839 3 : if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey,
1840 : &dfNatOriginLong, 0, 1 ) == 0
1841 3 : && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey,
1842 : &dfNatOriginLong, 0, 1 ) == 0
1843 3 : && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey,
1844 : &dfNatOriginLong, 0, 1 ) == 0 )
1845 0 : dfNatOriginLong = 0.0;
1846 :
1847 3 : if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey,
1848 : &dfNatOriginLat, 0, 1 ) == 0
1849 3 : && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey,
1850 : &dfNatOriginLat, 0, 1 ) == 0
1851 3 : && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey,
1852 : &dfNatOriginLat, 0, 1 ) == 0 )
1853 0 : dfNatOriginLat = 0.0;
1854 :
1855 3 : if( GTIFKeyGetDOUBLE(psGTIF, ProjAzimuthAngleGeoKey,
1856 : &dfAzimuth, 0, 1 ) == 0 )
1857 0 : dfAzimuth = 0.0;
1858 :
1859 3 : if( GTIFKeyGetDOUBLE(psGTIF, ProjRectifiedGridAngleGeoKey,
1860 : &dfRectGridAngle, 0, 1 ) == 0 )
1861 0 : dfRectGridAngle = 90.0;
1862 :
1863 3 : if( GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtNatOriginGeoKey,
1864 : &dfNatOriginScale, 0, 1 ) == 0
1865 3 : && GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtCenterGeoKey,
1866 : &dfNatOriginScale, 0, 1 ) == 0 )
1867 0 : dfNatOriginScale = 1.0;
1868 :
1869 : /* notdef: should transform to decimal degrees at this point */
1870 :
1871 3 : psDefn->ProjParm[0] = dfNatOriginLat;
1872 3 : psDefn->ProjParmId[0] = ProjCenterLatGeoKey;
1873 3 : psDefn->ProjParm[1] = dfNatOriginLong;
1874 3 : psDefn->ProjParmId[1] = ProjCenterLongGeoKey;
1875 3 : psDefn->ProjParm[2] = dfAzimuth;
1876 3 : psDefn->ProjParmId[2] = ProjAzimuthAngleGeoKey;
1877 3 : psDefn->ProjParm[3] = dfRectGridAngle;
1878 3 : psDefn->ProjParmId[3] = ProjRectifiedGridAngleGeoKey;
1879 3 : psDefn->ProjParm[4] = dfNatOriginScale;
1880 3 : psDefn->ProjParmId[4] = ProjScaleAtCenterGeoKey;
1881 3 : psDefn->ProjParm[5] = dfFalseEasting;
1882 3 : psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
1883 3 : psDefn->ProjParm[6] = dfFalseNorthing;
1884 3 : psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
1885 :
1886 3 : psDefn->nParms = 7;
1887 3 : break;
1888 :
1889 : /* -------------------------------------------------------------------- */
1890 0 : case CT_ObliqueMercator_Laborde:
1891 : /* -------------------------------------------------------------------- */
1892 0 : if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey,
1893 : &dfNatOriginLong, 0, 1 ) == 0
1894 0 : && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey,
1895 : &dfNatOriginLong, 0, 1 ) == 0
1896 0 : && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey,
1897 : &dfNatOriginLong, 0, 1 ) == 0 )
1898 0 : dfNatOriginLong = 0.0;
1899 :
1900 0 : if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey,
1901 : &dfNatOriginLat, 0, 1 ) == 0
1902 0 : && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey,
1903 : &dfNatOriginLat, 0, 1 ) == 0
1904 0 : && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey,
1905 : &dfNatOriginLat, 0, 1 ) == 0 )
1906 0 : dfNatOriginLat = 0.0;
1907 :
1908 0 : if( GTIFKeyGetDOUBLE(psGTIF, ProjAzimuthAngleGeoKey,
1909 : &dfAzimuth, 0, 1 ) == 0 )
1910 0 : dfAzimuth = 0.0;
1911 :
1912 0 : if( GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtNatOriginGeoKey,
1913 : &dfNatOriginScale, 0, 1 ) == 0
1914 0 : && GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtCenterGeoKey,
1915 : &dfNatOriginScale, 0, 1 ) == 0 )
1916 0 : dfNatOriginScale = 1.0;
1917 :
1918 : /* notdef: should transform to decimal degrees at this point */
1919 :
1920 0 : psDefn->ProjParm[0] = dfNatOriginLat;
1921 0 : psDefn->ProjParmId[0] = ProjCenterLatGeoKey;
1922 0 : psDefn->ProjParm[1] = dfNatOriginLong;
1923 0 : psDefn->ProjParmId[1] = ProjCenterLongGeoKey;
1924 0 : psDefn->ProjParm[2] = dfAzimuth;
1925 0 : psDefn->ProjParmId[2] = ProjAzimuthAngleGeoKey;
1926 0 : psDefn->ProjParm[4] = dfNatOriginScale;
1927 0 : psDefn->ProjParmId[4] = ProjScaleAtCenterGeoKey;
1928 0 : psDefn->ProjParm[5] = dfFalseEasting;
1929 0 : psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
1930 0 : psDefn->ProjParm[6] = dfFalseNorthing;
1931 0 : psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
1932 :
1933 0 : psDefn->nParms = 7;
1934 0 : break;
1935 :
1936 : /* -------------------------------------------------------------------- */
1937 2 : case CT_CassiniSoldner:
1938 : case CT_Polyconic:
1939 : /* -------------------------------------------------------------------- */
1940 2 : if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey,
1941 : &dfNatOriginLong, 0, 1 ) == 0
1942 0 : && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey,
1943 : &dfNatOriginLong, 0, 1 ) == 0
1944 0 : && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey,
1945 : &dfNatOriginLong, 0, 1 ) == 0 )
1946 0 : dfNatOriginLong = 0.0;
1947 :
1948 2 : if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey,
1949 : &dfNatOriginLat, 0, 1 ) == 0
1950 0 : && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey,
1951 : &dfNatOriginLat, 0, 1 ) == 0
1952 0 : && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey,
1953 : &dfNatOriginLat, 0, 1 ) == 0 )
1954 0 : dfNatOriginLat = 0.0;
1955 :
1956 2 : if( GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtNatOriginGeoKey,
1957 : &dfNatOriginScale, 0, 1 ) == 0
1958 1 : && GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtCenterGeoKey,
1959 : &dfNatOriginScale, 0, 1 ) == 0 )
1960 1 : dfNatOriginScale = 1.0;
1961 :
1962 : /* notdef: should transform to decimal degrees at this point */
1963 :
1964 2 : psDefn->ProjParm[0] = dfNatOriginLat;
1965 2 : psDefn->ProjParmId[0] = ProjNatOriginLatGeoKey;
1966 2 : psDefn->ProjParm[1] = dfNatOriginLong;
1967 2 : psDefn->ProjParmId[1] = ProjNatOriginLongGeoKey;
1968 2 : psDefn->ProjParm[4] = dfNatOriginScale;
1969 2 : psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey;
1970 2 : psDefn->ProjParm[5] = dfFalseEasting;
1971 2 : psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
1972 2 : psDefn->ProjParm[6] = dfFalseNorthing;
1973 2 : psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
1974 :
1975 2 : psDefn->nParms = 7;
1976 2 : break;
1977 :
1978 : /* -------------------------------------------------------------------- */
1979 18 : case CT_AzimuthalEquidistant:
1980 : case CT_MillerCylindrical:
1981 : case CT_Gnomonic:
1982 : case CT_LambertAzimEqualArea:
1983 : case CT_Orthographic:
1984 : case CT_NewZealandMapGrid:
1985 : /* -------------------------------------------------------------------- */
1986 18 : if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey,
1987 : &dfNatOriginLong, 0, 1 ) == 0
1988 17 : && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey,
1989 : &dfNatOriginLong, 0, 1 ) == 0
1990 17 : && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey,
1991 : &dfNatOriginLong, 0, 1 ) == 0 )
1992 0 : dfNatOriginLong = 0.0;
1993 :
1994 18 : if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey,
1995 : &dfNatOriginLat, 0, 1 ) == 0
1996 17 : && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey,
1997 : &dfNatOriginLat, 0, 1 ) == 0
1998 17 : && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey,
1999 : &dfNatOriginLat, 0, 1 ) == 0 )
2000 0 : dfNatOriginLat = 0.0;
2001 :
2002 : /* notdef: should transform to decimal degrees at this point */
2003 :
2004 18 : psDefn->ProjParm[0] = dfNatOriginLat;
2005 18 : psDefn->ProjParmId[0] = ProjCenterLatGeoKey;
2006 18 : psDefn->ProjParm[1] = dfNatOriginLong;
2007 18 : psDefn->ProjParmId[1] = ProjCenterLongGeoKey;
2008 18 : psDefn->ProjParm[5] = dfFalseEasting;
2009 18 : psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
2010 18 : psDefn->ProjParm[6] = dfFalseNorthing;
2011 18 : psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
2012 :
2013 18 : psDefn->nParms = 7;
2014 18 : break;
2015 :
2016 : /* -------------------------------------------------------------------- */
2017 5 : case CT_Equirectangular:
2018 : /* -------------------------------------------------------------------- */
2019 5 : if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey,
2020 : &dfNatOriginLong, 0, 1 ) == 0
2021 5 : && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey,
2022 : &dfNatOriginLong, 0, 1 ) == 0
2023 5 : && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey,
2024 : &dfNatOriginLong, 0, 1 ) == 0 )
2025 0 : dfNatOriginLong = 0.0;
2026 :
2027 5 : if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey,
2028 : &dfNatOriginLat, 0, 1 ) == 0
2029 5 : && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey,
2030 : &dfNatOriginLat, 0, 1 ) == 0
2031 5 : && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey,
2032 : &dfNatOriginLat, 0, 1 ) == 0 )
2033 0 : dfNatOriginLat = 0.0;
2034 :
2035 5 : if( GTIFKeyGetDOUBLE(psGTIF, ProjStdParallel1GeoKey,
2036 : &dfStdParallel1, 0, 1 ) == 0 )
2037 0 : dfStdParallel1 = 0.0;
2038 :
2039 : /* notdef: should transform to decimal degrees at this point */
2040 :
2041 5 : psDefn->ProjParm[0] = dfNatOriginLat;
2042 5 : psDefn->ProjParmId[0] = ProjCenterLatGeoKey;
2043 5 : psDefn->ProjParm[1] = dfNatOriginLong;
2044 5 : psDefn->ProjParmId[1] = ProjCenterLongGeoKey;
2045 5 : psDefn->ProjParm[2] = dfStdParallel1;
2046 5 : psDefn->ProjParmId[2] = ProjStdParallel1GeoKey;
2047 5 : psDefn->ProjParm[5] = dfFalseEasting;
2048 5 : psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
2049 5 : psDefn->ProjParm[6] = dfFalseNorthing;
2050 5 : psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
2051 :
2052 5 : psDefn->nParms = 7;
2053 5 : break;
2054 :
2055 : /* -------------------------------------------------------------------- */
2056 7 : case CT_Robinson:
2057 : case CT_Sinusoidal:
2058 : case CT_VanDerGrinten:
2059 : /* -------------------------------------------------------------------- */
2060 7 : if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey,
2061 : &dfNatOriginLong, 0, 1 ) == 0
2062 7 : && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey,
2063 : &dfNatOriginLong, 0, 1 ) == 0
2064 7 : && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey,
2065 : &dfNatOriginLong, 0, 1 ) == 0 )
2066 0 : dfNatOriginLong = 0.0;
2067 :
2068 : /* notdef: should transform to decimal degrees at this point */
2069 :
2070 7 : psDefn->ProjParm[1] = dfNatOriginLong;
2071 7 : psDefn->ProjParmId[1] = ProjCenterLongGeoKey;
2072 7 : psDefn->ProjParm[5] = dfFalseEasting;
2073 7 : psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
2074 7 : psDefn->ProjParm[6] = dfFalseNorthing;
2075 7 : psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
2076 :
2077 7 : psDefn->nParms = 7;
2078 7 : break;
2079 :
2080 : /* -------------------------------------------------------------------- */
2081 6 : case CT_PolarStereographic:
2082 : /* -------------------------------------------------------------------- */
2083 6 : if( GTIFKeyGetDOUBLE(psGTIF, ProjStraightVertPoleLongGeoKey,
2084 : &dfNatOriginLong, 0, 1 ) == 0
2085 0 : && GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey,
2086 : &dfNatOriginLong, 0, 1 ) == 0
2087 0 : && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey,
2088 : &dfNatOriginLong, 0, 1 ) == 0
2089 0 : && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey,
2090 : &dfNatOriginLong, 0, 1 ) == 0 )
2091 0 : dfNatOriginLong = 0.0;
2092 :
2093 6 : if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey,
2094 : &dfNatOriginLat, 0, 1 ) == 0
2095 0 : && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey,
2096 : &dfNatOriginLat, 0, 1 ) == 0
2097 0 : && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey,
2098 : &dfNatOriginLat, 0, 1 ) == 0 )
2099 0 : dfNatOriginLat = 0.0;
2100 :
2101 6 : if( GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtNatOriginGeoKey,
2102 : &dfNatOriginScale, 0, 1 ) == 0
2103 0 : && GTIFKeyGetDOUBLE(psGTIF, ProjScaleAtCenterGeoKey,
2104 : &dfNatOriginScale, 0, 1 ) == 0 )
2105 0 : dfNatOriginScale = 1.0;
2106 :
2107 : /* notdef: should transform to decimal degrees at this point */
2108 :
2109 6 : psDefn->ProjParm[0] = dfNatOriginLat;
2110 6 : psDefn->ProjParmId[0] = ProjNatOriginLatGeoKey;;
2111 6 : psDefn->ProjParm[1] = dfNatOriginLong;
2112 6 : psDefn->ProjParmId[1] = ProjStraightVertPoleLongGeoKey;
2113 6 : psDefn->ProjParm[4] = dfNatOriginScale;
2114 6 : psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey;
2115 6 : psDefn->ProjParm[5] = dfFalseEasting;
2116 6 : psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
2117 6 : psDefn->ProjParm[6] = dfFalseNorthing;
2118 6 : psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
2119 :
2120 6 : psDefn->nParms = 7;
2121 6 : break;
2122 :
2123 : /* -------------------------------------------------------------------- */
2124 12 : case CT_LambertConfConic_2SP:
2125 : /* -------------------------------------------------------------------- */
2126 12 : if( GTIFKeyGetDOUBLE(psGTIF, ProjStdParallel1GeoKey,
2127 : &dfStdParallel1, 0, 1 ) == 0 )
2128 0 : dfStdParallel1 = 0.0;
2129 :
2130 12 : if( GTIFKeyGetDOUBLE(psGTIF, ProjStdParallel2GeoKey,
2131 : &dfStdParallel2, 0, 1 ) == 0 )
2132 0 : dfStdParallel1 = 0.0;
2133 :
2134 12 : if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey,
2135 : &dfNatOriginLong, 0, 1 ) == 0
2136 12 : && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey,
2137 : &dfNatOriginLong, 0, 1 ) == 0
2138 0 : && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey,
2139 : &dfNatOriginLong, 0, 1 ) == 0 )
2140 0 : dfNatOriginLong = 0.0;
2141 :
2142 12 : if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey,
2143 : &dfNatOriginLat, 0, 1 ) == 0
2144 12 : && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey,
2145 : &dfNatOriginLat, 0, 1 ) == 0
2146 0 : && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey,
2147 : &dfNatOriginLat, 0, 1 ) == 0 )
2148 0 : dfNatOriginLat = 0.0;
2149 :
2150 : /* notdef: should transform to decimal degrees at this point */
2151 :
2152 12 : psDefn->ProjParm[0] = dfNatOriginLat;
2153 12 : psDefn->ProjParmId[0] = ProjFalseOriginLatGeoKey;
2154 12 : psDefn->ProjParm[1] = dfNatOriginLong;
2155 12 : psDefn->ProjParmId[1] = ProjFalseOriginLongGeoKey;
2156 12 : psDefn->ProjParm[2] = dfStdParallel1;
2157 12 : psDefn->ProjParmId[2] = ProjStdParallel1GeoKey;
2158 12 : psDefn->ProjParm[3] = dfStdParallel2;
2159 12 : psDefn->ProjParmId[3] = ProjStdParallel2GeoKey;
2160 12 : psDefn->ProjParm[5] = dfFalseEasting;
2161 12 : psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
2162 12 : psDefn->ProjParm[6] = dfFalseNorthing;
2163 12 : psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
2164 :
2165 12 : psDefn->nParms = 7;
2166 12 : break;
2167 :
2168 : /* -------------------------------------------------------------------- */
2169 6 : case CT_AlbersEqualArea:
2170 : case CT_EquidistantConic:
2171 : /* -------------------------------------------------------------------- */
2172 6 : if( GTIFKeyGetDOUBLE(psGTIF, ProjStdParallel1GeoKey,
2173 : &dfStdParallel1, 0, 1 ) == 0 )
2174 0 : dfStdParallel1 = 0.0;
2175 :
2176 6 : if( GTIFKeyGetDOUBLE(psGTIF, ProjStdParallel2GeoKey,
2177 : &dfStdParallel2, 0, 1 ) == 0 )
2178 0 : dfStdParallel2 = 0.0;
2179 :
2180 6 : if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey,
2181 : &dfNatOriginLong, 0, 1 ) == 0
2182 0 : && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey,
2183 : &dfNatOriginLong, 0, 1 ) == 0
2184 0 : && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey,
2185 : &dfNatOriginLong, 0, 1 ) == 0 )
2186 0 : dfNatOriginLong = 0.0;
2187 :
2188 6 : if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLatGeoKey,
2189 : &dfNatOriginLat, 0, 1 ) == 0
2190 0 : && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLatGeoKey,
2191 : &dfNatOriginLat, 0, 1 ) == 0
2192 0 : && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLatGeoKey,
2193 : &dfNatOriginLat, 0, 1 ) == 0 )
2194 0 : dfNatOriginLat = 0.0;
2195 :
2196 : /* notdef: should transform to decimal degrees at this point */
2197 :
2198 6 : psDefn->ProjParm[0] = dfStdParallel1;
2199 6 : psDefn->ProjParmId[0] = ProjStdParallel1GeoKey;
2200 6 : psDefn->ProjParm[1] = dfStdParallel2;
2201 6 : psDefn->ProjParmId[1] = ProjStdParallel2GeoKey;
2202 6 : psDefn->ProjParm[2] = dfNatOriginLat;
2203 6 : psDefn->ProjParmId[2] = ProjNatOriginLatGeoKey;
2204 6 : psDefn->ProjParm[3] = dfNatOriginLong;
2205 6 : psDefn->ProjParmId[3] = ProjNatOriginLongGeoKey;
2206 6 : psDefn->ProjParm[5] = dfFalseEasting;
2207 6 : psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
2208 6 : psDefn->ProjParm[6] = dfFalseNorthing;
2209 6 : psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
2210 :
2211 6 : psDefn->nParms = 7;
2212 6 : break;
2213 :
2214 : /* -------------------------------------------------------------------- */
2215 1 : case CT_CylindricalEqualArea:
2216 : /* -------------------------------------------------------------------- */
2217 1 : if( GTIFKeyGetDOUBLE(psGTIF, ProjStdParallel1GeoKey,
2218 : &dfStdParallel1, 0, 1 ) == 0 )
2219 0 : dfStdParallel1 = 0.0;
2220 :
2221 1 : if( GTIFKeyGetDOUBLE(psGTIF, ProjNatOriginLongGeoKey,
2222 : &dfNatOriginLong, 0, 1 ) == 0
2223 0 : && GTIFKeyGetDOUBLE(psGTIF, ProjFalseOriginLongGeoKey,
2224 : &dfNatOriginLong, 0, 1 ) == 0
2225 0 : && GTIFKeyGetDOUBLE(psGTIF, ProjCenterLongGeoKey,
2226 : &dfNatOriginLong, 0, 1 ) == 0 )
2227 0 : dfNatOriginLong = 0.0;
2228 :
2229 : /* notdef: should transform to decimal degrees at this point */
2230 :
2231 1 : psDefn->ProjParm[0] = dfStdParallel1;
2232 1 : psDefn->ProjParmId[0] = ProjStdParallel1GeoKey;
2233 1 : psDefn->ProjParm[1] = dfNatOriginLong;
2234 1 : psDefn->ProjParmId[1] = ProjNatOriginLongGeoKey;
2235 1 : psDefn->ProjParm[5] = dfFalseEasting;
2236 1 : psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
2237 1 : psDefn->ProjParm[6] = dfFalseNorthing;
2238 1 : psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
2239 :
2240 1 : psDefn->nParms = 7;
2241 1 : break;
2242 : }
2243 :
2244 : /* -------------------------------------------------------------------- */
2245 : /* Normalize any linear parameters into meters. In GeoTIFF */
2246 : /* the linear projection parameter tags are normally in the */
2247 : /* units of the coordinate system described. */
2248 : /* -------------------------------------------------------------------- */
2249 592 : for( int iParam = 0; iParam < psDefn->nParms; iParam++ )
2250 : {
2251 518 : switch( psDefn->ProjParmId[iParam] )
2252 : {
2253 148 : case ProjFalseEastingGeoKey:
2254 : case ProjFalseNorthingGeoKey:
2255 : case ProjFalseOriginEastingGeoKey:
2256 : case ProjFalseOriginNorthingGeoKey:
2257 : case ProjCenterEastingGeoKey:
2258 : case ProjCenterNorthingGeoKey:
2259 148 : if( psDefn->UOMLengthInMeters != 0
2260 148 : && psDefn->UOMLengthInMeters != 1.0 )
2261 : {
2262 26 : psDefn->ProjParm[iParam] *= psDefn->UOMLengthInMeters;
2263 : }
2264 148 : break;
2265 :
2266 370 : default:
2267 370 : break;
2268 : }
2269 : }
2270 74 : }
2271 :
2272 : /************************************************************************/
2273 : /* GTIFGetDefn() */
2274 : /************************************************************************/
2275 :
2276 : /**
2277 : @param psGTIF GeoTIFF information handle as returned by GTIFNew.
2278 : @param psDefn Pointer to an existing GTIFDefn structure allocated by GTIFAllocDefn().
2279 :
2280 : @return TRUE if the function has been successful, otherwise FALSE.
2281 :
2282 : This function reads the coordinate system definition from a GeoTIFF file,
2283 : and <i>normalizes</i> it into a set of component information using
2284 : definitions from the EPSG database as provided by the PROJ library.
2285 : This function is intended to simplify correct support for
2286 : reading files with defined PCS (Projected Coordinate System) codes that
2287 : wouldn't otherwise be directly known by application software by reducing
2288 : it to the underlying projection method, parameters, datum, ellipsoid,
2289 : prime meridian and units.<p>
2290 :
2291 : The application should pass a pointer to an existing uninitialized
2292 : GTIFDefn structure, and GTIFGetDefn() will fill it in. The function
2293 : currently always returns TRUE but in the future will return FALSE if
2294 : the database is not found. In any event, all geokeys actually found in the
2295 : file will be copied into the GTIFDefn. However, if the database isn't
2296 : found, codes implied by other codes will not be set properly.<p>
2297 :
2298 : GTIFGetDefn() will not generally work if the EPSG derived database cannot
2299 : be found.<p>
2300 :
2301 : The normalization methodology operates by fetching tags from the GeoTIFF
2302 : file, and then setting all other tags implied by them in the structure. The
2303 : implied relationships are worked out by reading definitions from the
2304 : various EPSG derived database tables.<p>
2305 :
2306 : For instance, if a PCS (ProjectedCSTypeGeoKey) is found in the GeoTIFF file
2307 : this code is used to lookup a record in the database.
2308 : For example given the PCS 26746 we can find the name
2309 : (NAD27 / California zone VI), the GCS 4257 (NAD27), and the ProjectionCode
2310 : 10406 (California CS27 zone VI). The GCS, and ProjectionCode can in turn
2311 : be looked up in other tables until all the details of units, ellipsoid,
2312 : prime meridian, datum, projection (LambertConfConic_2SP) and projection
2313 : parameters are established. A full listgeo dump of a file
2314 : for this result might look like the following, all based on a single PCS
2315 : value:<p>
2316 :
2317 : <pre>
2318 : % listgeo -norm ~/data/geotiff/pci_eg/spaf27.tif
2319 : Geotiff_Information:
2320 : Version: 1
2321 : Key_Revision: 1.0
2322 : Tagged_Information:
2323 : ModelTiepointTag (2,3):
2324 : 0 0 0
2325 : 1577139.71 634349.176 0
2326 : ModelPixelScaleTag (1,3):
2327 : 195.509321 198.32184 0
2328 : End_Of_Tags.
2329 : Keyed_Information:
2330 : GTModelTypeGeoKey (Short,1): ModelTypeProjected
2331 : GTRasterTypeGeoKey (Short,1): RasterPixelIsArea
2332 : ProjectedCSTypeGeoKey (Short,1): PCS_NAD27_California_VI
2333 : End_Of_Keys.
2334 : End_Of_Geotiff.
2335 :
2336 : PCS = 26746 (NAD27 / California zone VI)
2337 : Projection = 10406 (California CS27 zone VI)
2338 : Projection Method: CT_LambertConfConic_2SP
2339 : ProjStdParallel1GeoKey: 33.883333
2340 : ProjStdParallel2GeoKey: 32.766667
2341 : ProjFalseOriginLatGeoKey: 32.166667
2342 : ProjFalseOriginLongGeoKey: -116.233333
2343 : ProjFalseEastingGeoKey: 609601.219202
2344 : ProjFalseNorthingGeoKey: 0.000000
2345 : GCS: 4267/NAD27
2346 : Datum: 6267/North American Datum 1927
2347 : Ellipsoid: 7008/Clarke 1866 (6378206.40,6356583.80)
2348 : Prime Meridian: 8901/Greenwich (0.000000)
2349 : Projection Linear Units: 9003/US survey foot (0.304801m)
2350 : </pre>
2351 :
2352 : Note that GTIFGetDefn() does not inspect or return the tiepoints and scale.
2353 : This must be handled separately as it normally would. It is intended to
2354 : simplify capture and normalization of the coordinate system definition.
2355 : Note that GTIFGetDefn() also does the following things:
2356 :
2357 : <ol>
2358 : <li> Convert all angular values to decimal degrees.
2359 : <li> Convert all linear values to meters.
2360 : <li> Return the linear units and conversion to meters for the tiepoints and
2361 : scale (though the tiepoints and scale remain in their native units).
2362 : <li> When reading projection parameters a variety of differences between
2363 : different GeoTIFF generators are handled, and a normalized set of parameters
2364 : for each projection are always returned.
2365 : </ol>
2366 :
2367 : Code fields in the GTIFDefn are filled with KvUserDefined if there is not
2368 : value to assign. The parameter lists for each of the underlying projection
2369 : transform methods can be found at the
2370 : <a href="http://www.remotesensing.org/geotiff/proj_list">Projections</a>
2371 : page. Note that nParms will be set based on the maximum parameter used.
2372 : Some of the parameters may not be used in which case the
2373 : GTIFDefn::ProjParmId[] will
2374 : be zero. This is done to retain correspondence to the EPSG parameter
2375 : numbering scheme.<p>
2376 :
2377 : The
2378 : <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
2379 : be used as an example of code that converts a GTIFDefn into another projection
2380 : system.<p>
2381 :
2382 : @see GTIFKeySet()
2383 :
2384 : */
2385 :
2386 7785 : int GTIFGetDefn( GTIF * psGTIF, GTIFDefn * psDefn )
2387 :
2388 : {
2389 7785 : if( !GTIFGetPROJContext(psGTIF, TRUE, NULL) )
2390 : {
2391 0 : return FALSE;
2392 : }
2393 :
2394 : /* -------------------------------------------------------------------- */
2395 : /* Initially we default all the information we can. */
2396 : /* -------------------------------------------------------------------- */
2397 7785 : psDefn->DefnSet = 1;
2398 7785 : psDefn->Model = KvUserDefined;
2399 7785 : psDefn->PCS = KvUserDefined;
2400 7785 : psDefn->GCS = KvUserDefined;
2401 7785 : psDefn->UOMLength = KvUserDefined;
2402 7785 : psDefn->UOMLengthInMeters = 1.0;
2403 7785 : psDefn->UOMAngle = KvUserDefined;
2404 7785 : psDefn->UOMAngleInDegrees = 1.0;
2405 7785 : psDefn->Datum = KvUserDefined;
2406 7785 : psDefn->Ellipsoid = KvUserDefined;
2407 7785 : psDefn->SemiMajor = 0.0;
2408 7785 : psDefn->SemiMinor = 0.0;
2409 7785 : psDefn->PM = KvUserDefined;
2410 7785 : psDefn->PMLongToGreenwich = 0.0;
2411 : #if !defined(GEO_NORMALIZE_DISABLE_TOWGS84)
2412 7785 : psDefn->TOWGS84Count = 0;
2413 7785 : memset( psDefn->TOWGS84, 0, sizeof(psDefn->TOWGS84) );
2414 : #endif
2415 :
2416 7785 : psDefn->ProjCode = KvUserDefined;
2417 7785 : psDefn->Projection = KvUserDefined;
2418 7785 : psDefn->CTProjection = KvUserDefined;
2419 :
2420 7785 : psDefn->nParms = 0;
2421 85635 : for( int i = 0; i < MAX_GTIF_PROJPARMS; i++ )
2422 : {
2423 77850 : psDefn->ProjParm[i] = 0.0;
2424 77850 : psDefn->ProjParmId[i] = 0;
2425 : }
2426 :
2427 7785 : psDefn->MapSys = KvUserDefined;
2428 7785 : psDefn->Zone = 0;
2429 :
2430 : /* -------------------------------------------------------------------- */
2431 : /* Do we have any geokeys? */
2432 : /* -------------------------------------------------------------------- */
2433 : {
2434 7785 : int nKeyCount = 0;
2435 : int anVersion[3];
2436 7785 : GTIFDirectoryInfo( psGTIF, anVersion, &nKeyCount );
2437 :
2438 7785 : if( nKeyCount == 0 )
2439 : {
2440 2281 : psDefn->DefnSet = 0;
2441 2281 : return FALSE;
2442 : }
2443 : }
2444 :
2445 : /* -------------------------------------------------------------------- */
2446 : /* Try to get the overall model type. */
2447 : /* -------------------------------------------------------------------- */
2448 5504 : GTIFKeyGetSSHORT(psGTIF,GTModelTypeGeoKey,&(psDefn->Model));
2449 :
2450 : /* -------------------------------------------------------------------- */
2451 : /* Extract the Geog units. */
2452 : /* -------------------------------------------------------------------- */
2453 5504 : short nGeogUOMLinear = 9001; /* Linear_Meter */
2454 5504 : if( GTIFKeyGetSSHORT(psGTIF, GeogLinearUnitsGeoKey, &nGeogUOMLinear) == 1 )
2455 : {
2456 23 : psDefn->UOMLength = nGeogUOMLinear;
2457 : }
2458 :
2459 : /* -------------------------------------------------------------------- */
2460 : /* Try to get a PCS. */
2461 : /* -------------------------------------------------------------------- */
2462 5504 : if( GTIFKeyGetSSHORT(psGTIF,ProjectedCSTypeGeoKey, &(psDefn->PCS)) == 1
2463 3035 : && psDefn->PCS != KvUserDefined )
2464 : {
2465 : /*
2466 : * Translate this into useful information.
2467 : */
2468 2947 : GTIFGetPCSInfoEx( psGTIF->pj_context,
2469 2947 : psDefn->PCS, NULL, &(psDefn->ProjCode),
2470 : &(psDefn->UOMLength), &(psDefn->GCS) );
2471 : }
2472 :
2473 : /* -------------------------------------------------------------------- */
2474 : /* If we have the PCS code, but didn't find it in the database */
2475 : /* (likely because we can't find them) we will try some ``jiffy */
2476 : /* rules'' for UTM and state plane. */
2477 : /* -------------------------------------------------------------------- */
2478 5504 : if( psDefn->PCS != KvUserDefined && psDefn->ProjCode == KvUserDefined )
2479 : {
2480 : int nZone;
2481 0 : int nGCS = psDefn->GCS;
2482 :
2483 0 : const int nMapSys = GTIFPCSToMapSys( psDefn->PCS, &nGCS, &nZone );
2484 0 : if( nMapSys != KvUserDefined )
2485 : {
2486 0 : psDefn->ProjCode = (short) GTIFMapSysToProj( nMapSys, nZone );
2487 0 : psDefn->GCS = (short) nGCS;
2488 : }
2489 : }
2490 :
2491 : /* -------------------------------------------------------------------- */
2492 : /* If the Proj_ code is specified directly, use that. */
2493 : /* -------------------------------------------------------------------- */
2494 5504 : if( psDefn->ProjCode == KvUserDefined )
2495 2557 : GTIFKeyGetSSHORT(psGTIF, ProjectionGeoKey, &(psDefn->ProjCode));
2496 :
2497 5504 : if( psDefn->ProjCode != KvUserDefined )
2498 : {
2499 : /*
2500 : * We have an underlying projection transformation value. Look
2501 : * this up. For a PCS of ``WGS 84 / UTM 11'' the transformation
2502 : * would be Transverse Mercator, with a particular set of options.
2503 : * The nProjTRFCode itself would correspond to the name
2504 : * ``UTM zone 11N'', and doesn't include datum info.
2505 : */
2506 2960 : GTIFGetProjTRFInfoEx( psGTIF->pj_context,
2507 2960 : psDefn->ProjCode, NULL, &(psDefn->Projection),
2508 2960 : psDefn->ProjParm );
2509 :
2510 : /*
2511 : * Set the GeoTIFF identity of the parameters.
2512 : */
2513 2960 : psDefn->CTProjection = (short)
2514 2960 : EPSGProjMethodToCTProjMethod( psDefn->Projection, FALSE );
2515 :
2516 2960 : SetGTParamIds( EPSGProjMethodToCTProjMethod(psDefn->Projection, TRUE),
2517 2960 : psDefn->Projection,
2518 2960 : psDefn->ProjParmId, NULL);
2519 2960 : psDefn->nParms = 7;
2520 : }
2521 :
2522 : /* -------------------------------------------------------------------- */
2523 : /* Try to get a GCS. If found, it will override any implied by */
2524 : /* the PCS. */
2525 : /* -------------------------------------------------------------------- */
2526 5504 : GTIFKeyGetSSHORT(psGTIF, GeographicTypeGeoKey, &(psDefn->GCS));
2527 5504 : if( psDefn->GCS < 1 || psDefn->GCS >= KvUserDefined )
2528 405 : psDefn->GCS = KvUserDefined;
2529 :
2530 : /* -------------------------------------------------------------------- */
2531 : /* Derive the datum, and prime meridian from the GCS. */
2532 : /* -------------------------------------------------------------------- */
2533 5504 : if( psDefn->GCS != KvUserDefined )
2534 : {
2535 5099 : GTIFGetGCSInfoEx( psGTIF->pj_context,
2536 5099 : psDefn->GCS, NULL, &(psDefn->Datum), &(psDefn->PM),
2537 : &(psDefn->UOMAngle) );
2538 : }
2539 :
2540 : /* -------------------------------------------------------------------- */
2541 : /* Handle the GCS angular units. GeogAngularUnitsGeoKey */
2542 : /* overrides the GCS or PCS setting. */
2543 : /* -------------------------------------------------------------------- */
2544 5504 : GTIFKeyGetSSHORT(psGTIF, GeogAngularUnitsGeoKey, &(psDefn->UOMAngle));
2545 5504 : if( psDefn->UOMAngle != KvUserDefined )
2546 : {
2547 5478 : GTIFGetUOMAngleInfoEx( psGTIF->pj_context,
2548 5478 : psDefn->UOMAngle, NULL,
2549 : &(psDefn->UOMAngleInDegrees) );
2550 : }
2551 :
2552 : /* -------------------------------------------------------------------- */
2553 : /* Check for a datum setting, and then use the datum to derive */
2554 : /* an ellipsoid. */
2555 : /* -------------------------------------------------------------------- */
2556 5504 : GTIFKeyGetSSHORT(psGTIF, GeogGeodeticDatumGeoKey, &(psDefn->Datum));
2557 :
2558 5504 : if( psDefn->Datum != KvUserDefined )
2559 : {
2560 5101 : GTIFGetDatumInfoEx( psGTIF->pj_context,
2561 5101 : psDefn->Datum, NULL, &(psDefn->Ellipsoid) );
2562 : }
2563 :
2564 : /* -------------------------------------------------------------------- */
2565 : /* Check for an explicit ellipsoid. Use the ellipsoid to */
2566 : /* derive the ellipsoid characteristics, if possible. */
2567 : /* -------------------------------------------------------------------- */
2568 5504 : GTIFKeyGetSSHORT(psGTIF, GeogEllipsoidGeoKey, &(psDefn->Ellipsoid));
2569 :
2570 5504 : if( psDefn->Ellipsoid != KvUserDefined )
2571 : {
2572 5104 : GTIFGetEllipsoidInfoEx( psGTIF->pj_context,
2573 5104 : psDefn->Ellipsoid, NULL,
2574 : &(psDefn->SemiMajor), &(psDefn->SemiMinor) );
2575 : }
2576 :
2577 : /* -------------------------------------------------------------------- */
2578 : /* Check for overridden ellipsoid parameters. It would be nice */
2579 : /* to warn if they conflict with provided information, but for */
2580 : /* now we just override. */
2581 : /* -------------------------------------------------------------------- */
2582 5504 : CPL_IGNORE_RET_VAL_INT(GTIFKeyGetDOUBLE(psGTIF, GeogSemiMajorAxisGeoKey, &(psDefn->SemiMajor), 0, 1 ));
2583 5504 : CPL_IGNORE_RET_VAL_INT(GTIFKeyGetDOUBLE(psGTIF, GeogSemiMinorAxisGeoKey, &(psDefn->SemiMinor), 0, 1 ));
2584 :
2585 : double dfInvFlattening;
2586 5504 : if( GTIFKeyGetDOUBLE(psGTIF, GeogInvFlatteningGeoKey, &dfInvFlattening,
2587 : 0, 1 ) == 1 )
2588 : {
2589 1826 : if( dfInvFlattening != 0.0 )
2590 1826 : psDefn->SemiMinor =
2591 1826 : psDefn->SemiMajor * (1 - 1.0/dfInvFlattening);
2592 : else
2593 0 : psDefn->SemiMinor = psDefn->SemiMajor;
2594 : }
2595 :
2596 : /* -------------------------------------------------------------------- */
2597 : /* Get the prime meridian info. */
2598 : /* -------------------------------------------------------------------- */
2599 5504 : GTIFKeyGetSSHORT(psGTIF, GeogPrimeMeridianGeoKey, &(psDefn->PM));
2600 :
2601 5504 : if( psDefn->PM != KvUserDefined )
2602 : {
2603 5100 : GTIFGetPMInfoEx( psGTIF->pj_context,
2604 5100 : psDefn->PM, NULL, &(psDefn->PMLongToGreenwich) );
2605 : }
2606 : else
2607 : {
2608 404 : CPL_IGNORE_RET_VAL_INT(GTIFKeyGetDOUBLE(psGTIF, GeogPrimeMeridianLongGeoKey,
2609 : &(psDefn->PMLongToGreenwich), 0, 1 ));
2610 :
2611 404 : psDefn->PMLongToGreenwich =
2612 404 : GTIFAngleToDD( psDefn->PMLongToGreenwich,
2613 404 : psDefn->UOMAngle );
2614 : }
2615 :
2616 : /* -------------------------------------------------------------------- */
2617 : /* Get the TOWGS84 parameters. */
2618 : /* -------------------------------------------------------------------- */
2619 : #if !defined(GEO_NORMALIZE_DISABLE_TOWGS84)
2620 5504 : psDefn->TOWGS84Count =
2621 5504 : (short)GTIFKeyGetDOUBLE(psGTIF, GeogTOWGS84GeoKey, psDefn->TOWGS84, 0, 7 );
2622 : #endif
2623 :
2624 : /* -------------------------------------------------------------------- */
2625 : /* Have the projection units of measure been overridden? We */
2626 : /* should likely be doing something about angular units too, */
2627 : /* but these are very rarely not decimal degrees for actual */
2628 : /* file coordinates. */
2629 : /* -------------------------------------------------------------------- */
2630 5504 : GTIFKeyGetSSHORT(psGTIF,ProjLinearUnitsGeoKey,&(psDefn->UOMLength));
2631 :
2632 5504 : if( psDefn->UOMLength != KvUserDefined )
2633 : {
2634 3360 : GTIFGetUOMLengthInfoEx( psGTIF->pj_context,
2635 3360 : psDefn->UOMLength, NULL,
2636 : &(psDefn->UOMLengthInMeters) );
2637 : }
2638 : else
2639 : {
2640 2144 : CPL_IGNORE_RET_VAL_INT(GTIFKeyGetDOUBLE(psGTIF,ProjLinearUnitSizeGeoKey,&(psDefn->UOMLengthInMeters),0,1));
2641 : }
2642 :
2643 : /* -------------------------------------------------------------------- */
2644 : /* Handle a variety of user defined transform types. */
2645 : /* -------------------------------------------------------------------- */
2646 5504 : if( GTIFKeyGetSSHORT(psGTIF,ProjCoordTransGeoKey,
2647 : &(psDefn->CTProjection)) == 1)
2648 : {
2649 74 : GTIFFetchProjParms( psGTIF, psDefn );
2650 : }
2651 :
2652 : /* -------------------------------------------------------------------- */
2653 : /* Try to set the zoned map system information. */
2654 : /* -------------------------------------------------------------------- */
2655 5504 : psDefn->MapSys = GTIFProjToMapSys( psDefn->ProjCode, &(psDefn->Zone) );
2656 :
2657 : /* -------------------------------------------------------------------- */
2658 : /* If this is UTM, and we were unable to extract the projection */
2659 : /* parameters from the database just set them directly now, */
2660 : /* since it's pretty easy, and a common case. */
2661 : /* -------------------------------------------------------------------- */
2662 5504 : if( (psDefn->MapSys == MapSys_UTM_North
2663 2697 : || psDefn->MapSys == MapSys_UTM_South)
2664 2810 : && psDefn->CTProjection == KvUserDefined )
2665 : {
2666 0 : psDefn->CTProjection = CT_TransverseMercator;
2667 0 : psDefn->nParms = 7;
2668 0 : psDefn->ProjParmId[0] = ProjNatOriginLatGeoKey;
2669 0 : psDefn->ProjParm[0] = 0.0;
2670 :
2671 0 : psDefn->ProjParmId[1] = ProjNatOriginLongGeoKey;
2672 0 : psDefn->ProjParm[1] = psDefn->Zone*6 - 183.0;
2673 :
2674 0 : psDefn->ProjParmId[4] = ProjScaleAtNatOriginGeoKey;
2675 0 : psDefn->ProjParm[4] = 0.9996;
2676 :
2677 0 : psDefn->ProjParmId[5] = ProjFalseEastingGeoKey;
2678 0 : psDefn->ProjParm[5] = 500000.0;
2679 :
2680 0 : psDefn->ProjParmId[6] = ProjFalseNorthingGeoKey;
2681 :
2682 0 : if( psDefn->MapSys == MapSys_UTM_North )
2683 0 : psDefn->ProjParm[6] = 0.0;
2684 : else
2685 0 : psDefn->ProjParm[6] = 10000000.0;
2686 : }
2687 :
2688 5504 : return TRUE;
2689 : }
2690 :
2691 : /************************************************************************/
2692 : /* GTIFDecToDMS() */
2693 : /* */
2694 : /* Convenient function to translate decimal degrees to DMS */
2695 : /* format for reporting to a user. */
2696 : /************************************************************************/
2697 :
2698 0 : const char *GTIFDecToDMS( double dfAngle, const char * pszAxis,
2699 : int nPrecision )
2700 : {
2701 0 : if( !(dfAngle >= -360 && dfAngle <= 360) )
2702 0 : return "";
2703 :
2704 0 : double dfRound = 0.5/60;
2705 0 : for( int i = 0; i < nPrecision; i++ )
2706 0 : dfRound = dfRound * 0.1;
2707 :
2708 0 : int nDegrees = (int) ABS(dfAngle);
2709 0 : int nMinutes = (int) ((ABS(dfAngle) - nDegrees) * 60 + dfRound);
2710 0 : if( nMinutes == 60 )
2711 : {
2712 0 : nDegrees ++;
2713 0 : nMinutes = 0;
2714 : }
2715 0 : const double dfSeconds = ABS((ABS(dfAngle) * 3600 - nDegrees*3600 - nMinutes*60));
2716 :
2717 0 : const char *pszHemisphere = NULL;
2718 0 : if( EQUAL(pszAxis,"Long") && dfAngle < 0.0 )
2719 0 : pszHemisphere = "W";
2720 0 : else if( EQUAL(pszAxis,"Long") )
2721 0 : pszHemisphere = "E";
2722 0 : else if( dfAngle < 0.0 )
2723 0 : pszHemisphere = "S";
2724 : else
2725 0 : pszHemisphere = "N";
2726 :
2727 : char szFormat[30];
2728 0 : snprintf(szFormat, sizeof(szFormat), "%%3dd%%2d\'%%%d.%df\"%s",
2729 : nPrecision+3, nPrecision, pszHemisphere);
2730 : static char szBuffer[50];
2731 0 : snprintf(
2732 : szBuffer, sizeof(szBuffer), szFormat, nDegrees, nMinutes, dfSeconds);
2733 :
2734 0 : return szBuffer;
2735 : }
2736 :
2737 : /************************************************************************/
2738 : /* GTIFPrintDefn() */
2739 : /* */
2740 : /* Report the contents of a GTIFDefn structure ... mostly for */
2741 : /* debugging. */
2742 : /************************************************************************/
2743 :
2744 0 : void GTIFPrintDefnEx( GTIF *psGTIF, GTIFDefn * psDefn, FILE * fp )
2745 :
2746 : {
2747 0 : GTIFGetPROJContext(psGTIF, TRUE, NULL);
2748 :
2749 : /* -------------------------------------------------------------------- */
2750 : /* Do we have anything to report? */
2751 : /* -------------------------------------------------------------------- */
2752 0 : if( !psDefn->DefnSet )
2753 : {
2754 0 : fprintf( fp, "No GeoKeys found.\n" );
2755 0 : return;
2756 : }
2757 :
2758 : /* -------------------------------------------------------------------- */
2759 : /* Get the PCS name if possible. */
2760 : /* -------------------------------------------------------------------- */
2761 0 : if( psDefn->PCS != KvUserDefined )
2762 : {
2763 0 : char *pszPCSName = NULL;
2764 :
2765 0 : if( psGTIF->pj_context )
2766 : {
2767 0 : GTIFGetPCSInfoEx( psGTIF->pj_context,
2768 0 : psDefn->PCS, &pszPCSName, NULL, NULL, NULL );
2769 : }
2770 0 : if( pszPCSName == NULL )
2771 0 : pszPCSName = CPLStrdup("name unknown");
2772 :
2773 0 : fprintf( fp, "PCS = %d (%s)\n", psDefn->PCS, pszPCSName );
2774 0 : CPLFree( pszPCSName );
2775 : }
2776 :
2777 : /* -------------------------------------------------------------------- */
2778 : /* Dump the projection code if possible. */
2779 : /* -------------------------------------------------------------------- */
2780 0 : if( psDefn->ProjCode != KvUserDefined )
2781 : {
2782 0 : char *pszTRFName = NULL;
2783 :
2784 0 : if( psGTIF->pj_context )
2785 : {
2786 0 : GTIFGetProjTRFInfoEx( psGTIF->pj_context,
2787 0 : psDefn->ProjCode, &pszTRFName, NULL, NULL );
2788 : }
2789 0 : if( pszTRFName == NULL )
2790 0 : pszTRFName = CPLStrdup("");
2791 :
2792 0 : fprintf( fp, "Projection = %d (%s)\n",
2793 0 : psDefn->ProjCode, pszTRFName );
2794 :
2795 0 : CPLFree( pszTRFName );
2796 : }
2797 :
2798 : /* -------------------------------------------------------------------- */
2799 : /* Try to dump the projection method name, and parameters if possible.*/
2800 : /* -------------------------------------------------------------------- */
2801 0 : if( psDefn->CTProjection != KvUserDefined )
2802 : {
2803 : const char *pszProjectionMethodName =
2804 0 : GTIFValueNameEx(psGTIF,
2805 : ProjCoordTransGeoKey,
2806 0 : psDefn->CTProjection);
2807 :
2808 0 : if( pszProjectionMethodName == NULL )
2809 0 : pszProjectionMethodName = "(unknown)";
2810 :
2811 0 : fprintf( fp, "Projection Method: %s\n", pszProjectionMethodName );
2812 :
2813 0 : for( int i = 0; i < psDefn->nParms; i++ )
2814 : {
2815 0 : if( psDefn->ProjParmId[i] == 0 )
2816 0 : continue;
2817 :
2818 0 : char* pszName = GTIFKeyName((geokey_t) psDefn->ProjParmId[i]);
2819 0 : if( pszName == NULL )
2820 0 : pszName = "(unknown)";
2821 :
2822 0 : if( i < 4 )
2823 : {
2824 : char *pszAxisName;
2825 :
2826 0 : if( strstr(pszName,"Long") != NULL )
2827 0 : pszAxisName = "Long";
2828 0 : else if( strstr(pszName,"Lat") != NULL )
2829 0 : pszAxisName = "Lat";
2830 : else
2831 0 : pszAxisName = "?";
2832 :
2833 0 : fprintf( fp, " %s: %f (%s)\n",
2834 : pszName, psDefn->ProjParm[i],
2835 : GTIFDecToDMS( psDefn->ProjParm[i], pszAxisName, 2 ) );
2836 : }
2837 0 : else if( i == 4 )
2838 0 : fprintf( fp, " %s: %f\n", pszName, psDefn->ProjParm[i] );
2839 : else
2840 0 : fprintf( fp, " %s: %f m\n", pszName, psDefn->ProjParm[i] );
2841 : }
2842 : }
2843 :
2844 : /* -------------------------------------------------------------------- */
2845 : /* Report the GCS name, and number. */
2846 : /* -------------------------------------------------------------------- */
2847 0 : if( psDefn->GCS != KvUserDefined )
2848 : {
2849 0 : char *pszName = NULL;
2850 :
2851 0 : if( psGTIF->pj_context )
2852 : {
2853 0 : GTIFGetGCSInfoEx( psGTIF->pj_context,
2854 0 : psDefn->GCS, &pszName, NULL, NULL, NULL );
2855 : }
2856 0 : if( pszName == NULL )
2857 0 : pszName = CPLStrdup("(unknown)");
2858 :
2859 0 : fprintf( fp, "GCS: %d/%s\n", psDefn->GCS, pszName );
2860 0 : CPLFree( pszName );
2861 : }
2862 :
2863 : /* -------------------------------------------------------------------- */
2864 : /* Report the datum name. */
2865 : /* -------------------------------------------------------------------- */
2866 0 : if( psDefn->Datum != KvUserDefined )
2867 : {
2868 0 : char *pszName = NULL;
2869 :
2870 0 : if( psGTIF->pj_context )
2871 : {
2872 0 : GTIFGetDatumInfoEx( psGTIF->pj_context,
2873 0 : psDefn->Datum, &pszName, NULL );
2874 : }
2875 0 : if( pszName == NULL )
2876 0 : pszName = CPLStrdup("(unknown)");
2877 :
2878 0 : fprintf( fp, "Datum: %d/%s\n", psDefn->Datum, pszName );
2879 0 : CPLFree( pszName );
2880 : }
2881 :
2882 : /* -------------------------------------------------------------------- */
2883 : /* Report the ellipsoid. */
2884 : /* -------------------------------------------------------------------- */
2885 0 : if( psDefn->Ellipsoid != KvUserDefined )
2886 : {
2887 0 : char *pszName = NULL;
2888 :
2889 0 : if( psGTIF->pj_context )
2890 : {
2891 0 : GTIFGetEllipsoidInfoEx( psGTIF->pj_context,
2892 0 : psDefn->Ellipsoid, &pszName, NULL, NULL );
2893 : }
2894 0 : if( pszName == NULL )
2895 0 : pszName = CPLStrdup("(unknown)");
2896 :
2897 0 : fprintf( fp, "Ellipsoid: %d/%s (%.2f,%.2f)\n",
2898 0 : psDefn->Ellipsoid, pszName,
2899 : psDefn->SemiMajor, psDefn->SemiMinor );
2900 0 : CPLFree( pszName );
2901 : }
2902 :
2903 : /* -------------------------------------------------------------------- */
2904 : /* Report the prime meridian. */
2905 : /* -------------------------------------------------------------------- */
2906 0 : if( psDefn->PM != KvUserDefined )
2907 : {
2908 0 : char *pszName = NULL;
2909 :
2910 0 : if( psGTIF->pj_context )
2911 : {
2912 0 : GTIFGetPMInfoEx( psGTIF->pj_context,
2913 0 : psDefn->PM, &pszName, NULL );
2914 : }
2915 :
2916 0 : if( pszName == NULL )
2917 0 : pszName = CPLStrdup("(unknown)");
2918 :
2919 0 : fprintf( fp, "Prime Meridian: %d/%s (%f/%s)\n",
2920 0 : psDefn->PM, pszName,
2921 : psDefn->PMLongToGreenwich,
2922 : GTIFDecToDMS( psDefn->PMLongToGreenwich, "Long", 2 ) );
2923 0 : CPLFree( pszName );
2924 : }
2925 :
2926 : /* -------------------------------------------------------------------- */
2927 : /* Report TOWGS84 parameters. */
2928 : /* -------------------------------------------------------------------- */
2929 : #if !defined(GEO_NORMALIZE_DISABLE_TOWGS84)
2930 0 : if( psDefn->TOWGS84Count > 0 )
2931 : {
2932 0 : fprintf( fp, "TOWGS84: " );
2933 :
2934 0 : for( int i = 0; i < psDefn->TOWGS84Count; i++ )
2935 : {
2936 0 : if( i > 0 )
2937 0 : fprintf( fp, "," );
2938 0 : fprintf( fp, "%g", psDefn->TOWGS84[i] );
2939 : }
2940 :
2941 0 : fprintf( fp, "\n" );
2942 : }
2943 : #endif
2944 :
2945 : /* -------------------------------------------------------------------- */
2946 : /* Report the projection units of measure (currently just */
2947 : /* linear). */
2948 : /* -------------------------------------------------------------------- */
2949 0 : if( psDefn->UOMLength != KvUserDefined )
2950 : {
2951 0 : char *pszName = NULL;
2952 :
2953 0 : if( psGTIF->pj_context )
2954 : {
2955 0 : GTIFGetUOMLengthInfoEx(
2956 0 : psGTIF->pj_context, psDefn->UOMLength, &pszName, NULL );
2957 : }
2958 0 : if( pszName == NULL )
2959 0 : pszName = CPLStrdup( "(unknown)" );
2960 :
2961 0 : fprintf( fp, "Projection Linear Units: %d/%s (%fm)\n",
2962 0 : psDefn->UOMLength, pszName, psDefn->UOMLengthInMeters );
2963 0 : CPLFree( pszName );
2964 : }
2965 : else
2966 : {
2967 0 : fprintf( fp, "Projection Linear Units: User-Defined (%fm)\n",
2968 : psDefn->UOMLengthInMeters );
2969 : }
2970 : }
2971 :
2972 0 : void GTIFPrintDefn( GTIFDefn * psDefn, FILE * fp )
2973 : {
2974 0 : GTIF *psGTIF = GTIFNew(NULL);
2975 0 : if( psGTIF )
2976 : {
2977 0 : GTIFPrintDefnEx(psGTIF, psDefn, fp);
2978 0 : GTIFFree(psGTIF);
2979 : }
2980 0 : }
2981 :
2982 : /************************************************************************/
2983 : /* GTIFFreeMemory() */
2984 : /* */
2985 : /* Externally visible function to free memory allocated within */
2986 : /* geo_normalize.c. */
2987 : /************************************************************************/
2988 :
2989 31957 : void GTIFFreeMemory( char * pMemory )
2990 :
2991 : {
2992 31957 : if( pMemory != NULL )
2993 31943 : VSIFree( pMemory );
2994 31957 : }
2995 :
2996 : /************************************************************************/
2997 : /* GTIFAllocDefn() */
2998 : /* */
2999 : /* This allocates a GTIF structure in such a way that the */
3000 : /* calling application doesn't need to know the size and */
3001 : /* initializes it appropriately. */
3002 : /************************************************************************/
3003 :
3004 7785 : GTIFDefn *GTIFAllocDefn()
3005 : {
3006 7785 : return (GTIFDefn *) CPLCalloc(sizeof(GTIFDefn),1);
3007 : }
3008 :
3009 : /************************************************************************/
3010 : /* GTIFFreeDefn() */
3011 : /* */
3012 : /* Free a GTIF structure allocated by GTIFAllocDefn(). */
3013 : /************************************************************************/
3014 :
3015 7785 : void GTIFFreeDefn( GTIFDefn *defn )
3016 : {
3017 7785 : VSIFree( defn );
3018 7785 : }
3019 :
3020 : /************************************************************************/
3021 : /* GTIFAttachPROJContext() */
3022 : /* */
3023 : /* Attach an existing PROJ context to the GTIF handle, but */
3024 : /* ownership of the context remains to the caller. */
3025 : /************************************************************************/
3026 :
3027 21192 : void GTIFAttachPROJContext( GTIF *psGTIF, void* pjContext )
3028 : {
3029 21192 : if( psGTIF->own_pj_context )
3030 : {
3031 0 : proj_context_destroy(psGTIF->pj_context);
3032 : }
3033 21192 : psGTIF->own_pj_context = FALSE;
3034 21192 : psGTIF->pj_context = (PJ_CONTEXT*) pjContext;
3035 21192 : }
3036 :
3037 : /************************************************************************/
3038 : /* GTIFGetPROJContext() */
3039 : /* */
3040 : /* Return the PROJ context attached to the GTIF handle. */
3041 : /* If it has not yet been instantiated and instantiateIfNeeded=TRUE*/
3042 : /* then, it will be instantiated (and owned by GTIF handle). */
3043 : /************************************************************************/
3044 :
3045 18479 : void *GTIFGetPROJContext( GTIF *psGTIF, int instantiateIfNeeded,
3046 : int* out_gtif_own_pj_context )
3047 : {
3048 18479 : if( psGTIF->pj_context || !instantiateIfNeeded )
3049 : {
3050 18479 : if( out_gtif_own_pj_context )
3051 : {
3052 0 : *out_gtif_own_pj_context = psGTIF->own_pj_context;
3053 : }
3054 18479 : return psGTIF->pj_context;
3055 : }
3056 0 : psGTIF->pj_context = proj_context_create();
3057 0 : psGTIF->own_pj_context = psGTIF->pj_context != NULL;
3058 0 : if( out_gtif_own_pj_context )
3059 : {
3060 0 : *out_gtif_own_pj_context = psGTIF->own_pj_context;
3061 : }
3062 0 : return psGTIF->pj_context;
3063 : }
3064 :
3065 :
3066 0 : void GTIFDeaccessCSV( void )
3067 : {
3068 : /* No operation */
3069 0 : }
3070 :
3071 : #ifndef GDAL_COMPILATION
3072 : void SetCSVFilenameHook( const char *(*CSVFileOverride)(const char *) )
3073 : {
3074 : (void)CSVFileOverride;
3075 : /* No operation */
3076 : }
3077 : #endif
|