OSSIM - Open Source Software Image Map  Version 1.9.0 (20180803)
ossimGdalProjectionFactory.cpp
Go to the documentation of this file.
1 //*****************************************************************************
2 //
3 // License: LGPL
4 //
5 // See LICENSE.txt file in the top level directory for more details.
6 //
7 // DESCRIPTION:
8 // Contains implementation of class ossimMapProjectionFactory
9 //
10 //*****************************************************************************
11 // $Id: ossimGdalProjectionFactory.cpp 22734 2014-04-15 19:28:42Z gpotts $
12 #include <sstream>
14 #include "ossimGdalTileSource.h"
22 #include "ossimOgcWktTranslator.h"
23 #include <ossim/base/ossimTrace.h>
24 #include "ossimOgcWktTranslator.h"
27 #include <gdal_priv.h>
28 #include <cpl_string.h>
32 
33 static ossimTrace traceDebug("ossimGdalProjectionFactory:debug");
34 
36 static ossimOgcWktTranslator wktTranslator;
37 
39 {
40  if(!theInstance)
41  {
43  }
44 
46 }
47 
49  ossim_uint32 entryIdx)const
50 {
51  ossimKeywordlist kwl;
52 // ossimRefPtr<ossimImageHandler> h = new ossimGdalTileSource;
53  //std::cout << "ossimGdalProjectionFactory::createProjection: " << filename << std::endl;
54  GDALDatasetH h;
55  GDALDriverH driverH = 0;
56  ossimProjection* proj = 0;
57 
58  if(ossimString(filename).trim().empty()) return 0;
59 
60  h = GDALOpen(filename.c_str(), GA_ReadOnly);
61  if(h)
62  {
63  driverH = GDALGetDatasetDriver( h );
64  ossimString driverName( driverH ? GDALGetDriverShortName( driverH ) : "" );
65  // use OSSIM's projection loader for NITF
66  //
67  if(driverName == "NITF")
68  {
69  GDALClose(h);
70  return 0;
71  }
72  if(entryIdx != 0)
73  {
74  char** papszMetadata = GDALGetMetadata( h, "SUBDATASETS" );
75 
76  //---
77  // ??? (drb) Should this be:
78  // if ( entryIdx >= CSLCount(papszMetadata) ) close...
79  //---
80  if( papszMetadata&&(CSLCount(papszMetadata) < static_cast<ossim_int32>(entryIdx)) )
81  {
82  ossimNotify(ossimNotifyLevel_WARN) << "ossimGdalProjectionFactory::createProjection: We don't support multi entry handlers through the factory yet, only through the handler!";
83  GDALClose(h);
84  return 0;
85  }
86  else
87  {
88  GDALClose(h);
89  return 0;
90  }
91  }
92 
93  ossimString wkt(GDALGetProjectionRef( h ));
94  double geoTransform[6];
95  bool transOk = GDALGetGeoTransform( h, geoTransform ) == CE_None;
96  bool wktTranslatorOk = wkt.empty()?false:wktTranslator.toOssimKwl(wkt, kwl);
97  if(!wktTranslatorOk)
98  {
99  ossim_uint32 gcpCount = GDALGetGCPCount(h);
100  if(gcpCount > 3)
101  {
102  ossim_uint32 idx = 0;
103  const GDAL_GCP* gcpList = GDALGetGCPs(h);
104  ossimTieGptSet tieSet;
105  if(gcpList)
106  {
107  for(idx = 0; idx < gcpCount; ++idx)
108  {
109  ossimDpt dpt(gcpList[idx].dfGCPPixel,
110  gcpList[idx].dfGCPLine);
111  ossimGpt gpt(gcpList[idx].dfGCPY,
112  gcpList[idx].dfGCPX,
113  gcpList[idx].dfGCPZ);
114  tieSet.addTiePoint(new ossimTieGpt(gpt, dpt, .5));
115  }
116 
117  //ossimPolynomProjection* tempProj = new ossimPolynomProjection;
119  //tempProj->setupOptimizer("1 x y x2 xy y2 x3 y3 xy2 x2y z xz yz");
120  tempProj->optimizeFit(tieSet);
121  proj = tempProj;
122  }
123  }
124  }
125  if ( transOk && proj==0 )
126  {
130  if ( proj_type.trim().empty() &&
131  (driverName == "MrSID" || driverName == "JP2MrSID") )
132  {
133  bool bClose = true;
134  // ESH 04/2008, #54: if no rotation factors use geographic system
135  if( geoTransform[2] == 0.0 && geoTransform[4] == 0.0 )
136  {
137  ossimString projTag( GDALGetMetadataItem( h, "IMG__PROJECTION_NAME", "" ) );
138  if ( projTag.contains("Geographic") )
139  {
140  bClose = false;
141 
143  "ossimEquDistCylProjection", true);
144  proj_type = kwl.find( ossimKeywordNames::TYPE_KW );
145 
146  // Assign units if set in Metadata
147  ossimString unitTag( GDALGetMetadataItem( h, "IMG__HORIZONTAL_UNITS", "" ) );
148  if ( unitTag.contains("dd") ) // decimal degrees
149  {
150  units = "degrees";
151  }
152  else if ( unitTag.contains("dm") ) // decimal minutes
153  {
154  units = "minutes";
155  }
156  else if ( unitTag.contains("ds") ) // decimal seconds
157  {
158  units = "seconds";
159  }
160  }
161  }
162 
163  if ( bClose == true )
164  {
165  GDALClose(h);
166  return 0;
167  }
168  }
169 
170  //---
171  // Pixel type, "area", tie upper left corner of pixel, or "point", tie
172  // center of pixel.
173  //
174  // Making an assumption that gdal normalize the geoTransform to area.
175  // Geotiffs tagged with raster type of "pixel_is_point" are
176  // shifted.
177  // (drb - 26 Oct. 2016)
178  //---
179  bool pixelTypeIsArea = true; // Assume area unless point is detected.
180 
181  /* removed replaced to fix world wrap issues with vrt bmng scene. 20160928 drb */
182 #if 0
183 
184  //---
185  // Look for AREA_OR_POINT:
186  // This controls PIXEL_TYPE_KW value downstream which will do the half
187  // pixel shift for us downstream.
188  //---
189  const char* areaOrPoint = GDALGetMetadataItem(h, "AREA_OR_POINT", "");
190  if (areaOrPoint)
191  {
192  if ( ossimString( areaOrPoint ).downcase().contains("point") )
193  {
194  pixelTypeIsArea = false;
195  }
196  }
197  else
198  {
199  // Look for
200  const char* rasterTypeStr = GDALGetMetadataItem(
201  h, "GEOTIFF_CHAR__GTRasterTypeGeoKey", "");
202  if ( rasterTypeStr )
203  {
204  if ( ossimString( rasterTypeStr ).downcase().contains("point") )
205  {
206  pixelTypeIsArea = false;
207  }
208  }
209  }
210 
211  // Pixel-is-point of pixel-is area affects the location of the tiepoint since OSSIM is
212  // always pixel-is-point so 1/2 pixel shift may be necessary:
213  if( (driverName == "MrSID") || (driverName == "JP2MrSID") ||
214  (driverName == "AIG") || (driverName == "VRT") )
215  {
216  const char* rasterTypeStr = GDALGetMetadataItem( h, "GEOTIFF_CHAR__GTRasterTypeGeoKey", "" );
217  ossimString rasterTypeTag( rasterTypeStr );
218 
219  // If the raster type is pixel_is_area, shift the tie point by
220  // half a pixel to locate it at the pixel center.
221  if ( (driverName == "AIG") ||
222  (driverName == "VRT") ||
223  (rasterTypeTag.contains("RasterPixelIsArea")) )
224  {
225  geoTransform[0] += fabs(geoTransform[1]) / 2.0;
226  geoTransform[3] -= fabs(geoTransform[5]) / 2.0;
227  }
228  }
229  else
230  {
231  // Conventionally, HFA stores the pixel alignment type for each band. Here assume all
232  // bands are the same. Consider only the first band:
233  GDALRasterBandH bBand = GDALGetRasterBand( h, 1 );
234  char** papszMetadata = GDALGetMetadata( bBand, NULL );
235  if (CSLCount(papszMetadata) > 0)
236  {
237  for(int i = 0; papszMetadata[i] != NULL; i++ )
238  {
239  ossimString metaStr = papszMetadata[i];
240  metaStr.upcase();
241  if (metaStr.contains("AREA_OR_POINT"))
242  {
243  ossimString pixel_is_point_or_area = metaStr.split("=")[1];
244  pixel_is_point_or_area.upcase();
245  if (pixel_is_point_or_area.contains("AREA"))
246  {
247  // Need to shift the tie point so that pixel is point:
248  geoTransform[0] += fabs(geoTransform[1]) / 2.0;
249  geoTransform[3] -= fabs(geoTransform[5]) / 2.0;
250  }
251  break;
252  }
253  }
254  }
255  }
256 #endif
257 
259  ossimDpt gsd(fabs(geoTransform[1]), fabs(geoTransform[5]));
260  ossimDpt tie(geoTransform[0], geoTransform[3]);
261 
262  ossimUnitType savedUnitType =
264  ossimUnitType unitType = savedUnitType;
265  if(unitType == OSSIM_UNIT_UNKNOWN)
266  unitType = OSSIM_METERS;
267 
268  if((proj_type == "ossimLlxyProjection") || (proj_type == "ossimEquDistCylProjection"))
269  {
270  ossimDpt halfGsd = gsd/2.0;
271 
272  //---
273  // Sanity check the tie and scale for world scenes that can cause
274  // a wrap issue.
275  //---
276  if ( (tie.x-halfGsd.x) < -180.0 )
277  {
278  tie.x = -180.0 + halfGsd.x;
279  geoTransform[0] = tie.x;
280  }
281  if ( (tie.y+halfGsd.y) > 90.0 )
282  {
283  tie.y = 90.0 - halfGsd.y;
284  geoTransform[3] = tie.y;
285  }
286 
287  int samples = GDALGetRasterXSize(h);
288  ossim_float64 degrees = samples * gsd.x;
289  if ( degrees > 360.0 )
290  {
291  //---
292  // If within one pixel of 360 degrees adjust it. Assume every
293  // thing else is what they want.
294  //---
295  if ( fabs(degrees - 360.0) <= gsd.x )
296  {
297  gsd.x = 360.0 / samples;
298  geoTransform[1] = gsd.x;
299 
300  // If we adjusted scale, fix the tie if it was on the edge.
301  if ( ossim::almostEqual( (tie.x-halfGsd.x), -180.0 ) == true )
302  {
303  tie.x = -180 + gsd.x / 2.0;
304  geoTransform[0] = tie.x;
305  }
306  }
307  }
308  int lines = GDALGetRasterYSize(h);
309  degrees = lines * gsd.y;
310  if ( degrees > 180.0 )
311  {
312  //---
313  // If within one pixel of 180 degrees adjust it. Assume every
314  // thing else is what they want.
315  //---
316  if ( fabs(degrees - 180.0) <= gsd.y )
317  {
318  gsd.y = 180.0 / lines;
319  geoTransform[5] = gsd.y;
320 
321  // If we adjusted scale, fix the tie if it was on the edge.
322  if ( ossim::almostEqual( (tie.y+halfGsd.y), 90.0 ) == true )
323  {
324  tie.y = 90.0 - gsd.y / 2.0;
325  geoTransform[3] = tie.y;
326  }
327  }
328  }
329 
330  // ESH 09/2008 -- Add the orig_lat and central_lon if the image
331  // is using geographic coordsys. This is used to convert the
332  // gsd to linear units.
333 
334  // gsd could of changed above:
335  halfGsd = gsd / 2.0;
336 
337  // Half the number of pixels in lon/lat directions
338  int nPixelsLon = GDALGetRasterXSize(h)/2.0;
339  int nPixelsLat = GDALGetRasterYSize(h)/2.0;
340 
341  // Shift from image corner to center in lon/lat
342  double shiftLon = nPixelsLon * fabs(gsd.x);
343  double shiftLat = -nPixelsLat * fabs(gsd.y);
344 
345  // lon/lat of center pixel of the image
346  double centerLon = (tie.x - halfGsd.x) + shiftLon;
347  double centerLat = (tie.y + halfGsd.y) + shiftLat;
348 
350  centerLat,
351  true);
353  centerLon,
354  true);
355 
356  if(savedUnitType == OSSIM_UNIT_UNKNOWN)
357  {
358  unitType = OSSIM_DEGREES;
359  }
360  }
361 
363  gsd.toString(),
364  true);
366  units,
367  true);
369  (pixelTypeIsArea?"area":"point"),
370  true);
372  tie.toString(),
373  true);
375  units,
376  true);
377 
378  //---
379  // Image Model Transform:
380  //
381  // Only output this if there is a rotation. There are two issues with
382  // this:
383  //
384  // 1) When the image model tranform is set calls to
385  // ossimMapProjection::setMetersPerPixel(gsd) have no effect.
386  //
387  // 2) Picking up the famous half pixel shift on output products.
388  //
389  // (drb 26 Oct. 2016)
390  //---
391  if ( (geoTransform[2] != 0.0) || (geoTransform[4] != 0.0) )
392  {
393 
394  if ( pixelTypeIsArea == true )
395  {
396  // Not handled in ossimMapProjection::loadState so shift it here.
397  geoTransform[0] += fabs(geoTransform[1]) / 2.0;
398  geoTransform[3] -= fabs(geoTransform[5]) / 2.0;
399  }
400 
401  std::stringstream matrixString;
402  // store as a 4x4 matrix
403  matrixString
404  << ossimString::toString(geoTransform[1], 20)
405  << " " << ossimString::toString(geoTransform[2], 20)
406  << " " << 0 << " "
407  << ossimString::toString(geoTransform[0], 20)
408  << " " << ossimString::toString(geoTransform[4], 20)
409  << " " << ossimString::toString(geoTransform[5], 20)
410  << " " << 0 << " "
411  << ossimString::toString(geoTransform[3], 20)
412  << " " << 0 << " " << 0 << " " << 1 << " " << 0
413  << " " << 0 << " " << 0 << " " << 0 << " " << 1;
414 
416  matrixString.str().c_str(), true);
418  units.string().c_str(), true);
419  }
420 
421  //---
422  // SPECIAL CASE: ArcGrid in British National Grid
423  //---
424  if(driverName == "AIG" && datum_type == "OSGB_1936")
425  {
426  ossimFilename prj_file = filename.path() + "/prj.adf";
427 
428  if(prj_file.exists())
429  {
430  ossimKeywordlist prj_kwl(' ');
431  prj_kwl.addFile(prj_file);
432 
433  ossimString proj = prj_kwl.find("Projection");
434 
435  // Reset projection and Datum correctly for BNG.
436  if(proj.upcase().contains("GREATBRITAIN"))
437  {
438 
440  "ossimBngProjection", true);
441 
442  ossimString datum = prj_kwl.find("Datum");
443 
444  if(datum != "")
445  {
446  if(datum == "OGB_A")
447  datum = "OGB-A";
448  else if(datum == "OGB_B")
449  datum = "OGB-B";
450  else if(datum == "OGB_C")
451  datum = "OGB-C";
452  else if(datum == "OGB_D")
453  datum = "OGB-D";
454  else if(datum == "OGB_M")
455  datum = "OGB-M";
456  else if(datum == "OGB_7")
457  datum = "OGB-7";
458 
460  datum, true);
461  }
462  }
463  }
464  }
465  }
466 
467  if(traceDebug())
468  {
469  ossimNotify(ossimNotifyLevel_DEBUG) << "ossimGdalProjectionFactory: createProjection KWL = \n " << kwl << std::endl;
470  }
471 
472  GDALClose(h);
474  }
475 
476  return proj;
477 }
478 
480  const char *prefix) const
481 {
482  const char *lookup = keywordList.find(prefix, ossimKeywordNames::TYPE_KW);
483  if(lookup&&(!ossimString(lookup).empty()))
484  {
486  if(proj)
487  {
488  // make sure we restore any passed in tie points and meters per pixel information
489  ossimKeywordlist tempKwl;
490  ossimKeywordlist tempKwl2;
491  proj->saveState(tempKwl);
492  tempKwl2.add(keywordList, prefix, true);
493  tempKwl.add(prefix, tempKwl2, true);
494  tempKwl.add(prefix, ossimKeywordNames::TYPE_KW, proj->getClassName(), true);
495  proj->loadState(tempKwl);
496  if(traceDebug())
497  {
498  tempKwl.clear();
499  proj->saveState(tempKwl);
500  if(traceDebug())
501  {
502  ossimNotify(ossimNotifyLevel_DEBUG)<< "ossimGdalProjectionFactory::createProjection Debug: resulting projection \n " << tempKwl << std::endl;
503  }
504  }
505 
506  return proj;
507  }
508  }
509  return 0;
510 }
511 
513 {
514  ossimString tempName = name;
515  if(traceDebug())
516  {
517  ossimNotify(ossimNotifyLevel_WARN) << "ossimGdalProjectionFactory::createProjection: "<< name << "\n";
518  }
519  tempName = tempName.trim();
520 
521  if ( tempName.size() >= 6 )
522  {
523  ossimString testName(tempName.begin(),
524  tempName.begin()+6);
525  testName = testName.upcase();
526  if((testName == "PROJCS")||
527  (testName == "GEOGCS"))
528  {
529  ossimKeywordlist kwl;
530  if ( theWktTranslator.toOssimKwl(name.c_str(), kwl, "") )
531  {
532  if(traceDebug())
533  {
534  ossimNotify(ossimNotifyLevel_DEBUG)<< "ossimGdalProjectionFactory::createProjection Debug: trying to create projection \n " << kwl << std::endl;
535  }
537  }
538  }
539  }
540 
541  return 0;
542 }
543 
545 {
546  return createProjection(typeName);
547 }
548 
550  const char* prefix)const
551 {
552  return createProjection(kwl, prefix);
553 }
554 
555 void ossimGdalProjectionFactory::getTypeNameList(std::vector<ossimString>& /* typeList */)const
556 {
557 }
558 
559 std::list<ossimString> ossimGdalProjectionFactory::getList()const
560 {
561  std::list<ossimString> result;
562 
563  return result;
564 }
static ossimString upcase(const ossimString &aString)
Definition: ossimString.cpp:34
std::basic_stringstream< char > stringstream
Class for char mixed input and output memory streams.
Definition: ossimIosFwd.h:38
static const char * DATUM_KW
static const char * CENTRAL_MERIDIAN_KW
ossimUnitType
Represents serializable keyword/value map.
bool addFile(const char *file)
const char * find(const char *key) const
virtual void getTypeNameList(std::vector< ossimString > &typeList) const
bool almostEqual(T x, T y, T tolerance=FLT_EPSILON)
Definition: ossimCommon.h:53
static const char * IMAGE_MODEL_TRANSFORM_MATRIX_KW
double y
Definition: ossimDpt.h:165
bool contains(char aChar) const
Definition: ossimString.h:58
virtual std::list< ossimString > getList() const
static ossimString toString(bool aValue)
Numeric to string methods.
ossimOgcWktTranslator theWktTranslator
void split(std::vector< ossimString > &result, const ossimString &separatorList, bool skipBlankFields=false) const
Splits this string into a vector of strings (fields) using the delimiter list specified.
virtual ossim_int32 getEntryNumber(const char *entry_string, bool case_insensitive=true) const
virtual ossimString getClassName() const
Definition: ossimObject.cpp:64
static const char * TYPE_KW
virtual ossimObject * createObject(const ossimString &typeName) const
virtual double optimizeFit(const ossimTieGptSet &tieSet, double *targetVariance=0)
static const char * PIXEL_SCALE_XY_KW
double ossim_float64
void add(const char *prefix, const ossimKeywordlist &kwl, bool overwrite=true)
virtual bool loadState(const ossimKeywordlist &kwl, const char *prefix=0)
ossimProjection * createProjection(const ossimFilename &filename, ossim_uint32 entryIdx) const
static const char * TIE_POINT_XY_KW
bool exists() const
std::string::size_type size() const
Definition: ossimString.h:405
std::string::iterator begin()
Definition: ossimString.h:420
unsigned int ossim_uint32
ossimString trim(const ossimString &valueToTrim=ossimString(" \\)) const
this will strip lead and trailing character passed in.
static const char * IMAGE_MODEL_TRANSFORM_UNIT_KW
static ossimGdalProjectionFactory * theInstance
storage class for tie point between ground and image based on ossimGpt
Definition: ossimTieGpt.h:24
static ossimProjectionFactoryRegistry * instance()
static const char * ORIGIN_LATITUDE_KW
static const char * UNITS_KW
void addTiePoint(ossimRefPtr< ossimTieGpt > aTiePt)
operations
ossimString toString(ossim_uint32 precision=15) const
Definition: ossimDpt.cpp:160
static ossimGdalProjectionFactory * instance()
storage class for a set of geographic tie points, between master and slave images ...
static const char * PIXEL_TYPE_KW
double x
Definition: ossimDpt.h:164
const char * c_str() const
Returns a pointer to a null-terminated array of characters representing the string&#39;s contents...
Definition: ossimString.h:396
bool empty() const
Definition: ossimString.h:411
virtual bool saveState(ossimKeywordlist &kwl, const char *prefix=0) const
bool toOssimKwl(const ossimString &wktString, ossimKeywordlist &kwl, const char *prefix=NULL) const
static ossimUnitTypeLut * instance()
Returns the static instance of an ossimUnitTypeLut object.
ossimFilename path() const
void remove(const char *key)
OSSIMDLLEXPORT std::ostream & ossimNotify(ossimNotifyLevel level=ossimNotifyLevel_WARN)
static const char * PIXEL_SCALE_UNITS_KW
virtual ossimProjection * createProjection(const ossimFilename &filename, ossim_uint32 entryIdx) const
takes a filename.
static const char * TIE_POINT_UNITS_KW