1 : /******************************************************************************
2 : * $Id $
3 : *
4 : * Project: SPOT Dimap Driver
5 : * Purpose: Implementation of SPOT Dimap driver.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : * Docs: http://www.spotimage.fr/dimap/spec/documentation/refdoc.htm
9 : *
10 : ******************************************************************************
11 : * Copyright (c) 2007, Frank Warmerdam <warmerdam@pobox.com>
12 : *
13 : * Permission is hereby granted, free of charge, to any person obtaining a
14 : * copy of this software and associated documentation files (the "Software"),
15 : * to deal in the Software without restriction, including without limitation
16 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
17 : * and/or sell copies of the Software, and to permit persons to whom the
18 : * Software is furnished to do so, subject to the following conditions:
19 : *
20 : * The above copyright notice and this permission notice shall be included
21 : * in all copies or substantial portions of the Software.
22 : *
23 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29 : * DEALINGS IN THE SOFTWARE.
30 : ****************************************************************************/
31 :
32 : #include "gdal_pam.h"
33 : #include "cpl_minixml.h"
34 : #include "ogr_spatialref.h"
35 : #include "gdal_proxy.h"
36 :
37 : CPL_CVSID("$Id: dimapdataset.cpp 25110 2012-10-13 13:53:53Z rouault $");
38 :
39 : CPL_C_START
40 : void GDALRegister_DIMAP(void);
41 : CPL_C_END
42 :
43 : /************************************************************************/
44 : /* ==================================================================== */
45 : /* DIMAPDataset */
46 : /* ==================================================================== */
47 : /************************************************************************/
48 :
49 : class DIMAPDataset : public GDALPamDataset
50 : {
51 : CPLXMLNode *psProduct;
52 :
53 : CPLXMLNode *psProductDim; /* DIMAP2, DIM_<product_id>.XML */
54 : CPLXMLNode *psProductStrip; /* DIMAP2, STRIP_<product_id>.XML */
55 :
56 : GDALDataset *poImageDS;
57 :
58 : int nGCPCount;
59 : GDAL_GCP *pasGCPList;
60 : char *pszGCPProjection;
61 :
62 : CPLString osProjection;
63 :
64 : int bHaveGeoTransform;
65 : double adfGeoTransform[6];
66 :
67 : CPLString osMDFilename;
68 : CPLString osImageDSFilename;
69 : CPLString osDIMAPFilename;
70 : int nProductVersion;
71 :
72 : char **papszXMLDimapMetadata;
73 :
74 : protected:
75 : virtual int CloseDependentDatasets();
76 :
77 : int ReadImageInformation();
78 : int ReadImageInformation2(); /* DIMAP 2 */
79 :
80 : void SetMetadataFromXML(CPLXMLNode *psProduct, const char *apszMetadataTranslation[]);
81 : public:
82 : DIMAPDataset();
83 : ~DIMAPDataset();
84 :
85 : virtual const char *GetProjectionRef(void);
86 : virtual CPLErr GetGeoTransform( double * );
87 : virtual int GetGCPCount();
88 : virtual const char *GetGCPProjection();
89 : virtual const GDAL_GCP *GetGCPs();
90 : virtual char **GetMetadata( const char *pszDomain );
91 : virtual char **GetFileList(void);
92 :
93 : static int Identify( GDALOpenInfo * );
94 : static GDALDataset *Open( GDALOpenInfo * );
95 :
96 : CPLXMLNode *GetProduct() { return psProduct; }
97 : };
98 :
99 : /************************************************************************/
100 : /* ==================================================================== */
101 : /* DIMAPWrapperRasterBand */
102 : /* ==================================================================== */
103 : /************************************************************************/
104 : class DIMAPWrapperRasterBand : public GDALProxyRasterBand
105 : {
106 : GDALRasterBand* poBaseBand;
107 :
108 : protected:
109 112 : virtual GDALRasterBand* RefUnderlyingRasterBand() { return poBaseBand; }
110 :
111 : public:
112 2 : DIMAPWrapperRasterBand( GDALRasterBand* poBaseBand )
113 2 : {
114 2 : this->poBaseBand = poBaseBand;
115 2 : eDataType = poBaseBand->GetRasterDataType();
116 2 : poBaseBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
117 2 : }
118 2 : ~DIMAPWrapperRasterBand() {}
119 : };
120 : /************************************************************************/
121 : /* ==================================================================== */
122 : /* DIMAPDataset */
123 : /* ==================================================================== */
124 : /************************************************************************/
125 :
126 : /************************************************************************/
127 : /* DIMAPDataset() */
128 : /************************************************************************/
129 :
130 2 : DIMAPDataset::DIMAPDataset()
131 : {
132 2 : psProduct = NULL;
133 :
134 2 : psProductDim = NULL;
135 2 : psProductStrip = NULL;
136 :
137 2 : nGCPCount = 0;
138 2 : pasGCPList = NULL;
139 2 : pszGCPProjection = CPLStrdup("");
140 :
141 2 : poImageDS = NULL;
142 2 : bHaveGeoTransform = FALSE;
143 :
144 2 : nProductVersion = 1;
145 :
146 2 : papszXMLDimapMetadata = NULL;
147 2 : }
148 :
149 : /************************************************************************/
150 : /* ~DIMAPDataset() */
151 : /************************************************************************/
152 :
153 2 : DIMAPDataset::~DIMAPDataset()
154 :
155 : {
156 2 : FlushCache();
157 :
158 2 : CPLDestroyXMLNode( psProduct );
159 :
160 2 : if( nProductVersion == 2 )
161 : {
162 0 : CPLDestroyXMLNode( psProductDim );
163 0 : CPLDestroyXMLNode( psProductStrip );
164 : }
165 :
166 2 : CPLFree( pszGCPProjection );
167 2 : if( nGCPCount > 0 )
168 : {
169 2 : GDALDeinitGCPs( nGCPCount, pasGCPList );
170 2 : CPLFree( pasGCPList );
171 : }
172 :
173 2 : CSLDestroy(papszXMLDimapMetadata);
174 :
175 2 : CloseDependentDatasets();
176 2 : }
177 :
178 :
179 : /************************************************************************/
180 : /* CloseDependentDatasets() */
181 : /************************************************************************/
182 :
183 2 : int DIMAPDataset::CloseDependentDatasets()
184 : {
185 2 : int bHasDroppedRef = GDALPamDataset::CloseDependentDatasets();
186 :
187 2 : if( poImageDS != NULL )
188 : {
189 2 : delete poImageDS;
190 2 : poImageDS = NULL;
191 2 : bHasDroppedRef = TRUE;
192 : }
193 :
194 : /* -------------------------------------------------------------------- */
195 : /* Disconnect the bands so our destructor doesn't try and */
196 : /* delete them since they really belonged to poImageDS. */
197 : /* -------------------------------------------------------------------- */
198 : int iBand;
199 4 : for( iBand = 0; iBand < nBands; iBand++ )
200 2 : delete papoBands[iBand];
201 2 : nBands = 0;
202 :
203 2 : return bHasDroppedRef;
204 : }
205 :
206 : /************************************************************************/
207 : /* GetMetadata() */
208 : /* */
209 : /* We implement special support for fetching the full product */
210 : /* metadata as xml. */
211 : /************************************************************************/
212 :
213 2 : char **DIMAPDataset::GetMetadata( const char *pszDomain )
214 :
215 : {
216 2 : if( pszDomain && EQUAL(pszDomain,"xml:dimap") )
217 : {
218 0 : if (papszXMLDimapMetadata == NULL)
219 : {
220 0 : papszXMLDimapMetadata = (char **) CPLCalloc(sizeof(char*),2);
221 0 : papszXMLDimapMetadata[0] = CPLSerializeXMLTree( psProduct );
222 : }
223 0 : return papszXMLDimapMetadata;
224 : }
225 : else
226 2 : return GDALPamDataset::GetMetadata( pszDomain );
227 : }
228 :
229 : /************************************************************************/
230 : /* GetProjectionRef() */
231 : /************************************************************************/
232 :
233 0 : const char *DIMAPDataset::GetProjectionRef()
234 :
235 : {
236 0 : if( strlen(osProjection) > 0 )
237 0 : return osProjection;
238 : else
239 0 : return GDALPamDataset::GetProjectionRef();
240 : }
241 :
242 : /************************************************************************/
243 : /* GetGeoTransform() */
244 : /************************************************************************/
245 :
246 0 : CPLErr DIMAPDataset::GetGeoTransform( double *padfGeoTransform )
247 :
248 : {
249 0 : if( bHaveGeoTransform )
250 : {
251 0 : memcpy( padfGeoTransform, adfGeoTransform, sizeof(double)*6 );
252 0 : return CE_None;
253 : }
254 : else
255 0 : return GDALPamDataset::GetGeoTransform( padfGeoTransform );
256 : }
257 :
258 : /************************************************************************/
259 : /* GetFileList() */
260 : /************************************************************************/
261 :
262 0 : char **DIMAPDataset::GetFileList()
263 :
264 : {
265 0 : char **papszFileList = GDALPamDataset::GetFileList();
266 0 : char **papszImageFiles = poImageDS->GetFileList();
267 :
268 0 : papszFileList = CSLInsertStrings( papszFileList, -1, papszImageFiles );
269 :
270 0 : CSLDestroy( papszImageFiles );
271 :
272 0 : return papszFileList;
273 : }
274 :
275 : /************************************************************************/
276 : /* Identify() */
277 : /************************************************************************/
278 :
279 13614 : int DIMAPDataset::Identify( GDALOpenInfo * poOpenInfo )
280 :
281 : {
282 13614 : if( poOpenInfo->nHeaderBytes >= 100 )
283 : {
284 1668 : if( ( strstr((const char *) poOpenInfo->pabyHeader,
285 : "<Dimap_Document" ) == NULL ) &&
286 : ( strstr((const char *) poOpenInfo->pabyHeader,
287 : "<PHR_DIMAP_Document" ) == NULL ) )
288 1666 : return FALSE;
289 : else
290 2 : return TRUE;
291 : }
292 11946 : else if( poOpenInfo->bIsDirectory )
293 : {
294 : VSIStatBufL sStat;
295 :
296 : /* DIMAP file */
297 : CPLString osMDFilename =
298 42 : CPLFormCIFilename( poOpenInfo->pszFilename, "METADATA.DIM", NULL );
299 :
300 42 : if( VSIStatL( osMDFilename, &sStat ) == 0 )
301 : {
302 : /* Make sure this is really a Dimap format */
303 0 : GDALOpenInfo oOpenInfo( osMDFilename, GA_ReadOnly, NULL );
304 0 : if( oOpenInfo.nHeaderBytes >= 100 )
305 : {
306 0 : if( strstr((const char *) oOpenInfo.pabyHeader,
307 : "<Dimap_Document" ) == NULL )
308 0 : return FALSE;
309 : else
310 0 : return TRUE;
311 0 : }
312 : }
313 : else
314 : {
315 : /* DIMAP 2 file */
316 : osMDFilename =
317 42 : CPLFormCIFilename( poOpenInfo->pszFilename, "VOL_PHR.XML", NULL );
318 :
319 42 : if( VSIStatL( osMDFilename, &sStat ) == 0 )
320 0 : return TRUE;
321 : else
322 42 : return FALSE;
323 0 : }
324 : }
325 :
326 11904 : return FALSE;
327 : }
328 :
329 : /************************************************************************/
330 : /* Open() */
331 : /************************************************************************/
332 :
333 3520 : GDALDataset *DIMAPDataset::Open( GDALOpenInfo * poOpenInfo )
334 :
335 : {
336 3520 : int nProductVersion = 1;
337 :
338 3520 : if( !Identify( poOpenInfo ) )
339 3518 : return NULL;
340 :
341 : /* -------------------------------------------------------------------- */
342 : /* Confirm the requested access is supported. */
343 : /* -------------------------------------------------------------------- */
344 2 : if( poOpenInfo->eAccess == GA_Update )
345 : {
346 : CPLError( CE_Failure, CPLE_NotSupported,
347 : "The DIMAP driver does not support update access to existing"
348 0 : " datasets.\n" );
349 0 : return NULL;
350 : }
351 : /* -------------------------------------------------------------------- */
352 : /* Get the metadata filename. */
353 : /* -------------------------------------------------------------------- */
354 2 : CPLString osMDFilename, osImageDSFilename, osDIMAPFilename;
355 :
356 2 : if( poOpenInfo->bIsDirectory )
357 : {
358 : VSIStatBufL sStat;
359 :
360 : osMDFilename =
361 0 : CPLFormCIFilename( poOpenInfo->pszFilename, "METADATA.DIM", NULL );
362 :
363 : /* DIMAP2 */
364 0 : if( VSIStatL( osMDFilename, &sStat ) != 0 )
365 : osMDFilename =
366 0 : CPLFormCIFilename( poOpenInfo->pszFilename, "VOL_PHR.XML", NULL );
367 : }
368 : else
369 2 : osMDFilename = poOpenInfo->pszFilename;
370 :
371 : /* -------------------------------------------------------------------- */
372 : /* Ingest the xml file. */
373 : /* -------------------------------------------------------------------- */
374 : CPLXMLNode *psProduct, *psImageAttributes;
375 2 : CPLXMLNode *psProductDim = NULL, *psProductStrip = NULL;
376 :
377 : float nMetadataFormatVersion;
378 :
379 2 : psProduct = CPLParseXMLFile( osMDFilename );
380 2 : if( psProduct == NULL )
381 0 : return NULL;
382 :
383 2 : CPLXMLNode *psDoc = CPLGetXMLNode( psProduct, "=Dimap_Document" );
384 2 : if( !psDoc )
385 0 : psDoc = CPLGetXMLNode( psProduct, "=PHR_DIMAP_Document" );
386 :
387 : /* We check the for the tag Metadata_Identification.METADATA_FORMAT.
388 : * The metadata will be set to 2.0 for DIMAP2 */
389 : nMetadataFormatVersion = atof( CPLGetXMLValue(CPLGetXMLNode(psDoc, "Metadata_Identification.METADATA_FORMAT"),
390 2 : "version", "1") );
391 2 : if( nMetadataFormatVersion >= 2.0 )
392 : {
393 0 : nProductVersion = 2;
394 : }
395 :
396 : /* Check needed information for the DIMAP format */
397 2 : if (nProductVersion == 1)
398 : {
399 2 : psImageAttributes = CPLGetXMLNode( psDoc, "Raster_Dimensions" );
400 2 : if( psImageAttributes == NULL )
401 : {
402 : CPLError( CE_Failure, CPLE_OpenFailed,
403 0 : "Failed to find <Raster_Dimensions> in document." );
404 0 : return NULL;
405 : }
406 : }
407 : else /* DIMAP2 */
408 : {
409 : /* Verify the presence of the DIMAP product file */
410 0 : CPLXMLNode *psDatasetComponents = CPLGetXMLNode(psDoc, "Dataset_Content.Dataset_Components");
411 :
412 0 : if( psDatasetComponents == NULL )
413 : {
414 : CPLError( CE_Failure, CPLE_OpenFailed,
415 0 : "Failed to find <Dataset_Components> in document." );
416 0 : return NULL;
417 : }
418 :
419 0 : CPLXMLNode *psDatasetComponent = psDatasetComponents->psChild;
420 :
421 0 : for( ; psDatasetComponent != NULL; psDatasetComponent = psDatasetComponent->psNext )
422 : {
423 0 : const char* pszComponentType = CPLGetXMLValue(psDatasetComponent, "COMPONENT_TYPE","");
424 0 : if( strcmp(pszComponentType, "DIMAP") == 0 )
425 : {
426 : const char *pszHref = CPLGetXMLValue(
427 0 : psDatasetComponent, "COMPONENT_PATH.href", "" );
428 :
429 0 : if( strlen(pszHref) > 0 ) /* DIMAP product found*/
430 : {
431 0 : if( poOpenInfo->bIsDirectory )
432 : {
433 : osDIMAPFilename =
434 0 : CPLFormCIFilename( poOpenInfo->pszFilename, pszHref, NULL );
435 : }
436 : else
437 : {
438 0 : CPLString osPath = CPLGetPath(osMDFilename);
439 : osDIMAPFilename =
440 0 : CPLFormFilename( osPath, pszHref, NULL );
441 : }
442 :
443 : /* Data file might be specified there */
444 : const char *pszDataFileHref = CPLGetXMLValue(
445 0 : psDatasetComponent, "Data_Files.Data_File.DATA_FILE_PATH.href", "" );
446 :
447 0 : if( strlen(pszDataFileHref) > 0 )
448 : {
449 0 : CPLString osPath = CPLGetPath(osMDFilename);
450 : osImageDSFilename =
451 0 : CPLFormFilename( osPath, pszDataFileHref, NULL );
452 : }
453 :
454 0 : break;
455 : }
456 : }
457 : }
458 :
459 0 : psProductDim = CPLParseXMLFile( osDIMAPFilename );
460 0 : if( psProductDim == NULL )
461 0 : return NULL;
462 :
463 : /* We need the STRIP_<product_id>.XML file for a few metadata */
464 0 : CPLXMLNode *psDocDim = CPLGetXMLNode( psProductDim, "=Dimap_Document" );
465 0 : if( !psDocDim )
466 0 : psDocDim = CPLGetXMLNode( psProductDim, "=PHR_DIMAP_Document" );
467 :
468 0 : CPLXMLNode *psDatasetSources = CPLGetXMLNode(psDocDim, "Dataset_Sources");
469 0 : if( psDatasetSources != NULL )
470 : {
471 0 : CPLString osSTRIPFilename;
472 0 : CPLXMLNode *psDatasetSource = psDatasetSources->psChild;
473 :
474 0 : for( ; psDatasetSource != NULL; psDatasetSource = psDatasetSource->psNext )
475 : {
476 0 : const char* pszSourceType = CPLGetXMLValue(psDatasetSource, "SOURCE_TYPE","");
477 0 : if( strcmp(pszSourceType, "Strip_Source") == 0 )
478 : {
479 : const char *pszHref = CPLGetXMLValue(
480 0 : psDatasetSource, "Component.COMPONENT_PATH.href", "" );
481 :
482 0 : if( strlen(pszHref) > 0 ) /* STRIP product found*/
483 : {
484 0 : CPLString osPath = CPLGetPath(osDIMAPFilename);
485 : osSTRIPFilename =
486 0 : CPLFormFilename( osPath, pszHref, NULL );
487 :
488 0 : break;
489 : }
490 : }
491 : }
492 :
493 0 : psProductStrip = CPLParseXMLFile( osSTRIPFilename );
494 0 : if( psProductStrip == NULL )
495 0 : return NULL;
496 : }
497 : }
498 :
499 : /* -------------------------------------------------------------------- */
500 : /* Create the dataset. */
501 : /* -------------------------------------------------------------------- */
502 2 : DIMAPDataset *poDS = new DIMAPDataset();
503 :
504 2 : poDS->psProduct = psProduct;
505 2 : poDS->psProductDim = psProductDim;
506 2 : poDS->psProductStrip = psProductStrip;
507 2 : poDS->nProductVersion = nProductVersion;
508 2 : poDS->osMDFilename = osMDFilename;
509 2 : poDS->osImageDSFilename = osImageDSFilename;
510 2 : poDS->osDIMAPFilename = osDIMAPFilename;
511 :
512 2 : int res = TRUE;
513 2 : if( nProductVersion == 2 )
514 0 : res = poDS->ReadImageInformation2();
515 : else
516 2 : res = poDS->ReadImageInformation();
517 :
518 2 : if( res == FALSE )
519 : {
520 0 : delete poDS;
521 0 : return NULL;
522 : }
523 :
524 2 : return( poDS );
525 : }
526 :
527 :
528 : /************************************************************************/
529 : /* ReadImageInformation() DIMAP Version 1 */
530 : /************************************************************************/
531 :
532 2 : int DIMAPDataset::ReadImageInformation()
533 : {
534 2 : CPLXMLNode *psDoc = CPLGetXMLNode( psProduct, "=Dimap_Document" );
535 2 : if( !psDoc )
536 0 : psDoc = CPLGetXMLNode( psProduct, "=PHR_DIMAP_Document" );
537 :
538 2 : CPLXMLNode *psImageAttributes = CPLGetXMLNode( psDoc, "Raster_Dimensions" );
539 :
540 : /* -------------------------------------------------------------------- */
541 : /* Get overall image information. */
542 : /* -------------------------------------------------------------------- */
543 : #ifdef DEBUG
544 : int nBands =
545 2 : atoi(CPLGetXMLValue( psImageAttributes, "NBANDS", "-1" ));
546 : #endif
547 :
548 : nRasterXSize =
549 2 : atoi(CPLGetXMLValue( psImageAttributes, "NCOLS", "-1" ));
550 : nRasterYSize =
551 2 : atoi(CPLGetXMLValue( psImageAttributes, "NROWS", "-1" ));
552 :
553 : /* -------------------------------------------------------------------- */
554 : /* Get the name of the underlying file. */
555 : /* -------------------------------------------------------------------- */
556 :
557 : const char *pszHref = CPLGetXMLValue(
558 2 : psDoc, "Data_Access.Data_File.DATA_FILE_PATH.href", "" );
559 2 : CPLString osPath = CPLGetPath(osMDFilename);
560 : CPLString osImageFilename =
561 2 : CPLFormFilename( osPath, pszHref, NULL );
562 :
563 : /* -------------------------------------------------------------------- */
564 : /* Try and open the file. */
565 : /* -------------------------------------------------------------------- */
566 :
567 2 : poImageDS = (GDALDataset *) GDALOpen( osImageFilename, GA_ReadOnly );
568 2 : if( poImageDS == NULL )
569 : {
570 0 : return FALSE;
571 : }
572 :
573 : /* -------------------------------------------------------------------- */
574 : /* Attach the bands. */
575 : /* -------------------------------------------------------------------- */
576 : int iBand;
577 2 : CPLAssert( nBands == poImageDS->GetRasterCount() );
578 :
579 4 : for( iBand = 1; iBand <= poImageDS->GetRasterCount(); iBand++ )
580 2 : SetBand( iBand, new DIMAPWrapperRasterBand(poImageDS->GetRasterBand( iBand )) );
581 :
582 : /* -------------------------------------------------------------------- */
583 : /* Try to collect simple insertion point. */
584 : /* -------------------------------------------------------------------- */
585 : CPLXMLNode *psGeoLoc =
586 2 : CPLGetXMLNode( psDoc, "Geoposition.Geoposition_Insert" );
587 :
588 2 : if( psGeoLoc != NULL )
589 : {
590 0 : bHaveGeoTransform = TRUE;
591 0 : adfGeoTransform[0] = atof(CPLGetXMLValue(psGeoLoc,"ULXMAP","0"));
592 0 : adfGeoTransform[1] = atof(CPLGetXMLValue(psGeoLoc,"XDIM","0"));
593 0 : adfGeoTransform[2] = 0.0;
594 0 : adfGeoTransform[3] = atof(CPLGetXMLValue(psGeoLoc,"ULYMAP","0"));
595 0 : adfGeoTransform[4] = 0.0;
596 0 : adfGeoTransform[5] = -atof(CPLGetXMLValue(psGeoLoc,"YDIM","0"));
597 : }
598 :
599 : /* -------------------------------------------------------------------- */
600 : /* Collect GCPs. */
601 : /* -------------------------------------------------------------------- */
602 2 : psGeoLoc = CPLGetXMLNode( psDoc, "Geoposition.Geoposition_Points" );
603 :
604 2 : if( psGeoLoc != NULL )
605 : {
606 : CPLXMLNode *psNode;
607 :
608 : // count gcps.
609 2 : nGCPCount = 0;
610 10 : for( psNode = psGeoLoc->psChild; psNode != NULL;
611 : psNode = psNode->psNext )
612 : {
613 8 : if( EQUAL(psNode->pszValue,"Tie_Point") )
614 8 : nGCPCount++ ;
615 : }
616 :
617 : pasGCPList = (GDAL_GCP *)
618 2 : CPLCalloc(sizeof(GDAL_GCP),nGCPCount);
619 :
620 2 : nGCPCount = 0;
621 :
622 10 : for( psNode = psGeoLoc->psChild; psNode != NULL;
623 : psNode = psNode->psNext )
624 : {
625 : char szID[32];
626 8 : GDAL_GCP *psGCP = pasGCPList + nGCPCount;
627 :
628 8 : if( !EQUAL(psNode->pszValue,"Tie_Point") )
629 0 : continue;
630 :
631 8 : nGCPCount++ ;
632 :
633 8 : sprintf( szID, "%d", nGCPCount );
634 8 : psGCP->pszId = CPLStrdup( szID );
635 8 : psGCP->pszInfo = CPLStrdup("");
636 : psGCP->dfGCPPixel =
637 8 : atof(CPLGetXMLValue(psNode,"TIE_POINT_DATA_X","0"))-0.5;
638 : psGCP->dfGCPLine =
639 8 : atof(CPLGetXMLValue(psNode,"TIE_POINT_DATA_Y","0"))-0.5;
640 : psGCP->dfGCPX =
641 8 : atof(CPLGetXMLValue(psNode,"TIE_POINT_CRS_X",""));
642 : psGCP->dfGCPY =
643 8 : atof(CPLGetXMLValue(psNode,"TIE_POINT_CRS_Y",""));
644 : psGCP->dfGCPZ =
645 8 : atof(CPLGetXMLValue(psNode,"TIE_POINT_CRS_Z",""));
646 : }
647 : }
648 :
649 : /* -------------------------------------------------------------------- */
650 : /* Collect the CRS. For now we look only for EPSG codes. */
651 : /* -------------------------------------------------------------------- */
652 : const char *pszSRS = CPLGetXMLValue(
653 : psDoc,
654 : "Coordinate_Reference_System.Horizontal_CS.HORIZONTAL_CS_CODE",
655 2 : NULL );
656 :
657 2 : if( pszSRS != NULL )
658 : {
659 2 : OGRSpatialReference oSRS;
660 2 : if( oSRS.SetFromUserInput( pszSRS ) == OGRERR_NONE )
661 : {
662 2 : if( nGCPCount > 0 )
663 : {
664 2 : CPLFree(pszGCPProjection);
665 2 : oSRS.exportToWkt( &(pszGCPProjection) );
666 : }
667 : else
668 : {
669 0 : char *pszProjection = NULL;
670 0 : oSRS.exportToWkt( &pszProjection );
671 0 : osProjection = pszProjection;
672 0 : CPLFree( pszProjection );
673 : }
674 2 : }
675 : }
676 : else
677 : {
678 : // Check underlying raster for SRS. We have cases where
679 : // HORIZONTAL_CS_CODE is empty and the underlying raster
680 : // is georeferenced (rprinceley).
681 0 : if ( poImageDS->GetProjectionRef() )
682 : {
683 0 : osProjection = poImageDS->GetProjectionRef();
684 : }
685 : }
686 :
687 : /* -------------------------------------------------------------------- */
688 : /* Translate other metadata of interest. */
689 : /* -------------------------------------------------------------------- */
690 : static const char *apszMetadataTranslation[] =
691 : {
692 : "Production", "",
693 : "Production.Facility", "FACILITY_",
694 : "Dataset_Sources.Source_Information.Scene_Source", "",
695 : "Data_Processing", "",
696 : "Image_Interpretation.Spectral_Band_Info", "SPECTRAL_",
697 : NULL, NULL
698 : };
699 :
700 2 : SetMetadataFromXML(psProduct, apszMetadataTranslation);
701 :
702 : /* -------------------------------------------------------------------- */
703 : /* Set Band metadata from the <Spectral_Band_Info> content */
704 : /* -------------------------------------------------------------------- */
705 :
706 : CPLXMLNode *psImageInterpretationNode =
707 2 : CPLGetXMLNode( psDoc, "Image_Interpretation" );
708 2 : if (psImageInterpretationNode != NULL)
709 : {
710 2 : CPLXMLNode *psSpectralBandInfoNode = psImageInterpretationNode->psChild;
711 6 : while (psSpectralBandInfoNode != NULL)
712 : {
713 2 : if (psSpectralBandInfoNode->eType == CXT_Element &&
714 : EQUAL(psSpectralBandInfoNode->pszValue, "Spectral_Band_Info"))
715 : {
716 2 : CPLXMLNode *psTag = psSpectralBandInfoNode->psChild;
717 2 : int nBandIndex = 0;
718 16 : while(psTag != NULL)
719 : {
720 12 : if (psTag->eType == CXT_Element && psTag->psChild != NULL &&
721 : psTag->psChild->eType == CXT_Text && psTag->pszValue != NULL)
722 : {
723 12 : if (EQUAL(psTag->pszValue, "BAND_INDEX"))
724 : {
725 2 : nBandIndex = atoi(psTag->psChild->pszValue);
726 2 : if (nBandIndex <= 0 ||
727 : nBandIndex > poImageDS->GetRasterCount())
728 : {
729 : CPLError(CE_Warning, CPLE_AppDefined,
730 0 : "Bad BAND_INDEX value : %s", psTag->psChild->pszValue);
731 0 : nBandIndex = 0;
732 : }
733 : }
734 10 : else if (nBandIndex >= 1)
735 : {
736 : GetRasterBand(nBandIndex)->SetMetadataItem(
737 10 : psTag->pszValue, psTag->psChild->pszValue);
738 : }
739 : }
740 12 : psTag = psTag->psNext;
741 : }
742 : }
743 2 : psSpectralBandInfoNode = psSpectralBandInfoNode->psNext;
744 : }
745 : }
746 :
747 : /* -------------------------------------------------------------------- */
748 : /* Initialize any PAM information. */
749 : /* -------------------------------------------------------------------- */
750 2 : SetDescription( osMDFilename );
751 2 : TryLoadXML();
752 :
753 : /* -------------------------------------------------------------------- */
754 : /* Check for overviews. */
755 : /* -------------------------------------------------------------------- */
756 2 : oOvManager.Initialize( this, osMDFilename );
757 :
758 2 : return TRUE;
759 : }
760 :
761 :
762 : /************************************************************************/
763 : /* ReadImageInformation() DIMAP Version 2 */
764 : /************************************************************************/
765 :
766 0 : int DIMAPDataset::ReadImageInformation2()
767 : {
768 0 : CPLXMLNode *psDoc = CPLGetXMLNode( psProductDim, "=Dimap_Document" );
769 0 : if( !psDoc )
770 0 : psDoc = CPLGetXMLNode( psProductDim, "=PHR_DIMAP_Document" );
771 :
772 0 : CPLXMLNode *psImageAttributes = CPLGetXMLNode( psDoc, "Raster_Data.Raster_Dimensions" );
773 0 : if( psImageAttributes == NULL )
774 : {
775 : CPLError( CE_Failure, CPLE_OpenFailed,
776 0 : "Failed to find <Raster_Dimensions> in document." );
777 0 : return FALSE;
778 : }
779 :
780 : /* -------------------------------------------------------------------- */
781 : /* Get overall image information. */
782 : /* -------------------------------------------------------------------- */
783 : #ifdef DEBUG
784 : int nBands =
785 0 : atoi(CPLGetXMLValue( psImageAttributes, "NBANDS", "-1" ));
786 : #endif
787 :
788 : nRasterXSize =
789 0 : atoi(CPLGetXMLValue( psImageAttributes, "NCOLS", "-1" ));
790 : nRasterYSize =
791 0 : atoi(CPLGetXMLValue( psImageAttributes, "NROWS", "-1" ));
792 :
793 : /* -------------------------------------------------------------------- */
794 : /* Get the name of the underlying file. */
795 : /* -------------------------------------------------------------------- */
796 :
797 : /* If the data file was not in the product file, it should be here */
798 0 : if ( osImageDSFilename.size() == 0 )
799 : {
800 : const char *pszHref = CPLGetXMLValue(
801 0 : psDoc, "Raster_Data.Data_Access.Data_Files.Data_File.DATA_FILE_PATH.href", "" );
802 0 : if( strlen(pszHref) > 0 )
803 : {
804 0 : CPLString osPath = CPLGetPath( osDIMAPFilename );
805 : osImageDSFilename =
806 0 : CPLFormFilename( osPath, pszHref, NULL );
807 : }
808 : else
809 : {
810 : CPLError( CE_Failure, CPLE_OpenFailed,
811 0 : "Failed to find <DATA_FILE_PATH> in document." );
812 0 : return FALSE;
813 : }
814 : }
815 :
816 :
817 : /* -------------------------------------------------------------------- */
818 : /* Try and open the file. */
819 : /* -------------------------------------------------------------------- */
820 0 : poImageDS = (GDALDataset *) GDALOpen( osImageDSFilename, GA_ReadOnly );
821 0 : if( poImageDS == NULL )
822 : {
823 0 : return FALSE;
824 : }
825 :
826 :
827 : /* -------------------------------------------------------------------- */
828 : /* Attach the bands. */
829 : /* -------------------------------------------------------------------- */
830 : int iBand;
831 0 : CPLAssert( nBands == poImageDS->GetRasterCount() );
832 :
833 0 : for( iBand = 1; iBand <= poImageDS->GetRasterCount(); iBand++ )
834 0 : SetBand( iBand, new DIMAPWrapperRasterBand(poImageDS->GetRasterBand( iBand )) );
835 :
836 : /* -------------------------------------------------------------------- */
837 : /* Try to collect simple insertion point. */
838 : /* -------------------------------------------------------------------- */
839 : CPLXMLNode *psGeoLoc =
840 0 : CPLGetXMLNode( psDoc, "Geoposition.Geoposition_Insert" );
841 :
842 0 : if( psGeoLoc != NULL )
843 : {
844 0 : bHaveGeoTransform = TRUE;
845 0 : adfGeoTransform[0] = atof(CPLGetXMLValue(psGeoLoc,"ULXMAP","0"));
846 0 : adfGeoTransform[1] = atof(CPLGetXMLValue(psGeoLoc,"XDIM","0"));
847 0 : adfGeoTransform[2] = 0.0;
848 0 : adfGeoTransform[3] = atof(CPLGetXMLValue(psGeoLoc,"ULYMAP","0"));
849 0 : adfGeoTransform[4] = 0.0;
850 0 : adfGeoTransform[5] = -atof(CPLGetXMLValue(psGeoLoc,"YDIM","0"));
851 : }
852 :
853 : /* -------------------------------------------------------------------- */
854 : /* Collect the CRS. For now we look only for EPSG codes. */
855 : /* -------------------------------------------------------------------- */
856 : const char *pszSRS = CPLGetXMLValue(
857 : psDoc,
858 : "Coordinate_Reference_System.Projected_CRS..PROJECTED_CRS_CODE",
859 0 : NULL );
860 :
861 0 : if( pszSRS != NULL )
862 : {
863 0 : OGRSpatialReference oSRS;
864 0 : if( oSRS.SetFromUserInput( pszSRS ) == OGRERR_NONE )
865 : {
866 0 : if( nGCPCount > 0 )
867 : {
868 0 : CPLFree(pszGCPProjection);
869 0 : oSRS.exportToWkt( &(pszGCPProjection) );
870 : }
871 : else
872 : {
873 0 : char *pszProjection = NULL;
874 0 : oSRS.exportToWkt( &pszProjection );
875 0 : osProjection = pszProjection;
876 0 : CPLFree( pszProjection );
877 : }
878 0 : }
879 : }
880 : else
881 : {
882 : // Check underlying raster for SRS. We have cases where
883 : // HORIZONTAL_CS_CODE is empty and the underlying raster
884 : // is georeferenced (rprinceley).
885 0 : if ( poImageDS->GetProjectionRef() )
886 : {
887 0 : osProjection = poImageDS->GetProjectionRef();
888 : }
889 : }
890 :
891 : /* -------------------------------------------------------------------- */
892 : /* Translate other metadata of interest: DIM_<product_name>.XML */
893 : /* -------------------------------------------------------------------- */
894 :
895 : static const char *apszMetadataTranslationDim[] =
896 : {
897 : "Product_Information.Delivery_Identification", "DATASET_",
898 : "Product_Information.Producer_Information", "DATASET_",
899 : "Dataset_Sources.Source_Identification.Strip_Source", "",
900 : "Processing_Information.Production_Facility", "FACILITY_",
901 : "Processing_Information.Product_Settings", "",
902 : "Processing_Information.Product_Settings.Geometric_Settings", "GEOMETRIC_",
903 : "Quality_Assessment.Imaging_Quality_Measurement", "CLOUDCOVER_",
904 : NULL, NULL
905 : };
906 :
907 0 : SetMetadataFromXML(psProductDim, apszMetadataTranslationDim);
908 :
909 : /* -------------------------------------------------------------------- */
910 : /* Translate other metadata of interest: STRIP_<product_name>.XML */
911 : /* -------------------------------------------------------------------- */
912 :
913 : static const char *apszMetadataTranslationStrip[] =
914 : {
915 : "Catalog.Full_Strip.Notations.Cloud_And_Quality_Notation.Data_Strip_Notation", "CLOUDCOVER_",
916 : "Acquisition_Configuration.Platform_Configuration.Ephemeris_Configuration", "EPHEMERIS_",
917 : NULL, NULL
918 : };
919 :
920 0 : SetMetadataFromXML(psProductStrip, apszMetadataTranslationStrip);
921 :
922 : /* -------------------------------------------------------------------- */
923 : /* Set Band metadata from the <Band_Radiance> and */
924 : /* <Band_Spectral_Range> content */
925 : /* -------------------------------------------------------------------- */
926 :
927 : CPLXMLNode *psImageInterpretationNode =
928 : CPLGetXMLNode( psDoc,
929 0 : "Radiometric_Data.Radiometric_Calibration.Instrument_Calibration.Band_Measurement_List" );
930 0 : if (psImageInterpretationNode != NULL)
931 : {
932 0 : CPLXMLNode *psSpectralBandInfoNode = psImageInterpretationNode->psChild;
933 0 : while (psSpectralBandInfoNode != NULL)
934 : {
935 0 : if (psSpectralBandInfoNode->eType == CXT_Element &&
936 : (EQUAL(psSpectralBandInfoNode->pszValue, "Band_Radiance") ||
937 : EQUAL(psSpectralBandInfoNode->pszValue, "Band_Spectral_Range") ||
938 : EQUAL(psSpectralBandInfoNode->pszValue, "Band_Solar_Irradiance")))
939 : {
940 0 : CPLString osName;
941 :
942 0 : if (EQUAL(psSpectralBandInfoNode->pszValue, "Band_Radiance"))
943 0 : osName = "RADIANCE_";
944 0 : else if (EQUAL(psSpectralBandInfoNode->pszValue, "Band_Spectral_Range"))
945 0 : osName = "SPECTRAL_RANGE_";
946 0 : else if (EQUAL(psSpectralBandInfoNode->pszValue, "Band_Solar_Irradiance"))
947 0 : osName = "SOLAR_IRRADIANCE_";
948 :
949 0 : CPLXMLNode *psTag = psSpectralBandInfoNode->psChild;
950 0 : int nBandIndex = 0;
951 0 : while(psTag != NULL)
952 : {
953 0 : if (psTag->eType == CXT_Element && psTag->psChild != NULL &&
954 : psTag->psChild->eType == CXT_Text && psTag->pszValue != NULL)
955 : {
956 0 : if (EQUAL(psTag->pszValue, "BAND_ID"))
957 : {
958 : /* BAND_ID is: B0, B1, .... P */
959 0 : if (!EQUAL(psTag->psChild->pszValue, "P"))
960 : {
961 0 : if (strlen(psTag->psChild->pszValue) < 2) /* shouldn't happen */
962 : {
963 : CPLError(CE_Warning, CPLE_AppDefined,
964 0 : "Bad BAND_INDEX value : %s", psTag->psChild->pszValue);
965 0 : nBandIndex = 0;
966 : }
967 : else
968 : {
969 0 : nBandIndex = atoi(&psTag->psChild->pszValue[1]) + 1;
970 0 : if (nBandIndex <= 0 ||
971 : nBandIndex > poImageDS->GetRasterCount())
972 : {
973 : CPLError(CE_Warning, CPLE_AppDefined,
974 0 : "Bad BAND_INDEX value : %s", psTag->psChild->pszValue);
975 0 : nBandIndex = 0;
976 : }
977 : }
978 : }
979 : }
980 0 : else if (nBandIndex >= 1)
981 : {
982 0 : CPLString osMDName = osName;
983 0 : osMDName += psTag->pszValue;
984 :
985 : GetRasterBand(nBandIndex)->SetMetadataItem(
986 0 : osMDName, psTag->psChild->pszValue);
987 : }
988 :
989 : }
990 0 : psTag = psTag->psNext;
991 0 : }
992 : }
993 0 : psSpectralBandInfoNode = psSpectralBandInfoNode->psNext;
994 : }
995 : }
996 :
997 : /* -------------------------------------------------------------------- */
998 : /* Initialize any PAM information. */
999 : /* -------------------------------------------------------------------- */
1000 0 : SetDescription( osMDFilename );
1001 0 : TryLoadXML();
1002 :
1003 : /* -------------------------------------------------------------------- */
1004 : /* Check for overviews. */
1005 : /* -------------------------------------------------------------------- */
1006 0 : oOvManager.Initialize( this, osMDFilename );
1007 :
1008 0 : return TRUE;
1009 : }
1010 :
1011 : /************************************************************************/
1012 : /* SetMetadataFromXML() */
1013 : /************************************************************************/
1014 :
1015 2 : void DIMAPDataset::SetMetadataFromXML(CPLXMLNode *psProduct, const char *apszMetadataTranslation[])
1016 : {
1017 2 : CPLXMLNode *psDoc = CPLGetXMLNode( psProduct, "=Dimap_Document" );
1018 2 : if( psDoc == NULL )
1019 : {
1020 0 : psDoc = CPLGetXMLNode( psProduct, "=PHR_DIMAP_Document" );
1021 : }
1022 :
1023 : int iTrItem;
1024 :
1025 12 : for( iTrItem = 0; apszMetadataTranslation[iTrItem] != NULL; iTrItem += 2 )
1026 : {
1027 : CPLXMLNode *psParent =
1028 10 : CPLGetXMLNode( psDoc, apszMetadataTranslation[iTrItem] );
1029 :
1030 10 : if( psParent == NULL )
1031 2 : continue;
1032 :
1033 : // hackey logic to support directly access a name/value entry
1034 : // or a parent element with many name/values.
1035 :
1036 : CPLXMLNode *psTarget;
1037 8 : if( psParent->psChild != NULL
1038 : && psParent->psChild->eType == CXT_Text )
1039 0 : psTarget = psParent;
1040 : else
1041 8 : psTarget = psParent->psChild;
1042 :
1043 70 : for( ; psTarget != NULL && psTarget != psParent;
1044 : psTarget = psTarget->psNext )
1045 : {
1046 62 : if( psTarget->eType == CXT_Element
1047 : && psTarget->psChild != NULL)
1048 : {
1049 62 : CPLString osName = apszMetadataTranslation[iTrItem+1];
1050 :
1051 62 : if (psTarget->psChild->eType == CXT_Text)
1052 : {
1053 54 : osName += psTarget->pszValue;
1054 54 : SetMetadataItem( osName, psTarget->psChild->pszValue );
1055 8 : } else if (psTarget->psChild->eType == CXT_Attribute)
1056 : {
1057 : /* find the tag value, at the end of the attributes */
1058 2 : CPLXMLNode *psNode = psTarget->psChild;
1059 4 : for( ; psNode != NULL; psNode = psNode->psNext )
1060 : {
1061 2 : if (psNode->eType == CXT_Attribute)
1062 2 : continue;
1063 0 : else if (psNode->eType == CXT_Text)
1064 : {
1065 0 : osName += psTarget->pszValue;
1066 0 : SetMetadataItem( osName, psNode->pszValue );
1067 : }
1068 : }
1069 62 : }
1070 : }
1071 : }
1072 : }
1073 2 : }
1074 :
1075 : /************************************************************************/
1076 : /* GetGCPCount() */
1077 : /************************************************************************/
1078 :
1079 1 : int DIMAPDataset::GetGCPCount()
1080 :
1081 : {
1082 1 : return nGCPCount;
1083 : }
1084 :
1085 : /************************************************************************/
1086 : /* GetGCPProjection() */
1087 : /************************************************************************/
1088 :
1089 1 : const char *DIMAPDataset::GetGCPProjection()
1090 :
1091 : {
1092 1 : return pszGCPProjection;
1093 : }
1094 :
1095 : /************************************************************************/
1096 : /* GetGCPs() */
1097 : /************************************************************************/
1098 :
1099 1 : const GDAL_GCP *DIMAPDataset::GetGCPs()
1100 :
1101 : {
1102 1 : return pasGCPList;
1103 : }
1104 :
1105 : /************************************************************************/
1106 : /* GDALRegister_DIMAP() */
1107 : /************************************************************************/
1108 :
1109 582 : void GDALRegister_DIMAP()
1110 :
1111 : {
1112 : GDALDriver *poDriver;
1113 :
1114 582 : if( GDALGetDriverByName( "DIMAP" ) == NULL )
1115 : {
1116 561 : poDriver = new GDALDriver();
1117 :
1118 561 : poDriver->SetDescription( "DIMAP" );
1119 : poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
1120 561 : "SPOT DIMAP" );
1121 : poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
1122 561 : "frmt_various.html#DIMAP" );
1123 561 : poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
1124 :
1125 561 : poDriver->pfnOpen = DIMAPDataset::Open;
1126 561 : poDriver->pfnIdentify = DIMAPDataset::Identify;
1127 :
1128 561 : GetGDALDriverManager()->RegisterDriver( poDriver );
1129 : }
1130 582 : }
1131 :
|