1 : /******************************************************************************
2 : * $Id: tsxdataset.cpp 17664 2009-09-21 21:16:45Z rouault $
3 : *
4 : * Project: TerraSAR-X XML Product Support
5 : * Purpose: Support for TerraSAR-X XML Metadata files
6 : * Author: Philippe Vachon <philippe@cowpig.ca>
7 : * Description: This driver adds support for reading metadata and georef data
8 : * associated with TerraSAR-X products.
9 : *
10 : ******************************************************************************
11 : * Copyright (c) 2007, Philippe Vachon <philippe@cowpig.ca>
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 :
35 : CPL_CVSID("$Id: tsxdataset.cpp 17664 2009-09-21 21:16:45Z rouault $");
36 :
37 : CPL_C_START
38 : void GDALRegister_TSX(void);
39 : CPL_C_END
40 :
41 :
42 : enum ePolarization {
43 : HH=0,
44 : HV,
45 : VH,
46 : VV
47 : };
48 :
49 : enum eProductType {
50 : eSSC = 0,
51 : eMGD,
52 : eEEC,
53 : eGEC,
54 : eUnknown
55 : };
56 :
57 : /************************************************************************/
58 : /* Helper Functions */
59 : /************************************************************************/
60 :
61 : /* GetFilePath: return a relative path to a file within an XML node.
62 : * Returns Null on failure
63 : */
64 0 : const char *GetFilePath(CPLXMLNode *psXMLNode, char **pszNodeType) {
65 : const char *pszDirectory, *pszFilename;
66 :
67 0 : pszDirectory = CPLGetXMLValue( psXMLNode, "file.location.path", "" );
68 0 : pszFilename = CPLGetXMLValue( psXMLNode, "file.location.filename", "" );
69 0 : *pszNodeType = strdup(CPLGetXMLValue (psXMLNode, "type", " " ));
70 :
71 0 : if (pszDirectory == NULL || pszFilename == NULL) {
72 0 : return NULL;
73 : }
74 :
75 0 : return strdup( CPLFormFilename( pszDirectory, pszFilename, "" ) );
76 : }
77 :
78 : /************************************************************************/
79 : /* ==================================================================== */
80 : /* TSXDataset */
81 : /* ==================================================================== */
82 : /************************************************************************/
83 :
84 : class TSXDataset : public GDALPamDataset {
85 : int nGCPCount;
86 : GDAL_GCP *pasGCPList;
87 :
88 : char *pszGCPProjection;
89 :
90 : char *pszGeorefFile;
91 : FILE *fp;
92 :
93 : eProductType nProduct;
94 : public:
95 : TSXDataset();
96 : ~TSXDataset();
97 :
98 : virtual int GetGCPCount();
99 : virtual const char *GetGCPProjection();
100 : virtual const GDAL_GCP *GetGCPs();
101 :
102 : static GDALDataset *Open( GDALOpenInfo *poOpenInfo );
103 : static int Identify( GDALOpenInfo *poOpenInfo );
104 : };
105 :
106 : /************************************************************************/
107 : /* ==================================================================== */
108 : /* TSXRasterBand */
109 : /* ==================================================================== */
110 : /************************************************************************/
111 :
112 : class TSXRasterBand : public GDALPamRasterBand {
113 : GDALDataset *poBand;
114 : ePolarization ePol;
115 : public:
116 : TSXRasterBand( TSXDataset *poDSIn, GDALDataType eDataType,
117 : ePolarization ePol, GDALDataset *poBand );
118 : virtual ~TSXRasterBand();
119 :
120 : virtual CPLErr IReadBlock( int nBlockXOff, int nBlockYOff, void *pImage );
121 :
122 : static GDALDataset *Open( GDALOpenInfo *poOpenInfo );
123 : };
124 :
125 : /************************************************************************/
126 : /* TSXRasterBand */
127 : /************************************************************************/
128 :
129 0 : TSXRasterBand::TSXRasterBand( TSXDataset *poDS, GDALDataType eDataType,
130 0 : ePolarization ePol, GDALDataset *poBand )
131 : {
132 0 : this->poDS = poDS;
133 0 : this->eDataType = eDataType;
134 0 : this->ePol = ePol;
135 :
136 0 : switch (ePol) {
137 : case HH:
138 0 : SetMetadataItem( "POLARIMETRIC_INTERP", "HH" );
139 0 : break;
140 : case HV:
141 0 : SetMetadataItem( "POLARIMETRIC_INTERP", "HV" );
142 0 : break;
143 : case VH:
144 0 : SetMetadataItem( "POLARIMETRIC_INTERP", "VH" );
145 0 : break;
146 : case VV:
147 0 : SetMetadataItem( "POLARIMETRIC_INTERP", "VV" );
148 : break;
149 : }
150 :
151 :
152 : /* now setup the actual raster reader */
153 0 : this->poBand = poBand;
154 :
155 0 : GDALRasterBand *poSrcBand = poBand->GetRasterBand( 1 );
156 0 : poSrcBand->GetBlockSize( &nBlockXSize, &nBlockYSize );
157 0 : }
158 :
159 : /************************************************************************/
160 : /* TSXRasterBand() */
161 : /************************************************************************/
162 :
163 0 : TSXRasterBand::~TSXRasterBand() {
164 0 : if( poBand != NULL )
165 0 : GDALClose( (GDALRasterBandH) poBand );
166 0 : }
167 :
168 : /************************************************************************/
169 : /* IReadBlock() */
170 : /************************************************************************/
171 :
172 0 : CPLErr TSXRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
173 : void * pImage )
174 : {
175 : int nRequestYSize;
176 :
177 : /* Check if the last strip is partial so we can avoid over-requesting */
178 0 : if ( (nBlockYOff + 1) * nBlockYSize > nRasterYSize ) {
179 0 : nRequestYSize = nRasterYSize - nBlockYOff * nBlockYSize;
180 : memset( pImage, 0, (GDALGetDataTypeSize( eDataType ) / 8) *
181 0 : nBlockXSize * nBlockYSize);
182 : }
183 : else {
184 0 : nRequestYSize = nBlockYSize;
185 : }
186 :
187 : /* Read Complex Data */
188 0 : if ( eDataType == GDT_CInt16 ) {
189 : return poBand->RasterIO( GF_Read, nBlockXOff * nBlockXSize,
190 : nBlockYOff * nBlockYSize, nBlockXSize, nRequestYSize,
191 : pImage, nBlockXSize, nRequestYSize, GDT_CInt16, 1, NULL, 4,
192 0 : nBlockXSize * 4, 0 );
193 : }
194 : else { /* Detected Product */
195 : return poBand->RasterIO( GF_Read, nBlockXOff * nBlockXSize,
196 : nBlockYOff * nBlockYSize, nBlockXSize, nRequestYSize,
197 : pImage, nBlockXSize, nRequestYSize, GDT_UInt16, 1, NULL, 2,
198 0 : nBlockXSize * 2, 0 );
199 : }
200 : }
201 :
202 : /************************************************************************/
203 : /* ==================================================================== */
204 : /* TSXDataset */
205 : /* ==================================================================== */
206 : /************************************************************************/
207 :
208 : /************************************************************************/
209 : /* TSXDataset() */
210 : /************************************************************************/
211 :
212 0 : TSXDataset::TSXDataset() {
213 0 : nGCPCount = 0;
214 0 : pasGCPList = NULL;
215 0 : pszGCPProjection = CPLStrdup("");
216 0 : }
217 :
218 : /************************************************************************/
219 : /* ~TSXDataset() */
220 : /************************************************************************/
221 :
222 0 : TSXDataset::~TSXDataset() {
223 0 : FlushCache();
224 0 : }
225 :
226 : /************************************************************************/
227 : /* Identify() */
228 : /************************************************************************/
229 :
230 8784 : int TSXDataset::Identify( GDALOpenInfo *poOpenInfo ) {
231 8784 : if (poOpenInfo->fp == NULL || poOpenInfo->nHeaderBytes < 260)
232 8507 : return 0;
233 :
234 : /* Check if the filename contains TSX1_SAR */
235 277 : if (!EQUALN(CPLGetBasename( poOpenInfo->pszFilename ), "TSX1_SAR", 8))
236 277 : return 0;
237 :
238 : /* finally look for the <level1Product tag */
239 0 : if (!EQUALN((char *)poOpenInfo->pabyHeader, "<level1Product", 14))
240 0 : return 0;
241 :
242 0 : return 1;
243 : }
244 :
245 : /************************************************************************/
246 : /* Open() */
247 : /************************************************************************/
248 :
249 1057 : GDALDataset *TSXDataset::Open( GDALOpenInfo *poOpenInfo ) {
250 : /* -------------------------------------------------------------------- */
251 : /* Is this a TerraSAR-X product file? */
252 : /* -------------------------------------------------------------------- */
253 1057 : if (!TSXDataset::Identify( poOpenInfo )) {
254 1057 : return NULL; /* nope */
255 : }
256 :
257 : /* -------------------------------------------------------------------- */
258 : /* Confirm the requested access is supported. */
259 : /* -------------------------------------------------------------------- */
260 0 : if( poOpenInfo->eAccess == GA_Update )
261 : {
262 : CPLError( CE_Failure, CPLE_NotSupported,
263 : "The TSX driver does not support update access to existing"
264 0 : " datasets.\n" );
265 0 : return NULL;
266 : }
267 :
268 : /* Ingest the XML */
269 : CPLXMLNode *psData, *psComponents, *psProductInfo;
270 0 : psData = CPLParseXMLFile( poOpenInfo->pszFilename );
271 :
272 : /* find the product components */
273 0 : psComponents = CPLGetXMLNode( psData, "=level1Product.productComponents" );
274 0 : if (psComponents == NULL) {
275 : CPLError( CE_Failure, CPLE_OpenFailed,
276 0 : "Unable to find <productComponents> tag in file.\n" );
277 0 : return NULL;
278 : }
279 :
280 : /* find the product info tag */
281 0 : psProductInfo = CPLGetXMLNode( psData, "=level1Product.productInfo" );
282 0 : if (psComponents == NULL) {
283 : CPLError( CE_Failure, CPLE_OpenFailed,
284 0 : "Unable to find <productInfo> tag in file.\n" );
285 0 : return NULL;
286 : }
287 :
288 : /* -------------------------------------------------------------------- */
289 : /* Create the dataset. */
290 : /* -------------------------------------------------------------------- */
291 :
292 0 : TSXDataset *poDS = new TSXDataset();
293 0 : poDS->fp = poOpenInfo->fp;
294 0 : poOpenInfo->fp = NULL;
295 :
296 : /* -------------------------------------------------------------------- */
297 : /* Read in product info. */
298 : /* -------------------------------------------------------------------- */
299 :
300 : poDS->SetMetadataItem( "SCENE_CENTRE_TIME", CPLGetXMLValue( psProductInfo,
301 0 : "sceneInfo.sceneCenterCoord.azimuthTimeUTC", "unknown" ) );
302 : poDS->SetMetadataItem( "OPERATIONAL_MODE", CPLGetXMLValue( psProductInfo,
303 0 : "generationInfo.groundOperationsType", "unknown" ) );
304 : poDS->SetMetadataItem( "ORBIT_CYCLE", CPLGetXMLValue( psProductInfo,
305 0 : "missionInfo.orbitCycle", "unknown" ) );
306 : poDS->SetMetadataItem( "ABSOLUTE_ORBIT", CPLGetXMLValue( psProductInfo,
307 0 : "missionInfo.absOrbit", "unknown" ) );
308 : poDS->SetMetadataItem( "ORBIT_DIRECTION", CPLGetXMLValue( psProductInfo,
309 0 : "missionInfo.orbitDirection", "unknown" ) );
310 : poDS->SetMetadataItem( "IMAGING_MODE", CPLGetXMLValue( psProductInfo,
311 0 : "acquisitionInfo.imagingMode", "unknown" ) );
312 : poDS->SetMetadataItem( "PRODUCT_VARIANT", CPLGetXMLValue( psProductInfo,
313 0 : "productVariantInfo.productVariant", "unknown" ) );
314 : char *pszDataType = strdup( CPLGetXMLValue( psProductInfo,
315 0 : "imageDataInfo.imageDataType", "unknown" ) );
316 0 : poDS->SetMetadataItem( "IMAGE_TYPE", pszDataType );
317 :
318 : /* Get raster information */
319 : int nRows = atoi( CPLGetXMLValue( psProductInfo,
320 0 : "imageDataInfo.imageRaster.numberOfRows", "" ) );
321 : int nCols = atoi( CPLGetXMLValue( psProductInfo,
322 0 : "imageDataInfo.imageRaster.numberOfColumns", "" ) );
323 :
324 0 : poDS->nRasterXSize = nCols;
325 0 : poDS->nRasterYSize = nRows;
326 :
327 : poDS->SetMetadataItem( "ROW_SPACING", CPLGetXMLValue( psProductInfo,
328 0 : "imageDataInfo.imageRaster.rowSpacing", "unknown" ) );
329 : poDS->SetMetadataItem( "COL_SPACING", CPLGetXMLValue( psProductInfo,
330 0 : "imageDataInfo.imageRaster.columnSpacing", "unknown" ) );
331 : poDS->SetMetadataItem( "COL_SPACING_UNITS", CPLGetXMLValue( psProductInfo,
332 0 : "imageDataInfo.imageRaster.columnSpacing.units", "unknown" ) );
333 :
334 : /* Get equivalent number of looks */
335 : poDS->SetMetadataItem( "AZIMUTH_LOOKS", CPLGetXMLValue( psProductInfo,
336 0 : "imageDataInfo.imageRaster.azimuthLooks", "unknown" ) );
337 : poDS->SetMetadataItem( "RANGE_LOOKS", CPLGetXMLValue( psProductInfo,
338 0 : "imageDataInfo.imageRaster.rangeLooks", "unknown" ) );
339 :
340 : const char *pszProductVariant;
341 : pszProductVariant = CPLGetXMLValue( psProductInfo,
342 0 : "productVariantInfo.productVariant", "unknown" );
343 :
344 0 : poDS->SetMetadataItem( "PRODUCT_VARIANT", pszProductVariant );
345 :
346 : /* Determine what product variant this is */
347 0 : if (EQUALN(pszProductVariant,"SSC",3))
348 0 : poDS->nProduct = eSSC;
349 0 : else if (EQUALN(pszProductVariant,"MGD",3))
350 0 : poDS->nProduct = eMGD;
351 0 : else if (EQUALN(pszProductVariant,"EEC",3))
352 0 : poDS->nProduct = eEEC;
353 0 : else if (EQUALN(pszProductVariant,"GEC",3))
354 0 : poDS->nProduct = eGEC;
355 : else
356 0 : poDS->nProduct = eUnknown;
357 :
358 : /* Start reading in the product components */
359 : const char *pszPath;
360 0 : char *pszGeorefFile = NULL;
361 : CPLXMLNode *psComponent;
362 0 : for (psComponent = psComponents->psChild; psComponent != NULL;
363 : psComponent = psComponent->psNext)
364 : {
365 : char *pszType;
366 : pszPath = CPLFormFilename(
367 : CPLGetDirname( poOpenInfo->pszFilename ),
368 : GetFilePath(psComponent, &pszType),
369 0 : "" );
370 0 : const char *pszPolLayer = CPLGetXMLValue(psComponent, "polLayer", " ");
371 :
372 0 : if ( !EQUALN(pszType," ",1) ) {
373 0 : if (EQUALN(pszType, "MAPPING_GRID", 12) ) {
374 : /* the mapping grid... save as a metadata item this path */
375 0 : poDS->SetMetadataItem( "MAPPING_GRID", pszPath );
376 : }
377 0 : else if (EQUALN(pszType, "GEOREF", 6)) {
378 : /* save the path to the georef data for later use */
379 0 : pszGeorefFile = strdup( pszPath );
380 : }
381 0 : CPLFree(pszType);
382 : }
383 0 : else if( !EQUALN(pszPolLayer, " ", 1) &&
384 : EQUALN(psComponent->pszValue, "imageData", 9) ) {
385 : /* determine the polarization of this band */
386 : ePolarization ePol;
387 0 : if ( EQUALN(pszPolLayer, "HH", 2) ) {
388 0 : ePol = HH;
389 : }
390 0 : else if ( EQUALN(pszPolLayer, "HV" , 2) ) {
391 0 : ePol = HV;
392 : }
393 0 : else if ( EQUALN(pszPolLayer, "VH", 2) ) {
394 0 : ePol = VH;
395 : }
396 : else {
397 0 : ePol = VV;
398 : }
399 :
400 : GDALDataType eDataType = EQUALN(pszDataType, "COMPLEX", 7) ?
401 0 : GDT_CInt16 : GDT_UInt16;
402 :
403 : /* try opening the file that represents that band */
404 : TSXRasterBand *poBand;
405 : GDALDataset *poBandData;
406 :
407 0 : poBandData = (GDALDataset *) GDALOpen( pszPath, GA_ReadOnly );
408 0 : if ( poBandData != NULL ) {
409 : poBand = new TSXRasterBand( poDS, eDataType, ePol,
410 0 : poBandData );
411 0 : poDS->SetBand( poDS->GetRasterCount() + 1, poBand );
412 : }
413 : }
414 : }
415 :
416 0 : CPLFree(pszDataType);
417 :
418 :
419 : /* -------------------------------------------------------------------- */
420 : /* Check and set matrix representation. */
421 : /* -------------------------------------------------------------------- */
422 :
423 0 : if (poDS->GetRasterCount() == 4) {
424 0 : poDS->SetMetadataItem( "MATRIX_REPRESENTATION", "SCATTERING" );
425 : }
426 :
427 : /* -------------------------------------------------------------------- */
428 : /* Read the four corners and centre GCPs in */
429 : /* -------------------------------------------------------------------- */
430 :
431 : CPLXMLNode *psSceneInfo = CPLGetXMLNode( psData,
432 0 : "=level1Product.productInfo.sceneInfo" );
433 : /* for SSC products */
434 0 : if (poDS->nProduct == eSSC && psSceneInfo != NULL) {
435 : CPLXMLNode *psNode;
436 0 : int nGCP = 0;
437 : double dfAvgHeight = atof(CPLGetXMLValue(psSceneInfo,
438 0 : "sceneAverageHeight", "0.0"));
439 : char szID[3];
440 :
441 0 : poDS->nGCPCount = 5; /* 5 GCPs provided */
442 : poDS->pasGCPList = (GDAL_GCP *)CPLCalloc(sizeof(GDAL_GCP),
443 0 : poDS->nGCPCount);
444 :
445 : /* iterate over GCPs */
446 0 : for (psNode = psSceneInfo->psChild; psNode != NULL;
447 : psNode = psNode->psNext )
448 : {
449 0 : GDAL_GCP *psGCP = poDS->pasGCPList + nGCP;
450 :
451 0 : if (!EQUAL(psNode->pszValue, "sceneCenterCoord") &&
452 : !EQUAL(psNode->pszValue, "sceneCornerCoord"))
453 0 : continue;
454 :
455 0 : CPLSPrintf( szID, "%d", nGCP );
456 :
457 : psGCP->dfGCPPixel = atof(CPLGetXMLValue(psNode, "refColumn",
458 0 : "0.0"));
459 0 : psGCP->dfGCPLine = atof(CPLGetXMLValue(psNode, "refRow", "0.0"));
460 0 : psGCP->dfGCPX = atof(CPLGetXMLValue(psNode, "lon", "0.0"));
461 0 : psGCP->dfGCPY = atof(CPLGetXMLValue(psNode, "lat", "0.0"));
462 0 : psGCP->dfGCPZ = dfAvgHeight;
463 0 : psGCP->pszId = CPLStrdup( szID );
464 0 : psGCP->pszInfo = CPLStrdup("");
465 :
466 0 : nGCP++;
467 : }
468 : }
469 0 : else if (psSceneInfo != NULL) {
470 : /* extract the GCPs from the provided file */
471 :
472 : /* TODO */
473 : }
474 : else {
475 : CPLError(CE_Warning, CPLE_AppDefined,
476 : "Unable to find sceneInfo tag in XML document. "
477 0 : "Proceeding with caution.");
478 : }
479 :
480 : /* -------------------------------------------------------------------- */
481 : /* Initialize any PAM information. */
482 : /* -------------------------------------------------------------------- */
483 0 : poDS->SetDescription( poOpenInfo->pszFilename );
484 0 : poDS->TryLoadXML();
485 :
486 : /* -------------------------------------------------------------------- */
487 : /* Check for overviews. */
488 : /* -------------------------------------------------------------------- */
489 0 : poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );
490 :
491 0 : CPLDestroyXMLNode(psData);
492 :
493 0 : return poDS;
494 : }
495 :
496 : /************************************************************************/
497 : /* GetGCPCount() */
498 : /************************************************************************/
499 :
500 0 : int TSXDataset::GetGCPCount() {
501 0 : return nGCPCount;
502 : }
503 :
504 : /************************************************************************/
505 : /* GetGCPProjection() */
506 : /************************************************************************/
507 :
508 0 : const char *TSXDataset::GetGCPProjection() {
509 0 : return pszGCPProjection;
510 : }
511 :
512 : /************************************************************************/
513 : /* GetGCPs() */
514 : /************************************************************************/
515 :
516 0 : const GDAL_GCP *TSXDataset::GetGCPs() {
517 0 : return pasGCPList;
518 : }
519 :
520 : /************************************************************************/
521 : /* GDALRegister_TSX() */
522 : /************************************************************************/
523 :
524 338 : void GDALRegister_TSX() {
525 : GDALDriver *poDriver;
526 :
527 338 : if( GDALGetDriverByName( "TSX" ) == NULL )
528 : {
529 336 : poDriver = new GDALDriver();
530 :
531 336 : poDriver->SetDescription( "TSX" );
532 : poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
533 336 : "TerraSAR-X Product" );
534 : /* poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, "frmt_tsx.html" ); */
535 :
536 336 : poDriver->pfnOpen = TSXDataset::Open;
537 336 : poDriver->pfnIdentify = TSXDataset::Identify;
538 :
539 336 : GetGDALDriverManager()->RegisterDriver( poDriver );
540 : }
541 338 : }
542 :
|