Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: OGRSpatialReference translation to/from PCI georeferencing
5 : * information.
6 : * Author: Andrey Kiselev, dron@ak4719.spb.edu
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2003, Andrey Kiselev <dron@ak4719.spb.edu>
10 : * Copyright (c) 2009-2011, Even Rouault <even dot rouault at spatialys.com>
11 : *
12 : * SPDX-License-Identifier: MIT
13 : ****************************************************************************/
14 :
15 : #include "cpl_port.h"
16 : #include "ogr_srs_api.h"
17 :
18 : #include <cctype>
19 : #include <cstdio>
20 : #include <cstdlib>
21 : #include <cstring>
22 :
23 : #include "cpl_conv.h"
24 : #include "cpl_csv.h"
25 : #include "cpl_error.h"
26 : #include "cpl_string.h"
27 : #include "cpl_vsi.h"
28 : #include "ogr_core.h"
29 : #include "ogr_p.h"
30 : #include "ogr_spatialref.h"
31 :
32 : // PCI uses a 16-character string for coordinate system and datum/ellipsoid.
33 : constexpr int knProjSize = 16;
34 :
35 : struct PCIDatums
36 : {
37 : const char *pszPCIDatum;
38 : int nEPSGCode;
39 : };
40 :
41 : static const PCIDatums asDatums[] = {
42 : {"D-01", 4267}, // NAD27 (USA, NADCON)
43 : {"D-03", 4267}, // NAD27 (Canada, NTv1)
44 : {"D-02", 4269}, // NAD83 (USA, NADCON)
45 : {"D-04", 4269}, // NAD83 (Canada, NTv1)
46 : {"D000", 4326}, // WGS 1984
47 : {"D001", 4322}, // WGS 1972
48 : {"D008", 4296}, // Sudan
49 : {"D013", 4601}, // Antigua Island Astro 1943
50 : {"D029", 4202}, // Australian Geodetic 1966
51 : {"D030", 4203}, // Australian Geodetic 1984
52 : {"D033", 4216}, // Bermuda 1957
53 : {"D034", 4165}, // Bissau
54 : {"D036", 4219}, // Bukit Rimpah
55 : {"D038", 4221}, // Campo Inchauspe
56 : {"D040", 4222}, // Cape
57 : {"D042", 4223}, // Carthage
58 : {"D044", 4224}, // Chua Astro
59 : {"D045", 4225}, // Corrego Alegre
60 : {"D046", 4155}, // Dabola (Guinea)
61 : {"D066", 4272}, // Geodetic Datum 1949 (New Zealand)
62 : {"D071", 4255}, // Herat North (Afghanistan)
63 : {"D077", 4239}, // Indian 1954 (Thailand, Vietnam)
64 : {"D078", 4240}, // Indian 1975 (Thailand)
65 : {"D083", 4244}, // Kandawala (Sri Lanka)
66 : {"D085", 4245}, // Kertau 1948 (West Malaysia & Singapore)
67 : {"D088", 4250}, // Leigon (Ghana)
68 : {"D089", 4251}, // Liberia 1964 (Liberia)
69 : {"D092", 4256}, // Mahe 1971 (Mahe Island)
70 : {"D093", 4262}, // Massawa (Ethiopia (Eritrea))
71 : {"D094", 4261}, // Merchich (Morocco)
72 : {"D098",
73 : 4604}, // Montserrat Island Astro 1958 (Montserrat (Leeward Islands))
74 : {"D110", 4267}, // NAD27 / Alaska
75 : {"D139", 4282}, // Pointe Noire 1948 (Congo)
76 : {"D140", 4615}, // Porto Santo 1936 (Porto Santo, Madeira Islands)
77 : {"D151", 4139}, // Puerto Rico (Puerto Rico, Virgin Islands)
78 : {"D153", 4287}, // Qornoq (Greenland (South))
79 : {"D158", 4292}, // Sapper Hill 1943 (East Falkland Island)
80 : {"D159", 4293}, // Schwarzeck (Namibia)
81 : {"D160", 4616}, // Selvagem Grande 1938 (Salvage Islands)
82 : {"D176", 4297}, // Tananarive Observatory 1925 (Madagascar)
83 : {"D177", 4298}, // Timbalai 1948 (Brunei, East Malaysia (Sabah, Sarawak))
84 : {"D187", 4309}, // Yacare (Uruguay)
85 : {"D188", 4311}, // Zanderij (Suriname)
86 : {"D401", 4124}, // RT90 (Sweden)
87 : {"D501", 4312}, // MGI (Hermannskogel, Austria)
88 : {nullptr, 0}};
89 :
90 : static const PCIDatums asEllips[] = {
91 : {"E000", 7008}, // Clarke, 1866 (NAD1927)
92 : {"E001", 7034}, // Clarke, 1880
93 : {"E002", 7004}, // Bessel, 1841
94 : {"E004", 7022}, // International, 1924 (Hayford, 1909)
95 : {"E005", 7043}, // WGS, 1972
96 : {"E006", 7042}, // Everest, 1830
97 : {"E008", 7019}, // GRS, 1980 (NAD1983)
98 : {"E009", 7001}, // Airy, 1830
99 : {"E010", 7018}, // Modified Everest
100 : {"E011", 7002}, // Modified Airy
101 : {"E012", 7030}, // WGS, 1984 (GPS)
102 : {"E014", 7003}, // Australian National, 1965
103 : {"E015", 7024}, // Krassovsky, 1940
104 : {"E016", 7053}, // Hough
105 : {"E019", 7052}, // normal sphere
106 : {"E333", 7046}, // Bessel 1841 (Japan By Law)
107 : {"E900", 7006}, // Bessel, 1841 (Namibia)
108 : {"E901", 7044}, // Everest, 1956
109 : {"E902", 7056}, // Everest, 1969
110 : {"E903", 7016}, // Everest (Sabah & Sarawak)
111 : {"E904", 7020}, // Helmert, 1906
112 : {"E907", 7036}, // South American, 1969
113 : {"E910", 7041}, // ATS77
114 : {nullptr, 0}};
115 :
116 : /************************************************************************/
117 : /* OSRImportFromPCI() */
118 : /************************************************************************/
119 :
120 : /**
121 : * \brief Import coordinate system from PCI projection definition.
122 : *
123 : * This function is the same as OGRSpatialReference::importFromPCI().
124 : */
125 :
126 8 : OGRErr OSRImportFromPCI(OGRSpatialReferenceH hSRS, const char *pszProj,
127 : const char *pszUnits, double *padfPrjParams)
128 :
129 : {
130 8 : VALIDATE_POINTER1(hSRS, "OSRImportFromPCI", OGRERR_FAILURE);
131 :
132 8 : return OGRSpatialReference::FromHandle(hSRS)->importFromPCI(
133 8 : pszProj, pszUnits, padfPrjParams);
134 : }
135 :
136 : /************************************************************************/
137 : /* importFromPCI() */
138 : /************************************************************************/
139 :
140 : /**
141 : * \brief Import coordinate system from PCI projection definition.
142 : *
143 : * PCI software uses 16-character string to specify coordinate system
144 : * and datum/ellipsoid. You should supply at least this string to the
145 : * importFromPCI() function.
146 : *
147 : * This function is the equivalent of the C function OSRImportFromPCI().
148 : *
149 : * @param pszProj NULL terminated string containing the definition. Looks
150 : * like "pppppppppppp Ennn" or "pppppppppppp Dnnn", where "pppppppppppp" is
151 : * a projection code, "Ennn" is an ellipsoid code, "Dnnn" a datum code.
152 : *
153 : * @param pszUnits Grid units code ("DEGREE" or "METRE"). If NULL "METRE" will
154 : * be used.
155 : *
156 : * @param padfPrjParams Array of 17 coordinate system parameters:
157 : *
158 : * [0] Spheroid semi major axis
159 : * [1] Spheroid semi minor axis
160 : * [2] Reference Longitude
161 : * [3] Reference Latitude
162 : * [4] First Standard Parallel
163 : * [5] Second Standard Parallel
164 : * [6] False Easting
165 : * [7] False Northing
166 : * [8] Scale Factor
167 : * [9] Height above sphere surface
168 : * [10] Longitude of 1st point on center line
169 : * [11] Latitude of 1st point on center line
170 : * [12] Longitude of 2nd point on center line
171 : * [13] Latitude of 2nd point on center line
172 : * [14] Azimuth east of north for center line
173 : * [15] Landsat satellite number
174 : * [16] Landsat path number
175 : *
176 : * Particular projection uses different parameters, unused ones may be set to
177 : * zero. If NULL is supplied instead of an array pointer, default values will be
178 : * used (i.e., zeroes).
179 : *
180 : * @return OGRERR_NONE on success or an error code in case of failure.
181 : */
182 :
183 1205 : OGRErr OGRSpatialReference::importFromPCI(const char *pszProj,
184 : const char *pszUnits,
185 : const double *padfPrjParams)
186 :
187 : {
188 1205 : Clear();
189 :
190 2410 : if (pszProj == nullptr ||
191 1205 : CPLStrnlen(pszProj, knProjSize) < static_cast<size_t>(knProjSize))
192 : {
193 3 : return OGRERR_CORRUPT_DATA;
194 : }
195 :
196 1202 : CPLDebug("OSR_PCI", "Trying to import projection \"%s\"", pszProj);
197 :
198 : /* -------------------------------------------------------------------- */
199 : /* Use safe defaults if projection parameters are not supplied. */
200 : /* -------------------------------------------------------------------- */
201 : static const double adfZeroedPrjParams[17] = {0};
202 1202 : if (padfPrjParams == nullptr)
203 : {
204 0 : padfPrjParams = adfZeroedPrjParams;
205 : }
206 :
207 : /* -------------------------------------------------------------------- */
208 : /* Extract and "normalize" the earthmodel to look like E001, */
209 : /* D-02 or D109. */
210 : /* -------------------------------------------------------------------- */
211 1202 : char szEarthModel[5] = {};
212 :
213 1202 : strcpy(szEarthModel, "");
214 1202 : const char *pszEM = pszProj + strlen(pszProj) - 1;
215 14108 : while (pszEM != pszProj)
216 : {
217 14108 : if (*pszEM == 'e' || *pszEM == 'E' || *pszEM == 'd' || *pszEM == 'D')
218 : {
219 1202 : int nCode = atoi(pszEM + 1);
220 :
221 1202 : if (nCode >= -99 && nCode <= 999)
222 1202 : snprintf(szEarthModel, sizeof(szEarthModel), "%c%03d",
223 1202 : CPLToupper(static_cast<unsigned char>(*pszEM)), nCode);
224 :
225 1202 : break;
226 : }
227 :
228 12906 : pszEM--;
229 : }
230 :
231 1196 : const bool bIsNAD27 = EQUAL(pszEM, "E000") || EQUAL(pszEM, "D-01") ||
232 1196 : EQUAL(pszEM, "D-03") || EQUAL(pszEM, "D-07") ||
233 1196 : EQUAL(pszEM, "D-09") || EQUAL(pszEM, "D-11") ||
234 2398 : EQUAL(pszEM, "D-13") || EQUAL(pszEM, "D-17");
235 :
236 : /* -------------------------------------------------------------------- */
237 : /* Operate on the basis of the projection name. */
238 : /* -------------------------------------------------------------------- */
239 1202 : if (STARTS_WITH_CI(pszProj, "LONG/LAT"))
240 : {
241 : // TODO(schwehr): A NOP is okay?
242 : }
243 1193 : else if (STARTS_WITH_CI(pszProj, "METER") ||
244 1186 : STARTS_WITH_CI(pszProj, "METRE"))
245 : {
246 1171 : SetLocalCS("METER");
247 1171 : SetLinearUnits("METER", 1.0);
248 : }
249 22 : else if (STARTS_WITH_CI(pszProj, "FEET") || STARTS_WITH_CI(pszProj, "FOOT"))
250 : {
251 0 : SetLocalCS("FEET");
252 0 : SetLinearUnits("FEET", CPLAtof(SRS_UL_FOOT_CONV));
253 : }
254 22 : else if (STARTS_WITH_CI(pszProj, "ACEA"))
255 : {
256 0 : SetACEA(padfPrjParams[4], padfPrjParams[5], padfPrjParams[3],
257 0 : padfPrjParams[2], padfPrjParams[6], padfPrjParams[7]);
258 : }
259 22 : else if (STARTS_WITH_CI(pszProj, "AE"))
260 : {
261 0 : SetAE(padfPrjParams[3], padfPrjParams[2], padfPrjParams[6],
262 0 : padfPrjParams[7]);
263 : }
264 22 : else if (STARTS_WITH_CI(pszProj, "CASS "))
265 : {
266 0 : SetCS(padfPrjParams[3], padfPrjParams[2], padfPrjParams[6],
267 0 : padfPrjParams[7]);
268 : }
269 22 : else if (STARTS_WITH_CI(pszProj, "EC"))
270 : {
271 2 : SetEC(padfPrjParams[4], padfPrjParams[5], padfPrjParams[3],
272 2 : padfPrjParams[2], padfPrjParams[6], padfPrjParams[7]);
273 : }
274 20 : else if (STARTS_WITH_CI(pszProj, "ER"))
275 : {
276 : // PCI and GCTP don't support natural origin lat.
277 0 : SetEquirectangular2(0.0, padfPrjParams[2], padfPrjParams[3],
278 0 : padfPrjParams[6], padfPrjParams[7]);
279 : }
280 20 : else if (STARTS_WITH_CI(pszProj, "GNO"))
281 : {
282 0 : SetGnomonic(padfPrjParams[3], padfPrjParams[2], padfPrjParams[6],
283 0 : padfPrjParams[7]);
284 : }
285 : // FIXME: GVNP --- General Vertical Near- Side Perspective skipped.
286 : // FIXME: GOOD -- Our Goode's is not the interrupted version from PCI.
287 20 : else if (STARTS_WITH_CI(pszProj, "LAEA"))
288 : {
289 0 : SetLAEA(padfPrjParams[3], padfPrjParams[2], padfPrjParams[6],
290 0 : padfPrjParams[7]);
291 : }
292 20 : else if (STARTS_WITH_CI(pszProj, "LCC "))
293 : {
294 4 : SetLCC(padfPrjParams[4], padfPrjParams[5], padfPrjParams[3],
295 4 : padfPrjParams[2], padfPrjParams[6], padfPrjParams[7]);
296 : }
297 16 : else if (STARTS_WITH_CI(pszProj, "LCC_1SP "))
298 : {
299 0 : SetLCC1SP(padfPrjParams[3], padfPrjParams[2], padfPrjParams[8],
300 0 : padfPrjParams[6], padfPrjParams[7]);
301 : }
302 16 : else if (STARTS_WITH_CI(pszProj, "MC"))
303 : {
304 0 : SetMC(padfPrjParams[3], padfPrjParams[2], padfPrjParams[6],
305 0 : padfPrjParams[7]);
306 : }
307 16 : else if (STARTS_WITH_CI(pszProj, "MER"))
308 : {
309 1 : if (EQUAL(pszEM, "D894") && padfPrjParams[3] == 0 &&
310 1 : padfPrjParams[2] == 0 && padfPrjParams[8] == 1.0 &&
311 1 : padfPrjParams[6] == 0 && padfPrjParams[7] == 0)
312 : {
313 : // Special case for Web Mercator
314 1 : return importFromEPSG(3857);
315 : }
316 0 : SetMercator(padfPrjParams[3], padfPrjParams[2],
317 0 : (padfPrjParams[8] != 0.0) ? padfPrjParams[8] : 1.0,
318 0 : padfPrjParams[6], padfPrjParams[7]);
319 : }
320 15 : else if (STARTS_WITH_CI(pszProj, "OG"))
321 : {
322 0 : SetOrthographic(padfPrjParams[3], padfPrjParams[2], padfPrjParams[6],
323 0 : padfPrjParams[7]);
324 : }
325 15 : else if (STARTS_WITH_CI(pszProj, "OM "))
326 : {
327 0 : if (padfPrjParams[10] == 0.0 && padfPrjParams[11] == 0.0 &&
328 0 : padfPrjParams[12] == 0.0 && padfPrjParams[13] == 0.0)
329 : {
330 0 : SetHOM(padfPrjParams[3], padfPrjParams[2], padfPrjParams[14],
331 0 : padfPrjParams[14], // Use azimuth for grid angle.
332 0 : padfPrjParams[8], padfPrjParams[6], padfPrjParams[7]);
333 : }
334 : else
335 : {
336 0 : SetHOM2PNO(padfPrjParams[3], padfPrjParams[11], padfPrjParams[10],
337 0 : padfPrjParams[13], padfPrjParams[12], padfPrjParams[8],
338 0 : padfPrjParams[6], padfPrjParams[7]);
339 : }
340 : }
341 15 : else if (STARTS_WITH_CI(pszProj, "PC"))
342 : {
343 0 : SetPolyconic(padfPrjParams[3], padfPrjParams[2], padfPrjParams[6],
344 0 : padfPrjParams[7]);
345 : }
346 15 : else if (STARTS_WITH_CI(pszProj, "PS"))
347 : {
348 0 : SetPS(padfPrjParams[3], padfPrjParams[2],
349 0 : (padfPrjParams[8] != 0.0) ? padfPrjParams[8] : 1.0,
350 0 : padfPrjParams[6], padfPrjParams[7]);
351 : }
352 15 : else if (STARTS_WITH_CI(pszProj, "ROB"))
353 : {
354 0 : SetRobinson(padfPrjParams[2], padfPrjParams[6], padfPrjParams[7]);
355 : }
356 :
357 15 : else if (STARTS_WITH_CI(pszProj, "SGDO"))
358 : {
359 0 : SetOS(padfPrjParams[3], padfPrjParams[2],
360 0 : (padfPrjParams[8] != 0.0) ? padfPrjParams[8] : 1.0,
361 0 : padfPrjParams[6], padfPrjParams[7]);
362 : }
363 15 : else if (STARTS_WITH_CI(pszProj, "SG"))
364 : {
365 0 : SetStereographic(padfPrjParams[3], padfPrjParams[2],
366 0 : (padfPrjParams[8] != 0.0) ? padfPrjParams[8] : 1.0,
367 0 : padfPrjParams[6], padfPrjParams[7]);
368 : }
369 15 : else if (STARTS_WITH_CI(pszProj, "SIN"))
370 : {
371 0 : SetSinusoidal(padfPrjParams[2], padfPrjParams[6], padfPrjParams[7]);
372 : }
373 : // FIXME: SOM --- Space Oblique Mercator skipped.
374 15 : else if (STARTS_WITH_CI(pszProj, "SPCS"))
375 : {
376 : const int iZone =
377 0 : static_cast<int>(CPLScanLong(const_cast<char *>(pszProj) + 5, 4));
378 :
379 0 : SetStatePlane(iZone, !bIsNAD27);
380 0 : SetLinearUnitsAndUpdateParameters(SRS_UL_METER, 1.0);
381 : }
382 15 : else if (STARTS_WITH_CI(pszProj, "SPIF"))
383 : {
384 : const int iZone =
385 0 : static_cast<int>(CPLScanLong(const_cast<char *>(pszProj) + 5, 4));
386 :
387 0 : SetStatePlane(iZone, !bIsNAD27);
388 0 : SetLinearUnitsAndUpdateParameters(SRS_UL_FOOT,
389 : CPLAtof(SRS_UL_FOOT_CONV));
390 : }
391 15 : else if (STARTS_WITH_CI(pszProj, "SPAF"))
392 : {
393 : const int iZone =
394 0 : static_cast<int>(CPLScanLong(const_cast<char *>(pszProj) + 5, 4));
395 :
396 0 : SetStatePlane(iZone, !bIsNAD27);
397 0 : SetLinearUnitsAndUpdateParameters(SRS_UL_US_FOOT,
398 : CPLAtof(SRS_UL_US_FOOT_CONV));
399 : }
400 15 : else if (STARTS_WITH_CI(pszProj, "TM"))
401 : {
402 0 : SetTM(padfPrjParams[3], padfPrjParams[2],
403 0 : (padfPrjParams[8] != 0.0) ? padfPrjParams[8] : 1.0,
404 0 : padfPrjParams[6], padfPrjParams[7]);
405 : }
406 :
407 15 : else if (STARTS_WITH_CI(pszProj, "UTM"))
408 : {
409 15 : bool bNorth = true;
410 :
411 : int iZone =
412 15 : static_cast<int>(CPLScanLong(const_cast<char *>(pszProj) + 4, 5));
413 15 : if (iZone < 0)
414 : {
415 0 : iZone = -iZone;
416 0 : bNorth = false;
417 : }
418 :
419 : // Check for a zone letter. PCI uses, accidentally, MGRS
420 : // type row lettering in its UTM projection.
421 15 : char byZoneID = 0;
422 :
423 15 : if (strlen(pszProj) > 10 && pszProj[10] != ' ')
424 4 : byZoneID = pszProj[10];
425 :
426 : // Determine if the MGRS zone falls above or below the equator.
427 15 : if (byZoneID != 0)
428 : {
429 4 : CPLDebug("OSR_PCI", "Found MGRS zone in UTM projection string: %c",
430 : byZoneID);
431 :
432 4 : if (byZoneID >= 'N' && byZoneID <= 'X')
433 : {
434 3 : bNorth = true;
435 : }
436 1 : else if (byZoneID >= 'C' && byZoneID <= 'M')
437 : {
438 1 : bNorth = false;
439 : }
440 : else
441 : {
442 : // Yikes. Most likely we got something that was not really
443 : // an MGRS zone code so we ignore it.
444 : }
445 : }
446 :
447 15 : SetUTM(iZone, bNorth);
448 : }
449 0 : else if (STARTS_WITH_CI(pszProj, "VDG"))
450 : {
451 0 : SetVDG(padfPrjParams[2], padfPrjParams[6], padfPrjParams[7]);
452 : }
453 : else
454 : {
455 0 : CPLDebug("OSR_PCI", "Unsupported projection: %s", pszProj);
456 0 : SetLocalCS(pszProj);
457 : }
458 :
459 : /* ==================================================================== */
460 : /* Translate the datum/spheroid. */
461 : /* ==================================================================== */
462 :
463 : /* -------------------------------------------------------------------- */
464 : /* We have an earthmodel string, look it up in the datum list. */
465 : /* -------------------------------------------------------------------- */
466 2402 : if (strlen(szEarthModel) > 0 &&
467 1201 : (GetRoot() == nullptr || IsProjected() || IsGeographic()))
468 : {
469 30 : const PCIDatums *pasDatum = asDatums;
470 :
471 : // Search for matching datum.
472 729 : while (pasDatum->pszPCIDatum)
473 : {
474 715 : if (EQUALN(szEarthModel, pasDatum->pszPCIDatum, 4))
475 : {
476 32 : OGRSpatialReference oGCS;
477 16 : oGCS.importFromEPSG(pasDatum->nEPSGCode);
478 16 : CopyGeogCSFrom(&oGCS);
479 16 : break;
480 : }
481 699 : pasDatum++;
482 : }
483 :
484 : /* --------------------------------------------------------------------
485 : */
486 : /* If we did not find a datum definition in our in-code EPSG */
487 : /* lookup table, then try fetching from the pci_datum.txt */
488 : /* file. */
489 : /* --------------------------------------------------------------------
490 : */
491 30 : char **papszDatumDefn = nullptr;
492 :
493 30 : if (!pasDatum->pszPCIDatum && szEarthModel[0] == 'D')
494 : {
495 1 : const char *pszDatumCSV = CSVFilename("pci_datum.txt");
496 1 : VSILFILE *fp = pszDatumCSV ? VSIFOpenL(pszDatumCSV, "r") : nullptr;
497 :
498 1 : if (fp != nullptr)
499 : {
500 1 : char **papszLineItems = nullptr;
501 :
502 315 : while ((papszLineItems = CSVReadParseLineL(fp)) != nullptr)
503 : {
504 607 : if (CSLCount(papszLineItems) > 3 &&
505 292 : EQUALN(papszLineItems[0], szEarthModel,
506 : sizeof(szEarthModel) - 1))
507 : {
508 1 : papszDatumDefn = papszLineItems;
509 1 : strncpy(szEarthModel, papszLineItems[2],
510 : sizeof(szEarthModel) - 1);
511 1 : break;
512 : }
513 314 : CSLDestroy(papszLineItems);
514 : }
515 :
516 1 : VSIFCloseL(fp);
517 : }
518 : }
519 :
520 : /* --------------------------------------------------------------------
521 : */
522 : /* If not, look in the ellipsoid/EPSG matching list. */
523 : /* --------------------------------------------------------------------
524 : */
525 30 : if (!pasDatum->pszPCIDatum) // No matching; search for ellipsoids.
526 : {
527 14 : char *pszName = nullptr;
528 14 : double dfSemiMajor = 0.0;
529 14 : double dfInvFlattening = 0.0;
530 14 : int nEPSGCode = 0;
531 :
532 14 : pasDatum = asEllips;
533 :
534 63 : while (pasDatum->pszPCIDatum)
535 : {
536 62 : if (EQUALN(szEarthModel, pasDatum->pszPCIDatum, 4))
537 : {
538 13 : nEPSGCode = pasDatum->nEPSGCode;
539 13 : CPL_IGNORE_RET_VAL(
540 13 : OSRGetEllipsoidInfo(pasDatum->nEPSGCode, &pszName,
541 : &dfSemiMajor, &dfInvFlattening));
542 13 : break;
543 : }
544 49 : pasDatum++;
545 : }
546 :
547 : /* --------------------------------------------------------------------
548 : */
549 : /* If we don't find it in that list, do a lookup in the */
550 : /* pci_ellips.txt file. */
551 : /* --------------------------------------------------------------------
552 : */
553 14 : if (!pasDatum->pszPCIDatum && szEarthModel[0] == 'E')
554 : {
555 1 : const char *pszCSV = CSVFilename("pci_ellips.txt");
556 1 : VSILFILE *fp = pszCSV ? VSIFOpenL(pszCSV, "r") : nullptr;
557 :
558 1 : if (fp != nullptr)
559 : {
560 1 : char **papszLineItems = nullptr;
561 :
562 86 : while ((papszLineItems = CSVReadParseLineL(fp)) != nullptr)
563 : {
564 138 : if (CSLCount(papszLineItems) > 3 &&
565 52 : EQUALN(papszLineItems[0], szEarthModel, 4))
566 : {
567 1 : dfSemiMajor = CPLAtof(papszLineItems[2]);
568 : const double dfSemiMinor =
569 1 : CPLAtof(papszLineItems[3]);
570 1 : dfInvFlattening =
571 1 : OSRCalcInvFlattening(dfSemiMajor, dfSemiMinor);
572 1 : break;
573 : }
574 85 : CSLDestroy(papszLineItems);
575 : }
576 1 : CSLDestroy(papszLineItems);
577 :
578 1 : VSIFCloseL(fp);
579 : }
580 : }
581 :
582 : /* --------------------------------------------------------------------
583 : */
584 : /* Custom spheroid? */
585 : /* --------------------------------------------------------------------
586 : */
587 14 : if (dfSemiMajor == 0.0 && STARTS_WITH_CI(szEarthModel, "E999") &&
588 0 : padfPrjParams[0] != 0.0)
589 : {
590 0 : dfSemiMajor = padfPrjParams[0];
591 0 : double dfSemiMinor = padfPrjParams[1];
592 0 : dfInvFlattening =
593 0 : OSRCalcInvFlattening(dfSemiMajor, dfSemiMinor);
594 : }
595 :
596 : /* --------------------------------------------------------------------
597 : */
598 : /* If nothing else, fall back to WGS84 parameters. */
599 : /* --------------------------------------------------------------------
600 : */
601 14 : if (dfSemiMajor == 0.0)
602 : {
603 0 : dfSemiMajor = SRS_WGS84_SEMIMAJOR;
604 0 : dfInvFlattening = SRS_WGS84_INVFLATTENING;
605 : }
606 :
607 : /* --------------------------------------------------------------------
608 : */
609 : /* Now try to put this all together into a GEOGCS definition.
610 : */
611 : /* --------------------------------------------------------------------
612 : */
613 :
614 28 : CPLString osEllipseName;
615 14 : if (pszName)
616 13 : osEllipseName = pszName;
617 : else
618 1 : osEllipseName.Printf("Unknown - PCI %s", szEarthModel);
619 14 : CPLFree(pszName);
620 :
621 28 : CPLString osDatumName;
622 14 : if (papszDatumDefn)
623 1 : osDatumName = papszDatumDefn[1];
624 : else
625 13 : osDatumName.Printf("Unknown - PCI %s", szEarthModel);
626 :
627 28 : const CPLString osGCSName = osDatumName;
628 :
629 14 : SetGeogCS(osGCSName, osDatumName, osEllipseName, dfSemiMajor,
630 : dfInvFlattening);
631 :
632 : // Do we have an ellipsoid EPSG code?
633 14 : if (nEPSGCode != 0)
634 13 : SetAuthority("SPHEROID", "EPSG", nEPSGCode);
635 :
636 : // Do we have 7 datum shift parameters?
637 15 : if (papszDatumDefn != nullptr && CSLCount(papszDatumDefn) >= 15 &&
638 1 : CPLAtof(papszDatumDefn[14]) != 0.0)
639 : {
640 1 : double dfScale = CPLAtof(papszDatumDefn[14]);
641 :
642 : // we want scale in parts per million off 1.0
643 : // but pci uses a mix of forms.
644 1 : if (dfScale >= 0.999 && dfScale <= 1.001)
645 1 : dfScale = (dfScale - 1.0) * 1000000.0;
646 :
647 1 : SetTOWGS84(
648 1 : CPLAtof(papszDatumDefn[3]), CPLAtof(papszDatumDefn[4]),
649 1 : CPLAtof(papszDatumDefn[5]), CPLAtof(papszDatumDefn[11]),
650 1 : CPLAtof(papszDatumDefn[12]), CPLAtof(papszDatumDefn[13]),
651 : dfScale);
652 : }
653 :
654 : // Do we have 7 datum shift parameters?
655 13 : else if (papszDatumDefn != nullptr &&
656 13 : CSLCount(papszDatumDefn) == 11 &&
657 0 : (CPLAtof(papszDatumDefn[3]) != 0.0 ||
658 0 : CPLAtof(papszDatumDefn[4]) != 0.0 ||
659 0 : CPLAtof(papszDatumDefn[5]) != 0.0))
660 : {
661 0 : SetTOWGS84(CPLAtof(papszDatumDefn[3]),
662 0 : CPLAtof(papszDatumDefn[4]),
663 0 : CPLAtof(papszDatumDefn[5]));
664 : }
665 : }
666 :
667 30 : CSLDestroy(papszDatumDefn);
668 : }
669 :
670 : /* -------------------------------------------------------------------- */
671 : /* Grid units translation */
672 : /* -------------------------------------------------------------------- */
673 1201 : if ((IsLocal() || IsProjected()) && pszUnits)
674 : {
675 5 : if (EQUAL(pszUnits, "METRE"))
676 5 : SetLinearUnits(SRS_UL_METER, 1.0);
677 0 : else if (EQUAL(pszUnits, "DEGREE"))
678 0 : SetAngularUnits(SRS_UA_DEGREE, CPLAtof(SRS_UA_DEGREE_CONV));
679 : else
680 0 : SetLinearUnits(SRS_UL_METER, 1.0);
681 : }
682 :
683 1201 : return OGRERR_NONE;
684 : }
685 :
686 : /************************************************************************/
687 : /* OSRExportToPCI() */
688 : /************************************************************************/
689 : /**
690 : * \brief Export coordinate system in PCI projection definition.
691 : *
692 : * This function is the same as OGRSpatialReference::exportToPCI().
693 : */
694 7 : OGRErr OSRExportToPCI(OGRSpatialReferenceH hSRS, char **ppszProj,
695 : char **ppszUnits, double **ppadfPrjParams)
696 :
697 : {
698 7 : VALIDATE_POINTER1(hSRS, "OSRExportToPCI", OGRERR_FAILURE);
699 :
700 7 : *ppszProj = nullptr;
701 7 : *ppszUnits = nullptr;
702 7 : *ppadfPrjParams = nullptr;
703 :
704 7 : return OGRSpatialReference::FromHandle(hSRS)->exportToPCI(
705 7 : ppszProj, ppszUnits, ppadfPrjParams);
706 : }
707 :
708 : /************************************************************************/
709 : /* exportToPCI() */
710 : /************************************************************************/
711 :
712 : /**
713 : * \brief Export coordinate system in PCI projection definition.
714 : *
715 : * Converts the loaded coordinate reference system into PCI projection
716 : * definition to the extent possible. The strings returned in ppszProj,
717 : * ppszUnits and ppadfPrjParams array should be deallocated by the caller
718 : * with CPLFree() when no longer needed.
719 : *
720 : * LOCAL_CS coordinate systems are not translatable. An empty string
721 : * will be returned along with OGRERR_NONE.
722 : *
723 : * This method is the equivalent of the C function OSRExportToPCI().
724 : *
725 : * @param ppszProj pointer to which dynamically allocated PCI projection
726 : * definition will be assigned.
727 : *
728 : * @param ppszUnits pointer to which dynamically allocated units definition
729 : * will be assigned.
730 : *
731 : * @param ppadfPrjParams pointer to which dynamically allocated array of
732 : * 17 projection parameters will be assigned. See importFromPCI() for the list
733 : * of parameters.
734 : *
735 : * @return OGRERR_NONE on success or an error code on failure.
736 : */
737 :
738 69 : OGRErr OGRSpatialReference::exportToPCI(char **ppszProj, char **ppszUnits,
739 : double **ppadfPrjParams) const
740 :
741 : {
742 69 : const char *pszProjection = GetAttrValue("PROJECTION");
743 :
744 : /* -------------------------------------------------------------------- */
745 : /* Fill all projection parameters with zero. */
746 : /* -------------------------------------------------------------------- */
747 69 : *ppadfPrjParams = static_cast<double *>(CPLMalloc(17 * sizeof(double)));
748 1242 : for (int i = 0; i < 17; i++)
749 1173 : (*ppadfPrjParams)[i] = 0.0;
750 :
751 : /* -------------------------------------------------------------------- */
752 : /* Get the prime meridian info. */
753 : /* -------------------------------------------------------------------- */
754 : #if 0
755 : const OGR_SRSNode *poPRIMEM = GetAttrNode( "PRIMEM" );
756 : double dfFromGreenwich = 0.0;
757 :
758 : if( poPRIMEM != NULL && poPRIMEM->GetChildCount() >= 2
759 : && CPLAtof(poPRIMEM->GetChild(1)->GetValue()) != 0.0 )
760 : {
761 : dfFromGreenwich = CPLAtof(poPRIMEM->GetChild(1)->GetValue());
762 : }
763 : #endif
764 :
765 : /* ==================================================================== */
766 : /* Handle the projection definition. */
767 : /* ==================================================================== */
768 69 : char szProj[knProjSize + 1] = {};
769 :
770 69 : if (IsLocal())
771 : {
772 2 : if (GetLinearUnits() > 0.30479999 && GetLinearUnits() < 0.3048010)
773 0 : CPLPrintStringFill(szProj, "FEET", knProjSize);
774 : else
775 2 : CPLPrintStringFill(szProj, "METER", knProjSize);
776 : }
777 67 : else if (pszProjection == nullptr)
778 : {
779 54 : CPLPrintStringFill(szProj, "LONG/LAT", knProjSize);
780 : }
781 13 : else if (EQUAL(pszProjection, SRS_PT_ALBERS_CONIC_EQUAL_AREA))
782 : {
783 0 : CPLPrintStringFill(szProj, "ACEA", knProjSize);
784 0 : (*ppadfPrjParams)[2] = GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
785 0 : (*ppadfPrjParams)[3] = GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
786 0 : (*ppadfPrjParams)[4] = GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, 0.0);
787 0 : (*ppadfPrjParams)[5] = GetNormProjParm(SRS_PP_STANDARD_PARALLEL_2, 0.0);
788 0 : (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
789 0 : (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
790 : }
791 13 : else if (EQUAL(pszProjection, SRS_PT_AZIMUTHAL_EQUIDISTANT))
792 : {
793 0 : CPLPrintStringFill(szProj, "AE", knProjSize);
794 0 : (*ppadfPrjParams)[2] = GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
795 0 : (*ppadfPrjParams)[3] = GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
796 0 : (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
797 0 : (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
798 : }
799 13 : else if (EQUAL(pszProjection, SRS_PT_CASSINI_SOLDNER))
800 : {
801 0 : CPLPrintStringFill(szProj, "CASS", knProjSize);
802 0 : (*ppadfPrjParams)[2] = GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
803 0 : (*ppadfPrjParams)[3] = GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
804 0 : (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
805 0 : (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
806 : }
807 13 : else if (EQUAL(pszProjection, SRS_PT_EQUIDISTANT_CONIC))
808 : {
809 1 : CPLPrintStringFill(szProj, "EC", knProjSize);
810 1 : (*ppadfPrjParams)[2] = GetNormProjParm(SRS_PP_LONGITUDE_OF_CENTER, 0.0);
811 1 : (*ppadfPrjParams)[3] = GetNormProjParm(SRS_PP_LATITUDE_OF_CENTER, 0.0);
812 1 : (*ppadfPrjParams)[4] = GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, 0.0);
813 1 : (*ppadfPrjParams)[5] = GetNormProjParm(SRS_PP_STANDARD_PARALLEL_2, 0.0);
814 1 : (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
815 1 : (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
816 : }
817 12 : else if (EQUAL(pszProjection, SRS_PT_EQUIRECTANGULAR))
818 : {
819 0 : CPLPrintStringFill(szProj, "ER", knProjSize);
820 0 : (*ppadfPrjParams)[2] = GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
821 0 : (*ppadfPrjParams)[3] = GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, 0.0);
822 0 : (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
823 0 : (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
824 : }
825 12 : else if (EQUAL(pszProjection, SRS_PT_GNOMONIC))
826 : {
827 0 : CPLPrintStringFill(szProj, "GNO", knProjSize);
828 0 : (*ppadfPrjParams)[2] = GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
829 0 : (*ppadfPrjParams)[3] = GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
830 0 : (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
831 0 : (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
832 : }
833 12 : else if (EQUAL(pszProjection, SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA))
834 : {
835 0 : CPLPrintStringFill(szProj, "LAEA", knProjSize);
836 0 : (*ppadfPrjParams)[2] = GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
837 0 : (*ppadfPrjParams)[3] = GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
838 0 : (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
839 0 : (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
840 : }
841 12 : else if (EQUAL(pszProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP))
842 : {
843 2 : CPLPrintStringFill(szProj, "LCC", knProjSize);
844 2 : (*ppadfPrjParams)[2] = GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
845 2 : (*ppadfPrjParams)[3] = GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
846 2 : (*ppadfPrjParams)[4] = GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, 0.0);
847 2 : (*ppadfPrjParams)[5] = GetNormProjParm(SRS_PP_STANDARD_PARALLEL_2, 0.0);
848 2 : (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
849 2 : (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
850 : }
851 10 : else if (EQUAL(pszProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP))
852 : {
853 0 : CPLPrintStringFill(szProj, "LCC_1SP", knProjSize);
854 0 : (*ppadfPrjParams)[2] = GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
855 0 : (*ppadfPrjParams)[3] = GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
856 0 : (*ppadfPrjParams)[8] = GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0);
857 0 : (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
858 0 : (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
859 : }
860 10 : else if (EQUAL(pszProjection, SRS_PT_MILLER_CYLINDRICAL))
861 : {
862 0 : CPLPrintStringFill(szProj, "MC", knProjSize);
863 0 : (*ppadfPrjParams)[2] = GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
864 0 : (*ppadfPrjParams)[3] = GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
865 0 : (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
866 0 : (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
867 : }
868 10 : else if (EQUAL(pszProjection, SRS_PT_MERCATOR_1SP))
869 : {
870 1 : CPLPrintStringFill(szProj, "MER", knProjSize);
871 1 : (*ppadfPrjParams)[2] = GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
872 1 : (*ppadfPrjParams)[3] = GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
873 1 : (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
874 1 : (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
875 1 : (*ppadfPrjParams)[8] = GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0);
876 : }
877 9 : else if (EQUAL(pszProjection, SRS_PT_ORTHOGRAPHIC))
878 : {
879 0 : CPLPrintStringFill(szProj, "OG", knProjSize);
880 0 : (*ppadfPrjParams)[2] = GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
881 0 : (*ppadfPrjParams)[3] = GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
882 0 : (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
883 0 : (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
884 : }
885 9 : else if (EQUAL(pszProjection, SRS_PT_HOTINE_OBLIQUE_MERCATOR))
886 : {
887 0 : CPLPrintStringFill(szProj, "OM", knProjSize);
888 0 : (*ppadfPrjParams)[2] = GetNormProjParm(SRS_PP_LONGITUDE_OF_CENTER, 0.0);
889 0 : (*ppadfPrjParams)[3] = GetNormProjParm(SRS_PP_LATITUDE_OF_CENTER, 0.0);
890 0 : (*ppadfPrjParams)[14] = GetNormProjParm(SRS_PP_AZIMUTH, 0.0);
891 : // Note: Ignoring rectified_grid_angle which has no PCI analog.
892 0 : (*ppadfPrjParams)[8] = GetNormProjParm(SRS_PP_SCALE_FACTOR, 0.0);
893 0 : (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
894 0 : (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
895 : }
896 9 : else if (EQUAL(pszProjection,
897 : SRS_PT_HOTINE_OBLIQUE_MERCATOR_TWO_POINT_NATURAL_ORIGIN))
898 : {
899 0 : CPLPrintStringFill(szProj, "OM", knProjSize);
900 0 : (*ppadfPrjParams)[3] = GetNormProjParm(SRS_PP_LATITUDE_OF_CENTER, 0.0);
901 0 : (*ppadfPrjParams)[11] =
902 0 : GetNormProjParm(SRS_PP_LATITUDE_OF_POINT_1, 0.0);
903 0 : (*ppadfPrjParams)[10] =
904 0 : GetNormProjParm(SRS_PP_LONGITUDE_OF_POINT_1, 0.0);
905 0 : (*ppadfPrjParams)[13] =
906 0 : GetNormProjParm(SRS_PP_LATITUDE_OF_POINT_2, 0.0);
907 0 : (*ppadfPrjParams)[12] =
908 0 : GetNormProjParm(SRS_PP_LONGITUDE_OF_POINT_2, 0.0);
909 0 : (*ppadfPrjParams)[8] = GetNormProjParm(SRS_PP_SCALE_FACTOR, 0.0);
910 0 : (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
911 0 : (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
912 : }
913 9 : else if (EQUAL(pszProjection, SRS_PT_POLYCONIC))
914 : {
915 0 : CPLPrintStringFill(szProj, "PC", knProjSize);
916 0 : (*ppadfPrjParams)[2] = GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
917 0 : (*ppadfPrjParams)[3] = GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
918 0 : (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
919 0 : (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
920 : }
921 9 : else if (EQUAL(pszProjection, SRS_PT_POLAR_STEREOGRAPHIC))
922 : {
923 0 : CPLPrintStringFill(szProj, "PS", knProjSize);
924 0 : (*ppadfPrjParams)[2] = GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
925 0 : (*ppadfPrjParams)[3] = GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
926 0 : (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
927 0 : (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
928 0 : (*ppadfPrjParams)[8] = GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0);
929 : }
930 9 : else if (EQUAL(pszProjection, SRS_PT_ROBINSON))
931 : {
932 0 : CPLPrintStringFill(szProj, "ROB", knProjSize);
933 0 : (*ppadfPrjParams)[2] = GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
934 0 : (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
935 0 : (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
936 : }
937 9 : else if (EQUAL(pszProjection, SRS_PT_OBLIQUE_STEREOGRAPHIC))
938 : {
939 0 : CPLPrintStringFill(szProj, "SGDO", knProjSize);
940 0 : (*ppadfPrjParams)[2] = GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
941 0 : (*ppadfPrjParams)[3] = GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
942 0 : (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
943 0 : (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
944 0 : (*ppadfPrjParams)[8] = GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0);
945 : }
946 9 : else if (EQUAL(pszProjection, SRS_PT_STEREOGRAPHIC))
947 : {
948 0 : CPLPrintStringFill(szProj, "SG", knProjSize);
949 0 : (*ppadfPrjParams)[2] = GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
950 0 : (*ppadfPrjParams)[3] = GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
951 0 : (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
952 0 : (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
953 0 : (*ppadfPrjParams)[8] = GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0);
954 : }
955 9 : else if (EQUAL(pszProjection, SRS_PT_SINUSOIDAL))
956 : {
957 0 : CPLPrintStringFill(szProj, "SIN", knProjSize);
958 0 : (*ppadfPrjParams)[2] = GetNormProjParm(SRS_PP_LONGITUDE_OF_CENTER, 0.0);
959 0 : (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
960 0 : (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
961 : }
962 9 : else if (EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR))
963 : {
964 9 : int bNorth = FALSE;
965 9 : int nZone = GetUTMZone(&bNorth);
966 :
967 9 : if (nZone != 0)
968 : {
969 9 : CPLPrintStringFill(szProj, "UTM", knProjSize);
970 9 : if (bNorth)
971 9 : CPLPrintInt32(szProj + 5, nZone, 4);
972 : else
973 0 : CPLPrintInt32(szProj + 5, -nZone, 4);
974 : }
975 : else
976 : {
977 0 : CPLPrintStringFill(szProj, "TM", knProjSize);
978 0 : (*ppadfPrjParams)[2] =
979 0 : GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
980 0 : (*ppadfPrjParams)[3] =
981 0 : GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
982 0 : (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
983 0 : (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
984 0 : (*ppadfPrjParams)[8] = GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0);
985 : }
986 : }
987 0 : else if (EQUAL(pszProjection, SRS_PT_VANDERGRINTEN))
988 : {
989 0 : CPLPrintStringFill(szProj, "VDG", knProjSize);
990 0 : (*ppadfPrjParams)[2] = GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0);
991 0 : (*ppadfPrjParams)[6] = GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0);
992 0 : (*ppadfPrjParams)[7] = GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0);
993 : }
994 : // Projection unsupported by PCI.
995 : else
996 : {
997 0 : CPLDebug("OSR_PCI",
998 : "Projection \"%s\" unsupported by PCI. "
999 : "PIXEL value will be used.",
1000 : pszProjection);
1001 0 : CPLPrintStringFill(szProj, "PIXEL", knProjSize);
1002 : }
1003 :
1004 : /* ==================================================================== */
1005 : /* Translate the earth model. */
1006 : /* ==================================================================== */
1007 :
1008 : /* -------------------------------------------------------------------- */
1009 : /* Is this a well known datum? */
1010 : /* -------------------------------------------------------------------- */
1011 69 : const char *pszDatum = GetAttrValue("DATUM");
1012 69 : char szEarthModel[5] = {};
1013 :
1014 69 : if (pszDatum == nullptr || strlen(pszDatum) == 0)
1015 : {
1016 : // Do nothing.
1017 : }
1018 67 : else if (EQUAL(pszDatum, SRS_DN_NAD27))
1019 : {
1020 6 : CPLPrintStringFill(szEarthModel, "D-01", 4);
1021 : }
1022 61 : else if (EQUAL(pszDatum, SRS_DN_NAD83))
1023 : {
1024 0 : CPLPrintStringFill(szEarthModel, "D-02", 4);
1025 : }
1026 61 : else if (EQUAL(pszDatum, SRS_DN_WGS84))
1027 : {
1028 53 : CPLPrintStringFill(szEarthModel, "D000", 4);
1029 53 : if (pszProjection && EQUAL(pszProjection, SRS_PT_MERCATOR_1SP))
1030 : {
1031 : // Special case for Web Mercator
1032 1 : const char *method_name = "";
1033 1 : GetWKT2ProjectionMethod(&method_name, nullptr, nullptr);
1034 1 : if (EQUAL(method_name, "Popular Visualisation Pseudo Mercator"))
1035 : {
1036 1 : CPLPrintStringFill(szEarthModel, "D894", 4);
1037 : }
1038 : }
1039 : }
1040 :
1041 : /* -------------------------------------------------------------------- */
1042 : /* If not a very well known datum, try for an EPSG based */
1043 : /* translation. */
1044 : /* -------------------------------------------------------------------- */
1045 69 : if (szEarthModel[0] == '\0')
1046 : {
1047 10 : const char *pszAuthority = GetAuthorityName("GEOGCS");
1048 :
1049 10 : if (pszAuthority && EQUAL(pszAuthority, "EPSG"))
1050 : {
1051 1 : const int nGCS_EPSG = atoi(GetAuthorityCode("GEOGCS"));
1052 :
1053 10 : for (int i = 0; asDatums[i].nEPSGCode != 0; i++)
1054 : {
1055 10 : if (asDatums[i].nEPSGCode == nGCS_EPSG)
1056 : {
1057 1 : snprintf(szEarthModel, sizeof(szEarthModel), "%s",
1058 1 : asDatums[i].pszPCIDatum);
1059 1 : break;
1060 : }
1061 : }
1062 : }
1063 : }
1064 :
1065 : /* -------------------------------------------------------------------- */
1066 : /* If we haven't found something yet, try translating the */
1067 : /* ellipsoid. */
1068 : /* -------------------------------------------------------------------- */
1069 69 : const double dfSemiMajor = GetSemiMajor();
1070 69 : const double dfInvFlattening = GetInvFlattening();
1071 69 : if (szEarthModel[0] == '\0')
1072 : {
1073 9 : const PCIDatums *pasDatum = asEllips;
1074 :
1075 137 : while (pasDatum->pszPCIDatum)
1076 : {
1077 133 : double dfSM = 0.0;
1078 133 : double dfIF = 0.0;
1079 :
1080 133 : if (OSRGetEllipsoidInfo(pasDatum->nEPSGCode, nullptr, &dfSM,
1081 133 : &dfIF) == OGRERR_NONE &&
1082 143 : CPLIsEqual(dfSemiMajor, dfSM) &&
1083 10 : CPLIsEqual(dfInvFlattening, dfIF))
1084 : {
1085 5 : CPLPrintStringFill(szEarthModel, pasDatum->pszPCIDatum, 4);
1086 5 : break;
1087 : }
1088 :
1089 128 : pasDatum++;
1090 : }
1091 : }
1092 :
1093 : // Try to find in pci_ellips.txt.
1094 69 : if (szEarthModel[0] == '\0')
1095 : {
1096 4 : const char *pszCSV = CSVFilename("pci_ellips.txt");
1097 : const double dfSemiMinor =
1098 4 : OSRCalcSemiMinorFromInvFlattening(dfSemiMajor, dfInvFlattening);
1099 :
1100 4 : VSILFILE *fp = pszCSV ? VSIFOpenL(pszCSV, "r") : nullptr;
1101 :
1102 4 : if (fp != nullptr)
1103 : {
1104 4 : char **papszLineItems = nullptr;
1105 :
1106 194 : while ((papszLineItems = CSVReadParseLineL(fp)) != nullptr)
1107 : {
1108 194 : if (CSLCount(papszLineItems) >= 4 &&
1109 198 : CPLIsEqual(dfSemiMajor, CPLAtof(papszLineItems[2])) &&
1110 4 : CPLIsEqual(dfSemiMinor, CPLAtof(papszLineItems[3])))
1111 : {
1112 4 : snprintf(szEarthModel, sizeof(szEarthModel), "%s",
1113 : papszLineItems[0]);
1114 4 : break;
1115 : }
1116 :
1117 190 : CSLDestroy(papszLineItems);
1118 : }
1119 :
1120 4 : CSLDestroy(papszLineItems);
1121 4 : VSIFCloseL(fp);
1122 : }
1123 : }
1124 :
1125 : // Custom ellipsoid parameters.
1126 69 : if (szEarthModel[0] == '\0')
1127 : {
1128 0 : CPLPrintStringFill(szEarthModel, "E999", 4);
1129 0 : (*ppadfPrjParams)[0] = dfSemiMajor;
1130 0 : (*ppadfPrjParams)[1] =
1131 0 : OSRCalcSemiMinorFromInvFlattening(dfSemiMajor, dfInvFlattening);
1132 : }
1133 :
1134 : /* -------------------------------------------------------------------- */
1135 : /* If we have a non-parameteric ellipsoid, scan the */
1136 : /* pci_datum.txt for a match. */
1137 : /* -------------------------------------------------------------------- */
1138 69 : if (szEarthModel[0] == 'E' && !EQUAL(szEarthModel, "E999") &&
1139 : pszDatum != nullptr)
1140 : {
1141 7 : const char *pszDatumCSV = CSVFilename("pci_datum.txt");
1142 7 : double adfTOWGS84[7] = {};
1143 7 : const bool bHaveTOWGS84 = GetTOWGS84(adfTOWGS84, 7) == OGRERR_NONE;
1144 :
1145 7 : VSILFILE *fp = pszDatumCSV ? VSIFOpenL(pszDatumCSV, "r") : nullptr;
1146 :
1147 7 : if (fp != nullptr)
1148 : {
1149 7 : char **papszLineItems = nullptr;
1150 :
1151 3285 : while ((papszLineItems = CSVReadParseLineL(fp)) != nullptr)
1152 : {
1153 : // Compare based on datum name. This is mostly for
1154 : // PCI round-tripping. We won't usually get exact matches
1155 : // from other sources.
1156 3280 : if (CSLCount(papszLineItems) > 3 &&
1157 3281 : EQUAL(papszLineItems[1], pszDatum) &&
1158 1 : EQUAL(papszLineItems[2], szEarthModel))
1159 : {
1160 1 : snprintf(szEarthModel, sizeof(szEarthModel), "%s",
1161 : papszLineItems[0]);
1162 1 : break;
1163 : }
1164 :
1165 3279 : bool bTOWGS84Match = bHaveTOWGS84;
1166 :
1167 3279 : if (CSLCount(papszLineItems) < 11)
1168 848 : bTOWGS84Match = false;
1169 :
1170 3780 : if (bTOWGS84Match &&
1171 501 : (!CPLIsEqual(adfTOWGS84[0], CPLAtof(papszLineItems[3])) ||
1172 1 : !CPLIsEqual(adfTOWGS84[1], CPLAtof(papszLineItems[4])) ||
1173 1 : !CPLIsEqual(adfTOWGS84[2], CPLAtof(papszLineItems[5]))))
1174 500 : bTOWGS84Match = false;
1175 :
1176 3280 : if (bTOWGS84Match && CSLCount(papszLineItems) >= 15 &&
1177 1 : (!CPLIsEqual(adfTOWGS84[3], CPLAtof(papszLineItems[11])) ||
1178 1 : !CPLIsEqual(adfTOWGS84[4], CPLAtof(papszLineItems[12])) ||
1179 1 : !CPLIsEqual(adfTOWGS84[5], CPLAtof(papszLineItems[13]))))
1180 0 : bTOWGS84Match = false;
1181 :
1182 3279 : if (bTOWGS84Match && CSLCount(papszLineItems) >= 15)
1183 : {
1184 1 : double dfScale = CPLAtof(papszLineItems[14]);
1185 :
1186 : // Convert to parts per million if is a 1 based scaling.
1187 1 : if (dfScale >= 0.999 && dfScale <= 1.001)
1188 1 : dfScale = (dfScale - 1.0) * 1000000.0;
1189 :
1190 1 : if (fabs(adfTOWGS84[6] - dfScale) >
1191 1 : 1e-11 * fabs(adfTOWGS84[6]))
1192 0 : bTOWGS84Match = false;
1193 : }
1194 :
1195 3279 : if (bTOWGS84Match && CSLCount(papszLineItems) < 15 &&
1196 0 : (!CPLIsEqual(adfTOWGS84[3], 0.0) ||
1197 0 : !CPLIsEqual(adfTOWGS84[4], 0.0) ||
1198 0 : !CPLIsEqual(adfTOWGS84[5], 0.0) ||
1199 0 : !CPLIsEqual(adfTOWGS84[6], 0.0)))
1200 0 : bTOWGS84Match = false;
1201 :
1202 3279 : if (bTOWGS84Match)
1203 : {
1204 1 : snprintf(szEarthModel, sizeof(szEarthModel), "%s",
1205 : papszLineItems[0]);
1206 1 : break;
1207 : }
1208 :
1209 3278 : CSLDestroy(papszLineItems);
1210 : }
1211 :
1212 7 : CSLDestroy(papszLineItems);
1213 7 : VSIFCloseL(fp);
1214 : }
1215 : }
1216 :
1217 69 : CPLPrintStringFill(szProj + 12, szEarthModel, 4);
1218 :
1219 69 : CPLDebug("OSR_PCI", "Translated as '%s'", szProj);
1220 :
1221 : /* -------------------------------------------------------------------- */
1222 : /* Translate the linear units. */
1223 : /* -------------------------------------------------------------------- */
1224 69 : const char *pszUnits =
1225 69 : STARTS_WITH_CI(szProj, "LONG/LAT") ? "DEGREE" : "METRE";
1226 :
1227 : /* -------------------------------------------------------------------- */
1228 : /* Report results. */
1229 : /* -------------------------------------------------------------------- */
1230 69 : szProj[knProjSize] = '\0';
1231 69 : *ppszProj = CPLStrdup(szProj);
1232 :
1233 69 : *ppszUnits = CPLStrdup(pszUnits);
1234 :
1235 69 : return OGRERR_NONE;
1236 : }
|