/// <summary>
        /// Reads the imaging information stored in the metafile stream.
        /// </summary>
        /// <returns>The imaging data.</returns>
        protected override RasterImaging ReadImagingInternal()
        {
            ReadContent();

            // read the device data
            ImagingDevice device = ReadDeviceInternal();

            // time
            DateTime imagingDateTime = DateTime.Parse(_metadata["DATE_ACQUIRED"] + " " + _metadata["SCENE_CENTER_TIME"], CultureInfo.InvariantCulture.DateTimeFormat, DateTimeStyles.AssumeUniversal);

            // view
            Double incidenceAngle = Double.NaN;
            Double viewingAngle   = _metadata.ContainsKey("ROLL_ANGLE") ? Double.Parse(_metadata["ROLL_ANGLE"], NumberStyles.Float, CultureInfo.InvariantCulture.NumberFormat) : Double.NaN; // only Landsat 8 contains the roll angle property
            Double sunAzimuth     = Double.Parse(_metadata["SUN_AZIMUTH"], NumberStyles.Float, CultureInfo.InvariantCulture.NumberFormat);
            Double sunElevation   = Double.Parse(_metadata["SUN_ELEVATION"], NumberStyles.Float, CultureInfo.InvariantCulture.NumberFormat);

            // image location
            GeoCoordinate[] imageLocation = new GeoCoordinate[]
            {
                new GeoCoordinate(Double.Parse(_metadata["CORNER_UL_LAT_PRODUCT"], NumberStyles.Float, CultureInfo.InvariantCulture.NumberFormat),
                                  Double.Parse(_metadata["CORNER_UL_LON_PRODUCT"], NumberStyles.Float, CultureInfo.InvariantCulture.NumberFormat)),
                new GeoCoordinate(Double.Parse(_metadata["CORNER_UR_LAT_PRODUCT"], NumberStyles.Float, CultureInfo.InvariantCulture.NumberFormat),
                                  Double.Parse(_metadata["CORNER_UR_LON_PRODUCT"], NumberStyles.Float, CultureInfo.InvariantCulture.NumberFormat)),
                new GeoCoordinate(Double.Parse(_metadata["CORNER_LL_LAT_PRODUCT"], NumberStyles.Float, CultureInfo.InvariantCulture.NumberFormat),
                                  Double.Parse(_metadata["CORNER_LL_LON_PRODUCT"], NumberStyles.Float, CultureInfo.InvariantCulture.NumberFormat)),
                new GeoCoordinate(Double.Parse(_metadata["CORNER_LR_LAT_PRODUCT"], NumberStyles.Float, CultureInfo.InvariantCulture.NumberFormat),
                                  Double.Parse(_metadata["CORNER_LR_LON_PRODUCT"], NumberStyles.Float, CultureInfo.InvariantCulture.NumberFormat))
            };

            // band parameters
            List <RasterImagingBand> bandData = new List <RasterImagingBand>();

            Int32 numberOfBands = 0;

            Double[] solarIrradiance = null;

            switch (device.MissionNumber)
            {
            case 7:
                numberOfBands = 8;
                // solar irradiance is constant for Landsat 7, see: http://landsathandbook.gsfc.nasa.gov/pdfs/Landsat_Calibration_Summary_RSE.pdf
                solarIrradiance = new Double[] { 1997, 1812, 1533, 1039, 230.8, Double.NaN, 84.9, 1362 };
                break;

            case 8:
                numberOfBands = 11;
                // solar irradiance is not provided for Landsat 8, see: http://landsat.usgs.gov/ESUN.php
                solarIrradiance = Enumerable.Repeat(Double.NaN, 11).ToArray();
                break;
            }

            for (Int32 bandNumber = 1; bandNumber <= numberOfBands; bandNumber++)
            {
                String indexString = bandNumber.ToString();
                if (device.MissionNumber == 7 && bandNumber == 6) // Landsat 7 band 6 has high gain and low gain modes
                {
                    indexString += "_VCID_1";
                }

                Double maximumRadiance = Double.Parse(_metadata["RADIANCE_MAXIMUM_BAND_" + indexString], NumberStyles.Float, CultureInfo.InvariantCulture.NumberFormat);
                Double minimumRadiance = Double.Parse(_metadata["RADIANCE_MINIMUM_BAND_" + indexString], NumberStyles.Float, CultureInfo.InvariantCulture.NumberFormat);
                Double qCalMax         = Double.Parse(_metadata["QUANTIZE_CAL_MAX_BAND_" + indexString], NumberStyles.Float, CultureInfo.InvariantCulture.NumberFormat);
                Double qCalMin         = Double.Parse(_metadata["QUANTIZE_CAL_MIN_BAND_" + indexString], NumberStyles.Float, CultureInfo.InvariantCulture.NumberFormat);


                Double physicalGain = (maximumRadiance - minimumRadiance) / (qCalMax - qCalMin);
                Double physicalBias = minimumRadiance;

                // match the device band data
                ImagingDeviceBand deviceBand = null;

                if (device != null)
                {
                    deviceBand = device.Bands.FirstOrDefault(band => band.Description.Contains("BAND " + bandNumber));
                }

                if (deviceBand != null)
                {
                    bandData.Add(new RasterImagingBand(deviceBand.Description, physicalGain, physicalBias, solarIrradiance[bandNumber - 1], deviceBand.SpectralDomain, deviceBand.SpectralRange));
                }
                else // if no match is found
                {
                    bandData.Add(new RasterImagingBand("BAND " + bandNumber, physicalGain, physicalBias, solarIrradiance[bandNumber - 1], SpectralDomain.Undefined, null));
                }
            }

            RasterImaging imaging = new RasterImaging(device, imagingDateTime, GeoCoordinate.Undefined, imageLocation, incidenceAngle, viewingAngle, sunAzimuth, sunElevation, bandData);

            foreach (String key in _metadata.Keys)
            {
                if (_imagingPropertyKeys.Contains(key))
                {
                    imaging[key] = _metadata[key];
                }
            }

            return(imaging);
        }
 /// <summary>
 /// Creates a metafile reader for the specified metafile path and device.
 /// </summary>
 /// <param name="device">The imaging device.</param>
 /// <param name="path">The path of the metafile.</param>
 /// <returns>The valid metafile reader for the path if any; otherwise, <c>null</c>.</returns>
 /// <exception cref="System.ArgumentNullException">
 /// The path is null.
 /// or
 /// The device is null.
 /// </exception>
 /// <exception cref="System.ArgumentException">
 /// The path is empty.
 /// or
 /// The path is invalid.
 /// or
 /// The path is a zero-length string, contains only white space, or contains one or more invalid characters.
 /// or
 /// The path, file name, or both exceed the system-defined maximum length.
 /// </exception>
 /// <exception cref="System.UnauthorizedAccessException">
 /// The file on path is hidden.
 /// or
 /// The file on path is read-only.
 /// or
 /// The caller does not have the required permission for the path.
 /// </exception>
 /// <exception cref="System.NotSupportedException">The specified device is not supported.</exception>
 /// <exception cref="System.IO.FileNotFoundException">The metafile does not exist.</exception>
 public static GeoTiffMetafileReader CreateReader(ImagingDevice device, String path)
 {
     return(CreateReader(device, new Uri(path, UriKind.RelativeOrAbsolute)));
 }