1 /******************************************************************************
2  * $Id: ogr_srs_pci.cpp 15826 2008-11-27 22:28:25Z warmerdam $
3  *
4  * Project:  OpenGIS Simple Features Reference Implementation
5  * Purpose:  OGRSpatialReference translation to/from PCI georeferencing
6  *           information.
7  * Author:   Andrey Kiselev, dron@remotesensing.org
8  *
9  ******************************************************************************
10  * Copyright (c) 2003, Andrey Kiselev <dron@remotesensing.org>
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a
13  * copy of this software and associated documentation files (the "Software"),
14  * to deal in the Software without restriction, including without limitation
15  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16  * and/or sell copies of the Software, and to permit persons to whom the
17  * Software is furnished to do so, subject to the following conditions:
18  *
19  * The above copyright notice and this permission notice shall be included
20  * in all copies or substantial portions of the Software.
21  *
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28  * DEALINGS IN THE SOFTWARE.
29  ****************************************************************************/
30 
31 #include "ogr_spatialref.h"
32 #include "../port/cpl_conv.h"
33 #include "../port/cpl_csv.h"
34 
35 CPL_CVSID("$Id: ogr_srs_pci.cpp 15826 2008-11-27 22:28:25Z warmerdam $");
36 
37 typedef struct
38 {
39     const char  *pszPCIDatum;
40     int         nEPSGCode;
41     double      dfSemiMajor;
42     double      dfSemiMinor;
43 } PCIDatums;
44 
45 static const PCIDatums aoDatums[] =
46 {
47     { "D-01", 4267, 0, 0 },   // NAD27 (USA, NADCON)
48     { "D-03", 4267, 0, 0 },   // NAD27 (Canada, NTv1)
49     { "D-02", 4269, 0, 0 },   // NAD83 (USA, NADCON)
50     { "D-04", 4269, 0, 0 },   // NAD83 (Canada, NTv1)
51     { "D000", 4326, 0, 0 },   // WGS 1984
52     { "D001", 4322, 0, 0 },   // WGS 1972
53     { "D008", 4296, 0, 0 },   // Sudan
54     { "D013", 4601, 0, 0 },   // Antigua Island Astro 1943
55     { "D029", 4202, 0, 0 },   // Australian Geodetic 1966
56     { "D030", 4203, 0, 0 },   // Australian Geodetic 1984
57     { "D033", 4216, 0, 0 },   // Bermuda 1957
58     { "D034", 4165, 0, 0 },   // Bissau
59     { "D036", 4219, 0, 0 },   // Bukit Rimpah
60     { "D038", 4221, 0, 0 },   // Campo Inchauspe
61     { "D040", 4222, 0, 0 },   // Cape
62     { "D042", 4223, 0, 0 },   // Carthage
63     { "D044", 4224, 0, 0 },   // Chua Astro
64     { "D045", 4225, 0, 0 },   // Corrego Alegre
65     { "D046", 4155, 0, 0 },   // Dabola (Guinea)
66     { "D066", 4272, 0, 0 },   // Geodetic Datum 1949 (New Zealand)
67     { "D071", 4255, 0, 0 },   // Herat North (Afghanistan)
68     { "D077", 4239, 0, 0 },   // Indian 1954 (Thailand, Vietnam)
69     { "D078", 4240, 0, 0 },   // Indian 1975 (Thailand)
70     { "D083", 4244, 0, 0 },   // Kandawala (Sri Lanka)
71     { "D085", 4245, 0, 0 },   // Kertau 1948 (West Malaysia & Singapore)
72     { "D088", 4250, 0, 0 },   // Leigon (Ghana)
73     { "D089", 4251, 0, 0 },   // Liberia 1964 (Liberia)
74     { "D092", 4256, 0, 0 },   // Mahe 1971 (Mahe Island)
75     { "D093", 4262, 0, 0 },   // Massawa (Ethiopia (Eritrea))
76     { "D094", 4261, 0, 0 },   // Merchich (Morocco)
77     { "D098", 4604, 0, 0 },   // Montserrat Island Astro 1958 (Montserrat (Leeward Islands))
78     { "D139", 4282, 0, 0 },   // Pointe Noire 1948 (Congo)
79     { "D140", 4615, 0, 0 },   // Porto Santo 1936 (Porto Santo, Madeira Islands)
80     { "D151", 4139, 0, 0 },   // Puerto Rico (Puerto Rico, Virgin Islands)
81     { "D153", 4287, 0, 0 },   // Qornoq (Greenland (South))
82     { "D158", 4292, 0, 0 },   // Sapper Hill 1943 (East Falkland Island)
83     { "D159", 4293, 0, 0 },   // Schwarzeck (Namibia)
84     { "D160", 4616, 0, 0 },   // Selvagem Grande 1938 (Salvage Islands)
85     { "D176", 4297, 0, 0 },   // Tananarive Observatory 1925 (Madagascar)
86     { "D177", 4298, 0, 0 },   // Timbalai 1948 (Brunei, East Malaysia (Sabah, Sarawak))
87     { "D187", 4309, 0, 0 },   // Yacare (Uruguay)
88     { "D188", 4311, 0, 0 },   // Zanderij (Suriname)
89     { "D401", 4124, 0, 0 },   // RT90 (Sweden)
90     { "D501", 4312, 0, 0 },   // MGI (Hermannskogel, Austria)
91     { NULL, 0 }
92 };
93 
94 static const PCIDatums aoEllips[] =
95 {
96     { "E000", 7008, 0, 0 },     // Clarke, 1866 (NAD1927)
97     { "E001", 7034, 0, 0 },     // Clarke, 1880
98     { "E002", 7004, 0, 0 },     // Bessel, 1841
99     { "E003", 0, 6378157.5,6356772.2 },   // New International, 1967
100     { "E004", 7022, 0, 0 },     // International, 1924 (Hayford, 1909)
101     { "E005", 7043, 0, 0 },     // WGS, 1972
102     { "E006", 7042, 0, 0 },     // Everest, 1830
103     { "E007", 0, 6378145.,6356759.769356 }, // WGS, 1966
104     { "E008", 7019, 0, 0 },     // GRS, 1980 (NAD1983)
105     { "E009", 7001, 0, 0 },     // Airy, 1830
106     { "E010", 7018, 0, 0 },     // Modified Everest
107     { "E011", 7002, 0, 0 },     // Modified Airy
108     { "E012", 7030, 0, 0 },     // WGS, 1984 (GPS)
109     { "E013", 0, 6378155.,6356773.3205 }, // Southeast Asia
110     { "E014", 7003, 0, 0 },     // Australian National, 1965
111     { "E015", 7024, 0, 0 },     // Krassovsky, 1940
112     { "E016", 7053, 0, 0 },     // Hough
113     { "E017", 0, 6378166.,6356784.283666 }, // Mercury, 1960
114     { "E018", 0, 6378150.,6356768.337303 }, //  Modified Mercury, 1968
115     { "E019", 7052, 0, 0},      // normal sphere
116     { "E333", 7046, 0, 0 },     // Bessel 1841 (Japan By Law)
117     { "E600", 0, 6378144.0,6356759.0 }, // D-PAF (Orbits)
118     { "E900", 7006, 0, 0 },     // Bessel, 1841 (Namibia)
119     { "E901", 7044, 0, 0 },     // Everest, 1956
120     { "E902", 7056, 0, 0 },     // Everest, 1969
121     { "E903", 7016, 0, 0 },     // Everest (Sabah & Sarawak)
122     { "E904", 7020, 0, 0 },     // Helmert, 1906
123     { "E905", 0, 6378136.,6356751.301569 }, // SGS 85
124     { "E906", 0, 6378165.,6356783.286959 }, // WGS 60
125     { "E907", 7036, 0, 0 },     // South American, 1969
126     { "E910", 7041, 0, 0 },     // ATS77
127     { NULL, 0 }
128 };
129 
130 /************************************************************************/
131 /*                        PCIGetUOMLengthInfo()                         */
132 /*                                                                      */
133 /*      Note: This function should eventually also know how to          */
134 /*      lookup length aliases in the UOM_LE_ALIAS table.                */
135 /************************************************************************/
136 
137 static int
PCIGetUOMLengthInfo(int nUOMLengthCode,char ** ppszUOMName,double * pdfInMeters)138 PCIGetUOMLengthInfo( int nUOMLengthCode, char **ppszUOMName,
139                      double * pdfInMeters )
140 
141 {
142     char        **papszUnitsRecord;
143     char        szSearchKey[24];
144     int         iNameField;
145 
146 #define UOM_FILENAME CSVFilename( "unit_of_measure.csv" )
147 
148 /* -------------------------------------------------------------------- */
149 /*      We short cut meter to save work in the most common case.        */
150 /* -------------------------------------------------------------------- */
151     if( nUOMLengthCode == 9001 )
152     {
153         if( ppszUOMName != NULL )
154             *ppszUOMName = CPLStrdup( "metre" );
155         if( pdfInMeters != NULL )
156             *pdfInMeters = 1.0;
157 
158         return TRUE;
159     }
160 
161 /* -------------------------------------------------------------------- */
162 /*      Search the units database for this unit.  If we don't find      */
163 /*      it return failure.                                              */
164 /* -------------------------------------------------------------------- */
165     sprintf( szSearchKey, "%d", nUOMLengthCode );
166     papszUnitsRecord =
167         CSVScanFileByName( UOM_FILENAME, "UOM_CODE", szSearchKey, CC_Integer );
168 
169     if( papszUnitsRecord == NULL )
170         return FALSE;
171 
172 /* -------------------------------------------------------------------- */
173 /*      Get the name, if requested.                                     */
174 /* -------------------------------------------------------------------- */
175     if( ppszUOMName != NULL )
176     {
177         iNameField = CSVGetFileFieldId( UOM_FILENAME, "UNIT_OF_MEAS_NAME" );
178         *ppszUOMName = CPLStrdup( CSLGetField(papszUnitsRecord, iNameField) );
179     }
180 
181 /* -------------------------------------------------------------------- */
182 /*      Get the A and B factor fields, and create the multiplicative    */
183 /*      factor.                                                         */
184 /* -------------------------------------------------------------------- */
185     if( pdfInMeters != NULL )
186     {
187         int     iBFactorField, iCFactorField;
188 
189         iBFactorField = CSVGetFileFieldId( UOM_FILENAME, "FACTOR_B" );
190         iCFactorField = CSVGetFileFieldId( UOM_FILENAME, "FACTOR_C" );
191 
192         if( atof(CSLGetField(papszUnitsRecord, iCFactorField)) > 0.0 )
193             *pdfInMeters = atof(CSLGetField(papszUnitsRecord, iBFactorField))
194                 / atof(CSLGetField(papszUnitsRecord, iCFactorField));
195         else
196             *pdfInMeters = 0.0;
197     }
198 
199     return( TRUE );
200 }
201 
202 /************************************************************************/
203 /*                        PCIGetEllipsoidInfo()                         */
204 /*                                                                      */
205 /*      Fetch info about an ellipsoid.  Axes are always returned in     */
206 /*      meters.  SemiMajor computed based on inverse flattening         */
207 /*      where that is provided.                                         */
208 /************************************************************************/
209 
210 static int
PCIGetEllipsoidInfo(int nCode,char ** ppszName,double * pdfSemiMajor,double * pdfInvFlattening)211 PCIGetEllipsoidInfo( int nCode, char ** ppszName,
212                      double * pdfSemiMajor, double * pdfInvFlattening )
213 
214 {
215     char        szSearchKey[24];
216     double      dfSemiMajor, dfToMeters = 1.0;
217     int         nUOMLength;
218 
219 /* -------------------------------------------------------------------- */
220 /*      Get the semi major axis.                                        */
221 /* -------------------------------------------------------------------- */
222     sprintf( szSearchKey, "%d", nCode );
223 
224     dfSemiMajor =
225         atof(CSVGetField( CSVFilename("ellipsoid.csv" ),
226                           "ELLIPSOID_CODE", szSearchKey, CC_Integer,
227                           "SEMI_MAJOR_AXIS" ) );
228     if( dfSemiMajor == 0.0 )
229         return FALSE;
230 
231 /* -------------------------------------------------------------------- */
232 /*      Get the translation factor into meters.                         */
233 /* -------------------------------------------------------------------- */
234     nUOMLength = atoi(CSVGetField( CSVFilename("ellipsoid.csv" ),
235                                    "ELLIPSOID_CODE", szSearchKey, CC_Integer,
236                                    "UOM_CODE" ));
237     PCIGetUOMLengthInfo( nUOMLength, NULL, &dfToMeters );
238 
239     dfSemiMajor *= dfToMeters;
240 
241     if( pdfSemiMajor != NULL )
242         *pdfSemiMajor = dfSemiMajor;
243 
244 /* -------------------------------------------------------------------- */
245 /*      Get the semi-minor if requested.  If the Semi-minor axis        */
246 /*      isn't available, compute it based on the inverse flattening.    */
247 /* -------------------------------------------------------------------- */
248     if( pdfInvFlattening != NULL )
249     {
250         *pdfInvFlattening =
251             atof(CSVGetField( CSVFilename("ellipsoid.csv" ),
252                               "ELLIPSOID_CODE", szSearchKey, CC_Integer,
253                               "INV_FLATTENING" ));
254 
255         if( *pdfInvFlattening == 0.0 )
256         {
257             double dfSemiMinor;
258 
259             dfSemiMinor =
260                 atof(CSVGetField( CSVFilename("ellipsoid.csv" ),
261                                   "ELLIPSOID_CODE", szSearchKey, CC_Integer,
262                                   "SEMI_MINOR_AXIS" )) * dfToMeters;
263 
264             if( dfSemiMajor != 0.0 && dfSemiMajor != dfSemiMinor )
265                 *pdfInvFlattening =
266                     -1.0 / (dfSemiMinor/dfSemiMajor - 1.0);
267             else
268                 *pdfInvFlattening = 0.0;
269         }
270     }
271 
272 /* -------------------------------------------------------------------- */
273 /*      Get the name, if requested.                                     */
274 /* -------------------------------------------------------------------- */
275     if( ppszName != NULL )
276         *ppszName =
277             CPLStrdup(CSVGetField( CSVFilename("ellipsoid.csv" ),
278                                    "ELLIPSOID_CODE", szSearchKey, CC_Integer,
279                                    "ELLIPSOID_NAME" ));
280 
281     return( TRUE );
282 }
283 
284 /************************************************************************/
285 /*                         OSRImportFromPCI()                           */
286 /************************************************************************/
287 
OSRImportFromPCI(OGRSpatialReferenceH hSRS,const char * pszProj,const char * pszUnits,double * padfPrjParams)288 OGRErr OSRImportFromPCI( OGRSpatialReferenceH hSRS, const char *pszProj,
289                          const char *pszUnits, double *padfPrjParams )
290 
291 {
292     VALIDATE_POINTER1( hSRS, "OSRImportFromPCI", CE_Failure );
293 
294     return ((OGRSpatialReference *) hSRS)->importFromPCI( pszProj,
295                                                           pszUnits,
296                                                           padfPrjParams );
297 }
298 
299 /************************************************************************/
300 /*                          importFromPCI()                             */
301 /************************************************************************/
302 
303 /**
304  * Import coordinate system from PCI projection definition.
305  *
306  * PCI software uses 16-character string to specify coordinate system
307  * and datum/ellipsoid. You should supply at least this string to the
308  * importFromPCI() function.
309  *
310  * This function is the equilvelent of the C function OSRImportFromPCI().
311  *
312  * @param pszProj NULL terminated string containing the definition. Looks
313  * like "pppppppppppp Ennn" or "pppppppppppp Dnnn", where "pppppppppppp" is
314  * a projection code, "Ennn" is an ellipsoid code, "Dnnn" --- a datum code.
315  *
316  * @param pszUnits Grid units code ("DEGREE" or "METRE"). If NULL "METRE" will
317  * be used.
318  *
319  * @param padfPrjParams Array of 16 coordinate system parameters:
320  *
321  * [0]  Spheroid semi major axis
322  * [1]  Spheroid semi minor axis
323  * [2]  Reference Longitude
324  * [3]  Reference Latitude
325  * [4]  First Standard Parallel
326  * [5]  Second Standard Parallel
327  * [6]  False Easting
328  * [7]  False Northing
329  * [8]  Scale Factor
330  * [9]  Height above sphere surface
331  * [10] Longitude of 1st point on center line
332  * [11] Latitude of 1st point on center line
333  * [12] Longitude of 2nd point on center line
334  * [13] Latitude of 2nd point on center line
335  * [14] Azimuth east of north for center line
336  * [15] Landsat satellite number
337  * [16] Landsat path number
338  *
339  * Particular projection uses different parameters, unused ones may be set to
340  * zero. If NULL suppliet instead of array pointer default values will be
341  * used (i.e., zeroes).
342  *
343  * @return OGRERR_NONE on success or an error code in case of failure.
344  */
345 
importFromPCI(const char * pszProj,const char * pszUnits,double * padfPrjParams)346 OGRErr OGRSpatialReference::importFromPCI( const char *pszProj,
347                                            const char *pszUnits,
348                                            double *padfPrjParams )
349 
350 {
351     Clear();
352 
353     if( pszProj == NULL )
354         return OGRERR_CORRUPT_DATA;
355 
356 #ifdef DEBUG
357     CPLDebug( "OSR_PCI", "Trying to import projection \"%s\"", pszProj );
358 #endif
359 
360 /* -------------------------------------------------------------------- */
361 /*      Use safe defaults if projection parameters are not supplied.    */
362 /* -------------------------------------------------------------------- */
363     int     bProjAllocated = FALSE;
364 
365     if( padfPrjParams == NULL )
366     {
367         int     i;
368 
369         padfPrjParams = (double *)CPLMalloc( 17 * sizeof(double) );
370         if ( !padfPrjParams )
371             return OGRERR_NOT_ENOUGH_MEMORY;
372         for ( i = 0; i < 17; i++ )
373             padfPrjParams[i] = 0.0;
374         bProjAllocated = TRUE;
375     }
376 
377 /* -------------------------------------------------------------------- */
378 /*      Operate on the basis of the projection name.                    */
379 /* -------------------------------------------------------------------- */
380     if( EQUALN( pszProj, "LONG/LAT", 8 ) )
381     {
382     }
383 
384     else if( EQUALN( pszProj, "METER", 5 )
385              || EQUALN( pszProj, "METRE", 5 ) )
386     {
387         SetLocalCS( "METER" );
388         SetLinearUnits( "METER", 1.0 );
389     }
390 
391     else if( EQUALN( pszProj, "FEET", 4 )
392              || EQUALN( pszProj, "FOOT", 4 ) )
393     {
394         SetLocalCS( "FEET" );
395         SetLinearUnits( "FEET", atof(SRS_UL_FOOT_CONV) );
396     }
397 
398     else if( EQUALN( pszProj, "ACEA", 4 ) )
399     {
400         SetACEA( padfPrjParams[4], padfPrjParams[5],
401                  padfPrjParams[3], padfPrjParams[2],
402                  padfPrjParams[6], padfPrjParams[7] );
403     }
404 
405     else if( EQUALN( pszProj, "AE", 2 ) )
406     {
407         SetAE( padfPrjParams[3], padfPrjParams[2],
408                padfPrjParams[6], padfPrjParams[7] );
409     }
410 
411     else if( EQUALN( pszProj, "EC", 2 ) )
412     {
413         SetEC( padfPrjParams[4], padfPrjParams[5],
414                padfPrjParams[3], padfPrjParams[2],
415                padfPrjParams[6], padfPrjParams[7] );
416     }
417 
418     else if( EQUALN( pszProj, "ER", 2 ) )
419     {
420         // PCI and GCTP don't support natural origin lat.
421         SetEquirectangular2( 0.0, padfPrjParams[2],
422                              padfPrjParams[3],
423                              padfPrjParams[6], padfPrjParams[7] );
424     }
425 
426     else if( EQUALN( pszProj, "GNO", 3 ) )
427     {
428         SetGnomonic( padfPrjParams[3], padfPrjParams[2],
429                      padfPrjParams[6], padfPrjParams[7] );
430     }
431 
432     // FIXME: GVNP --- General Vertical Near- Side Perspective skipped
433 
434     else if( EQUALN( pszProj, "LAEA", 4 ) )
435     {
436         SetLAEA( padfPrjParams[3], padfPrjParams[2],
437                  padfPrjParams[6], padfPrjParams[7] );
438     }
439 
440     else if( EQUALN( pszProj, "LCC", 3 ) )
441     {
442         SetLCC( padfPrjParams[4], padfPrjParams[5],
443                 padfPrjParams[3], padfPrjParams[2],
444                 padfPrjParams[6], padfPrjParams[7] );
445     }
446 
447     else if( EQUALN( pszProj, "MC", 2 ) )
448     {
449         SetMC( padfPrjParams[3], padfPrjParams[2],
450                padfPrjParams[6], padfPrjParams[7] );
451     }
452 
453     else if( EQUALN( pszProj, "MER", 3 ) )
454     {
455         SetMercator( padfPrjParams[3], padfPrjParams[2],
456                      padfPrjParams[8],
457                      padfPrjParams[6], padfPrjParams[7] );
458     }
459 
460     else if( EQUALN( pszProj, "OG", 2 ) )
461     {
462         SetOrthographic( padfPrjParams[3], padfPrjParams[2],
463                          padfPrjParams[6], padfPrjParams[7] );
464     }
465 
466     // FIXME: OM --- Oblique Mercator skipped
467 
468     else if( EQUALN( pszProj, "PC", 2 ) )
469     {
470         SetPolyconic( padfPrjParams[3], padfPrjParams[2],
471                       padfPrjParams[6], padfPrjParams[7] );
472     }
473 
474     else if( EQUALN( pszProj, "PS", 2 ) )
475     {
476         SetPS( padfPrjParams[3], padfPrjParams[2],
477                padfPrjParams[8],
478                padfPrjParams[6], padfPrjParams[7] );
479     }
480 
481     else if( EQUALN( pszProj, "ROB", 3 ) )
482     {
483         SetRobinson( padfPrjParams[2],
484                      padfPrjParams[6], padfPrjParams[7] );
485     }
486 
487     else if( EQUALN( pszProj, "SG", 2 ) )
488     {
489         SetStereographic( padfPrjParams[3], padfPrjParams[2],
490                           padfPrjParams[8],
491                           padfPrjParams[6], padfPrjParams[7] );
492     }
493 
494     else if( EQUALN( pszProj, "SIN", 3 ) )
495     {
496         SetSinusoidal( padfPrjParams[2],
497                        padfPrjParams[6], padfPrjParams[7] );
498     }
499 
500     // FIXME: SOM --- Space Oblique Mercator skipped
501 
502     else if( EQUALN( pszProj, "SPCS", 4 ) )
503     {
504         int     iZone, bNAD83 = TRUE;
505 
506         iZone = CPLScanLong( (char *)pszProj + 5, 4 );
507 
508         if ( !EQUALN( pszProj + 12, "E008", 4 ) )
509             bNAD83 = FALSE;
510 
511         SetStatePlane( iZone, bNAD83 );
512     }
513 
514     // FIXME: Add SPIF and SPAF?  (state plane international or american feet)
515 
516     else if( EQUALN( pszProj, "TM", 2 ) )
517     {
518         SetTM( padfPrjParams[3], padfPrjParams[2],
519                padfPrjParams[8],
520                padfPrjParams[6], padfPrjParams[7] );
521     }
522 
523     else if( EQUALN( pszProj, "UTM", 3 ) )
524     {
525         int     iZone, bNorth = TRUE;
526 
527         iZone = CPLScanLong( (char *)pszProj + 4, 5 );;
528         if ( iZone < 0 )
529         {
530             iZone = -iZone;
531             bNorth = FALSE;
532         }
533 
534         SetUTM( iZone, bNorth );
535     }
536 
537     else if( EQUALN( pszProj, "VDG", 3 ) )
538     {
539         SetVDG( padfPrjParams[2],
540                 padfPrjParams[6], padfPrjParams[7] );
541     }
542 
543     else
544     {
545         CPLDebug( "OSR_PCI", "Unsupported projection: %s", pszProj );
546         SetLocalCS( pszProj );
547     }
548 
549 /* ==================================================================== */
550 /*      Translate the datum/spheroid.                                   */
551 /* ==================================================================== */
552 
553 /* -------------------------------------------------------------------- */
554 /*      Extract and "normalize" the earthmodel to look like E001,       */
555 /*      D-02 or D109.                                                   */
556 /* -------------------------------------------------------------------- */
557     char szEarthModel[5];
558     const char *pszEM;
559 
560     strcpy( szEarthModel, "" );
561     pszEM = pszProj + strlen(pszProj) - 1;
562     while( pszEM != pszProj )
563     {
564         if( *pszEM == 'e' || *pszEM == 'E' || *pszEM == 'd' || *pszEM == 'D' )
565         {
566             int nCode = atoi(pszEM+1);
567 
568             if( nCode >= -99 && nCode <= 999 )
569                 sprintf( szEarthModel, "%c%03d", toupper(*pszEM), nCode );
570 
571             break;
572         }
573 
574         pszEM--;
575     }
576 
577 /* -------------------------------------------------------------------- */
578 /*      We have an earthmodel string, look it up in the datum list.     */
579 /* -------------------------------------------------------------------- */
580     if( strlen(szEarthModel) > 0
581         && (poRoot == NULL || IsProjected() || IsGeographic()) )
582     {
583         const PCIDatums   *paoDatum = aoDatums;
584 
585         // Search for matching datum
586         while ( paoDatum->pszPCIDatum )
587         {
588             if( EQUALN( szEarthModel, paoDatum->pszPCIDatum, 4 ) )
589             {
590                 OGRSpatialReference oGCS;
591                 oGCS.importFromEPSG( paoDatum->nEPSGCode );
592                 CopyGeogCSFrom( &oGCS );
593                 break;
594             }
595             paoDatum++;
596         }
597 
598 /* -------------------------------------------------------------------- */
599 /*      If not, look in the ellipsoid list.                             */
600 /* -------------------------------------------------------------------- */
601         if ( !paoDatum->pszPCIDatum )  // No matching; search for ellipsoids
602         {
603             paoDatum = aoEllips;
604 
605 #ifdef DEBUG
606             CPLDebug( "OSR_PCI",
607                       "Cannot found matching datum definition, "
608                       "search for ellipsoids." );
609 #endif
610 
611             while ( paoDatum->pszPCIDatum )
612             {
613                 if( EQUALN( szEarthModel, paoDatum->pszPCIDatum, 4 )
614                     && paoDatum->nEPSGCode != 0 )
615                 {
616                     char    *pszName = NULL;
617                     double  dfSemiMajor;
618                     double  dfInvFlattening;
619 
620                     PCIGetEllipsoidInfo( paoDatum->nEPSGCode, &pszName,
621                                          &dfSemiMajor, &dfInvFlattening );
622                     SetGeogCS( CPLString().Printf(
623                                    "Unknown datum based upon the %s ellipsoid",
624                                    pszName ),
625                                CPLString().Printf(
626                                    "Not specified (based on %s spheroid)",
627                                    pszName ),
628                                pszName, dfSemiMajor, dfInvFlattening,
629                                NULL, 0.0, NULL, 0.0 );
630                     SetAuthority( "SPHEROID", "EPSG", paoDatum->nEPSGCode );
631 
632                     if ( pszName )
633                         CPLFree( pszName );
634 
635                     break;
636                 }
637                 else if( EQUALN( szEarthModel, paoDatum->pszPCIDatum, 4 ) )
638                 {
639                     double      dfInvFlattening;
640 
641                     if( ABS(paoDatum->dfSemiMajor - paoDatum->dfSemiMinor)
642                         < 0.01 )
643                         dfInvFlattening = 0.0;
644                     else
645                         dfInvFlattening = paoDatum->dfSemiMajor
646                             / (paoDatum->dfSemiMajor-paoDatum->dfSemiMinor);
647 
648                     SetGeogCS( "Unknown datum based upon the custom spheroid",
649                                "Not specified (based on custom spheroid)",
650                                CPLString().Printf( "PCI Ellipse %s",
651                                                    szEarthModel),
652                                paoDatum->dfSemiMajor, dfInvFlattening,
653                                NULL, 0, NULL, 0 );
654                     break;
655                 }
656                 paoDatum++;
657             }
658         }
659 
660 /* -------------------------------------------------------------------- */
661 /*      Custom spheroid.                                                */
662 /* -------------------------------------------------------------------- */
663         if ( !paoDatum->pszPCIDatum )      // Didn't found matches
664         {
665 #ifdef DEBUG
666             CPLDebug( "OSR_PCI",
667                       "Cannot found matching ellipsoid definition." );
668 #endif
669 
670             if( EQUALN( pszProj + 12, "E999", 4 ) )
671             {
672                 double      dfInvFlattening;
673 
674                 if( ABS(padfPrjParams[0] - padfPrjParams[1]) < 0.01 )
675                 {
676                     dfInvFlattening = 0.0;
677                 }
678                 else
679                 {
680                     dfInvFlattening =
681                         padfPrjParams[0]/(padfPrjParams[0]-padfPrjParams[1]);
682                 }
683 
684                 SetGeogCS( "Unknown datum based upon the custom spheroid",
685                            "Not specified (based on custom spheroid)",
686                            "Custom spheroid",
687                            padfPrjParams[0], dfInvFlattening,
688                            NULL, 0, NULL, 0 );
689             }
690             else
691             {
692                 // If we don't know, default to WGS84
693                 // so there is something there.
694                 SetWellKnownGeogCS( "WGS84" );
695 #ifdef DEBUG
696                 CPLDebug( "OSR_PCI", "Setting WGS84 as a fallback." );
697 #endif
698 
699             }
700         }
701     }
702 
703 /* -------------------------------------------------------------------- */
704 /*      Grid units translation                                          */
705 /* -------------------------------------------------------------------- */
706     if( (IsLocal() || IsProjected()) && pszUnits )
707     {
708         if( EQUAL( pszUnits, "METRE" ) )
709             SetLinearUnits( SRS_UL_METER, 1.0 );
710         else if( EQUAL( pszUnits, "DEGREE" ) )
711             SetAngularUnits( SRS_UA_DEGREE, atof(SRS_UA_DEGREE_CONV) );
712         else
713             SetLinearUnits( SRS_UL_METER, 1.0 );
714     }
715 
716     FixupOrdering();
717 
718     if ( bProjAllocated && padfPrjParams )
719         CPLFree( padfPrjParams );
720 
721     return OGRERR_NONE;
722 }
723 
724 /************************************************************************/
725 /*                          OSRExportToPCI()                            */
726 /************************************************************************/
727 
OSRExportToPCI(OGRSpatialReferenceH hSRS,char ** ppszProj,char ** ppszUnits,double ** ppadfPrjParams)728 OGRErr OSRExportToPCI( OGRSpatialReferenceH hSRS,
729                        char **ppszProj, char **ppszUnits,
730                        double **ppadfPrjParams )
731 
732 {
733     VALIDATE_POINTER1( hSRS, "OSRExportToPCI", CE_Failure );
734 
735     *ppszProj = NULL;
736     *ppszUnits = NULL;
737     *ppadfPrjParams = NULL;
738 
739     return ((OGRSpatialReference *) hSRS)->exportToPCI( ppszProj, ppszUnits,
740                                                         ppadfPrjParams );
741 }
742 
743 /************************************************************************/
744 /*                           exportToPCI()                              */
745 /************************************************************************/
746 
747 /**
748  * Export coordinate system in PCI projection definition.
749  *
750  * Converts the loaded coordinate reference system into PCI projection
751  * definition to the extent possible. The strings returned in ppszProj,
752  * ppszUnits and ppadfPrjParams array should be deallocated by the caller
753  * with CPLFree() when no longer needed.
754  *
755  * LOCAL_CS coordinate systems are not translatable.  An empty string
756  * will be returned along with OGRERR_NONE.
757  *
758  * This method is the equivelent of the C function OSRExportToPCI().
759  *
760  * @param ppszProj pointer to which dynamically allocated PCI projection
761  * definition will be assigned.
762  *
763  * @param ppszUnits pointer to which dynamically allocated units definition
764  * will be assigned.
765  *
766  * @param ppadfPrjParams pointer to which dynamically allocated array of
767  * 17 projection parameters will be assigned. See importFromPCI() for the list
768  * of parameters.
769  *
770  * @return OGRERR_NONE on success or an error code on failure.
771  */
772 
exportToPCI(char ** ppszProj,char ** ppszUnits,double ** ppadfPrjParams) const773 OGRErr OGRSpatialReference::exportToPCI( char **ppszProj, char **ppszUnits,
774                                          double **ppadfPrjParams ) const
775 
776 {
777     const char  *pszProjection = GetAttrValue("PROJECTION");
778 
779 /* -------------------------------------------------------------------- */
780 /*      Fill all projection parameters with zero.                       */
781 /* -------------------------------------------------------------------- */
782     int         i;
783 
784     *ppadfPrjParams = (double *)CPLMalloc( 17 * sizeof(double) );
785     for ( i = 0; i < 17; i++ )
786         (*ppadfPrjParams)[i] = 0.0;
787 
788 /* -------------------------------------------------------------------- */
789 /*      Get the prime meridian info.                                    */
790 /* -------------------------------------------------------------------- */
791     const OGR_SRSNode *poPRIMEM = GetAttrNode( "PRIMEM" );
792     double dfFromGreenwich = 0.0;
793 
794     if( poPRIMEM != NULL && poPRIMEM->GetChildCount() >= 2
795         && atof(poPRIMEM->GetChild(1)->GetValue()) != 0.0 )
796     {
797         dfFromGreenwich = atof(poPRIMEM->GetChild(1)->GetValue());
798     }
799 
800 /* ==================================================================== */
801 /*      Handle the projection definition.                               */
802 /* ==================================================================== */
803     char        szProj[17];
804 
805     if( IsLocal() )
806     {
807         if( GetLinearUnits() > 0.30479999 && GetLinearUnits() < 0.3048010 )
808             CPLPrintStringFill( szProj, "FEET", 17 );
809         else
810             CPLPrintStringFill( szProj, "METER", 17 );
811     }
812 
813     else if( pszProjection == NULL )
814     {
815 #ifdef DEBUG
816         CPLDebug( "OSR_PCI",
817                   "Empty projection definition, considered as LONG/LAT" );
818 #endif
819         CPLPrintStringFill( szProj, "LONG/LAT", 17 );
820     }
821 
822     else if( EQUAL(pszProjection, SRS_PT_ALBERS_CONIC_EQUAL_AREA) )
823     {
824         CPLPrintStringFill( szProj, "ACEA", 16 );
825         (*ppadfPrjParams)[2] = GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 );
826         (*ppadfPrjParams)[3] =
827             GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 );
828         (*ppadfPrjParams)[4] =
829             GetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, 0.0 );
830         (*ppadfPrjParams)[5] =
831             GetNormProjParm( SRS_PP_STANDARD_PARALLEL_2, 0.0 );
832         (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 );
833         (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 );
834     }
835 
836     else if( EQUAL(pszProjection, SRS_PT_AZIMUTHAL_EQUIDISTANT) )
837     {
838         CPLPrintStringFill( szProj, "AE", 16 );
839         (*ppadfPrjParams)[2] = GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 );
840         (*ppadfPrjParams)[3] =
841             GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 );
842         (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 );
843         (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 );
844     }
845 
846     else if( EQUAL(pszProjection, SRS_PT_EQUIDISTANT_CONIC) )
847     {
848         CPLPrintStringFill( szProj, "EC", 16 );
849         (*ppadfPrjParams)[2] =
850             GetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, 0.0 );
851         (*ppadfPrjParams)[3] =
852             GetNormProjParm( SRS_PP_LATITUDE_OF_CENTER, 0.0 );
853         (*ppadfPrjParams)[4] =
854             GetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, 0.0 );
855         (*ppadfPrjParams)[5] =
856             GetNormProjParm( SRS_PP_STANDARD_PARALLEL_2, 0.0 );
857         (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 );
858         (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 );
859     }
860 
861     else if( EQUAL(pszProjection, SRS_PT_EQUIRECTANGULAR) )
862     {
863         CPLPrintStringFill( szProj, "ER", 16 );
864         (*ppadfPrjParams)[2] = GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 );
865         (*ppadfPrjParams)[3] =
866             GetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, 0.0 );
867         (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 );
868         (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 );
869     }
870 
871     else if( EQUAL(pszProjection, SRS_PT_GNOMONIC) )
872     {
873         CPLPrintStringFill( szProj, "GNO", 16 );
874         (*ppadfPrjParams)[2] = GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 );
875         (*ppadfPrjParams)[3] =
876             GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 );
877         (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 );
878         (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 );
879     }
880 
881     else if( EQUAL(pszProjection, SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA) )
882     {
883         CPLPrintStringFill( szProj, "LAEA", 16 );
884         (*ppadfPrjParams)[2] = GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 );
885         (*ppadfPrjParams)[3] =
886             GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 );
887         (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 );
888         (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 );
889     }
890 
891     else if( EQUAL(pszProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP) )
892     {
893         CPLPrintStringFill( szProj, "LCC", 16 );
894         (*ppadfPrjParams)[2] = GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 );
895         (*ppadfPrjParams)[3] =
896             GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 );
897         (*ppadfPrjParams)[4] =
898             GetNormProjParm( SRS_PP_STANDARD_PARALLEL_1, 0.0 );
899         (*ppadfPrjParams)[5] =
900             GetNormProjParm( SRS_PP_STANDARD_PARALLEL_2, 0.0 );
901         (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 );
902         (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 );
903     }
904 
905     else if( EQUAL(pszProjection, SRS_PT_MILLER_CYLINDRICAL) )
906     {
907         CPLPrintStringFill( szProj, "MC", 16 );
908         (*ppadfPrjParams)[2] = GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 );
909         (*ppadfPrjParams)[3] =
910             GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 );
911         (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 );
912         (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 );
913     }
914 
915     else if( EQUAL(pszProjection, SRS_PT_MERCATOR_1SP) )
916     {
917         CPLPrintStringFill( szProj, "MER", 16 );
918         (*ppadfPrjParams)[2] = GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 );
919         (*ppadfPrjParams)[3] =
920             GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 );
921         (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 );
922         (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 );
923         (*ppadfPrjParams)[8] = GetNormProjParm( SRS_PP_SCALE_FACTOR, 1.0 );
924     }
925 
926     else if( EQUAL(pszProjection, SRS_PT_ORTHOGRAPHIC) )
927     {
928         CPLPrintStringFill( szProj, "OG", 16 );
929         (*ppadfPrjParams)[2] = GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 );
930         (*ppadfPrjParams)[3] =
931             GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 );
932         (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 );
933         (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 );
934     }
935 
936     else if( EQUAL(pszProjection, SRS_PT_POLYCONIC) )
937     {
938         CPLPrintStringFill( szProj, "PC", 16 );
939         (*ppadfPrjParams)[2] = GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 );
940         (*ppadfPrjParams)[3] =
941             GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 );
942         (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 );
943         (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 );
944     }
945 
946     else if( EQUAL(pszProjection, SRS_PT_POLAR_STEREOGRAPHIC) )
947     {
948         CPLPrintStringFill( szProj, "PS", 16 );
949         (*ppadfPrjParams)[2] = GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 );
950         (*ppadfPrjParams)[3] =
951             GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 );
952         (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 );
953         (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 );
954         (*ppadfPrjParams)[8] = GetNormProjParm( SRS_PP_SCALE_FACTOR, 1.0 );
955     }
956 
957     else if( EQUAL(pszProjection, SRS_PT_ROBINSON) )
958     {
959         CPLPrintStringFill( szProj, "ROB", 16 );
960         (*ppadfPrjParams)[2] = GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 );
961         (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 );
962         (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 );
963     }
964 
965     else if( EQUAL(pszProjection, SRS_PT_STEREOGRAPHIC) )
966     {
967         CPLPrintStringFill( szProj, "SG", 16 );
968         (*ppadfPrjParams)[2] = GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 );
969         (*ppadfPrjParams)[3] =
970             GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 );
971         (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 );
972         (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 );
973         (*ppadfPrjParams)[8] = GetNormProjParm( SRS_PP_SCALE_FACTOR, 1.0 );
974     }
975 
976     else if( EQUAL(pszProjection, SRS_PT_SINUSOIDAL) )
977     {
978         CPLPrintStringFill( szProj, "SIN", 16 );
979         (*ppadfPrjParams)[2] =
980             GetNormProjParm( SRS_PP_LONGITUDE_OF_CENTER, 0.0 );
981         (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 );
982         (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 );
983     }
984 
985     else if( EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR) )
986     {
987         int bNorth;
988         int nZone = GetUTMZone( &bNorth );
989 
990         if( nZone != 0 )
991         {
992             CPLPrintStringFill( szProj, "UTM", 16 );
993             if( bNorth )
994                 CPLPrintInt32( szProj + 5, nZone, 4 );
995             else
996                 CPLPrintInt32( szProj + 5, -nZone, 4 );
997         }
998         else
999         {
1000             CPLPrintStringFill( szProj, "TM", 16 );
1001             (*ppadfPrjParams)[2] =
1002                 GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 );
1003             (*ppadfPrjParams)[3] =
1004                 GetNormProjParm( SRS_PP_LATITUDE_OF_ORIGIN, 0.0 );
1005             (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
1006             (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
1007             (*ppadfPrjParams)[8] = GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0);
1008         }
1009     }
1010 
1011     else if( EQUAL(pszProjection, SRS_PT_VANDERGRINTEN) )
1012     {
1013         CPLPrintStringFill( szProj, "VDG", 16 );
1014         (*ppadfPrjParams)[2] = GetNormProjParm( SRS_PP_CENTRAL_MERIDIAN, 0.0 );
1015         (*ppadfPrjParams)[6] = GetNormProjParm( SRS_PP_FALSE_EASTING, 0.0 );
1016         (*ppadfPrjParams)[7] = GetNormProjParm( SRS_PP_FALSE_NORTHING, 0.0 );
1017     }
1018 
1019     // Projection unsupported by PCI
1020     else
1021     {
1022         CPLDebug( "OSR_PCI",
1023                   "Projection \"%s\" unsupported by PCI. "
1024                   "PIXEL value will be used.", pszProjection );
1025         CPLPrintStringFill( szProj, "PIXEL", 16 );
1026     }
1027 
1028 /* -------------------------------------------------------------------- */
1029 /*      Translate the datum.                                            */
1030 /* -------------------------------------------------------------------- */
1031     const char  *pszDatum = GetAttrValue( "DATUM" );
1032 
1033     if( pszDatum == NULL || strlen(pszDatum) == 0 )
1034         /* do nothing */;
1035     else if( EQUAL( pszDatum, SRS_DN_NAD27 ) )
1036         CPLPrintStringFill( szProj + 12, "D-01", 4 );
1037 
1038     else if( EQUAL( pszDatum, SRS_DN_NAD83 ) )
1039         CPLPrintStringFill( szProj + 12, "D-02", 4 );
1040 
1041     else if( EQUAL( pszDatum, SRS_DN_WGS84 ) )
1042         CPLPrintStringFill( szProj + 12, "D000", 4 );
1043 
1044     // If not found well known datum, translate ellipsoid
1045     else
1046     {
1047         double      dfSemiMajor = GetSemiMajor();
1048         double      dfInvFlattening = GetInvFlattening();
1049 
1050         const PCIDatums   *paoDatum = aoEllips;
1051 
1052 #ifdef DEBUG
1053         CPLDebug( "OSR_PCI",
1054                   "Datum \"%s\" unsupported by PCI. "
1055                   "Try to translate ellipsoid definition.", pszDatum );
1056 #endif
1057 
1058         while ( paoDatum->pszPCIDatum )
1059         {
1060             double  dfSM;
1061             double  dfIF;
1062 
1063             PCIGetEllipsoidInfo( paoDatum->nEPSGCode, NULL, &dfSM, &dfIF );
1064             if( ABS( dfSemiMajor - dfSM ) < 0.01
1065                 && ABS( dfInvFlattening - dfIF ) < 0.0001 )
1066             {
1067                 CPLPrintStringFill( szProj + 12, paoDatum->pszPCIDatum, 4 );
1068                 break;
1069             }
1070 
1071             paoDatum++;
1072         }
1073 
1074         if ( !paoDatum->pszPCIDatum )       // Didn't found matches; set
1075         {                                   // custom ellipsoid parameters
1076 #ifdef DEBUG
1077             CPLDebug( "OSR_PCI",
1078                       "Ellipsoid \"%s\" unsupported by PCI. "
1079                       "Custom PCI ellipsoid will be used.", pszDatum );
1080 #endif
1081             CPLPrintStringFill( szProj + 12, "E999", 4 );
1082             (*ppadfPrjParams)[0] = dfSemiMajor;
1083             if ( ABS( dfInvFlattening ) < 0.000000000001 )
1084             {
1085                 (*ppadfPrjParams)[1] = dfSemiMajor;
1086             }
1087             else
1088             {
1089                 (*ppadfPrjParams)[1] =
1090                     dfSemiMajor * (1.0 - 1.0/dfInvFlattening);
1091             }
1092         }
1093     }
1094 
1095 /* -------------------------------------------------------------------- */
1096 /*      Translate the linear units.                                     */
1097 /* -------------------------------------------------------------------- */
1098     const char  *pszUnits;
1099 
1100     if( EQUALN( szProj, "LONG/LAT", 8 ) )
1101         pszUnits = "DEGREE";
1102     else
1103         pszUnits = "METRE";
1104 
1105 /* -------------------------------------------------------------------- */
1106 /*      Report results.                                                 */
1107 /* -------------------------------------------------------------------- */
1108     szProj[16] = '\0';
1109     *ppszProj = CPLStrdup( szProj );
1110 
1111     *ppszUnits = CPLStrdup( pszUnits );
1112 
1113     return OGRERR_NONE;
1114 }
1115 
1116