DataPromisePtr DirectorySharingPromise::data (const Path & subPath, const String & user) const {
	if (sf::checkDangerousPath(subPath.toString())) {
		Log (LogWarning) << LOGID << subPath << " looks dangerous, rejecting" << std::endl;
	}
	
	if (user.empty()){
		// Check if it is a directory
		DataPromisePtr tryDirectory = directoryListing (subPath);
		if (tryDirectory) return tryDirectory;

	
		// It must be a file
		return subFile (subPath);
	}
	if (user == "list"){
		return directoryListing (subPath);
	}
	if (user == "file"){
		return subFile (subPath);
	}
	if (user == "glob") {
		return DataPromisePtr (new GlobPromise (mGlobber, osPath (subPath)));
	}
	// unknown user flag
	Log (LogWarning) << LOGID << "Unknown user flag: " << user << std::endl;
	return DataPromisePtr ();
}
DataPromisePtr DirectorySharingPromise::subFile (const Path & path) const {
	String osp = osPath (path);
	if (mError) {
		return DataPromisePtr();
	}
	FileSharingPromisePtr p (new FileSharingPromise (osp));
	if (mTransmissionUpdated){
		p->transmissionUpdated() = abind (mTransmissionUpdated, weak_ptr<FileSharingPromise> (p));
	}
	return DataPromisePtr (p);
}
DataPromisePtr DirectorySharingPromise::directoryListing (const Path & path) const {
	String osp = osPath (path);
	
	DirectoryListing listing;
	Error error = sf::listDirectory(osp, &listing);
	if (error) {
		Log (LogInfo) << LOGID << "List directory of " << osp << " returned " << toString (error) << std::endl;
		return DataPromisePtr();
	}
	ByteArrayPtr result  = createByteArrayPtr();
	*result = toJSON (listing);
	return sf::createDataPromise(result);
}
Exemple #4
0
Path & Path::operator+=( const QString & add )
{
	mPath = osPath( mPath + add );
	return *this;
}
Exemple #5
0
GDALDataset *SAFEDataset::Open( GDALOpenInfo * poOpenInfo )

{
/* -------------------------------------------------------------------- */
/*      Is this a SENTINEL-1 manifest.safe definition?                  */
/* -------------------------------------------------------------------- */
    if ( !SAFEDataset::Identify( poOpenInfo ) ) {
        return nullptr;
    }

/* -------------------------------------------------------------------- */
/*        Get subdataset information, if relevant                       */
/* -------------------------------------------------------------------- */
    CPLString osMDFilename;

    //Subdataset 1st level selection (ex: for swath selection)
    CPLString osSelectedSubDS1;
    //Subdataset 2nd level selection (ex: for polarisation selection)
    CPLString osSelectedSubDS2;

    if (STARTS_WITH_CI(poOpenInfo->pszFilename, "SENTINEL1_DS:"))
    {
      osMDFilename = poOpenInfo->pszFilename + strlen("SENTINEL1_DS:");
      const char* pszSelection1 = strrchr(osMDFilename.c_str(), ':');
      if (pszSelection1 == nullptr || pszSelection1 == osMDFilename.c_str() )
      {
          CPLError(CE_Failure, CPLE_AppDefined, "Invalid syntax for SENTINEL1_DS:");
          return nullptr;
      }
      osMDFilename.resize( pszSelection1 - osMDFilename.c_str() );
      osSelectedSubDS1 = pszSelection1 + strlen(":");

      const char* pszSelection2 = strchr(osSelectedSubDS1.c_str(), '_');
      if (pszSelection2 != nullptr && pszSelection2 != pszSelection1 )
      {
          osSelectedSubDS1.resize( pszSelection2 - osSelectedSubDS1.c_str() );
          osSelectedSubDS2 = pszSelection2 + strlen("_");
      }

      //update directory check:
      VSIStatBufL  sStat;
      if( VSIStatL( osMDFilename.c_str(), &sStat ) == 0 )
          poOpenInfo->bIsDirectory = VSI_ISDIR( sStat.st_mode );
    }
    else
    {
      osMDFilename = poOpenInfo->pszFilename;
    }

    if( poOpenInfo->bIsDirectory )
    {
        osMDFilename =
            CPLFormCIFilename( osMDFilename.c_str(), "manifest.safe", nullptr );
    }

/* -------------------------------------------------------------------- */
/*      Ingest the manifest.safe file.                                  */
/* -------------------------------------------------------------------- */
    //TODO REMOVE CPLXMLNode *psImageAttributes, *psImageGenerationParameters;
    CPLXMLNode *psManifest = CPLParseXMLFile( osMDFilename );
    if( psManifest == nullptr )
        return nullptr;

    CPLString osPath(CPLGetPath( osMDFilename ));

/* -------------------------------------------------------------------- */
/*      Confirm the requested access is supported.                      */
/* -------------------------------------------------------------------- */
    if( poOpenInfo->eAccess == GA_Update )
    {
        CPLDestroyXMLNode( psManifest );
        CPLError( CE_Failure, CPLE_NotSupported,
                  "The SAFE driver does not support update access to existing"
                  " datasets.\n" );
        return nullptr;
    }

/* -------------------------------------------------------------------- */
/*      Get contentUnit parent element.                                 */
/* -------------------------------------------------------------------- */
    CPLXMLNode *psContentUnits = CPLGetXMLNode(
            psManifest,
            "=xfdu:XFDU.informationPackageMap.xfdu:contentUnit" );
    if( psContentUnits == nullptr )
    {
        CPLDestroyXMLNode( psManifest );
        CPLError( CE_Failure, CPLE_OpenFailed,
                  "Failed to find <xfdu:XFDU><informationPackageMap>"
                  "<xfdu:contentUnit> in manifest file." );
        return nullptr;
    }

/* -------------------------------------------------------------------- */
/*      Get Metadata Objects element.                                   */
/* -------------------------------------------------------------------- */
    CPLXMLNode *psMetaDataObjects
        = CPLGetXMLNode( psManifest, "=xfdu:XFDU.metadataSection" );
    if( psMetaDataObjects == nullptr )
    {
        CPLDestroyXMLNode( psManifest );
        CPLError( CE_Failure, CPLE_OpenFailed,
                  "Failed to find <xfdu:XFDU><metadataSection>"
                  "in manifest file." );
        return nullptr;
    }

/* -------------------------------------------------------------------- */
/*      Get Data Objects element.                                       */
/* -------------------------------------------------------------------- */
    CPLXMLNode *psDataObjects
        = CPLGetXMLNode( psManifest, "=xfdu:XFDU.dataObjectSection" );
    if( psDataObjects == nullptr )
    {
        CPLDestroyXMLNode( psManifest );
        CPLError( CE_Failure, CPLE_OpenFailed,
                "Failed to find <xfdu:XFDU><dataObjectSection> in document." );
        return nullptr;
    }

/* -------------------------------------------------------------------- */
/*      Create the dataset.                                             */
/* -------------------------------------------------------------------- */
    SAFEDataset *poDS = new SAFEDataset();

    poDS->psManifest = psManifest;

/* -------------------------------------------------------------------- */
/*      Look for "Measurement Data Unit" contentUnit elements.          */
/* -------------------------------------------------------------------- */
    CPLXMLNode *psAnnotation = nullptr;
    //Map with all measures aggregated by swath
    std::map<CPLString, std::set<CPLString> > oMapSwaths2Pols;

    for( CPLXMLNode *psContentUnit = psContentUnits->psChild;
         psContentUnit != nullptr;
         psContentUnit = psContentUnit->psNext )
    {
        if( psContentUnit->eType != CXT_Element
            || !(EQUAL(psContentUnit->pszValue,"xfdu:contentUnit")) ) {
            continue;
        }

        const char *pszUnitType = CPLGetXMLValue( psContentUnit,
                "unitType", "" );

        const char *pszAnnotation  = nullptr;
        const char *pszCalibration = nullptr;
        const char *pszMeasurement = nullptr;

        if ( EQUAL(pszUnitType, "Measurement Data Unit") ) {
            /* Get dmdID and dataObjectID */
            const char *pszDmdID = CPLGetXMLValue(psContentUnit, "dmdID", "");

            const char *pszDataObjectID = CPLGetXMLValue(
                psContentUnit,
                "dataObjectPointer.dataObjectID", "" );
            if( *pszDataObjectID == '\0' || *pszDmdID == '\0' ) {
                continue;
            }

            CPLXMLNode *psDataObject = SAFEDataset::GetDataObject(
                    psDataObjects, pszDataObjectID);

            const char *pszRepId = CPLGetXMLValue( psDataObject, "repID", "" );
            if ( !EQUAL(pszRepId, "s1Level1MeasurementSchema") ) {
                continue;
            }
            pszMeasurement = CPLGetXMLValue(
                    psDataObject, "byteStream.fileLocation.href", "");
            if( *pszMeasurement == '\0' ) {
                continue;
            }

            char** papszTokens = CSLTokenizeString2( pszDmdID, " ",
                CSLT_ALLOWEMPTYTOKENS | CSLT_STRIPLEADSPACES
                | CSLT_STRIPENDSPACES );

            for( int j = 0; j < CSLCount( papszTokens ); j++ ) {
                const char* pszId = papszTokens[j];
                if( *pszId == '\0' ) {
                    continue;
                }

                //Map the metadata ID to the object element
                CPLXMLNode *psDO = SAFEDataset::GetDataObject(
                        psMetaDataObjects, psDataObjects, pszId);

                if (psDO == nullptr) {
                    continue;
                }

                //check object type
                pszRepId = CPLGetXMLValue( psDO, "repID", "" );

                if( EQUAL(pszRepId, "s1Level1ProductSchema") )
                {
                    /* Get annotation filename */
                    pszAnnotation = CPLGetXMLValue(
                            psDO, "byteStream.fileLocation.href", "");
                    if( *pszAnnotation == '\0' )
                    {
                        continue;
                    }
                }
                else if( EQUAL(pszRepId, "s1Level1CalibrationSchema") )
                {
                    pszCalibration = CPLGetXMLValue(
                            psDO, "byteStream.fileLocation.href", "");
                    if( *pszCalibration == '\0' ) {
                        continue;
                    }
                }
                else
                {
                    continue;
                }
            }

            CSLDestroy(papszTokens);

            if (pszAnnotation == nullptr || pszCalibration == nullptr ) {
                continue;
            }

            //open Annotation XML file
            CPLString osAnnotationFilePath = CPLFormFilename( osPath,
                                                       pszAnnotation, nullptr );
            if( psAnnotation )
                CPLDestroyXMLNode(psAnnotation);
            psAnnotation = CPLParseXMLFile( osAnnotationFilePath );
            if( psAnnotation == nullptr )
                continue;

/* -------------------------------------------------------------------- */
/*      Get overall image information.                                  */
/* -------------------------------------------------------------------- */
            poDS->nRasterXSize =
                atoi(CPLGetXMLValue( psAnnotation,
                    "=product.imageAnnotation.imageInformation.numberOfSamples",
                    "-1" ));
            poDS->nRasterYSize =
                atoi(CPLGetXMLValue( psAnnotation,
                    "=product.imageAnnotation.imageInformation.numberOfLines",
                    "-1" ));
            if (poDS->nRasterXSize <= 1 || poDS->nRasterYSize <= 1) {
                CPLError( CE_Failure, CPLE_OpenFailed,
                    "Non-sane raster dimensions provided in manifest.safe. "
                    "If this is a valid SENTINEL-1 scene, please contact your "
                    "data provider for a corrected dataset." );
                delete poDS;
                CPLDestroyXMLNode(psAnnotation);
                return nullptr;
            }

            CPLString osProductType = CPLGetXMLValue(
                psAnnotation, "=product.adsHeader.productType", "UNK" );
            CPLString osMissionId = CPLGetXMLValue(
                psAnnotation, "=product.adsHeader.missionId", "UNK" );
            CPLString osPolarisation = CPLGetXMLValue(
                psAnnotation, "=product.adsHeader.polarisation", "UNK" );
            CPLString osMode = CPLGetXMLValue(
                psAnnotation, "=product.adsHeader.mode", "UNK" );
            CPLString osSwath = CPLGetXMLValue(
                psAnnotation, "=product.adsHeader.swath", "UNK" );

            oMapSwaths2Pols[osSwath].insert(osPolarisation);

            if (osSelectedSubDS1.empty()) {
              // If not subdataset was selected,
              // open the first one we can find.
              osSelectedSubDS1 = osSwath;
            }

            if (!EQUAL(osSelectedSubDS1.c_str(), osSwath.c_str())) {
              //do not mix swath, otherwise it does not work for SLC products
              continue;
            }

            if (!osSelectedSubDS2.empty()
              && (osSelectedSubDS2.find(osPolarisation)== std::string::npos)) {
              // Add only selected polarisations.
              continue;
            }

            poDS->SetMetadataItem("PRODUCT_TYPE", osProductType.c_str());
            poDS->SetMetadataItem("MISSION_ID", osMissionId.c_str());
            poDS->SetMetadataItem("MODE", osMode.c_str());
            poDS->SetMetadataItem("SWATH", osSwath.c_str());

/* -------------------------------------------------------------------- */
/*      Get dataType (so we can recognize complex data), and the        */
/*      bitsPerSample.                                                  */
/* -------------------------------------------------------------------- */

            const char *pszDataType = CPLGetXMLValue(
                psAnnotation,
                "=product.imageAnnotation.imageInformation.outputPixels",
                "" );

            GDALDataType eDataType;
            if( EQUAL(pszDataType,"16 bit Signed Integer") )
                eDataType = GDT_CInt16;
            else if( EQUAL(pszDataType,"16 bit Unsigned Integer") )
                eDataType = GDT_UInt16;
            else
            {
                delete poDS;
                CPLError( CE_Failure, CPLE_AppDefined,
                          "dataType=%s: not a supported configuration.",
                          pszDataType );
                CPLDestroyXMLNode(psAnnotation);
                return nullptr;
            }

            /* Extract pixel spacing information */
            const char *pszPixelSpacing = CPLGetXMLValue(
                psAnnotation,
                "=product.imageAnnotation.imageInformation.rangePixelSpacing",
                "UNK" );
            poDS->SetMetadataItem( "PIXEL_SPACING", pszPixelSpacing );

            const char *pszLineSpacing = CPLGetXMLValue(
                psAnnotation,
                "=product.imageAnnotation.imageInformation.azimuthPixelSpacing",
                "UNK" );
            poDS->SetMetadataItem( "LINE_SPACING", pszLineSpacing );

/* -------------------------------------------------------------------- */
/*      Form full filename (path of manifest.safe + measurement file).  */
/* -------------------------------------------------------------------- */
            char *pszFullname =
                CPLStrdup(CPLFormFilename( osPath, pszMeasurement, nullptr ));

/* -------------------------------------------------------------------- */
/*      Try and open the file.                                          */
/* -------------------------------------------------------------------- */
            GDALDataset *poBandFile = reinterpret_cast<GDALDataset *>(
                GDALOpen( pszFullname, GA_ReadOnly ) );
            if( poBandFile == nullptr )
            {
                // NOP
            }
            else
            if (poBandFile->GetRasterCount() == 0)
            {
                GDALClose( (GDALRasterBandH) poBandFile );
             }
            else {
                poDS->papszExtraFiles = CSLAddString( poDS->papszExtraFiles,
                                                  osAnnotationFilePath );
                poDS->papszExtraFiles = CSLAddString( poDS->papszExtraFiles,
                                                  pszFullname );

/* -------------------------------------------------------------------- */
/*      Create the band.                                                */
/* -------------------------------------------------------------------- */
                SAFERasterBand *poBand
                    = new SAFERasterBand( poDS, eDataType,
                                          osSwath.c_str(),
                                          osPolarisation.c_str(),
                                          poBandFile );

                poDS->SetBand( poDS->GetRasterCount() + 1, poBand );
            }

            CPLFree( pszFullname );
        }
    }

    //loop through all Swath/pols to add subdatasets
    int iSubDS = 1;
    for (std::map<CPLString, std::set<CPLString> >::iterator iterSwath=oMapSwaths2Pols.begin();
         iterSwath!=oMapSwaths2Pols.end(); ++iterSwath)
    {
        CPLString osSubDS1 = iterSwath->first;
        CPLString osSubDS2;

        for (std::set<CPLString>::iterator iterPol=iterSwath->second.begin();
            iterPol!=iterSwath->second.end(); ++iterPol)
        {
            if (!osSubDS2.empty()) {
                osSubDS2 += "+";
            }
            osSubDS2 += *iterPol;

            //Create single band SubDataset
            SAFEDataset::AddSubDataset(poDS, iSubDS,
                CPLSPrintf("SENTINEL1_DS:%s:%s_%s",
                    osPath.c_str(),
                    osSubDS1.c_str(),
                    (*iterPol).c_str()),
                CPLSPrintf("Single band with %s swath and %s polarisation",
                    osSubDS1.c_str(),
                    (*iterPol).c_str())
            );
            iSubDS++;
        }

        if (iterSwath->second.size()>1) {
            //Create single band SubDataset with all polarisations
            SAFEDataset::AddSubDataset(poDS, iSubDS,
                CPLSPrintf("SENTINEL1_DS:%s:%s",
                    osPath.c_str(),
                    osSubDS1.c_str()),
                CPLSPrintf("%s swath with all polarisations as bands",
                    osSubDS1.c_str())
            );
            iSubDS++;
        }
    }

    if (poDS->GetRasterCount() == 0) {
        CPLError( CE_Failure, CPLE_OpenFailed, "Measurement bands not found." );
        delete poDS;
        if( psAnnotation )
            CPLDestroyXMLNode(psAnnotation);
        return nullptr;
    }

/* -------------------------------------------------------------------- */
/*      Collect more metadata elements                                  */
/* -------------------------------------------------------------------- */

/* -------------------------------------------------------------------- */
/*      Platform information                                            */
/* -------------------------------------------------------------------- */
    CPLXMLNode *psPlatformAttrs = SAFEDataset::GetMetaDataObject(
        psMetaDataObjects, "platform");

    if (psPlatformAttrs != nullptr) {
        const char *pszItem = CPLGetXMLValue(
                psPlatformAttrs,
                "metadataWrap.xmlData.safe:platform"
                ".safe:familyName", "" );
        poDS->SetMetadataItem( "SATELLITE_IDENTIFIER", pszItem );

        pszItem = CPLGetXMLValue(
                psPlatformAttrs,
                "metadataWrap.xmlData.safe:platform"
                ".safe:instrument.safe:familyName.abbreviation", "" );
        poDS->SetMetadataItem( "SENSOR_IDENTIFIER", pszItem );

        pszItem = CPLGetXMLValue(
                psPlatformAttrs,
                "metadataWrap.xmlData.safe:platform"
                ".safe:instrument.safe:extension"
                ".s1sarl1:instrumentMode.s1sarl1:mode", "UNK" );
        poDS->SetMetadataItem( "BEAM_MODE", pszItem );

        pszItem = CPLGetXMLValue(
                psPlatformAttrs,
                "metadataWrap.xmlData.safe:platform"
                ".safe:instrument.safe:extension"
                ".s1sarl1:instrumentMode.s1sarl1:swath", "UNK" );
        poDS->SetMetadataItem( "BEAM_SWATH", pszItem );
    }

/* -------------------------------------------------------------------- */
/*      Acquisition Period information                                  */
/* -------------------------------------------------------------------- */
    CPLXMLNode *psAcquisitionAttrs = SAFEDataset::GetMetaDataObject(
        psMetaDataObjects, "acquisitionPeriod");

    if (psAcquisitionAttrs != nullptr) {
            const char *pszItem = CPLGetXMLValue(
            psAcquisitionAttrs,
            "metadataWrap.xmlData.safe:acquisitionPeriod"
            ".safe:startTime", "UNK" );
        poDS->SetMetadataItem( "ACQUISITION_START_TIME", pszItem );
        pszItem = CPLGetXMLValue(
            psAcquisitionAttrs,
            "metadataWrap.xmlData.safe:acquisitionPeriod"
            ".safe:stopTime", "UNK" );
        poDS->SetMetadataItem( "ACQUISITION_STOP_TIME", pszItem );
    }

/* -------------------------------------------------------------------- */
/*      Processing information                                          */
/* -------------------------------------------------------------------- */
    CPLXMLNode *psProcessingAttrs = SAFEDataset::GetMetaDataObject(
        psMetaDataObjects, "processing");

    if (psProcessingAttrs != nullptr) {
        const char *pszItem = CPLGetXMLValue(
            psProcessingAttrs,
            "metadataWrap.xmlData.safe:processing.safe:facility.name", "UNK" );
        poDS->SetMetadataItem( "FACILITY_IDENTIFIER", pszItem );
    }

/* -------------------------------------------------------------------- */
/*      Measurement Orbit Reference information                         */
/* -------------------------------------------------------------------- */
    CPLXMLNode *psOrbitAttrs = SAFEDataset::GetMetaDataObject(
        psMetaDataObjects, "measurementOrbitReference");

    if (psOrbitAttrs != nullptr) {
        const char *pszItem = CPLGetXMLValue( psOrbitAttrs,
            "metadataWrap.xmlData.safe:orbitReference"
            ".safe:orbitNumber", "UNK" );
        poDS->SetMetadataItem( "ORBIT_NUMBER", pszItem );
        pszItem = CPLGetXMLValue( psOrbitAttrs,
            "metadataWrap.xmlData.safe:orbitReference"
            ".safe:extension.s1:orbitProperties.s1:pass", "UNK" );
        poDS->SetMetadataItem( "ORBIT_DIRECTION", pszItem );
    }

/* -------------------------------------------------------------------- */
/*      Collect Annotation Processing Information                       */
/* -------------------------------------------------------------------- */
    CPLXMLNode *psProcessingInfo =
        CPLGetXMLNode( psAnnotation,
                       "=product.imageAnnotation.processingInformation" );

    if ( psProcessingInfo != nullptr ) {
        OGRSpatialReference oLL, oPrj;

        const char *pszEllipsoidName = CPLGetXMLValue(
            psProcessingInfo, "ellipsoidName", "" );
        const double minor_axis = CPLAtof(CPLGetXMLValue(
            psProcessingInfo, "ellipsoidSemiMinorAxis", "0.0" ));
        const double major_axis = CPLAtof(CPLGetXMLValue(
            psProcessingInfo, "ellipsoidSemiMajorAxis", "0.0" ));

        if ( EQUAL(pszEllipsoidName, "") || ( minor_axis == 0.0 ) ||
             ( major_axis == 0.0 ) )
        {
            CPLError(CE_Warning,CPLE_AppDefined,"Warning- incomplete"
                     " ellipsoid information.  Using wgs-84 parameters.\n");
            oLL.SetWellKnownGeogCS( "WGS84" );
            oPrj.SetWellKnownGeogCS( "WGS84" );
        }
        else if ( EQUAL( pszEllipsoidName, "WGS84" ) ) {
            oLL.SetWellKnownGeogCS( "WGS84" );
            oPrj.SetWellKnownGeogCS( "WGS84" );
        }
        else {
            const double inv_flattening = major_axis/(major_axis - minor_axis);
            oLL.SetGeogCS( "","",pszEllipsoidName, major_axis,
                           inv_flattening);
            oPrj.SetGeogCS( "","",pszEllipsoidName, major_axis,
                            inv_flattening);
        }

        CPLFree( poDS->pszGCPProjection );
        poDS->pszGCPProjection = nullptr;
        oLL.exportToWkt( &(poDS->pszGCPProjection) );
    }

/* -------------------------------------------------------------------- */
/*      Collect GCPs.                                                   */
/* -------------------------------------------------------------------- */
    CPLXMLNode *psGeoGrid =
        CPLGetXMLNode( psAnnotation,
                       "=product.geolocationGrid.geolocationGridPointList" );

    if( psGeoGrid != nullptr ) {
        /* count GCPs */
        poDS->nGCPCount = 0;

        for( CPLXMLNode *psNode = psGeoGrid->psChild; psNode != nullptr;
             psNode = psNode->psNext )
        {
            if( EQUAL(psNode->pszValue,"geolocationGridPoint") )
                poDS->nGCPCount++ ;
        }

        poDS->pasGCPList = reinterpret_cast<GDAL_GCP *>(
            CPLCalloc( sizeof(GDAL_GCP), poDS->nGCPCount ) );

        poDS->nGCPCount = 0;

        for( CPLXMLNode *psNode = psGeoGrid->psChild; psNode != nullptr;
             psNode = psNode->psNext )
        {
            GDAL_GCP *psGCP = poDS->pasGCPList + poDS->nGCPCount;

            if( !EQUAL(psNode->pszValue,"geolocationGridPoint") )
                continue;

            poDS->nGCPCount++ ;

            char szID[32];
            snprintf( szID, sizeof(szID), "%d", poDS->nGCPCount );
            psGCP->pszId = CPLStrdup( szID );
            psGCP->pszInfo = CPLStrdup("");
            psGCP->dfGCPPixel = CPLAtof(CPLGetXMLValue(psNode,"pixel","0"));
            psGCP->dfGCPLine = CPLAtof(CPLGetXMLValue(psNode,"line","0"));
            psGCP->dfGCPX = CPLAtof(CPLGetXMLValue(psNode,"longitude",""));
            psGCP->dfGCPY = CPLAtof(CPLGetXMLValue(psNode,"latitude",""));
            psGCP->dfGCPZ = CPLAtof(CPLGetXMLValue(psNode,"height",""));
        }
    }

    CPLDestroyXMLNode(psAnnotation);

/* -------------------------------------------------------------------- */
/*      Initialize any PAM information.                                 */
/* -------------------------------------------------------------------- */
    const CPLString osDescription = osMDFilename;

/* -------------------------------------------------------------------- */
/*      Initialize any PAM information.                                 */
/* -------------------------------------------------------------------- */
    poDS->SetDescription( osDescription );

    poDS->SetPhysicalFilename( osMDFilename );
    poDS->SetSubdatasetName( osDescription );

    poDS->TryLoadXML();

/* -------------------------------------------------------------------- */
/*      Check for overviews.                                            */
/* -------------------------------------------------------------------- */
    poDS->oOvManager.Initialize( poDS, ":::VIRTUAL:::" );

    return poDS;
}