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