Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GeoTIFF Driver
4 : * Purpose: Implements special parsing of Imagine citation strings, and
5 : * to encode PE String info in citation fields as needed.
6 : * Author: Xiuguang Zhou (ESRI)
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2008, Xiuguang Zhou (ESRI)
10 : * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
11 : *
12 : * SPDX-License-Identifier: MIT
13 : ****************************************************************************/
14 :
15 : #include "cpl_port.h"
16 :
17 : #include "gt_citation.h"
18 :
19 : #include <cstddef>
20 : #include <cstdlib>
21 : #include <cstring>
22 : #include <algorithm>
23 : #include <string>
24 :
25 : #include "cpl_conv.h"
26 : #include "cpl_string.h"
27 : #include "geokeys.h"
28 : #include "geotiff.h"
29 : #include "geovalues.h"
30 : #include "gt_wkt_srs_priv.h"
31 : #include "ogr_core.h"
32 :
33 : static const char *const apszUnitMap[] = {"meters",
34 : "1.0",
35 : "meter",
36 : "1.0",
37 : "m",
38 : "1.0",
39 : "centimeters",
40 : "0.01",
41 : "centimeter",
42 : "0.01",
43 : "cm",
44 : "0.01",
45 : "millimeters",
46 : "0.001",
47 : "millimeter",
48 : "0.001",
49 : "mm",
50 : "0.001",
51 : "kilometers",
52 : "1000.0",
53 : "kilometer",
54 : "1000.0",
55 : "km",
56 : "1000.0",
57 : "us_survey_feet",
58 : "0.3048006096012192",
59 : "us_survey_foot",
60 : "0.3048006096012192",
61 : "feet",
62 : "0.3048006096012192",
63 : "foot",
64 : "0.3048006096012192",
65 : "ft",
66 : "0.3048006096012192",
67 : "international_feet",
68 : "0.3048",
69 : "international_foot",
70 : "0.3048",
71 : "inches",
72 : "0.0254000508001",
73 : "inch",
74 : "0.0254000508001",
75 : "in",
76 : "0.0254000508001",
77 : "yards",
78 : "0.9144",
79 : "yard",
80 : "0.9144",
81 : "yd",
82 : "0.9144",
83 : "miles",
84 : "1304.544",
85 : "mile",
86 : "1304.544",
87 : "mi",
88 : "1304.544",
89 : "modified_american_feet",
90 : "0.3048122530",
91 : "modified_american_foot",
92 : "0.3048122530",
93 : "clarke_feet",
94 : "0.3047972651",
95 : "clarke_foot",
96 : "0.3047972651",
97 : "indian_feet",
98 : "0.3047995142",
99 : "indian_foot",
100 : "0.3047995142",
101 : "Yard_Indian",
102 : "0.9143985307444408",
103 : "Foot_Clarke",
104 : "0.30479726540",
105 : "Foot_Gold_Coast",
106 : "0.3047997101815088",
107 : "Link_Clarke",
108 : "0.2011661951640",
109 : "Yard_Sears",
110 : "0.9143984146160287",
111 : "50_Kilometers",
112 : "50000.0",
113 : "150_Kilometers",
114 : "150000.0",
115 : nullptr,
116 : nullptr};
117 :
118 : /************************************************************************/
119 : /* ImagineCitationTranslation() */
120 : /* */
121 : /* Translate ERDAS Imagine GeoTif citation */
122 : /************************************************************************/
123 3333 : char *ImagineCitationTranslation(char *psCitation, geokey_t keyID)
124 : {
125 3333 : if (!psCitation)
126 0 : return nullptr;
127 3333 : char *ret = nullptr;
128 3333 : if (STARTS_WITH_CI(psCitation, "IMAGINE GeoTIFF Support"))
129 : {
130 : static const char *const keyNames[] = {
131 : "NAD = ", "Datum = ", "Ellipsoid = ", "Units = ", nullptr};
132 :
133 : // This is a handle IMAGING style citation.
134 12 : CPLString osName;
135 6 : char *p1 = nullptr;
136 :
137 6 : char *p = strchr(psCitation, '$');
138 6 : if (p && strchr(p, '\n'))
139 5 : p = strchr(p, '\n') + 1;
140 6 : if (p)
141 : {
142 5 : p1 = p + strlen(p);
143 5 : char *p2 = strchr(p, '\n');
144 5 : if (p2)
145 5 : p1 = std::min(p1, p2);
146 5 : p2 = strchr(p, '\0');
147 5 : if (p2)
148 5 : p1 = std::min(p1, p2);
149 :
150 25 : for (int i = 0; keyNames[i] != nullptr; i++)
151 : {
152 20 : p2 = strstr(p, keyNames[i]);
153 20 : if (p2)
154 5 : p1 = std::min(p1, p2);
155 : }
156 : }
157 :
158 : // PCS name, GCS name and PRJ name.
159 6 : if (p && p1)
160 : {
161 5 : switch (keyID)
162 : {
163 0 : case PCSCitationGeoKey:
164 0 : if (strstr(psCitation, "Projection = "))
165 0 : osName = "PRJ Name = ";
166 : else
167 0 : osName = "PCS Name = ";
168 0 : break;
169 5 : case GTCitationGeoKey:
170 5 : osName = "PCS Name = ";
171 5 : break;
172 0 : case GeogCitationGeoKey:
173 0 : if (!strstr(p, "Unable to"))
174 0 : osName = "GCS Name = ";
175 0 : break;
176 0 : default:
177 0 : break;
178 : }
179 5 : if (!osName.empty())
180 : {
181 : // TODO(schwehr): What exactly is this code trying to do?
182 : // Added in r15993 and modified in r21844 by warmerdam.
183 5 : char *p2 = nullptr;
184 5 : if ((p2 = strstr(psCitation, "Projection Name = ")) != nullptr)
185 5 : p = p2 + strlen("Projection Name = ");
186 5 : if ((p2 = strstr(psCitation, "Projection = ")) != nullptr)
187 0 : p = p2 + strlen("Projection = ");
188 5 : if (p1[0] == '\0' || p1[0] == '\n' || p1[0] == ' ')
189 5 : p1--;
190 5 : p2 = p1 - 1;
191 5 : while (p2 != nullptr &&
192 5 : (p2[0] == ' ' || p2[0] == '\0' || p2[0] == '\n'))
193 : {
194 0 : p2--;
195 : }
196 5 : if (p2 != p1 - 1)
197 : {
198 0 : p1 = p2;
199 : }
200 5 : if (p1 >= p)
201 : {
202 5 : osName.append(p, p1 - p + 1);
203 5 : osName += '|';
204 : }
205 : }
206 : }
207 :
208 : // All other parameters.
209 30 : for (int i = 0; keyNames[i] != nullptr; i++)
210 : {
211 24 : p = strstr(psCitation, keyNames[i]);
212 24 : if (p)
213 : {
214 6 : p += strlen(keyNames[i]);
215 6 : p1 = p + strlen(p);
216 6 : char *p2 = strchr(p, '\n');
217 6 : if (p2)
218 6 : p1 = std::min(p1, p2);
219 6 : p2 = strchr(p, '\0');
220 6 : if (p2)
221 6 : p1 = std::min(p1, p2);
222 30 : for (int j = 0; keyNames[j] != nullptr; j++)
223 : {
224 24 : p2 = strstr(p, keyNames[j]);
225 24 : if (p2)
226 6 : p1 = std::min(p1, p2);
227 : }
228 : }
229 24 : if (p && p1 && p1 > p)
230 : {
231 6 : if (EQUAL(keyNames[i], "Units = "))
232 6 : osName += "LUnits = ";
233 : else
234 0 : osName += keyNames[i];
235 6 : if (p1[0] == '\0' || p1[0] == '\n' || p1[0] == ' ')
236 6 : p1--;
237 6 : char *p2 = p1 - 1;
238 6 : while (p2 != nullptr &&
239 6 : (p2[0] == ' ' || p2[0] == '\0' || p2[0] == '\n'))
240 : {
241 0 : p2--;
242 : }
243 6 : if (p2 != p1 - 1)
244 : {
245 0 : p1 = p2;
246 : }
247 6 : if (p1 >= p)
248 : {
249 6 : osName.append(p, p1 - p + 1);
250 6 : osName += '|';
251 : }
252 : }
253 : }
254 6 : if (!osName.empty())
255 6 : ret = CPLStrdup(osName);
256 : }
257 3333 : return ret;
258 : }
259 :
260 : /************************************************************************/
261 : /* CitationStringParse() */
262 : /* */
263 : /* Parse a Citation string */
264 : /************************************************************************/
265 :
266 3333 : char **CitationStringParse(char *psCitation, geokey_t keyID)
267 : {
268 3333 : if (!psCitation)
269 0 : return nullptr;
270 :
271 : char **ret =
272 3333 : static_cast<char **>(CPLCalloc(sizeof(char *), nCitationNameTypes));
273 3333 : char *pDelimit = nullptr;
274 3333 : char *pStr = psCitation;
275 3333 : char name[512] = {'\0'};
276 3333 : bool nameSet = false;
277 3333 : int nameLen = static_cast<int>(strlen(psCitation));
278 3333 : bool nameFound = false;
279 6893 : while ((pStr - psCitation + 1) < nameLen)
280 : {
281 3560 : if ((pDelimit = strstr(pStr, "|")) != nullptr)
282 : {
283 315 : strncpy(name, pStr, pDelimit - pStr);
284 315 : name[pDelimit - pStr] = '\0';
285 315 : pStr = pDelimit + 1;
286 315 : nameSet = true;
287 : }
288 : else
289 : {
290 3245 : strcpy(name, pStr);
291 3245 : pStr += strlen(pStr);
292 3245 : nameSet = true;
293 : }
294 3560 : if (strstr(name, "PCS Name = ") && ret[CitPcsName] == nullptr)
295 : {
296 12 : ret[CitPcsName] = CPLStrdup(name + strlen("PCS Name = "));
297 12 : nameFound = true;
298 : }
299 3560 : if (strstr(name, "PRJ Name = ") && ret[CitProjectionName] == nullptr)
300 : {
301 0 : ret[CitProjectionName] = CPLStrdup(name + strlen("PRJ Name = "));
302 0 : nameFound = true;
303 : }
304 3560 : if (strstr(name, "LUnits = ") && ret[CitLUnitsName] == nullptr)
305 : {
306 10 : ret[CitLUnitsName] = CPLStrdup(name + strlen("LUnits = "));
307 10 : nameFound = true;
308 : }
309 3560 : if (strstr(name, "GCS Name = ") && ret[CitGcsName] == nullptr)
310 : {
311 82 : ret[CitGcsName] = CPLStrdup(name + strlen("GCS Name = "));
312 82 : nameFound = true;
313 : }
314 3560 : if (strstr(name, "Datum = ") && ret[CitDatumName] == nullptr)
315 : {
316 63 : ret[CitDatumName] = CPLStrdup(name + strlen("Datum = "));
317 63 : nameFound = true;
318 : }
319 3560 : if (strstr(name, "Ellipsoid = ") && ret[CitEllipsoidName] == nullptr)
320 : {
321 62 : ret[CitEllipsoidName] = CPLStrdup(name + strlen("Ellipsoid = "));
322 62 : nameFound = true;
323 : }
324 3560 : if (strstr(name, "Primem = ") && ret[CitPrimemName] == nullptr)
325 : {
326 82 : ret[CitPrimemName] = CPLStrdup(name + strlen("Primem = "));
327 82 : nameFound = true;
328 : }
329 3560 : if (strstr(name, "AUnits = ") && ret[CitAUnitsName] == nullptr)
330 : {
331 15 : ret[CitAUnitsName] = CPLStrdup(name + strlen("AUnits = "));
332 15 : nameFound = true;
333 : }
334 : }
335 3333 : if (!nameFound && keyID == GeogCitationGeoKey && nameSet)
336 : {
337 1 : ret[CitGcsName] = CPLStrdup(name);
338 1 : nameFound = true;
339 : }
340 3333 : if (!nameFound)
341 : {
342 3233 : CPLFree(ret);
343 3233 : ret = nullptr;
344 : }
345 3333 : return ret;
346 : }
347 :
348 : /************************************************************************/
349 : /* SetLinearUnitCitation() */
350 : /* */
351 : /* Set linear unit Citation string */
352 : /************************************************************************/
353 2 : void SetLinearUnitCitation(std::map<geokey_t, std::string> &oMapAsciiKeys,
354 : const char *pszLinearUOMName)
355 : {
356 2 : CPLString osCitation;
357 2 : auto oIter = oMapAsciiKeys.find(PCSCitationGeoKey);
358 2 : if (oIter != oMapAsciiKeys.end())
359 : {
360 0 : osCitation = oIter->second;
361 : }
362 2 : if (!osCitation.empty())
363 : {
364 0 : size_t n = osCitation.size();
365 0 : if (osCitation[n - 1] != '|')
366 0 : osCitation += "|";
367 0 : osCitation += "LUnits = ";
368 0 : osCitation += pszLinearUOMName;
369 0 : osCitation += "|";
370 : }
371 : else
372 : {
373 2 : osCitation = "LUnits = ";
374 2 : osCitation += pszLinearUOMName;
375 : }
376 2 : oMapAsciiKeys[PCSCitationGeoKey] = std::move(osCitation);
377 2 : }
378 :
379 : /************************************************************************/
380 : /* SetGeogCSCitation() */
381 : /* */
382 : /* Set geogcs Citation string */
383 : /************************************************************************/
384 187 : void SetGeogCSCitation(GTIF *psGTIF,
385 : std::map<geokey_t, std::string> &oMapAsciiKeys,
386 : const OGRSpatialReference *poSRS,
387 : const char *angUnitName, int nDatum, short nSpheroid)
388 : {
389 187 : bool bRewriteGeogCitation = false;
390 187 : CPLString osOriginalGeogCitation;
391 187 : auto oIter = oMapAsciiKeys.find(GeogCitationGeoKey);
392 187 : if (oIter != oMapAsciiKeys.end())
393 : {
394 71 : osOriginalGeogCitation = oIter->second;
395 : }
396 187 : if (osOriginalGeogCitation.empty())
397 116 : return;
398 :
399 142 : CPLString osCitation;
400 71 : if (!STARTS_WITH_CI(osOriginalGeogCitation, "GCS Name = "))
401 : {
402 71 : osCitation = "GCS Name = ";
403 71 : osCitation += std::move(osOriginalGeogCitation);
404 : }
405 : else
406 : {
407 0 : osCitation = std::move(osOriginalGeogCitation);
408 : }
409 :
410 71 : if (nDatum == KvUserDefined)
411 : {
412 45 : const char *datumName = poSRS->GetAttrValue("DATUM");
413 45 : if (datumName && strlen(datumName) > 0)
414 : {
415 45 : osCitation += "|Datum = ";
416 45 : osCitation += datumName;
417 45 : bRewriteGeogCitation = true;
418 : }
419 : }
420 71 : if (nSpheroid == KvUserDefined)
421 : {
422 46 : const char *spheroidName = poSRS->GetAttrValue("SPHEROID");
423 46 : if (spheroidName && strlen(spheroidName) > 0)
424 : {
425 46 : osCitation += "|Ellipsoid = ";
426 46 : osCitation += spheroidName;
427 46 : bRewriteGeogCitation = true;
428 : }
429 : }
430 :
431 71 : const char *primemName = poSRS->GetAttrValue("PRIMEM");
432 71 : if (primemName && strlen(primemName) > 0)
433 : {
434 71 : osCitation += "|Primem = ";
435 71 : osCitation += primemName;
436 71 : bRewriteGeogCitation = true;
437 :
438 71 : double primemValue = poSRS->GetPrimeMeridian(nullptr);
439 71 : if (angUnitName && !EQUAL(angUnitName, "Degree"))
440 : {
441 8 : const double aUnit = poSRS->GetAngularUnits(nullptr);
442 8 : primemValue *= aUnit;
443 : }
444 71 : GTIFKeySet(psGTIF, GeogPrimeMeridianLongGeoKey, TYPE_DOUBLE, 1,
445 : primemValue);
446 : }
447 71 : if (angUnitName && strlen(angUnitName) > 0 && !EQUAL(angUnitName, "Degree"))
448 : {
449 8 : osCitation += "|AUnits = ";
450 8 : osCitation += angUnitName;
451 8 : bRewriteGeogCitation = true;
452 : }
453 :
454 71 : if (osCitation.back() != '|')
455 71 : osCitation += "|";
456 :
457 71 : if (bRewriteGeogCitation)
458 : {
459 71 : oMapAsciiKeys[GeogCitationGeoKey] = std::move(osCitation);
460 : }
461 : }
462 :
463 : /************************************************************************/
464 : /* SetCitationToSRS() */
465 : /* */
466 : /* Parse and set Citation string to SRS */
467 : /************************************************************************/
468 3250 : OGRBoolean SetCitationToSRS(GTIF *hGTIF, char *szCTString, int nCTStringLen,
469 : geokey_t geoKey, OGRSpatialReference *poSRS,
470 : OGRBoolean *linearUnitIsSet)
471 : {
472 3250 : OGRBoolean ret = FALSE;
473 3250 : const char *lUnitName = nullptr;
474 :
475 3250 : poSRS->GetLinearUnits(&lUnitName);
476 3250 : if (!lUnitName || strlen(lUnitName) == 0 || EQUAL(lUnitName, "unknown"))
477 3248 : *linearUnitIsSet = FALSE;
478 : else
479 2 : *linearUnitIsSet = TRUE;
480 :
481 3250 : char *imgCTName = ImagineCitationTranslation(szCTString, geoKey);
482 3250 : if (imgCTName)
483 : {
484 6 : strncpy(szCTString, imgCTName, nCTStringLen);
485 6 : szCTString[nCTStringLen - 1] = '\0';
486 6 : CPLFree(imgCTName);
487 : }
488 3250 : char **ctNames = CitationStringParse(szCTString, geoKey);
489 3250 : if (ctNames)
490 : {
491 17 : if (poSRS->GetRoot() == nullptr)
492 3 : poSRS->SetNode("PROJCS", "unnamed");
493 17 : if (ctNames[CitPcsName])
494 : {
495 12 : poSRS->SetNode("PROJCS", ctNames[CitPcsName]);
496 12 : ret = TRUE;
497 : }
498 17 : if (ctNames[CitProjectionName])
499 0 : poSRS->SetProjection(ctNames[CitProjectionName]);
500 :
501 17 : if (ctNames[CitLUnitsName])
502 : {
503 10 : double unitSize = 0.0;
504 10 : int size = static_cast<int>(strlen(ctNames[CitLUnitsName]));
505 10 : if (strchr(ctNames[CitLUnitsName], '\0'))
506 10 : size -= 1;
507 122 : for (int i = 0; apszUnitMap[i] != nullptr; i += 2)
508 : {
509 120 : if (EQUALN(apszUnitMap[i], ctNames[CitLUnitsName], size))
510 : {
511 8 : unitSize = CPLAtof(apszUnitMap[i + 1]);
512 8 : break;
513 : }
514 : }
515 10 : if (unitSize == 0.0)
516 : {
517 2 : CPL_IGNORE_RET_VAL(GDALGTIFKeyGetDOUBLE(
518 : hGTIF, ProjLinearUnitSizeGeoKey, &unitSize, 0, 1));
519 : }
520 10 : poSRS->SetLinearUnits(ctNames[CitLUnitsName], unitSize);
521 10 : *linearUnitIsSet = TRUE;
522 : }
523 170 : for (int i = 0; i < nCitationNameTypes; i++)
524 153 : CPLFree(ctNames[i]);
525 17 : CPLFree(ctNames);
526 : }
527 :
528 : // If no "PCS Name = " (from Erdas) in GTCitationGeoKey.
529 3250 : if (geoKey == GTCitationGeoKey)
530 : {
531 3242 : if (strlen(szCTString) > 0 && !strstr(szCTString, "PCS Name = "))
532 : {
533 3230 : const char *pszProjCS = poSRS->GetAttrValue("PROJCS");
534 3230 : if ((!(pszProjCS && strlen(pszProjCS) > 0) &&
535 3230 : !strstr(szCTString, "Projected Coordinates")) ||
536 3149 : (pszProjCS && strstr(pszProjCS, "unnamed")))
537 166 : poSRS->SetNode("PROJCS", szCTString);
538 3230 : ret = TRUE;
539 : }
540 : }
541 :
542 3250 : return ret;
543 : }
544 :
545 : /************************************************************************/
546 : /* GetGeogCSFromCitation() */
547 : /* */
548 : /* Parse and get geogcs names from a Citation string */
549 : /************************************************************************/
550 83 : void GetGeogCSFromCitation(char *szGCSName, int nGCSName, geokey_t geoKey,
551 : char **ppszGeogName, char **ppszDatumName,
552 : char **ppszPMName, char **ppszSpheroidName,
553 : char **ppszAngularUnits)
554 : {
555 83 : *ppszGeogName = nullptr;
556 83 : *ppszDatumName = nullptr;
557 83 : *ppszPMName = nullptr;
558 83 : *ppszSpheroidName = nullptr;
559 83 : *ppszAngularUnits = nullptr;
560 :
561 83 : char *imgCTName = ImagineCitationTranslation(szGCSName, geoKey);
562 83 : if (imgCTName)
563 : {
564 0 : strncpy(szGCSName, imgCTName, nGCSName);
565 0 : szGCSName[nGCSName - 1] = '\0';
566 0 : CPLFree(imgCTName);
567 : }
568 83 : char **ctNames = CitationStringParse(szGCSName, geoKey);
569 83 : if (ctNames)
570 : {
571 83 : if (ctNames[CitGcsName])
572 83 : *ppszGeogName = CPLStrdup(ctNames[CitGcsName]);
573 :
574 83 : if (ctNames[CitDatumName])
575 63 : *ppszDatumName = CPLStrdup(ctNames[CitDatumName]);
576 :
577 83 : if (ctNames[CitEllipsoidName])
578 62 : *ppszSpheroidName = CPLStrdup(ctNames[CitEllipsoidName]);
579 :
580 83 : if (ctNames[CitPrimemName])
581 82 : *ppszPMName = CPLStrdup(ctNames[CitPrimemName]);
582 :
583 83 : if (ctNames[CitAUnitsName])
584 15 : *ppszAngularUnits = CPLStrdup(ctNames[CitAUnitsName]);
585 :
586 830 : for (int i = 0; i < nCitationNameTypes; i++)
587 747 : CPLFree(ctNames[i]);
588 83 : CPLFree(ctNames);
589 : }
590 83 : return;
591 : }
592 :
593 : /************************************************************************/
594 : /* CheckCitationKeyForStatePlaneUTM() */
595 : /* */
596 : /* Handle state plane and UTM in citation key */
597 : /************************************************************************/
598 3173 : OGRBoolean CheckCitationKeyForStatePlaneUTM(GTIF *hGTIF, GTIFDefn *psDefn,
599 : OGRSpatialReference *poSRS,
600 : OGRBoolean *pLinearUnitIsSet)
601 : {
602 3173 : if (!hGTIF || !psDefn || !poSRS)
603 0 : return FALSE;
604 :
605 : /* -------------------------------------------------------------------- */
606 : /* For ESRI builds we are interested in maximizing PE */
607 : /* compatibility, but generally we prefer to use EPSG */
608 : /* definitions of the coordinate system if PCS is defined. */
609 : /* -------------------------------------------------------------------- */
610 : #if !defined(ESRI_BUILD)
611 3173 : if (psDefn->PCS != KvUserDefined)
612 3086 : return FALSE;
613 : #endif
614 :
615 87 : char szCTString[512] = {'\0'};
616 :
617 : // Check units.
618 87 : char units[32] = {'\0'};
619 :
620 87 : bool hasUnits = false;
621 87 : if (GDALGTIFKeyGetASCII(hGTIF, GTCitationGeoKey, szCTString,
622 87 : sizeof(szCTString)))
623 : {
624 84 : const CPLString osLCCT = CPLString(szCTString).tolower();
625 :
626 84 : if (strstr(osLCCT, "us") && strstr(osLCCT, "survey") &&
627 0 : (strstr(osLCCT, "feet") || strstr(osLCCT, "foot")))
628 0 : strcpy(units, "us_survey_feet");
629 84 : else if (strstr(osLCCT, "linear_feet") ||
630 168 : strstr(osLCCT, "linear_foot") ||
631 84 : strstr(osLCCT, "international"))
632 0 : strcpy(units, "international_feet");
633 84 : else if (strstr(osLCCT, "meter"))
634 0 : strcpy(units, "meters");
635 :
636 84 : if (strlen(units) > 0)
637 0 : hasUnits = true;
638 :
639 84 : if (strstr(szCTString, "Projection Name = ") &&
640 0 : strstr(szCTString, "_StatePlane_"))
641 : {
642 0 : const char *pStr = strstr(szCTString, "Projection Name = ") +
643 : strlen("Projection Name = ");
644 0 : CPLString osCSName(pStr);
645 0 : const char *pReturn = strchr(pStr, '\n');
646 0 : if (pReturn)
647 0 : osCSName.resize(pReturn - pStr);
648 0 : if (poSRS->ImportFromESRIStatePlaneWKT(0, nullptr, nullptr, 32767,
649 0 : osCSName) == OGRERR_NONE)
650 : {
651 : // For some erdas citation keys, the state plane CS name is
652 : // incomplete, the unit check is necessary.
653 0 : bool done = false;
654 0 : if (hasUnits)
655 : {
656 0 : OGR_SRSNode *poUnit = poSRS->GetAttrNode("PROJCS|UNIT");
657 :
658 0 : if (poUnit != nullptr && poUnit->GetChildCount() >= 2)
659 : {
660 : const CPLString unitName =
661 0 : CPLString(poUnit->GetChild(0)->GetValue())
662 0 : .tolower();
663 :
664 0 : if (strstr(units, "us_survey_feet"))
665 : {
666 0 : if (strstr(unitName, "us_survey_feet") ||
667 0 : strstr(unitName, "foot_us"))
668 0 : done = true;
669 : }
670 0 : else if (strstr(units, "international_feet"))
671 : {
672 0 : if (strstr(unitName, "feet") ||
673 0 : strstr(unitName, "foot"))
674 0 : done = true;
675 : }
676 0 : else if (strstr(units, "meters"))
677 : {
678 0 : if (strstr(unitName, "meter"))
679 0 : done = true;
680 : }
681 : }
682 : }
683 0 : if (done)
684 0 : return true;
685 : }
686 : }
687 : }
688 87 : if (!hasUnits)
689 : {
690 87 : char *pszUnitsName = nullptr;
691 87 : GTIFGetUOMLengthInfo(psDefn->UOMLength, &pszUnitsName, nullptr);
692 87 : if (pszUnitsName)
693 : {
694 164 : const CPLString osLCCT = CPLString(pszUnitsName).tolower();
695 82 : GTIFFreeMemory(pszUnitsName);
696 :
697 94 : if (strstr(osLCCT, "us") && strstr(osLCCT, "survey") &&
698 12 : (strstr(osLCCT, "feet") || strstr(osLCCT, "foot")))
699 12 : strcpy(units, "us_survey_feet");
700 70 : else if (strstr(osLCCT, "feet") || strstr(osLCCT, "foot"))
701 0 : strcpy(units, "international_feet");
702 70 : else if (strstr(osLCCT, "meter"))
703 0 : strcpy(units, "meters");
704 : // hasUnits = true;
705 : }
706 : }
707 :
708 87 : if (strlen(units) == 0)
709 75 : strcpy(units, "meters");
710 :
711 : // Check PCSCitationGeoKey if it exists.
712 87 : szCTString[0] = '\0';
713 87 : if (GDALGTIFKeyGetASCII(hGTIF, PCSCitationGeoKey, szCTString,
714 87 : sizeof(szCTString)))
715 : {
716 : // For tif created by LEICA(ERDAS), ESRI state plane pe string was
717 : // used and the state plane zone is given in PCSCitation. Therefore
718 : // try ESRI pe string first.
719 4 : SetCitationToSRS(hGTIF, szCTString,
720 4 : static_cast<int>(strlen(szCTString)),
721 : PCSCitationGeoKey, poSRS, pLinearUnitIsSet);
722 4 : const char *pcsName = poSRS->GetAttrValue("PROJCS");
723 4 : const char *pStr = nullptr;
724 8 : if ((pcsName &&
725 8 : (pStr = strstr(pcsName, "State Plane Zone ")) != nullptr) ||
726 4 : (pStr = strstr(szCTString, "State Plane Zone ")) != nullptr)
727 : {
728 0 : pStr += strlen("State Plane Zone ");
729 0 : int statePlaneZone = atoi(pStr);
730 : // Safe version of statePlaneZone = abs(statePlaneZone), but
731 : // I (ERO)'ve no idea why negative zone number would make sense...
732 0 : if (statePlaneZone < 0 && statePlaneZone > INT_MIN)
733 0 : statePlaneZone = -statePlaneZone;
734 : char nad[32];
735 0 : strcpy(nad, "HARN");
736 0 : if (strstr(szCTString, "NAD83") || strstr(szCTString, "NAD = 83"))
737 0 : strcpy(nad, "NAD83");
738 0 : else if (strstr(szCTString, "NAD27") ||
739 0 : strstr(szCTString, "NAD = 27"))
740 0 : strcpy(nad, "NAD27");
741 0 : if (poSRS->ImportFromESRIStatePlaneWKT(statePlaneZone, nad, units,
742 0 : psDefn->PCS) == OGRERR_NONE)
743 0 : return TRUE;
744 : }
745 4 : else if (pcsName &&
746 4 : (/* pStr = */ strstr(pcsName, "UTM Zone ")) != nullptr)
747 : {
748 0 : CheckUTM(psDefn, szCTString);
749 : }
750 : }
751 :
752 : // Check state plane again to see if a pe string is available.
753 87 : if (psDefn->PCS != KvUserDefined)
754 : {
755 0 : if (poSRS->ImportFromESRIStatePlaneWKT(0, nullptr, units,
756 0 : psDefn->PCS) == OGRERR_NONE)
757 0 : return TRUE;
758 : }
759 :
760 87 : return FALSE;
761 : }
762 :
763 : /************************************************************************/
764 : /* CheckUTM() */
765 : /* */
766 : /* Check utm proj code by its name. */
767 : /************************************************************************/
768 0 : void CheckUTM(GTIFDefn *psDefn, const char *pszCtString)
769 : {
770 0 : if (!psDefn || !pszCtString)
771 0 : return;
772 :
773 0 : const char *p = strstr(pszCtString, "Datum = ");
774 0 : char datumName[128] = {'\0'};
775 0 : if (p)
776 : {
777 0 : p += strlen("Datum = ");
778 0 : const char *p1 = strchr(p, '|');
779 0 : if (p1 && p1 - p < static_cast<int>(sizeof(datumName)))
780 : {
781 0 : strncpy(datumName, p, p1 - p);
782 0 : datumName[p1 - p] = '\0';
783 : }
784 : else
785 : {
786 0 : CPLStrlcpy(datumName, p, sizeof(datumName));
787 : }
788 : }
789 : else
790 : {
791 0 : datumName[0] = '\0';
792 : }
793 :
794 0 : p = strstr(pszCtString, "UTM Zone ");
795 0 : if (p)
796 : {
797 0 : p += strlen("UTM Zone ");
798 0 : const char *p1 = strchr(p, '|');
799 0 : char utmName[64] = {'\0'};
800 0 : if (p1 && p1 - p < static_cast<int>(sizeof(utmName)))
801 : {
802 0 : strncpy(utmName, p, p1 - p);
803 0 : utmName[p1 - p] = '\0';
804 : }
805 : else
806 : {
807 0 : CPLStrlcpy(utmName, p, sizeof(utmName));
808 : }
809 :
810 : // Static to get this off the stack and constructed only one time.
811 : static const char *const apszUtmProjCode[] = {
812 : "PSAD56", "17N", "16017", "PSAD56", "18N", "16018",
813 : "PSAD56", "19N", "16019", "PSAD56", "20N", "16020",
814 : "PSAD56", "21N", "16021", "PSAD56", "17S", "16117",
815 : "PSAD56", "18S", "16118", "PSAD56", "19S", "16119",
816 : "PSAD56", "20S", "16120", "PSAD56", "21S", "16121",
817 : "PSAD56", "22S", "16122", nullptr, nullptr, nullptr};
818 :
819 0 : for (int i = 0; apszUtmProjCode[i] != nullptr; i += 3)
820 : {
821 0 : if (EQUALN(utmName, apszUtmProjCode[i + 1],
822 0 : strlen(apszUtmProjCode[i + 1])) &&
823 0 : EQUAL(datumName, apszUtmProjCode[i]))
824 : {
825 0 : if (psDefn->ProjCode != atoi(apszUtmProjCode[i + 2]))
826 : {
827 0 : psDefn->ProjCode =
828 0 : static_cast<short>(atoi(apszUtmProjCode[i + 2]));
829 0 : GTIFGetProjTRFInfo(psDefn->ProjCode, nullptr,
830 0 : &(psDefn->Projection), psDefn->ProjParm);
831 0 : break;
832 : }
833 : }
834 : }
835 : }
836 :
837 0 : return;
838 : }
|