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