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