ArcObjects Library Reference  

DMCIIRasterType

About the Create a custom raster type from the ground up for DMCII data Sample

[C#]

DMCIIRasterType.cs

using System;
using System.IO;
using System.Xml;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using ESRI.ArcGIS.ADF;
using ESRI.ArcGIS.DataSourcesRaster;
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.Carto;

/*
 * 
 * This sample shows how to implement a Custom Raster Type to provide support for DMCII data. 
 * Also provided is an optional test application to create a Geodatabase and a mosaic dataset 
 * and add data using the custom type.
 * The main interface to be implemented is the IRasterBuilder interface along with 
 * secondary interfaces such as IRasterBuilderInit (which provides access to the parent MD),
 * IPersistvariant (which implements persistence), IRasterBuilderInit2 and IRasterBuilder2 
 * (new interfaces added at 10.1).
 * A IRasterFactory implementation also needs to be created in order for the Raster type to 
 * show up in the list of Raster Types in the Add Rasters GP Tool. The factory is responsible 
 * for creating the raster type object and setting some properties on it. It also enables the 
 * use of the Raster Product.
 * 
 */


namespace SampleRasterType
{
    [Guid("5DEF8E3C-51E9-49af-A3BE-EF8C68A4BBBE")]
    [ClassInterface(ClassInterfaceType.None)]
    [ProgId("SampleRasterType.CustomRasterTypeFactory")]
    [ComVisible(true)]
    public class DMCIIRasterTypeFactory : IRasterTypeFactory
    {
        #region Private Members
        IStringArray myRasterTypeNames; // List of Raster Types that the factory can create.
        UID myUID; // UID for the DMCII Raster Type.
        #endregion

        #region IRasterTypeFactory Members

        public DMCIIRasterTypeFactory()
        {
            string rasterTypeName = "DMCII Raster Type";
            myRasterTypeNames = new StrArrayClass();
            myRasterTypeNames.Add(rasterTypeName);

            myUID = new UIDClass();
            myUID.Value = "{5DEF8E3C-51E9-49af-A3BE-EF8C68A4BBBE}";
        }

        public UID CLSID
        {
            get { return myUID; }
        }

        /// <summary>
        /// Create a Raster Type object given the name of the raster type (usually 
        /// the same name as the one in the UI list of raster types).
        /// </summary>
        /// <param name="RasterTypeName">Name of the Raster Type object to create.</param>
        /// <returns>The Raster type object.</returns>
        public IRasterType CreateRasterType(string RasterTypeName)
        {
            // Create a new RasterType object and its corresponding name object.
            IRasterType theRasterType = new RasterTypeClass();
            IRasterTypeName theRasterTypeName = new RasterTypeNameClass();
            theRasterTypeName.Name = RasterTypeName;
            theRasterType.FullName = (IName)theRasterTypeName;

            // Set the properties for the raster type object. These are shown in the 
            // 'General' tab of the raster type properties page.
            ((IRasterTypeProperties)theRasterType).Name = "DMCII Raster Type";
            ((IRasterTypeProperties)theRasterType).Description = "Raster Type for DMCII data.";
            ((IRasterTypeProperties)theRasterType).DataSourceFilter = "*.dim";
            ((IRasterTypeProperties)theRasterType).SupportsOrthorectification = true;

            // Create the Custom Raster Builder object
            IRasterBuilder customRasterBuilder = new DMCIIRasterBuilder();
            // Set the Raster Builder of theRasterType to the above created builder.
            theRasterType.RasterBuilder = customRasterBuilder;

            // Enable the use of the Raster Type as a Raster Product.
            ((IRasterTypeProperties2)theRasterType).IsSensorRasterType = true;

            #region Set Product Templates
            // Create a new array of templates if needed.
            if (theRasterType.ItemTemplates == null)
                theRasterType.ItemTemplates = new ItemTemplateArrayClass();

            // Add a 'Raw' template.
            IItemTemplate nullTemplate = new ItemTemplateClass();
            nullTemplate.Enabled = false;
            nullTemplate.Name = "Raw";
            ((IItemTemplate2)nullTemplate).IsSensorTemplate = true;
            ((IItemTemplate2)nullTemplate).SupportsEnhancement = false;
            theRasterType.ItemTemplates.Add(nullTemplate);

            // Add a 'Stretch' template. This is the default template.
            IItemTemplate strTemplate = new ItemTemplateClass();
            strTemplate.Enabled = true;
            strTemplate.Name = "Stretch";
            IRasterFunction stretchFunction = new StretchFunctionClass();
            IStretchFunctionArguments stretchFunctionArgs = new StretchFunctionArgumentsClass();
            stretchFunctionArgs.StretchType = esriRasterStretchType.esriRasterStretchMinimumMaximum;
            IRasterFunctionVariable rasterVar = new RasterFunctionVariableClass();
            rasterVar.IsDataset = true;
            rasterVar.Name = "MS";
            rasterVar.Aliases = new StrArrayClass();
            rasterVar.Aliases.Add("MS");
            rasterVar.Description = "Variable for input raster";
            stretchFunctionArgs.Raster = rasterVar;
            IRasterFunctionTemplate stretchFunctionTemplate = new RasterFunctionTemplateClass();
            stretchFunctionTemplate.Function = stretchFunction;
            stretchFunctionTemplate.Arguments = stretchFunctionArgs;
            strTemplate.RasterFunctionTemplate = stretchFunctionTemplate;
            ((IItemTemplate2)strTemplate).IsSensorTemplate = true;
            ((IItemTemplate2)strTemplate).SupportsEnhancement = true;
            theRasterType.ItemTemplates.Add(strTemplate);
            #endregion

            #region Set Product Types
            // Add Product types (called URI filters in the code).
            if (((IRasterTypeProperties)theRasterType).SupportedURIFilters == null)
                ((IRasterTypeProperties)theRasterType).SupportedURIFilters = new ArrayClass();
            // Create and setup URI Filters
            IItemURIFilter allFilter = new URIProductNameFilterClass();
            allFilter.Name = "All";
            allFilter.SupportsOrthorectification = true;
            allFilter.SupportedTemplateNames = new StrArrayClass();
            allFilter.SupportedTemplateNames.Add("Raw");
            allFilter.SupportedTemplateNames.Add("Stretch");
            IStringArray allProductNames = new StrArrayClass();
            allProductNames.Add("L1T");
            allProductNames.Add("L1R");
            ((IURIProductNameFilter)allFilter).ProductNames = allProductNames;

            // The L1T filter does not support orthorectification.
            IItemURIFilter l1tFilter = new URIProductNameFilterClass();
            l1tFilter.Name = "L1T";
            l1tFilter.SupportsOrthorectification = false;
            l1tFilter.SupportedTemplateNames = new StrArrayClass();
            l1tFilter.SupportedTemplateNames.Add("Raw");
            l1tFilter.SupportedTemplateNames.Add("Stretch");
            IStringArray l1tProductNames = new StrArrayClass();
            l1tProductNames.Add("L1T");
            ((IURIProductNameFilter)l1tFilter).ProductNames = l1tProductNames;

            IItemURIFilter l1rFilter = new URIProductNameFilterClass();
            l1rFilter.Name = "L1R";
            l1rFilter.SupportsOrthorectification = true;
            l1rFilter.SupportedTemplateNames = new StrArrayClass();
            l1rFilter.SupportedTemplateNames.Add("Raw");
            l1rFilter.SupportedTemplateNames.Add("Stretch");
            IStringArray l1rProductNames = new StrArrayClass();
            l1rProductNames.Add("L1R");
            ((IURIProductNameFilter)l1rFilter).ProductNames = l1rProductNames;

            // Add them to the supported uri filters list
            ((IRasterTypeProperties)theRasterType).SupportedURIFilters.Add(allFilter);
            ((IRasterTypeProperties)theRasterType).SupportedURIFilters.Add(l1tFilter);
            ((IRasterTypeProperties)theRasterType).SupportedURIFilters.Add(l1rFilter);
            // Set 'All' as default
            theRasterType.URIFilter = allFilter;
            #endregion

            return theRasterType;
        }

        /// <summary>
        /// Name of the Raster Type Factory
        /// </summary>
        public string Name
        {
            get { return "Custom Raster Type Factory"; }
        }

        /// <summary>
        /// Names of the Raster Types supported by the factory.
        /// </summary>
        public IStringArray RasterTypeNames
        {
            get { return myRasterTypeNames; }
        }
        #endregion

        #region COM Registration Function(s)
        [ComRegisterFunction()]
        static void Reg(string regKey)
        {
            ESRI.ArcGIS.ADF.CATIDs.RasterTypeFactory.Register(regKey);
        }

        [ComUnregisterFunction()]
        static void Unreg(string regKey)
        {
            ESRI.ArcGIS.ADF.CATIDs.RasterTypeFactory.Unregister(regKey);
        }
        #endregion
    }

    [Guid("316725CB-35F2-4159-BEBB-A1445ECE9CF1")]
    [ClassInterface(ClassInterfaceType.None)]
    [ProgId("SampleRasterType.CustomRasterType")]
    [ComVisible(true)]
    public class DMCIIRasterBuilder : IRasterBuilder, IRasterBuilderInit, IPersistVariant,
        IRasterBuilder2, IRasterBuilderInit2
    {
        #region Private Members
        // The Mosaic Dataset currently using the Raster Type.
        IMosaicDataset myMosaicDataset;
        // The default spatial reference to apply to added data (if no spatial reference exists).
        ISpatialReference myDefaultSpatialReference;

        // The Raster Type Operation object (usually a Raster Type object).
        IRasterTypeOperation myRasterTypeOperation;
        // The Raster Type Properties.
        IPropertySet myRasterTypeProperties;

        // Array to fill with ItemURI's.
        IItemURIArray myURIArray;

        // GeoTransform helper object.
        IGeoTransformationHelper myGeoTransformationHelper;
        // Flags to specify whether the Raster Type can merge items and 
        bool myCanMergeItems;
        // if it has merged item.
        bool myMergeItems;

        // Mapping from field names to names or properties in the item propertyset.
        IPropertySet myAuxiliaryFieldAlias;
        // Fields to add to the Mosaic Dataset when items are added through this Raster Type.
        IFields myAuxiliaryFields;

        ITrackCancel myTrackCancel;
        UID myUID; // UID for the Custom Builder.

        // The current dimap file being processed.
        string myCurrentDimFile;
        #endregion

        public DMCIIRasterBuilder()
        {
            myMosaicDataset = null;
            myDefaultSpatialReference = null;
            myRasterTypeOperation = null;
            myRasterTypeProperties = null;
            myTrackCancel = null;

            myURIArray = null;

            myGeoTransformationHelper = null;
            myCanMergeItems = false;
            myMergeItems = false;

            myAuxiliaryFieldAlias = null;
            myAuxiliaryFields = null;

            myUID = new UIDClass();
            myUID.Value = "{316725CB-35F2-4159-BEBB-A1445ECE9CF1}";
        }

        #region IRasterBuilder Members

        /// <summary>
        /// This defines a mapping from field names in the attribute table of the mosaic to
        /// properties in the property set associated with the dataset, incase a user wants 
        /// specify fields which are different from the property in the dataset.
        /// e.g. The field CloudCover may map to a property called C_C in the dataset built 
        /// by the builder.
        /// </summary>
        public IPropertySet AuxiliaryFieldAlias
        {
            get
            {
                return myAuxiliaryFieldAlias;
            }
            set
            {
                myAuxiliaryFieldAlias = value;
            }
        }

        /// <summary>
        /// Specify fields if necessary to be added to the Mosaic Dataset when 
        /// items are added throug hthis Raster Type.
        /// </summary>
        public IFields AuxiliaryFields
        {
            get
            {
                if (myAuxiliaryFields == null)
                {
                    myAuxiliaryFields = new FieldsClass();
                    AddFields(myAuxiliaryFields);
                }
                return myAuxiliaryFields;
            }
            set
            {
                myAuxiliaryFields = value;
            }
        }

        /// <summary>
        /// Get a crawler recommended by the Raster Type based on the data srouce properties provided.
        /// </summary>
        /// <param name="pDataSourceProperties">Data source properties.</param>
        /// <returns>Data source crawler recommended by the raster type.</returns>
        public IDataSourceCrawler GetRecommendedCrawler(IPropertySet pDataSourceProperties)
        {
            try
            {
                // This is usually a file crawler because it can crawl directories as well, unless
                // special types of data needs to be crawled.
                IDataSourceCrawler myCrawler = new FileCrawlerClass();
                ((IFileCrawler)myCrawler).Path = Convert.ToString(pDataSourceProperties.GetProperty("Source"));
                ((IFileCrawler)myCrawler).Recurse = Convert.ToBoolean(pDataSourceProperties.GetProperty("Recurse"));
                myCrawler.Filter = Convert.ToString(pDataSourceProperties.GetProperty("Filter"));
                if (myCrawler.Filter == null || myCrawler.Filter == "")
                    myCrawler.Filter = "*.dim";
                return myCrawler;
            }
            catch (Exception)
            {
                throw;
            }
        }

        /// <summary>
        /// Prepare the Raster Type for generating item Unique Resource Identifier (URI)
        /// </summary>
        /// <param name="pCrawler">Crawler to use to generate the item URI's</param>
        public void BeginConstruction(IDataSourceCrawler pCrawler)
        {
            myURIArray = new ItemURIArrayClass();
        }

        /// <summary>
        /// Construct a Unique Resource Identifier (URI)
        /// for each crawler item
        /// </summary>
        /// <param name="crawlerItem">Crawled Item from which the URI is generated</param>
        public void ConstructURIs(object crawlerItem)
        {
            myCurrentDimFile = (string)crawlerItem;
        }

        /// <summary>
        /// Finish construction of the URI's
        /// </summary>
        /// <returns>Array containing finised URI's</returns>
        public IItemURIArray EndConstruction()
        {
            return myURIArray;
        }

        /// <summary>
        /// Generate the next URI.
        /// </summary>
        /// <returns>The URI generated.</returns>
        public IItemURI GetNextURI()
        {
            IItemURI newURI = null;
            try
            {
                // Check to see if the item cralwed is a .dim file.
                if (myCurrentDimFile != "" && myCurrentDimFile != null && myCurrentDimFile.EndsWith(".dim"))
                {
                    // Create a new Dimap Parser obect and item uri.
                    DiMapParser myDimParser = new DiMapParser(myCurrentDimFile);
                    newURI = new ItemURIClass();
                    // Set the display name, Group, Product Name, Tag and Key.
                    newURI.DisplayName = System.IO.Path.GetFileName(myCurrentDimFile);
                    newURI.Group = System.IO.Path.GetFileNameWithoutExtension(myCurrentDimFile);
                    newURI.Key = myCurrentDimFile;
                    newURI.ProductName = myDimParser.ProductType;
                    newURI.Tag = "MS";
                    // Set the timestamp of the dimfile as source time stamp. This helps 
                    // with synchronization later.
                    IRasterTypeEnvironment myEnv = new RasterTypeEnvironmentClass();
                    DateTime dimTS = myEnv.GetTimeStamp(myCurrentDimFile);
                    newURI.SourceTimeStamp = dimTS;

                    myDimParser = null;
                    myCurrentDimFile = "";
                    myURIArray.Add(newURI);
                }
            }
            catch (Exception)
            {
                throw;
            }
            return newURI;
        }

        /// <summary>
        /// Build the Builder Item which includes the function raster dataset and its footprint 
        /// given the ItemURI.
        /// </summary>
        /// <param name="pItemURI">ItemURi to use to build the Builder Item.</param>
        /// <returns>The builder item.</returns>
        public IBuilderItem Build(IItemURI pItemURI)
        {
            try
            {
                // Create a new parser object and builder item.
                DiMapParser myDimParser = new DiMapParser(pItemURI.Key);
                IBuilderItem currItem = new BuilderItemClass();

                // Set Category and URI
                currItem.Category = esriRasterCatalogItemCategory.esriRasterCatalogItemCategoryPrimary;
                currItem.URI = pItemURI;

                // Set FunctionRasterDataset
                IFunctionRasterDataset inputFrd = GetFRD(myDimParser, pItemURI);
                currItem.Dataset = inputFrd;
                // Set band information for the function dataset including names, wavelengths and stats if available.
                SetBandProperties((IDataset)inputFrd, myDimParser);

                // Set Footprint
                IGeoDataset geoDset = (IGeoDataset)inputFrd;
                // Set it to the current raster extent first. If the raster has no 
                // spatial reference, the extents will be in pixel space.
                currItem.Footprint = (IGeometry)geoDset.Extent;
                // The get the footprint from the dim file is it exists.
                currItem.Footprint = GetFootprint(myDimParser);

                // Set Properties. These properties are used to fill the Auxiliary Fields 
                // defined earlier and also key properties if the names are correct.
                IPropertySet propSet = currItem.Dataset.Properties;
                if (null == propSet)
                    propSet = new PropertySetClass();
                double sunAzimuth = Convert.ToDouble(myDimParser.SunAzimuth);
                double sunElevation = Convert.ToDouble(myDimParser.SunElevation);
                double sensorAzimuth = Convert.ToDouble(myDimParser.SensorAzimuth);
                double sensorElevation = 180 - Convert.ToDouble(myDimParser.IncidenceAngle);
                string acqDate = myDimParser.AcquisitionDate;
                string acqTime = myDimParser.AcquisitionTime;
                // Create a time object from the provided date and time.
                ITime acqDateTimeObj = new TimeClass();
                acqDateTimeObj.SetFromTimeString(esriTimeStringFormat.esriTSFYearThruSubSecondWithDash,
                    acqDate + " " + acqTime);
                // and obtain a DateTime object to set as value of the property. This ensures the 
                // field displays the value correctly.
                DateTime acqDateTimeFieldVal = acqDateTimeObj.QueryOleTime();

                propSet.SetProperty("AcquisitionDate", acqDateTimeFieldVal);
                propSet.SetProperty("SensorName", myDimParser.MetadataProfile);
                propSet.SetProperty("SunAzimuth", sunAzimuth);
                propSet.SetProperty("SunElevation", sunElevation);
                propSet.SetProperty("SatAzimuth", sensorAzimuth);
                propSet.SetProperty("SatElevation", sensorElevation);
                currItem.Dataset.Properties = propSet;

                return currItem;
            }
            catch (Exception exc)
            {
                throw exc;
            }
        }

        /// <summary>
        /// Flag to specify whether the Raster Builder can build items in place.
        /// </summary>
        public bool CanBuildInPlace
        {
            get { return false; }
        }

        /// <summary>
        /// Check if the item provided is "stale" or not valid
        /// </summary>
        /// <param name="pItemURI">URI for the item to be checked</param>
        /// <returns>Flag to specify whether the item is stale or not.</returns>
        public bool IsStale(IItemURI pItemURI)
        {
            try
            {
                IRasterTypeEnvironment myEnv = new RasterTypeEnvironmentClass();
                DateTime currDimTS = myEnv.GetTimeStamp(pItemURI.Key);
                return pItemURI.SourceTimeStamp != currDimTS;
            }
            catch (Exception)
            {
                throw;
            }
        }


        /// <summary>
        /// Properties associated with the Raster Type
        /// </summary>
        public IPropertySet Properties
        {
            get
            {
                if (myRasterTypeProperties == null)
                    myRasterTypeProperties = new PropertySetClass();
                return myRasterTypeProperties;
            }
            set
            {
                myRasterTypeProperties = value;
            }
        }

        /// <summary>
        /// Sets band properties on a given dataset including stats, band names and wavelengths.
        /// </summary>
        /// <param name="dataset">The dataset to set properties on.</param>
        /// <param name="dimParser">Dimap parser to read properties from.</param>
        private void SetBandProperties(IDataset dataset, DiMapParser dimParser)
        {
            try
            {
                // Set band band props.
                IRasterKeyProperties rasterKeyProps = (IRasterKeyProperties)dataset;
                IRasterBandCollection rasterBandColl = (IRasterBandCollection)dataset;
                int imageNumBands = ((IFunctionRasterDataset)dataset).RasterInfo.BandCount;
                int dinNumBands = dimParser.NumBands;
                int[] bandIndexes = new int[dinNumBands];
                IStringArray bandNames = new StrArrayClass();
                for (int i = 0; i < dinNumBands; ++i)
                {
                    // Get band index for the first band.
                    bandIndexes[i] = Convert.ToInt16(dimParser.GetBandIndex(i));
                    // Validate band index.
                    if (bandIndexes[i] > 0 && bandIndexes[i] <= imageNumBands)
                    {
                        // Get Band Name for the index.
                        bandNames.Add(dimParser.GetBandDesc(bandIndexes[i]));
                        // Get Band stats for the index.
                        IRasterStatistics bandStats = new RasterStatisticsClass();
                        bandStats.Minimum = Convert.ToDouble(dimParser.GetBandStatMin(bandIndexes[i]));
                        bandStats.Maximum = Convert.ToDouble(dimParser.GetBandStatMax(bandIndexes[i]));
                        bandStats.Mean = Convert.ToDouble(dimParser.GetBandStatMean(bandIndexes[i]));
                        bandStats.StandardDeviation = Convert.ToDouble(dimParser.GetBandStatStdDev(bandIndexes[i]));
                        // Set stats on the dataset.
                        ((IRasterBandEdit2)rasterBandColl.Item(bandIndexes[i] - 1)).AlterStatistics(bandStats);
                        // Set Band Name and wavelengths according to the name.
                        rasterKeyProps.SetBandProperty("BandName", (bandIndexes[i] - 1), bandNames.get_Element(i));
                        SetBandWavelengths(dataset, (bandIndexes[i] - 1));
                        // Refresh dataset so changes are saved.
                        ((IRasterDataset3)dataset).Refresh();
                    }
                }
            }
            catch (Exception exc)
            {
                string error = exc.Message;
            }
        }

        /// <summary>
        /// Set the wavelengths corresponding to the band name.
        /// </summary>
        private void SetBandWavelengths(IDataset dataset, int bandIndex)
        {
            IRasterKeyProperties rasterKeyProps = (IRasterKeyProperties)dataset;
            IRasterBandCollection rasterBandColl = (IRasterBandCollection)dataset;
            string bandName = (string)rasterKeyProps.GetBandProperty("BandName", bandIndex);
            // Set wavelengths for the bands
            switch (bandName.ToLower())
            {
                case "red":
                    {
                        rasterKeyProps.SetBandProperty("WavelengthMin", bandIndex, 630);
                        rasterKeyProps.SetBandProperty("WavelengthMax", bandIndex, 690);
                    }
                    break;

                case "green":
                    {
                        rasterKeyProps.SetBandProperty("WavelengthMin", bandIndex, 520);
                        rasterKeyProps.SetBandProperty("WavelengthMax", bandIndex, 600);
                    }
                    break;

                case "nir":
                case "nearinfrared":
                    {
                        rasterKeyProps.SetBandProperty("WavelengthMin", bandIndex, 770);
                        rasterKeyProps.SetBandProperty("WavelengthMax", bandIndex, 900);
                    }
                    break;
            }
        }

        //private IGeometry GetFootprint(DiMapParser dimParser)
        //{
        //    IGeometry currFootprint = null;
        //    dimParser.ResetVertexCount();
        //    string xs = "";
        //    string ys = "";
        //    string rows = "";
        //    string cols = "";
        //    double minX = 10000000000.0;
        //    double maxX = -1000000000.00;
        //    double minY = 1000000000.00;
        //    double maxY = -1000000000.00;
        //    double minRow = 1000000000.00;
        //    double maxRow = -1000000000.0;
        //    double minCol = 1000000000.00;
        //    double maxCol = -1000000000.0;
        //    double x = 0.0;
        //    double y = 0.0;
        //    double row = 0.0;
        //    double col = 0.0;

        //    while (dimParser.GetNextVertex(out xs, out ys, out rows, out cols))
        //    {
        //        x = Convert.ToDouble(xs);
        //        y = Convert.ToDouble(ys);
        //        row = Convert.ToDouble(rows);
        //        col = Convert.ToDouble(cols);

        //        if (x < minX)
        //            minX = x;
        //        if (x > maxX)
        //            maxX = x;

        //        if (y < minY)
        //            minY = y;
        //        if (y > maxY)
        //            maxY = y;

        //        if (row < minRow)
        //            minRow = row;
        //        if (row > maxRow)
        //            maxRow = row;

        //        if (col < minCol)
        //            minCol = col;
        //        if (col > maxCol)
        //            maxCol = col;

        //        x = 0.0;
        //        y = 0.0;
        //        row = 0.0;
        //        col = 0.0;
        //        xs = "";
        //        ys = "";
        //        rows = "";
        //        cols = "";
        //    }
        //    x = Convert.ToDouble(xs);
        //    y = Convert.ToDouble(ys);
        //    row = Convert.ToDouble(rows);
        //    col = Convert.ToDouble(cols);

        //    if (x < minX)
        //        minX = x;
        //    if (x > maxX)
        //        maxX = x;

        //    if (y < minY)
        //        minY = y;
        //    if (y > maxY)
        //        maxY = y;

        //    if (row < minRow)
        //        minRow = row;
        //    if (row > maxRow)
        //        maxRow = row;

        //    if (col < minCol)
        //        minCol = col;
        //    if (col > maxCol)
        //        maxCol = col;

        //    currFootprint = new PolygonClass();
        //    IPointCollection currPointColl = (IPointCollection)currFootprint;
        //    IEnvelope rectEnvelope = new EnvelopeClass();
        //    rectEnvelope.PutCoords(minX, minY, maxX, maxY);
        //    ISegmentCollection segmentCollection = (ISegmentCollection)currFootprint;
        //    segmentCollection.SetRectangle(rectEnvelope);

        //    // Get Srs
        //    int epsgcode = Convert.ToInt32((dimParser.SrsCode.Split(':'))[1]);
        //    ISpatialReferenceFactory3 srsfactory = new SpatialReferenceEnvironmentClass();
        //    ISpatialReference dimSrs = srsfactory.CreateSpatialReference(epsgcode);
        //    ISpatialReferenceResolution srsRes = (ISpatialReferenceResolution)dimSrs;
        //    srsRes.ConstructFromHorizon();
        //    srsRes.SetDefaultXYResolution();
        //    ((ISpatialReferenceTolerance)dimSrs).SetDefaultXYTolerance();
        //    currFootprint.SpatialReference = dimSrs;

        //    #region Commented
        //    //IEnvelope extent = new EnvelopeClass();
        //    //extent.XMin = geoDset.Extent.XMin;
        //    //extent.XMax = geoDset.Extent.XMax;
        //    //extent.YMin = geoDset.Extent.YMin;
        //    //extent.YMax = geoDset.Extent.YMax;
        //    //extent.SpatialReference = geoDset.SpatialReference;
        //    //extent.Width = inputFrd.RasterInfo.Extent.Width;
        //    //extent.Height = inputFrd.RasterInfo.Extent.Height;
        //    //currItem.Footprint = (IGeometry)extent;

        //    //myDimParser.ResetVertexCount();
        //    //string x = "";
        //    //string y = "";
        //    //string row = "";
        //    //string col = "";
        //    //IGeometry currFootprint = new PolygonClass();
        //    //IPointCollection currPointColl = (IPointCollection)currFootprint;

        //    // Creating a polygon!!!

        //    ////Build a polygon from a sequence of points. 
        //    ////Add arrays of points to a geometry using the IGeometryBridge2 interface on the 
        //    ////GeometryEnvironment singleton object.
        //    //IGeometryBridge2 geometryBridge2 = new GeometryEnvironmentClass();
        //    //IPointCollection4 pointCollection4 = new PolygonClass();

        //    ////TODO:
        //    ////pointCollection4.SpatialReference = 'Define the spatial reference of the new polygon.

        //    //WKSPoint[] aWKSPointBuffer = null;
        //    //long cPoints = 4; //The number of points in the first part.
        //    //aWKSPointBuffer = new WKSPoint[System.Convert.ToInt32(cPoints - 1) + 1];

        //    ////TODO:
        //    ////aWKSPointBuffer = 'Read cPoints into the point buffer.

        //    //geometryBridge2.SetWKSPoints(pointCollection4, ref aWKSPointBuffer);

        //    //myDimParser.GetNextVertex(out x, out y, out col, out row);
        //    //IPoint currPoint1 = new PointClass();
        //    //currPoint1.X = Convert.ToDouble(x);
        //    //currPoint1.Y = Convert.ToDouble(y);
        //    //myDimParser.GetNextVertex(out x, out y, out col, out row);
        //    //IPoint currPoint2 = new PointClass();
        //    //currPoint1.X = Convert.ToDouble(x);
        //    //currPoint1.Y = Convert.ToDouble(y);
        //    //myDimParser.GetNextVertex(out x, out y, out col, out row);
        //    //IPoint currPoint3 = new PointClass();
        //    //currPoint1.X = Convert.ToDouble(x);
        //    //currPoint1.Y = Convert.ToDouble(y);
        //    //myDimParser.GetNextVertex(out x, out y, out col, out row);
        //    //IPoint currPoint4 = new PointClass();
        //    //currPoint1.X = Convert.ToDouble(x);
        //    //currPoint1.Y = Convert.ToDouble(y);
        //    //object refPoint1 = (object)currPoint1;
        //    //object refPoint2 = (object)currPoint2;
        //    //object refPoint3 = (object)currPoint3;
        //    //object refPoint4 = (object)currPoint4;
        //    //currPointColl.AddPoint(currPoint1, ref refPoint4, ref refPoint2);
        //    //currPointColl.AddPoint(currPoint2, ref refPoint1, ref refPoint3);
        //    //currPointColl.AddPoint(currPoint3, ref refPoint2, ref refPoint4);
        //    //currPointColl.AddPoint(currPoint4, ref refPoint3, ref refPoint1);
        //    //((IPolygon)currFootprint).Close();
        //    //currFootprint.SpatialReference = dimSrs;
        //    #endregion
        //    return currFootprint;
        //}

        /// <summary>
        /// Get the footprint from the dimap file if it exists.
        /// </summary>
        /// <param name="dimParser">Dimap file parser.</param>
        /// <returns>Footprint geomtry.</returns>
        private IGeometry GetFootprint(DiMapParser dimParser)
        {
            IGeometry currFootprint = null;
            dimParser.ResetVertexCount();
            string xs = "";
            string ys = "";
            double minX = 10000000000.0;
            double maxX = -1000000000.00;
            double minY = 1000000000.00;
            double maxY = -1000000000.00;
            double x = 0.0;
            double y = 0.0;
            string units = dimParser.ProductType;
            if (units.ToLower() == "L1T".ToLower())
                units = "M";
            else if (units.ToLower() == "L1R".ToLower())
                units = "Deg";
            // Get vertices from the dimap file and figure out the min,max.
            while (dimParser.GetNextVertex2(out xs, out ys, units))
            {
                x = Convert.ToDouble(xs);
                y = Convert.ToDouble(ys);

                if (x < minX)
                    minX = x;
                if (x > maxX)
                    maxX = x;

                if (y < minY)
                    minY = y;
                if (y > maxY)
                    maxY = y;

                x = 0.0;
                y = 0.0;
                xs = "";
                ys = "";
            }
            x = Convert.ToDouble(xs);
            y = Convert.ToDouble(ys);

            if (x < minX)
                minX = x;
            if (x > maxX)
                maxX = x;

            if (y < minY)
                minY = y;
            if (y > maxY)
                maxY = y;

            // create a new polygon and fill it using the vertices calculated.
            currFootprint = new PolygonClass();
            IPointCollection currPointColl = (IPointCollection)currFootprint;
            IEnvelope rectEnvelope = new EnvelopeClass();
            rectEnvelope.PutCoords(minX, minY, maxX, maxY);
            ISegmentCollection segmentCollection = (ISegmentCollection)currFootprint;
            segmentCollection.SetRectangle(rectEnvelope);

            // Get Srs from the dim file and set it on the footprint.
            int epsgcode = Convert.ToInt32((dimParser.SrsCode.Split(':'))[1]);
            ISpatialReferenceFactory3 srsfactory = new SpatialReferenceEnvironmentClass();
            ISpatialReference dimSrs = srsfactory.CreateSpatialReference(epsgcode);
            ISpatialReferenceResolution srsRes = (ISpatialReferenceResolution)dimSrs;
            srsRes.ConstructFromHorizon();
            srsRes.SetDefaultXYResolution();
            ((ISpatialReferenceTolerance)dimSrs).SetDefaultXYTolerance();
            currFootprint.SpatialReference = dimSrs;
            return currFootprint;
        }

        /// <summary>
        /// Create the function raster dataset from the source images.
        /// </summary>
        /// <param name="dimPar">Parser for the dimap file.</param>
        /// <param name="pItemURI">ItemURi to use.</param>
        /// <returns>Function raster dataset created.</returns>
        private IFunctionRasterDataset GetFRD(DiMapParser dimPar, IItemURI pItemURI)
        {
            IFunctionRasterDataset opFrd = null;
            try
            {
                Type factoryType = Type.GetTypeFromProgID("esriDataSourcesRaster.RasterWorkspaceFactory");
                IWorkspaceFactory workspaceFactory = (IWorkspaceFactory)Activator.CreateInstance(factoryType);
                IWorkspace workspace = workspaceFactory.OpenFromFile(System.IO.Path.GetDirectoryName(pItemURI.Key), 0);
                IRasterWorkspace rasterWorkspace = (IRasterWorkspace)workspace;
                // Open the tif file associated with the .dim file as a raster dataset.
                string imagePath = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(pItemURI.Key),
                    pItemURI.Group + ".tif");
                string rpcPath = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(pItemURI.Key),
                    pItemURI.Group + ".rpc");
                IRasterDataset inputRasterDataset = null;
                if (File.Exists(imagePath))
                    inputRasterDataset = rasterWorkspace.OpenRasterDataset(pItemURI.Group + ".tif");
                else
                    return null;

                IFunctionRasterDataset intermedFrd = null;
                // If the file opes successfully, add a RasterInfo function on top.
                if (inputRasterDataset != null)
                {
                    // Create an Identity function dataset to get the raster info.
                    IRasterFunction identityFunction = new IdentityFunctionClass();
                    IFunctionRasterDataset idFrd = new FunctionRasterDatasetClass();
                    idFrd.Init(identityFunction, inputRasterDataset);
                    // Create a raster info function dataset.
                    IRasterFunction rasterInfoFunction = new RasterInfoFunctionClass();
                    IRasterInfoFunctionArguments rasterInfoFuncArgs = new RasterInfoFunctionArgumentsClass();
                    rasterInfoFuncArgs.Raster = inputRasterDataset;
                    rasterInfoFuncArgs.RasterInfo = idFrd.RasterInfo;
                    intermedFrd = new FunctionRasterDatasetClass();
                    intermedFrd.Init(rasterInfoFunction, rasterInfoFuncArgs);
                }
                else
                    return null;
                // Check if there is an RPC file associated with the image. If so
                // then add a geometric function to apply the rpc xform.
                if (File.Exists(rpcPath))
                    opFrd = ApplyRPC(rpcPath, (IRasterDataset)intermedFrd);

                // If no rpc pars exist or applying rpc fails, use the intermediate 
                // function raster dataset created.
                if (opFrd == null)
                    opFrd = intermedFrd;
                
                //IRasterFunction ebFunction = new ExtractBandFunctionClass();
                //IRasterFunctionArguments ebFuncArgs = new ExtractBandFunctionArgumentsClass();
                //ILongArray bandIDs = new LongArrayClass();
                //bandIDs.Add(2);
                //bandIDs.Add(1);
                //bandIDs.Add(0);
                ////bandIDs.Add(4);
                //((IExtractBandFunctionArguments)ebFuncArgs).BandIDs = bandIDs;
                //((IExtractBandFunctionArguments)ebFuncArgs).Raster = inputRasterDataset;
                //opFrd = new FunctionRasterDatasetClass();
                //opFrd.Init(ebFunction, ebFuncArgs);

                //if (opFrd == null)
                //{
                //    IRasterFunction identityFunction = new IdentityFunctionClass();
                //    opFrd = new FunctionRasterDatasetClass();
                //    opFrd.Init(identityFunction, inputRasterDataset);
                //}
            }
            catch (Exception exc)
            {
                string error = exc.Message;
            }
            return opFrd;
        }

        /// <summary>
        /// Parse the RPC parameters file associated with the image and create an RPCXform
        /// to bea applied to the inputDataset as a geomtric function.
        /// </summary>
        /// <param name="rpcPath">Path to the rpc parameters file.</param>
        /// <param name="inputDataset">Input dataset to apply the xform on.</param>
        /// <returns>Function raster dataset created.</returns>
        private IFunctionRasterDataset ApplyRPC(string rpcPath, IRasterDataset inputDataset)
        {
            IFunctionRasterDataset opFrd = null;
            try
            {
                // Open the RPC file and create Geometric transform
                IGeodataXform finalXForm = null;
                IRPCXform rpcXForm = GetRPCXForm(rpcPath);

                IFunctionRasterDataset idFrd = null;
                if (!(inputDataset is IFunctionRasterDataset))
                {
                    IRasterFunction identityFunction = new IdentityFunctionClass();
                    idFrd = new FunctionRasterDatasetClass();
                    idFrd.Init(identityFunction, inputDataset);
                }
                else
                    idFrd = (IFunctionRasterDataset)inputDataset;

                IRasterInfo datasetRasterInfo = idFrd.RasterInfo;
                IEnvelope datasetExtent = datasetRasterInfo.Extent;
                ISpatialReference datasetSrs = ((IGeoDataset)idFrd).SpatialReference;

                long dRows = datasetRasterInfo.Height;
                long dCols = datasetRasterInfo.Width;
                IEnvelope pixelExtent = new EnvelopeClass();
                pixelExtent.PutCoords(-0.5, 0.5 - dRows, -0.5 + dCols, 0.5);

                bool noAffineNeeded = ((IClone)pixelExtent).IsEqual((IClone)datasetExtent);
                if (!noAffineNeeded)
                {
                    // Tranform ground space to pixel space.
                    IAffineTransformation2D affineXform = new AffineTransformation2DClass();
                    affineXform.DefineFromEnvelopes(datasetExtent, pixelExtent);
                    IGeometricXform geoXform = new GeometricXformClass();
                    geoXform.Transformation = affineXform;
                    finalXForm = geoXform;
                }

                // Transform from pixel space back to ground space to set as the forward transform.
                IEnvelope groundExtent = ((IGeoDataset)idFrd).Extent;
                groundExtent.Project(datasetSrs);
                IAffineTransformation2D affineXform2 = new AffineTransformation2DClass();
                affineXform2.DefineFromEnvelopes(pixelExtent, groundExtent);
                IGeometricXform forwardXForm = new GeometricXformClass();
                forwardXForm.Transformation = affineXform2;
                rpcXForm.ForwardXform = forwardXForm;

                // Create the composite transform that changes ground values to pixel space
                // then applies the rpc transform which will transform them back to ground space.
                ICompositeXform compositeXForm = new CompositeXformClass();
                compositeXForm.Add(finalXForm);
                compositeXForm.Add(rpcXForm);
                finalXForm = (IGeodataXform)compositeXForm;

                // Then apply the transform on the raster using the geometric function.
                if (finalXForm != null)
                {
                    IRasterFunction geometricFunction = new GeometricFunctionClass();
                    IGeometricFunctionArguments geometricFunctionArgs = null;
                    // Get the geomtric function arguments if supplied by the user (in the UI).
                    if (myRasterTypeOperation != null &&
                        ((IRasterTypeProperties)myRasterTypeOperation).OrthorectificationParameters != null)
                        geometricFunctionArgs =
                        ((IRasterTypeProperties)myRasterTypeOperation).OrthorectificationParameters;
                    else
                        geometricFunctionArgs = new GeometricFunctionArgumentsClass();
                    // Append the xform to the existing ones from the image.
                    geometricFunctionArgs.AppendGeodataXform = true;
                    geometricFunctionArgs.GeodataXform = finalXForm;
                    geometricFunctionArgs.Raster = inputDataset;
                    opFrd = new FunctionRasterDatasetClass();
                    opFrd.Init(geometricFunction, geometricFunctionArgs);
                }
                return opFrd;
            }
            catch (Exception exc)
            {
                string error = exc.Message;
                return opFrd;
            }
        }

        /// <summary>
        /// Create an RPCXForm from a text file containing parameters.
        /// </summary>
        /// <param name="rpcFilePath">Text file containing the parameters.</param>
        /// <returns>The RPCXForm generated.</returns>
        private IRPCXform GetRPCXForm(string rpcFilePath)
        {
            try
            {
                // Array for parameters.
                double[] RPC = new double[90];
                // Propertyset to store properties as backup.
                //IPropertySet coefficients = new PropertySetClass();

                #region Parse RPC text file
                // Use the stream reader to open the file and read lines from it.
                using (StreamReader sr = new StreamReader(rpcFilePath))
                {
                    string line;
                    int lineNumber = 0;

                    while ((line = sr.ReadLine()) != null)
                    {
                        ++lineNumber;
                        try
                        {
                            // Split the line into tokens based on delimiters
                            char[] delimiters = { ':', ' ' };
                            string[] tokens = line.Split(delimiters, System.StringSplitOptions.RemoveEmptyEntries);
                            int numTokens = tokens.GetLength(0);
                            if (numTokens > 1)
                            {
                                string currPar = tokens[0];
                                double currValue = Convert.ToDouble(tokens[1]);
                                // Convert the Value to a double and store in the array.
                                RPC[(lineNumber - 1)] = currValue;
                                // Store the property and the value in the propertyset to lookup later if needed.
                                //coefficients.SetProperty(currPar, currValue);
                                // Read units for conversion if needed
                                //string currUnit = tokens[2];
                            }
                            else
                                Console.WriteLine("Could not parse line " + lineNumber.ToString());
                        }
                        catch (Exception ex)
                        {
                            Console.Write(ex.ToString());
                        }
                    }
                    sr.Close();
                }
                #endregion

                // Create the new RPCXForm from the parameter array.
                IRPCXform myRPCXform = (IRPCXform)new RPCXform();
                object rpcCoeffs = ((object)RPC);
                myRPCXform.DefineFromCoefficients(ref rpcCoeffs);

                return myRPCXform;
            }
            catch (Exception exc)
            {
                string error = exc.Message;
                throw exc;
            }
        }

        /// <summary>
        /// Create new fields to add to the mosaic dataset attribute table.
        /// </summary>
        /// <param name="myFields">Fields to be added.</param>
        private void AddFields(IFields myFields)
        {
            // Create a new field object
            IField pField = new FieldClass();
            // Set the field editor for this field
            IFieldEdit objectIDFieldEditor = (IFieldEdit)pField;
            // Set the name and alias of the field
            objectIDFieldEditor.Name_2 = "SensorName"; 
            objectIDFieldEditor.AliasName_2 = "Sensor Name";
            // Set the type of the field
            objectIDFieldEditor.Type_2 = esriFieldType.esriFieldTypeString;
            // Add the newly created field to list of existing fields
            IFieldsEdit fieldsEditor = (IFieldsEdit)myFields;
            fieldsEditor.AddField(pField);

            // Create a new field object
            pField = new FieldClass();
            // Set the field editor for this field
            objectIDFieldEditor = (IFieldEdit)pField;
            // Set the name and alias of the field
            objectIDFieldEditor.Name_2 = "AcquisitionDate"; 
            objectIDFieldEditor.AliasName_2 = "Acquisition Date";
            // Set the type of the field
            objectIDFieldEditor.Type_2 = esriFieldType.esriFieldTypeDate;
            fieldsEditor.AddField(pField);

            // Create a new field object
            pField = new FieldClass();
            // Set the field editor for this field
            objectIDFieldEditor = (IFieldEdit)pField;
            // Set the name and alias of the field
            objectIDFieldEditor.Name_2 = "SunAzimuth";
            objectIDFieldEditor.AliasName_2 = "Sun Azimuth";
            // Set the type of the field
            objectIDFieldEditor.Type_2 = esriFieldType.esriFieldTypeDouble;
            fieldsEditor.AddField(pField);

            // Create a new field object
            pField = new FieldClass();
            // Set the field editor for this field
            objectIDFieldEditor = (IFieldEdit)pField;
            // Set the name and alias of the field
            objectIDFieldEditor.Name_2 = "SunElevation";
            objectIDFieldEditor.AliasName_2 = "Sun Elevation";
            // Set the type of the field
            objectIDFieldEditor.Type_2 = esriFieldType.esriFieldTypeDouble;
            fieldsEditor.AddField(pField);

            // Create a new field object
            pField = new FieldClass();
            // Set the field editor for this field
            objectIDFieldEditor = (IFieldEdit)pField;
            // Set the name and alias of the field
            objectIDFieldEditor.Name_2 = "SatAzimuth";
            objectIDFieldEditor.AliasName_2 = "Satellite Azimuth";
            // Set the type of the field as Blob
            objectIDFieldEditor.Type_2 = esriFieldType.esriFieldTypeDouble;
            fieldsEditor.AddField(pField);

            // Create a new field object
            pField = new FieldClass();
            // Set the field editor for this field
            objectIDFieldEditor = (IFieldEdit)pField;
            // Set the name and alias of the field
            objectIDFieldEditor.Name_2 = "SatElevation";
            objectIDFieldEditor.AliasName_2 = "Satellite Elevation";
            // Set the type of the field as Blob
            objectIDFieldEditor.Type_2 = esriFieldType.esriFieldTypeDouble;
            fieldsEditor.AddField(pField);
        }
        #endregion

        #region IRasterBuilderInit Members

        public ISpatialReference DefaultSpatialReference
        {
            get
            {
                return myDefaultSpatialReference;
            }
            set
            {
                myDefaultSpatialReference = value;
            }
        }

        public IMosaicDataset MosaicDataset
        {
            get
            {
                return myMosaicDataset;
            }
            set
            {
                myMosaicDataset = value;
            }
        }

        public IRasterTypeOperation RasterTypeOperation
        {
            get
            {
                return myRasterTypeOperation;
            }
            set
            {
                myRasterTypeOperation = value;
            }
        }

        public ITrackCancel TrackCancel
        {
            get
            {
                return myTrackCancel;
            }
            set
            {
                myTrackCancel = value;
            }
        }

        #endregion

        #region IPersistVariant Members
        /// <summary>
        /// UID for the object implementing the Persist Variant
        /// </summary>
        public UID ID
        {
            get { return myUID; }
        }

        /// <summary>
        /// Load the object from the stream provided
        /// </summary>
        /// <param name="Stream">Stream that represents the serialized Raster Type</param>
        public void Load(IVariantStream Stream)
        {
            string name = (string)Stream.Read();
            //if (innerRasterBuilder is IPersistVariant)
            //    ((IPersistVariant)innerRasterBuilder).Load(Stream);
            //innerRasterBuilder = (IRasterBuilder)Stream.Read(); // Load the innerRasterBuilder from the stream.
        }

        /// <summary>
        /// Same the Raster Type to the stream provided
        /// </summary>
        /// <param name="Stream">Stream to serialize the Raster Type into</param>
        public void Save(IVariantStream Stream)
        {
            Stream.Write("CustomRasterType");
            //if (innerRasterBuilder is IPersistVariant)
            //    ((IPersistVariant)innerRasterBuilder).Save(Stream);
            //Stream.Write(innerRasterBuilder); // Save the innerRasterBuilder into the stream.
        }

        #endregion

        #region IRasterBuilder2 Members
        /// <summary>
        /// Check if the data source provided is a valid data source for the builder.
        /// </summary>
        /// <param name="vtDataSource">Data source (usually the path to a metadta file)</param>
        /// <returns>Flag to specify whether it is  valid source.</returns>
        public bool CanBuild(object vtDataSource)
        {
            if (!(vtDataSource is string))
                return false;
            string dimFilePath = (string)vtDataSource;
            if (!dimFilePath.ToLower().EndsWith(".dim"))
                return false;
            DiMapParser myDimParser = null;
            try
            {
                myDimParser = new DiMapParser(dimFilePath);
                if (myDimParser.MetadataProfile.ToLower() == "DMCII".ToLower())
                {
                    myDimParser = null;
                    return true;
                }
                else
                {
                    myDimParser = null;
                    return false;
                }
            }
            catch (Exception exc)
            {
                myDimParser = null;
                string error = exc.Message;
                return false;
            }
        }

        public bool CanMergeItems
        {
            get { return myCanMergeItems; }
        }

        public bool MergeItems
        {
            get
            {
                return myMergeItems;
            }
            set
            {
                myMergeItems = value;
            }
        }

        /// <summary>
        /// Check to see if the properties provided to the raster type/builder 
        /// are sufficient for it to work. Usually used for UI validation.
        /// </summary>
        public void Validate()
        {
            return;
        }
        #endregion

        #region IRasterBuilderInit2 Members

        /// <summary>
        /// Helper object to store geographic transformations set on the mosaic dataset.
        /// </summary>
        public IGeoTransformationHelper GeoTransformationHelper
        {
            get
            {
                return myGeoTransformationHelper;
            }
            set
            {
                myGeoTransformationHelper = null;
            }
        }

        #endregion
    }

    /// <summary>
    /// Class used to parse the dim file (xml format) and get relevant properties from it.
    /// </summary>
    public class DiMapParser
    {
        private string myXmlPath;
        private XmlDocument myXmlDoc;
        private XmlNodeList bandInfo;
        private XmlNodeList bandStats;
        private XmlNodeList footprintInfo;
        private int vertexCount;

        public DiMapParser()
        {
            myXmlPath = null;
            bandInfo = null;
            bandStats = null;
            footprintInfo = null;
            vertexCount = 0;
        }

        public DiMapParser(string xmlPath)
        {
            myXmlPath = xmlPath;
            bandInfo = null;
            bandStats = null;
            footprintInfo = null;
            vertexCount = 0;
            myXmlDoc = new XmlDocument();
            myXmlDoc.Load(myXmlPath);
        }

        /// <summary>
        /// Flag to specify whether the footprint exists in the xml file.
        /// </summary>
        public bool FootPrintExists
        {
            get
            {
                if (footprintInfo == null)
                    footprintInfo = myXmlDoc.SelectSingleNode("//Dataset_Frame").SelectNodes("Vertex");
                return footprintInfo != null;
            }
        }

        /// <summary>
        /// Reset the vertex count to get vertices of the footprint.
        /// </summary>
        public void ResetVertexCount()
        {
            vertexCount = 0;
        }

        public bool GetNextVertex(out string x, out string y, out string col, out string row)
        {
            if (footprintInfo == null)
                footprintInfo = myXmlDoc.SelectSingleNode("//Dataset_Frame").SelectNodes("Vertex");
            x = footprintInfo[vertexCount].SelectSingleNode("FRAME_LON").InnerText;
            y = footprintInfo[vertexCount].SelectSingleNode("FRAME_LAT").InnerText;
            col = footprintInfo[vertexCount].SelectSingleNode("FRAME_COL").InnerText;
            row = footprintInfo[vertexCount].SelectSingleNode("FRAME_ROW").InnerText;
            ++vertexCount;

            if (vertexCount >= footprintInfo.Count)
                return false;
            else
                return true;
        }

        /// <summary>
        /// Get next vertex from the footprint defined in the xml based on the vertex count and unit.
        /// </summary>
        /// <param name="x">The X value.</param>
        /// <param name="y">The Y value.</param>
        /// <param name="unit">Unit to check which parameter to get vertex from.</param>
        /// <returns>True if next vertex exists.</returns>
        public bool GetNextVertex2(out string x, out string y, string unit)
        {
            if (unit == "Deg")
            {
                if (footprintInfo == null)
                    footprintInfo = myXmlDoc.SelectSingleNode("//Dataset_Frame").SelectNodes("Vertex");
                x = footprintInfo[vertexCount].SelectSingleNode("FRAME_LON").InnerText;
                y = footprintInfo[vertexCount].SelectSingleNode("FRAME_LAT").InnerText;
                //col = footprintInfo[vertexCount].SelectSingleNode("FRAME_COL").InnerText;
                //row = footprintInfo[vertexCount].SelectSingleNode("FRAME_ROW").InnerText;
            }
            else
            {
                if (footprintInfo == null)
                    footprintInfo = myXmlDoc.SelectSingleNode("//Dataset_Frame").SelectNodes("Vertex");
                x = footprintInfo[vertexCount].SelectSingleNode("FRAME_X").InnerText;
                y = footprintInfo[vertexCount].SelectSingleNode("FRAME_Y").InnerText;
                //col = footprintInfo[vertexCount].SelectSingleNode("FRAME_COL").InnerText;
                //row = footprintInfo[vertexCount].SelectSingleNode("FRAME_ROW").InnerText;
            }
            ++vertexCount;

            if (vertexCount >= footprintInfo.Count)
                return false;
            else
                return true;
        }

        /// <summary>
        /// The number of bands defined in the xml.
        /// </summary>
        public int NumBands
        {
            get
            {
                if (bandInfo == null)
                    bandInfo = myXmlDoc.SelectNodes("//Spectral_Band_Info");
                if (bandStats == null)
                    bandStats = myXmlDoc.SelectNodes("//Band_Statistics");
                return bandInfo.Count;
            }
        }

        /// <summary>
        /// Index of the band based on the counter.
        /// </summary>
        /// <param name="indexCounter">Counter (similar to vertexCount) to get the index for.</param>
        /// <returns>Index of the band as string.</returns>
        public string GetBandIndex(int indexCounter)
        {
            if (bandInfo == null)
                bandInfo = myXmlDoc.SelectNodes("//Spectral_Band_Info");
            return bandInfo[indexCounter].SelectSingleNode("BAND_INDEX").InnerText;
        }

        /// <summary>
        /// Get the name of the band.
        /// </summary>
        /// <param name="bandIndex">Index of the band for which to get the name.</param>
        /// <returns>Band name as string.</returns>
        public string GetBandDesc(int bandIndex)
        {
            if (bandInfo == null)
                bandInfo = myXmlDoc.SelectNodes("//Spectral_Band_Info");
            return bandInfo[bandIndex - 1].SelectSingleNode("BAND_DESCRIPTION").InnerText;
        }

        /// <summary>
        /// Get minimum value for the band.
        /// </summary>
        /// <param name="bandIndex">Index of the band for which to get the value.</param>
        /// <returns>Value requested as string.</returns>
        public string GetBandStatMin(int bandIndex)
        {
            if (bandStats == null)
                bandStats = myXmlDoc.SelectNodes("//Band_Statistics");
            return bandStats[bandIndex - 1].SelectSingleNode("STX_LIN_MIN").InnerText;
        }

        /// <summary>
        /// Get maximum value for the band.
        /// </summary>
        /// <param name="bandIndex">Index of the band for which to get the value.</param>
        /// <returns>Value requested as string.</returns>
        public string GetBandStatMax(int bandIndex)
        {
            if (bandStats == null)
                bandStats = myXmlDoc.SelectNodes("//Band_Statistics");
            return bandStats[bandIndex - 1].SelectSingleNode("STX_LIN_MAX").InnerText;
        }

        /// <summary>
        /// Get mean value for the band.
        /// </summary>
        /// <param name="bandIndex">Index of the band for which to get the value.</param>
        /// <returns>Value requested as string.</returns>
        public string GetBandStatMean(int bandIndex)
        {
            if (bandStats == null)
                bandStats = myXmlDoc.SelectNodes("//Band_Statistics");
            return bandStats[bandIndex - 1].SelectSingleNode("STX_MEAN").InnerText;
        }

        /// <summary>
        /// Get standard deviation value for the band.
        /// </summary>
        /// <param name="bandIndex">Index of the band for which to get the value.</param>
        /// <returns>Value requested as string.</returns>
        public string GetBandStatStdDev(int bandIndex)
        {
            if (bandStats == null)
                bandStats = myXmlDoc.SelectNodes("//Band_Statistics");
            return bandStats[bandIndex - 1].SelectSingleNode("STX_STDV").InnerText;
        }

        /// <summary>
        /// Get the product type for the dataset.
        /// </summary>
        public string ProductType
        {
            get
            {
                return myXmlDoc.SelectSingleNode("//PRODUCT_TYPE").InnerText;
            }
        }

        /// <summary>
        /// Get the sensor name for the dataset.
        /// </summary>
        public string MetadataProfile
        {
            get
            {
                return myXmlDoc.SelectSingleNode("//METADATA_PROFILE").InnerText;
            }
        }

        /// <summary>
        /// Get the geometric processing for the dataset.
        /// </summary>
        public string GeometricProcessing
        {
            get
            {
                return myXmlDoc.SelectSingleNode("//GEOMETRIC_PROCESSING").InnerText;
            }
        }

        /// <summary>
        /// Get the Acquisition Date for the dataset.
        /// </summary>
        public string AcquisitionDate
        {
            get
            {
                return myXmlDoc.SelectSingleNode("//IMAGING_DATE").InnerText;
            }
        }

        /// <summary>
        /// Get the Acquisition Time for the dataset.
        /// </summary>
        public string AcquisitionTime
        {
            get
            {
                return myXmlDoc.SelectSingleNode("//IMAGING_TIME").InnerText;
            }
        }

        /// <summary>
        /// Get the Sensor Angle for the dataset.
        /// </summary>
        public string IncidenceAngle
        {
            get
            {
                return myXmlDoc.SelectSingleNode("//INCIDENCE_ANGLE").InnerText;
            }
        }

        /// <summary>
        /// Get the Sun Azimuth for the dataset.
        /// </summary>
        public string SunAzimuth
        {
            get
            {
                return myXmlDoc.SelectSingleNode("//SUN_AZIMUTH").InnerText;
            }
        }

        /// <summary>
        /// Get the Sun Elevation for the dataset.
        /// </summary>
        public string SunElevation
        {
            get
            {
                return myXmlDoc.SelectSingleNode("//SUN_ELEVATION").InnerText;
            }
        }

        /// <summary>
        /// Get the epsg code for the spatial reference of the dataset.
        /// </summary>
        public string SrsCode
        {
            get
            {
                return myXmlDoc.SelectSingleNode("//HORIZONTAL_CS_CODE").InnerText;
            }
        }

        /// <summary>
        /// Get the Sensor Azimuth for the dataset.
        /// </summary>
        public string SensorAzimuth
        {
            get
            {
                XmlNodeList qualityAssessments = myXmlDoc.SelectNodes("//Quality_Assessment");
                XmlNodeList qualityPars = qualityAssessments[1].SelectNodes("Quality_Parameter");
                for (int i = 0; i < qualityPars.Count; ++i)
                {
                    if (qualityPars[i].SelectSingleNode("QUALITY_PARAMETER_CODE").InnerText.Contains("SENSOR_AZIMUTH"))
                        return qualityPars[i].SelectSingleNode("QUALITY_PARAMETER_VALUE").InnerText;
                }
                return "";
            }
        }
    }
}
[Visual Basic .NET]

DMCIIRasterType.vb

Imports System.IO
Imports System.Xml
Imports System.Collections.Generic
Imports System.Linq
Imports System.Text
Imports System.Runtime.InteropServices
Imports ESRI.ArcGIS.ADF
Imports ESRI.ArcGIS.DataSourcesRaster
Imports ESRI.ArcGIS.esriSystem
Imports ESRI.ArcGIS.Geometry
Imports ESRI.ArcGIS.Geodatabase
Imports ESRI.ArcGIS.Carto

'
' * 
' * This sample shows how to implement a Custom Raster Type to provide support for DMCII data. 
' * Also provided is an optional test application to create a Geodatabase and a mosaic dataset 
' * and add data using the custom type.
' * The main interface to be implemented is the IRasterBuilder interface along with 
' * secondary interfaces such as IRasterBuilderInit (which provides access to the parent MD),
' * IPersistvariant (which implements persistence), IRasterBuilderInit2 and IRasterBuilder2 
' * (new interfaces added at 10.1).
' * A IRasterFactory implementation also needs to be created in order for the Raster type to 
' * show up in the list of Raster Types in the Add Rasters GP Tool. The factory is responsible 
' * for creating the raster type object and setting some properties on it. It also enables the 
' * use of the Raster Product.
' * 
' 



<Guid("5DEF8E3C-51E9-49af-A3BE-EF8C68A4BBBE")> _
<ClassInterface(ClassInterfaceType.None)> _
<ProgId("SampleRasterType.CustomRasterTypeFactory")> _
<ComVisible(True)> _
Public Class DMCIIRasterTypeFactory
	Implements IRasterTypeFactory
	#Region "Private Members"
	Private myRasterTypeNames As IStringArray
	' List of Raster Types that the factory can create.
	Private myUID As UID
	' UID for the DMCII Raster Type.
	#End Region
	#Region "IRasterTypeFactory Members"

	Public Sub New()
		Dim rasterTypeName As String = "DMCII Raster Type"
		myRasterTypeNames = New StrArrayClass()
		myRasterTypeNames.Add(rasterTypeName)

		myUID = New UIDClass()
		myUID.Value = "{5DEF8E3C-51E9-49af-A3BE-EF8C68A4BBBE}"
	End Sub

	Public ReadOnly Property CLSID() As UID Implements IRasterTypeFactory.CLSID
		Get
			Return myUID
		End Get
	End Property

	''' <summary>
	''' Create a Raster Type object given the name of the raster type (usually 
	''' the same name as the one in the UI list of raster types).
	''' </summary>
	''' <param name="RasterTypeName">Name of the Raster Type object to create.</param>
	''' <returns>The Raster type object.</returns>
	Public Function CreateRasterType(RasterTypeName As String) As IRasterType Implements IRasterTypeFactory.CreateRasterType
		' Create a new RasterType object and its corresponding name object.
		Dim theRasterType As IRasterType = New RasterTypeClass()
		Dim theRasterTypeName As IRasterTypeName = New RasterTypeNameClass()
		theRasterTypeName.Name = RasterTypeName
		theRasterType.FullName = DirectCast(theRasterTypeName, IName)

		' Set the properties for the raster type object. These are shown in the 
		' 'General' tab of the raster type properties page.
		DirectCast(theRasterType, IRasterTypeProperties).Name = "DMCII Raster Type"
		DirectCast(theRasterType, IRasterTypeProperties).Description = "Raster Type for DMCII data."
		DirectCast(theRasterType, IRasterTypeProperties).DataSourceFilter = "*.dim"
		DirectCast(theRasterType, IRasterTypeProperties).SupportsOrthorectification = True

		' Create the Custom Raster Builder object
		Dim customRasterBuilder As IRasterBuilder = New DMCIIRasterBuilder()
		' Set the Raster Builder of theRasterType to the above created builder.
		theRasterType.RasterBuilder = customRasterBuilder

		' Enable the use of the Raster Type as a Raster Product.
		DirectCast(theRasterType, IRasterTypeProperties2).IsSensorRasterType = True

		'#Region "Set Product Templates"
		' Create a new array of templates if needed.
		If theRasterType.ItemTemplates Is Nothing Then
			theRasterType.ItemTemplates = New ItemTemplateArrayClass()
		End If

		' Add a 'Raw' template.
		Dim nullTemplate As IItemTemplate = New ItemTemplateClass()
		nullTemplate.Enabled = False
		nullTemplate.Name = "Raw"
		DirectCast(nullTemplate, IItemTemplate2).IsSensorTemplate = True
		DirectCast(nullTemplate, IItemTemplate2).SupportsEnhancement = False
		theRasterType.ItemTemplates.Add(nullTemplate)

		' Add a 'Stretch' template. This is the default template.
		Dim strTemplate As IItemTemplate = New ItemTemplateClass()
		strTemplate.Enabled = True
		strTemplate.Name = "Stretch"
		Dim stretchFunction As IRasterFunction = New StretchFunctionClass()
		Dim stretchFunctionArgs As IStretchFunctionArguments = New StretchFunctionArgumentsClass()
		stretchFunctionArgs.StretchType = esriRasterStretchType.esriRasterStretchMinimumMaximum
		Dim rasterVar As IRasterFunctionVariable = New RasterFunctionVariableClass()
		rasterVar.IsDataset = True
		rasterVar.Name = "MS"
		rasterVar.Aliases = New StrArrayClass()
		rasterVar.Aliases.Add("MS")
		rasterVar.Description = "Variable for input raster"
		stretchFunctionArgs.Raster = rasterVar
		Dim stretchFunctionTemplate As IRasterFunctionTemplate = New RasterFunctionTemplateClass()
		stretchFunctionTemplate.[Function] = stretchFunction
		stretchFunctionTemplate.Arguments = stretchFunctionArgs
		strTemplate.RasterFunctionTemplate = stretchFunctionTemplate
		DirectCast(strTemplate, IItemTemplate2).IsSensorTemplate = True
		DirectCast(strTemplate, IItemTemplate2).SupportsEnhancement = True
		theRasterType.ItemTemplates.Add(strTemplate)
		'#End Region

		'#Region "Set Product Types"
		' Add Product types (called URI filters in the code).
		If DirectCast(theRasterType, IRasterTypeProperties).SupportedURIFilters Is Nothing Then
			DirectCast(theRasterType, IRasterTypeProperties).SupportedURIFilters = New ArrayClass()
		End If
		' Create and setup URI Filters
		Dim allFilter As IItemURIFilter = New URIProductNameFilterClass()
		allFilter.Name = "All"
		allFilter.SupportsOrthorectification = True
		allFilter.SupportedTemplateNames = New StrArrayClass()
		allFilter.SupportedTemplateNames.Add("Raw")
		allFilter.SupportedTemplateNames.Add("Stretch")
		Dim allProductNames As IStringArray = New StrArrayClass()
		allProductNames.Add("L1T")
		allProductNames.Add("L1R")
		DirectCast(allFilter, IURIProductNameFilter).ProductNames = allProductNames

		' The L1T filter does not support orthorectification.
		Dim l1tFilter As IItemURIFilter = New URIProductNameFilterClass()
		l1tFilter.Name = "L1T"
		l1tFilter.SupportsOrthorectification = False
		l1tFilter.SupportedTemplateNames = New StrArrayClass()
		l1tFilter.SupportedTemplateNames.Add("Raw")
		l1tFilter.SupportedTemplateNames.Add("Stretch")
		Dim l1tProductNames As IStringArray = New StrArrayClass()
		l1tProductNames.Add("L1T")
		DirectCast(l1tFilter, IURIProductNameFilter).ProductNames = l1tProductNames

		Dim l1rFilter As IItemURIFilter = New URIProductNameFilterClass()
		l1rFilter.Name = "L1R"
		l1rFilter.SupportsOrthorectification = True
		l1rFilter.SupportedTemplateNames = New StrArrayClass()
		l1rFilter.SupportedTemplateNames.Add("Raw")
		l1rFilter.SupportedTemplateNames.Add("Stretch")
		Dim l1rProductNames As IStringArray = New StrArrayClass()
		l1rProductNames.Add("L1R")
		DirectCast(l1rFilter, IURIProductNameFilter).ProductNames = l1rProductNames

		' Add them to the supported uri filters list
		DirectCast(theRasterType, IRasterTypeProperties).SupportedURIFilters.Add(allFilter)
		DirectCast(theRasterType, IRasterTypeProperties).SupportedURIFilters.Add(l1tFilter)
		DirectCast(theRasterType, IRasterTypeProperties).SupportedURIFilters.Add(l1rFilter)
		' Set 'All' as default
		theRasterType.URIFilter = allFilter
		'#End Region

		Return theRasterType
	End Function

	''' <summary>
	''' Name of the Raster Type Factory
	''' </summary>
	Public ReadOnly Property Name() As String Implements IRasterTypeFactory.Name
		Get
			Return "Custom Raster Type Factory"
		End Get
	End Property

	''' <summary>
	''' Names of the Raster Types supported by the factory.
	''' </summary>
	Public ReadOnly Property RasterTypeNames() As IStringArray Implements IRasterTypeFactory.RasterTypeNames
		Get
			Return myRasterTypeNames
		End Get
	End Property
	#End Region

	#Region "COM Registration Function(s)"
	<ComRegisterFunction> _
	Private Shared Sub Reg(regKey As String)
		ESRI.ArcGIS.ADF.CATIDs.RasterTypeFactory.Register(regKey)
	End Sub

	<ComUnregisterFunction> _
	Private Shared Sub Unreg(regKey As String)
		ESRI.ArcGIS.ADF.CATIDs.RasterTypeFactory.Unregister(regKey)
	End Sub
	#End Region
End Class

<Guid("316725CB-35F2-4159-BEBB-A1445ECE9CF1")> _
<ClassInterface(ClassInterfaceType.None)> _
<ProgId("SampleRasterType.CustomRasterType")> _
<ComVisible(True)> _
Public Class DMCIIRasterBuilder
	Implements IRasterBuilder
	Implements IRasterBuilderInit
	Implements IPersistVariant
	Implements IRasterBuilder2
	Implements IRasterBuilderInit2
	#Region "Private Members"
	' The Mosaic Dataset currently using the Raster Type.
	Private myMosaicDataset As IMosaicDataset
	' The default spatial reference to apply to added data (if no spatial reference exists).
	Private myDefaultSpatialReference As ISpatialReference

	' The Raster Type Operation object (usually a Raster Type object).
	Private myRasterTypeOperation As IRasterTypeOperation
	' The Raster Type Properties.
	Private myRasterTypeProperties As IPropertySet

	' Array to fill with ItemURI's.
	Private myURIArray As IItemURIArray

	' GeoTransform helper object.
	Private myGeoTransformationHelper As IGeoTransformationHelper
	' Flags to specify whether the Raster Type can merge items and 
	Private myCanMergeItems As Boolean
	' if it has merged item.
	Private myMergeItems As Boolean

	' Mapping from field names to names or properties in the item propertyset.
	Private myAuxiliaryFieldAlias As IPropertySet
	' Fields to add to the Mosaic Dataset when items are added through this Raster Type.
	Private myAuxiliaryFields As IFields

	Private myTrackCancel As ITrackCancel
	Private myUID As UID
	' UID for the Custom Builder.
	' The current dimap file being processed.
	Private myCurrentDimFile As String
	#End Region

	Public Sub New()
		myMosaicDataset = Nothing
		myDefaultSpatialReference = Nothing
		myRasterTypeOperation = Nothing
		myRasterTypeProperties = Nothing
		myTrackCancel = Nothing

		myURIArray = Nothing

		myGeoTransformationHelper = Nothing
		myCanMergeItems = False
		myMergeItems = False

		myAuxiliaryFieldAlias = Nothing
		myAuxiliaryFields = Nothing

		myUID = New UIDClass()
		myUID.Value = "{316725CB-35F2-4159-BEBB-A1445ECE9CF1}"
	End Sub

	#Region "IRasterBuilder Members"

	''' <summary>
	''' This defines a mapping from field names in the attribute table of the mosaic to
	''' properties in the property set associated with the dataset, incase a user wants 
	''' specify fields which are different from the property in the dataset.
	''' e.g. The field CloudCover may map to a property called C_C in the dataset built 
	''' by the builder.
	''' </summary>
	Public Property AuxiliaryFieldAlias() As IPropertySet Implements IRasterBuilder.AuxiliaryFieldAlias, IRasterBuilder2.AuxiliaryFieldAlias
		Get
			Return myAuxiliaryFieldAlias
		End Get
		Set
			myAuxiliaryFieldAlias = value
		End Set
	End Property

	''' <summary>
	''' Specify fields if necessary to be added to the Mosaic Dataset when 
	''' items are added throug hthis Raster Type.
	''' </summary>
	Public Property AuxiliaryFields() As IFields Implements IRasterBuilder.AuxiliaryFields, IRasterBuilder2.AuxiliaryFields
		Get
			If myAuxiliaryFields Is Nothing Then
				myAuxiliaryFields = New FieldsClass()
				AddFields(myAuxiliaryFields)
			End If
			Return myAuxiliaryFields
		End Get
		Set
			myAuxiliaryFields = value
		End Set
	End Property

	''' <summary>
	''' Get a crawler recommended by the Raster Type based on the data srouce properties provided.
	''' </summary>
	''' <param name="pDataSourceProperties">Data source properties.</param>
	''' <returns>Data source crawler recommended by the raster type.</returns>
	Public Function GetRecommendedCrawler(pDataSourceProperties As IPropertySet) As IDataSourceCrawler Implements IRasterBuilder.GetRecommendedCrawler, IRasterBuilder2.GetRecommendedCrawler
		Try
			' This is usually a file crawler because it can crawl directories as well, unless
			' special types of data needs to be crawled.
			Dim myCrawler As IDataSourceCrawler = New FileCrawlerClass()
			DirectCast(myCrawler, IFileCrawler).Path = Convert.ToString(pDataSourceProperties.GetProperty("Source"))
			DirectCast(myCrawler, IFileCrawler).Recurse = Convert.ToBoolean(pDataSourceProperties.GetProperty("Recurse"))
			myCrawler.Filter = Convert.ToString(pDataSourceProperties.GetProperty("Filter"))
			If myCrawler.Filter Is Nothing OrElse myCrawler.Filter = "" Then
				myCrawler.Filter = "*.dim"
			End If
			Return myCrawler
		Catch generatedExceptionName As Exception
			Throw
		End Try
	End Function

	''' <summary>
	''' Prepare the Raster Type for generating item Unique Resource Identifier (URI)
	''' </summary>
	''' <param name="pCrawler">Crawler to use to generate the item URI's</param>
	Public Sub BeginConstruction(pCrawler As IDataSourceCrawler) Implements IRasterBuilder.BeginConstruction, IRasterBuilder2.BeginConstruction
		myURIArray = New ItemURIArrayClass()
	End Sub

	''' <summary>
	''' Construct a Unique Resource Identifier (URI)
	''' for each crawler item
	''' </summary>
	''' <param name="crawlerItem">Crawled Item from which the URI is generated</param>
	Public Sub ConstructURIs(crawlerItem As Object) Implements IRasterBuilder.ConstructURIs, IRasterBuilder2.ConstructURIs
		myCurrentDimFile = DirectCast(crawlerItem, String)
	End Sub

	''' <summary>
	''' Finish construction of the URI's
	''' </summary>
	''' <returns>Array containing finised URI's</returns>
	Public Function EndConstruction() As IItemURIArray Implements IRasterBuilder.EndConstruction, IRasterBuilder2.EndConstruction
		Return myURIArray
	End Function

	''' <summary>
	''' Generate the next URI.
	''' </summary>
	''' <returns>The URI generated.</returns>
	Public Function GetNextURI() As IItemURI Implements IRasterBuilder.GetNextURI, IRasterBuilder2.GetNextURI
		Dim newURI As IItemURI = Nothing
		Try
			' Check to see if the item cralwed is a .dim file.
			If myCurrentDimFile <> "" AndAlso myCurrentDimFile IsNot Nothing AndAlso myCurrentDimFile.EndsWith(".dim") Then
				' Create a new Dimap Parser obect and item uri.
				Dim myDimParser As New DiMapParser(myCurrentDimFile)
				newURI = New ItemURIClass()
				' Set the display name, Group, Product Name, Tag and Key.
				newURI.DisplayName = System.IO.Path.GetFileName(myCurrentDimFile)
				newURI.Group = System.IO.Path.GetFileNameWithoutExtension(myCurrentDimFile)
				newURI.Key = myCurrentDimFile
				newURI.ProductName = myDimParser.ProductType
				newURI.Tag = "MS"
				' Set the timestamp of the dimfile as source time stamp. This helps 
				' with synchronization later.
				Dim myEnv As IRasterTypeEnvironment = New RasterTypeEnvironmentClass()
				Dim dimTS As DateTime = myEnv.GetTimeStamp(myCurrentDimFile)
				newURI.SourceTimeStamp = dimTS

				myDimParser = Nothing
				myCurrentDimFile = ""
				myURIArray.Add(newURI)
			End If
		Catch generatedExceptionName As Exception
			Throw
		End Try
		Return newURI
	End Function

	''' <summary>
	''' Build the Builder Item which includes the function raster dataset and its footprint 
	''' given the ItemURI.
	''' </summary>
	''' <param name="pItemURI">ItemURi to use to build the Builder Item.</param>
	''' <returns>The builder item.</returns>
	Public Function Build(pItemURI As IItemURI) As IBuilderItem Implements IRasterBuilder.Build, IRasterBuilder2.Build
		Try
			' Create a new parser object and builder item.
			Dim myDimParser As New DiMapParser(pItemURI.Key)
			Dim currItem As IBuilderItem = New BuilderItemClass()

			' Set Category and URI
			currItem.Category = esriRasterCatalogItemCategory.esriRasterCatalogItemCategoryPrimary
			currItem.URI = pItemURI

			' Set FunctionRasterDataset
			Dim inputFrd As IFunctionRasterDataset = GetFRD(myDimParser, pItemURI)
			currItem.Dataset = inputFrd
			' Set band information for the function dataset including names, wavelengths and stats if available.
			SetBandProperties(DirectCast(inputFrd, IDataset), myDimParser)

			' Set Footprint
			Dim geoDset As IGeoDataset = DirectCast(inputFrd, IGeoDataset)
			' Set it to the current raster extent first. If the raster has no 
			' spatial reference, the extents will be in pixel space.
			currItem.Footprint = DirectCast(geoDset.Extent, IGeometry)
			' The get the footprint from the dim file is it exists.
			currItem.Footprint = GetFootprint(myDimParser)

			' Set Properties. These properties are used to fill the Auxiliary Fields 
			' defined earlier and also key properties if the names are correct.
			Dim propSet As IPropertySet = currItem.Dataset.Properties
			If propSet Is Nothing Then
				propSet = New PropertySetClass()
			End If
			Dim sunAzimuth As Double = Convert.ToDouble(myDimParser.SunAzimuth)
			Dim sunElevation As Double = Convert.ToDouble(myDimParser.SunElevation)
			Dim sensorAzimuth As Double = Convert.ToDouble(myDimParser.SensorAzimuth)
			Dim sensorElevation As Double = 180 - Convert.ToDouble(myDimParser.IncidenceAngle)
			Dim acqDate As String = myDimParser.AcquisitionDate
			Dim acqTime As String = myDimParser.AcquisitionTime
			' Create a time object from the provided date and time.
			Dim acqDateTimeObj As ITime = New TimeClass()
			acqDateTimeObj.SetFromTimeString(esriTimeStringFormat.esriTSFYearThruSubSecondWithDash, acqDate & " " & acqTime)
			' and obtain a DateTime object to set as value of the property. This ensures the 
			' field displays the value correctly.
			Dim acqDateTimeFieldVal As DateTime = acqDateTimeObj.QueryOleTime()

			propSet.SetProperty("AcquisitionDate", acqDateTimeFieldVal)
			propSet.SetProperty("SensorName", myDimParser.MetadataProfile)
			propSet.SetProperty("SunAzimuth", sunAzimuth)
			propSet.SetProperty("SunElevation", sunElevation)
			propSet.SetProperty("SatAzimuth", sensorAzimuth)
			propSet.SetProperty("SatElevation", sensorElevation)
			currItem.Dataset.Properties = propSet

			Return currItem
		Catch exc As Exception
			Throw exc
		End Try
	End Function

	''' <summary>
	''' Flag to specify whether the Raster Builder can build items in place.
	''' </summary>
	Public ReadOnly Property CanBuildInPlace() As Boolean Implements IRasterBuilder.CanBuildInPlace, IRasterBuilder2.CanBuildInPlace
		Get
			Return False
		End Get
	End Property

	''' <summary>
	''' Check if the item provided is "stale" or not valid
	''' </summary>
	''' <param name="pItemURI">URI for the item to be checked</param>
	''' <returns>Flag to specify whether the item is stale or not.</returns>
	Public Function IsStale(pItemURI As IItemURI) As Boolean Implements IRasterBuilder.IsStale, IRasterBuilder2.IsStale
		Try
			Dim myEnv As IRasterTypeEnvironment = New RasterTypeEnvironmentClass()
			Dim currDimTS As DateTime = myEnv.GetTimeStamp(pItemURI.Key)
			Return pItemURI.SourceTimeStamp <> currDimTS
		Catch generatedExceptionName As Exception
			Throw
		End Try
	End Function


	''' <summary>
	''' Properties associated with the Raster Type
	''' </summary>
	Public Property Properties() As IPropertySet Implements IRasterBuilder.Properties, IRasterBuilder2.Properties
		Get
			If myRasterTypeProperties Is Nothing Then
				myRasterTypeProperties = New PropertySetClass()
			End If
			Return myRasterTypeProperties
		End Get
		Set
			myRasterTypeProperties = value
		End Set
	End Property

	''' <summary>
	''' Sets band properties on a given dataset including stats, band names and wavelengths.
	''' </summary>
	''' <param name="dataset">The dataset to set properties on.</param>
	''' <param name="dimParser">Dimap parser to read properties from.</param>
	Private Sub SetBandProperties(dataset As IDataset, dimParser As DiMapParser)
		Try
			' Set band band props.
			Dim rasterKeyProps As IRasterKeyProperties = DirectCast(dataset, IRasterKeyProperties)
			Dim rasterBandColl As IRasterBandCollection = DirectCast(dataset, IRasterBandCollection)
			Dim imageNumBands As Integer = DirectCast(dataset, IFunctionRasterDataset).RasterInfo.BandCount
			Dim dinNumBands As Integer = dimParser.NumBands
			Dim bandIndexes As Integer() = New Integer(dinNumBands - 1) {}
			Dim bandNames As IStringArray = New StrArrayClass()
			For i As Integer = 0 To dinNumBands - 1
				' Get band index for the first band.
				bandIndexes(i) = Convert.ToInt16(dimParser.GetBandIndex(i))
				' Validate band index.
				If bandIndexes(i) > 0 AndAlso bandIndexes(i) <= imageNumBands Then
					' Get Band Name for the index.
					bandNames.Add(dimParser.GetBandDesc(bandIndexes(i)))
					' Get Band stats for the index.
					Dim bandStats As IRasterStatistics = New RasterStatisticsClass()
					bandStats.Minimum = Convert.ToDouble(dimParser.GetBandStatMin(bandIndexes(i)))
					bandStats.Maximum = Convert.ToDouble(dimParser.GetBandStatMax(bandIndexes(i)))
					bandStats.Mean = Convert.ToDouble(dimParser.GetBandStatMean(bandIndexes(i)))
					bandStats.StandardDeviation = Convert.ToDouble(dimParser.GetBandStatStdDev(bandIndexes(i)))
					' Set stats on the dataset.
					DirectCast(rasterBandColl.Item(bandIndexes(i) - 1), IRasterBandEdit2).AlterStatistics(bandStats)
					' Set Band Name and wavelengths according to the name.
                    rasterKeyProps.SetBandProperty("BandName", (bandIndexes(i) - 1), bandNames.Element(i))
					SetBandWavelengths(dataset, (bandIndexes(i) - 1))
					' Refresh dataset so changes are saved.
					DirectCast(dataset, IRasterDataset3).Refresh()
				End If
			Next
		Catch exc As Exception
			Dim [error] As String = exc.Message
		End Try
	End Sub

	''' <summary>
	''' Set the wavelengths corresponding to the band name.
	''' </summary>
	Private Sub SetBandWavelengths(dataset As IDataset, bandIndex As Integer)
		Dim rasterKeyProps As IRasterKeyProperties = DirectCast(dataset, IRasterKeyProperties)
		Dim rasterBandColl As IRasterBandCollection = DirectCast(dataset, IRasterBandCollection)
		Dim bandName As String = DirectCast(rasterKeyProps.GetBandProperty("BandName", bandIndex), String)
		' Set wavelengths for the bands
		Select Case bandName.ToLower()
			Case "red"
				If True Then
					rasterKeyProps.SetBandProperty("WavelengthMin", bandIndex, 630)
					rasterKeyProps.SetBandProperty("WavelengthMax", bandIndex, 690)
				End If
				Exit Select

			Case "green"
				If True Then
					rasterKeyProps.SetBandProperty("WavelengthMin", bandIndex, 520)
					rasterKeyProps.SetBandProperty("WavelengthMax", bandIndex, 600)
				End If
				Exit Select

			Case "nir", "nearinfrared"
				If True Then
					rasterKeyProps.SetBandProperty("WavelengthMin", bandIndex, 770)
					rasterKeyProps.SetBandProperty("WavelengthMax", bandIndex, 900)
				End If
				Exit Select
		End Select
	End Sub

	'private IGeometry GetFootprint(DiMapParser dimParser)
	'{
	'    IGeometry currFootprint = null;
	'    dimParser.ResetVertexCount();
	'    string xs = "";
	'    string ys = "";
	'    string rows = "";
	'    string cols = "";
	'    double minX = 10000000000.0;
	'    double maxX = -1000000000.00;
	'    double minY = 1000000000.00;
	'    double maxY = -1000000000.00;
	'    double minRow = 1000000000.00;
	'    double maxRow = -1000000000.0;
	'    double minCol = 1000000000.00;
	'    double maxCol = -1000000000.0;
	'    double x = 0.0;
	'    double y = 0.0;
	'    double row = 0.0;
	'    double col = 0.0;

	'    while (dimParser.GetNextVertex(out xs, out ys, out rows, out cols))
	'    {
	'        x = Convert.ToDouble(xs);
	'        y = Convert.ToDouble(ys);
	'        row = Convert.ToDouble(rows);
	'        col = Convert.ToDouble(cols);

	'        if (x < minX)
	'            minX = x;
	'        if (x > maxX)
	'            maxX = x;

	'        if (y < minY)
	'            minY = y;
	'        if (y > maxY)
	'            maxY = y;

	'        if (row < minRow)
	'            minRow = row;
	'        if (row > maxRow)
	'            maxRow = row;

	'        if (col < minCol)
	'            minCol = col;
	'        if (col > maxCol)
	'            maxCol = col;

	'        x = 0.0;
	'        y = 0.0;
	'        row = 0.0;
	'        col = 0.0;
	'        xs = "";
	'        ys = "";
	'        rows = "";
	'        cols = "";
	'    }
	'    x = Convert.ToDouble(xs);
	'    y = Convert.ToDouble(ys);
	'    row = Convert.ToDouble(rows);
	'    col = Convert.ToDouble(cols);

	'    if (x < minX)
	'        minX = x;
	'    if (x > maxX)
	'        maxX = x;

	'    if (y < minY)
	'        minY = y;
	'    if (y > maxY)
	'        maxY = y;

	'    if (row < minRow)
	'        minRow = row;
	'    if (row > maxRow)
	'        maxRow = row;

	'    if (col < minCol)
	'        minCol = col;
	'    if (col > maxCol)
	'        maxCol = col;

	'    currFootprint = new PolygonClass();
	'    IPointCollection currPointColl = (IPointCollection)currFootprint;
	'    IEnvelope rectEnvelope = new EnvelopeClass();
	'    rectEnvelope.PutCoords(minX, minY, maxX, maxY);
	'    ISegmentCollection segmentCollection = (ISegmentCollection)currFootprint;
	'    segmentCollection.SetRectangle(rectEnvelope);

	'    // Get Srs
	'    int epsgcode = Convert.ToInt32((dimParser.SrsCode.Split(':'))[1]);
	'    ISpatialReferenceFactory3 srsfactory = new SpatialReferenceEnvironmentClass();
	'    ISpatialReference dimSrs = srsfactory.CreateSpatialReference(epsgcode);
	'    ISpatialReferenceResolution srsRes = (ISpatialReferenceResolution)dimSrs;
	'    srsRes.ConstructFromHorizon();
	'    srsRes.SetDefaultXYResolution();
	'    ((ISpatialReferenceTolerance)dimSrs).SetDefaultXYTolerance();
	'    currFootprint.SpatialReference = dimSrs;

	'    #region Commented
	'    //IEnvelope extent = new EnvelopeClass();
	'    //extent.XMin = geoDset.Extent.XMin;
	'    //extent.XMax = geoDset.Extent.XMax;
	'    //extent.YMin = geoDset.Extent.YMin;
	'    //extent.YMax = geoDset.Extent.YMax;
	'    //extent.SpatialReference = geoDset.SpatialReference;
	'    //extent.Width = inputFrd.RasterInfo.Extent.Width;
	'    //extent.Height = inputFrd.RasterInfo.Extent.Height;
	'    //currItem.Footprint = (IGeometry)extent;

	'    //myDimParser.ResetVertexCount();
	'    //string x = "";
	'    //string y = "";
	'    //string row = "";
	'    //string col = "";
	'    //IGeometry currFootprint = new PolygonClass();
	'    //IPointCollection currPointColl = (IPointCollection)currFootprint;

	'    // Creating a polygon!!!

	'    ////Build a polygon from a sequence of points. 
	'    ////Add arrays of points to a geometry using the IGeometryBridge2 interface on the 
	'    ////GeometryEnvironment singleton object.
	'    //IGeometryBridge2 geometryBridge2 = new GeometryEnvironmentClass();
	'    //IPointCollection4 pointCollection4 = new PolygonClass();

	'    ////TODO:
	'    ////pointCollection4.SpatialReference = 'Define the spatial reference of the new polygon.

	'    //WKSPoint[] aWKSPointBuffer = null;
	'    //long cPoints = 4; //The number of points in the first part.
	'    //aWKSPointBuffer = new WKSPoint[System.Convert.ToInt32(cPoints - 1) + 1];

	'    ////TODO:
	'    ////aWKSPointBuffer = 'Read cPoints into the point buffer.

	'    //geometryBridge2.SetWKSPoints(pointCollection4, ref aWKSPointBuffer);

	'    //myDimParser.GetNextVertex(out x, out y, out col, out row);
	'    //IPoint currPoint1 = new PointClass();
	'    //currPoint1.X = Convert.ToDouble(x);
	'    //currPoint1.Y = Convert.ToDouble(y);
	'    //myDimParser.GetNextVertex(out x, out y, out col, out row);
	'    //IPoint currPoint2 = new PointClass();
	'    //currPoint1.X = Convert.ToDouble(x);
	'    //currPoint1.Y = Convert.ToDouble(y);
	'    //myDimParser.GetNextVertex(out x, out y, out col, out row);
	'    //IPoint currPoint3 = new PointClass();
	'    //currPoint1.X = Convert.ToDouble(x);
	'    //currPoint1.Y = Convert.ToDouble(y);
	'    //myDimParser.GetNextVertex(out x, out y, out col, out row);
	'    //IPoint currPoint4 = new PointClass();
	'    //currPoint1.X = Convert.ToDouble(x);
	'    //currPoint1.Y = Convert.ToDouble(y);
	'    //object refPoint1 = (object)currPoint1;
	'    //object refPoint2 = (object)currPoint2;
	'    //object refPoint3 = (object)currPoint3;
	'    //object refPoint4 = (object)currPoint4;
	'    //currPointColl.AddPoint(currPoint1, ref refPoint4, ref refPoint2);
	'    //currPointColl.AddPoint(currPoint2, ref refPoint1, ref refPoint3);
	'    //currPointColl.AddPoint(currPoint3, ref refPoint2, ref refPoint4);
	'    //currPointColl.AddPoint(currPoint4, ref refPoint3, ref refPoint1);
	'    //((IPolygon)currFootprint).Close();
	'    //currFootprint.SpatialReference = dimSrs;
	'    #endregion
	'    return currFootprint;
	'}

	''' <summary>
	''' Get the footprint from the dimap file if it exists.
	''' </summary>
	''' <param name="dimParser">Dimap file parser.</param>
	''' <returns>Footprint geomtry.</returns>
	Private Function GetFootprint(dimParser As DiMapParser) As IGeometry
		Dim currFootprint As IGeometry = Nothing
		dimParser.ResetVertexCount()
		Dim xs As String = ""
		Dim ys As String = ""
		Dim minX As Double = 10000000000.0
		Dim maxX As Double = -1000000000.0
		Dim minY As Double = 1000000000.0
		Dim maxY As Double = -1000000000.0
		Dim x As Double = 0.0
		Dim y As Double = 0.0
		Dim units As String = dimParser.ProductType
		If units.ToLower() = "L1T".ToLower() Then
			units = "M"
		ElseIf units.ToLower() = "L1R".ToLower() Then
			units = "Deg"
		End If
		' Get vertices from the dimap file and figure out the min,max.
		While dimParser.GetNextVertex2(xs, ys, units)
			x = Convert.ToDouble(xs)
			y = Convert.ToDouble(ys)

			If x < minX Then
				minX = x
			End If
			If x > maxX Then
				maxX = x
			End If

			If y < minY Then
				minY = y
			End If
			If y > maxY Then
				maxY = y
			End If

			x = 0.0
			y = 0.0
			xs = ""
			ys = ""
		End While
		x = Convert.ToDouble(xs)
		y = Convert.ToDouble(ys)

		If x < minX Then
			minX = x
		End If
		If x > maxX Then
			maxX = x
		End If

		If y < minY Then
			minY = y
		End If
		If y > maxY Then
			maxY = y
		End If

		' create a new polygon and fill it using the vertices calculated.
		currFootprint = New PolygonClass()
		Dim currPointColl As IPointCollection = DirectCast(currFootprint, IPointCollection)
		Dim rectEnvelope As IEnvelope = New EnvelopeClass()
		rectEnvelope.PutCoords(minX, minY, maxX, maxY)
		Dim segmentCollection As ISegmentCollection = DirectCast(currFootprint, ISegmentCollection)
		segmentCollection.SetRectangle(rectEnvelope)

		' Get Srs from the dim file and set it on the footprint.
		Dim epsgcode As Integer = Convert.ToInt32((dimParser.SrsCode.Split(":"C))(1))
		Dim srsfactory As ISpatialReferenceFactory3 = New SpatialReferenceEnvironmentClass()
		Dim dimSrs As ISpatialReference = srsfactory.CreateSpatialReference(epsgcode)
		Dim srsRes As ISpatialReferenceResolution = DirectCast(dimSrs, ISpatialReferenceResolution)
		srsRes.ConstructFromHorizon()
		srsRes.SetDefaultXYResolution()
		DirectCast(dimSrs, ISpatialReferenceTolerance).SetDefaultXYTolerance()
		currFootprint.SpatialReference = dimSrs
		Return currFootprint
	End Function

	''' <summary>
	''' Create the function raster dataset from the source images.
	''' </summary>
	''' <param name="dimPar">Parser for the dimap file.</param>
	''' <param name="pItemURI">ItemURi to use.</param>
	''' <returns>Function raster dataset created.</returns>
	Private Function GetFRD(dimPar As DiMapParser, pItemURI As IItemURI) As IFunctionRasterDataset
		Dim opFrd As IFunctionRasterDataset = Nothing
		Try
			Dim factoryType As Type = Type.GetTypeFromProgID("esriDataSourcesRaster.RasterWorkspaceFactory")
			Dim workspaceFactory As IWorkspaceFactory = DirectCast(Activator.CreateInstance(factoryType), IWorkspaceFactory)
			Dim workspace As IWorkspace = workspaceFactory.OpenFromFile(System.IO.Path.GetDirectoryName(pItemURI.Key), 0)
			Dim rasterWorkspace As IRasterWorkspace = DirectCast(workspace, IRasterWorkspace)
			' Open the tif file associated with the .dim file as a raster dataset.
			Dim imagePath As String = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(pItemURI.Key), pItemURI.Group & ".tif")
			Dim rpcPath As String = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(pItemURI.Key), pItemURI.Group & ".rpc")
			Dim inputRasterDataset As IRasterDataset = Nothing
			If File.Exists(imagePath) Then
				inputRasterDataset = rasterWorkspace.OpenRasterDataset(pItemURI.Group & ".tif")
			Else
				Return Nothing
			End If

			Dim intermedFrd As IFunctionRasterDataset = Nothing
			' If the file opes successfully, add a RasterInfo function on top.
			If inputRasterDataset IsNot Nothing Then
				' Create an Identity function dataset to get the raster info.
				Dim identityFunction As IRasterFunction = New IdentityFunctionClass()
				Dim idFrd As IFunctionRasterDataset = New FunctionRasterDatasetClass()
				idFrd.Init(identityFunction, inputRasterDataset)
				' Create a raster info function dataset.
				Dim rasterInfoFunction As IRasterFunction = New RasterInfoFunctionClass()
				Dim rasterInfoFuncArgs As IRasterInfoFunctionArguments = New RasterInfoFunctionArgumentsClass()
				rasterInfoFuncArgs.Raster = inputRasterDataset
				rasterInfoFuncArgs.RasterInfo = idFrd.RasterInfo
				intermedFrd = New FunctionRasterDatasetClass()
				intermedFrd.Init(rasterInfoFunction, rasterInfoFuncArgs)
			Else
				Return Nothing
			End If
			' Check if there is an RPC file associated with the image. If so
			' then add a geometric function to apply the rpc xform.
			If File.Exists(rpcPath) Then
				opFrd = ApplyRPC(rpcPath, DirectCast(intermedFrd, IRasterDataset))
			End If

			' If no rpc pars exist or applying rpc fails, use the intermediate 
			' function raster dataset created.
			If opFrd Is Nothing Then
				opFrd = intermedFrd

				'IRasterFunction ebFunction = new ExtractBandFunctionClass();
				'IRasterFunctionArguments ebFuncArgs = new ExtractBandFunctionArgumentsClass();
				'ILongArray bandIDs = new LongArrayClass();
				'bandIDs.Add(2);
				'bandIDs.Add(1);
				'bandIDs.Add(0);
				'''/bandIDs.Add(4);
				'((IExtractBandFunctionArguments)ebFuncArgs).BandIDs = bandIDs;
				'((IExtractBandFunctionArguments)ebFuncArgs).Raster = inputRasterDataset;
				'opFrd = new FunctionRasterDatasetClass();
				'opFrd.Init(ebFunction, ebFuncArgs);

				'if (opFrd == null)
				'{
				'    IRasterFunction identityFunction = new IdentityFunctionClass();
				'    opFrd = new FunctionRasterDatasetClass();
				'    opFrd.Init(identityFunction, inputRasterDataset);
				'}
			End If
		Catch exc As Exception
			Dim [error] As String = exc.Message
		End Try
		Return opFrd
	End Function

	''' <summary>
	''' Parse the RPC parameters file associated with the image and create an RPCXform
	''' to bea applied to the inputDataset as a geomtric function.
	''' </summary>
	''' <param name="rpcPath">Path to the rpc parameters file.</param>
	''' <param name="inputDataset">Input dataset to apply the xform on.</param>
	''' <returns>Function raster dataset created.</returns>
	Private Function ApplyRPC(rpcPath As String, inputDataset As IRasterDataset) As IFunctionRasterDataset
		Dim opFrd As IFunctionRasterDataset = Nothing
		Try
			' Open the RPC file and create Geometric transform
			Dim finalXForm As IGeodataXform = Nothing
			Dim rpcXForm As IRPCXform = GetRPCXForm(rpcPath)

			Dim idFrd As IFunctionRasterDataset = Nothing
			If Not (TypeOf inputDataset Is IFunctionRasterDataset) Then
				Dim identityFunction As IRasterFunction = New IdentityFunctionClass()
				idFrd = New FunctionRasterDatasetClass()
				idFrd.Init(identityFunction, inputDataset)
			Else
				idFrd = DirectCast(inputDataset, IFunctionRasterDataset)
			End If

			Dim datasetRasterInfo As IRasterInfo = idFrd.RasterInfo
			Dim datasetExtent As IEnvelope = datasetRasterInfo.Extent
			Dim datasetSrs As ISpatialReference = DirectCast(idFrd, IGeoDataset).SpatialReference

			Dim dRows As Long = datasetRasterInfo.Height
			Dim dCols As Long = datasetRasterInfo.Width
			Dim pixelExtent As IEnvelope = New EnvelopeClass()
			pixelExtent.PutCoords(-0.5, 0.5 - dRows, -0.5 + dCols, 0.5)

			Dim noAffineNeeded As Boolean = DirectCast(pixelExtent, IClone).IsEqual(DirectCast(datasetExtent, IClone))
			If Not noAffineNeeded Then
				' Tranform ground space to pixel space.
				Dim affineXform As IAffineTransformation2D = New AffineTransformation2DClass()
				affineXform.DefineFromEnvelopes(datasetExtent, pixelExtent)
				Dim geoXform As IGeometricXform = New GeometricXformClass()
				geoXform.Transformation = affineXform
				finalXForm = geoXform
			End If

			' Transform from pixel space back to ground space to set as the forward transform.
			Dim groundExtent As IEnvelope = DirectCast(idFrd, IGeoDataset).Extent
			groundExtent.Project(datasetSrs)
			Dim affineXform2 As IAffineTransformation2D = New AffineTransformation2DClass()
			affineXform2.DefineFromEnvelopes(pixelExtent, groundExtent)
			Dim forwardXForm As IGeometricXform = New GeometricXformClass()
			forwardXForm.Transformation = affineXform2
			rpcXForm.ForwardXform = forwardXForm

			' Create the composite transform that changes ground values to pixel space
			' then applies the rpc transform which will transform them back to ground space.
			Dim compositeXForm As ICompositeXform = New CompositeXformClass()
			compositeXForm.Add(finalXForm)
			compositeXForm.Add(rpcXForm)
			finalXForm = DirectCast(compositeXForm, IGeodataXform)

			' Then apply the transform on the raster using the geometric function.
			If finalXForm IsNot Nothing Then
				Dim geometricFunction As IRasterFunction = New GeometricFunctionClass()
				Dim geometricFunctionArgs As IGeometricFunctionArguments = Nothing
				' Get the geomtric function arguments if supplied by the user (in the UI).
				If myRasterTypeOperation IsNot Nothing AndAlso DirectCast(myRasterTypeOperation, IRasterTypeProperties).OrthorectificationParameters IsNot Nothing Then
					geometricFunctionArgs = DirectCast(myRasterTypeOperation, IRasterTypeProperties).OrthorectificationParameters
				Else
					geometricFunctionArgs = New GeometricFunctionArgumentsClass()
				End If
				' Append the xform to the existing ones from the image.
				geometricFunctionArgs.AppendGeodataXform = True
				geometricFunctionArgs.GeodataXform = finalXForm
				geometricFunctionArgs.Raster = inputDataset
				opFrd = New FunctionRasterDatasetClass()
				opFrd.Init(geometricFunction, geometricFunctionArgs)
			End If
			Return opFrd
		Catch exc As Exception
			Dim [error] As String = exc.Message
			Return opFrd
		End Try
	End Function

	''' <summary>
	''' Create an RPCXForm from a text file containing parameters.
	''' </summary>
	''' <param name="rpcFilePath">Text file containing the parameters.</param>
	''' <returns>The RPCXForm generated.</returns>
	Private Function GetRPCXForm(rpcFilePath As String) As IRPCXform
		Try
			' Array for parameters.
			Dim RPC As Double() = New Double(89) {}
			' Propertyset to store properties as backup.
			'IPropertySet coefficients = new PropertySetClass();

			'#Region "Parse RPC text file"
			' Use the stream reader to open the file and read lines from it.
			Using sr As New StreamReader(rpcFilePath)
                Dim line As String = ""
				Dim lineNumber As Integer = 0

				While (InlineAssignHelper(line, sr.ReadLine())) IsNot Nothing
					lineNumber += 1
					Try
						' Split the line into tokens based on delimiters
						Dim delimiters As Char() = {":"C, " "C}
						Dim tokens As String() = line.Split(delimiters, System.StringSplitOptions.RemoveEmptyEntries)
						Dim numTokens As Integer = tokens.GetLength(0)
						If numTokens > 1 Then
							Dim currPar As String = tokens(0)
							Dim currValue As Double = Convert.ToDouble(tokens(1))
							' Convert the Value to a double and store in the array.
								' Store the property and the value in the propertyset to lookup later if needed.
								'coefficients.SetProperty(currPar, currValue);
								' Read units for conversion if needed
								'string currUnit = tokens[2];
							RPC((lineNumber - 1)) = currValue
						Else
							Console.WriteLine("Could not parse line " & lineNumber.ToString())
						End If
					Catch ex As Exception
						Console.Write(ex.ToString())
					End Try
				End While
				sr.Close()
			End Using
			'#End Region

			' Create the new RPCXForm from the parameter array.
			Dim myRPCXform As IRPCXform = DirectCast(New RPCXform(), IRPCXform)
			Dim rpcCoeffs As Object = DirectCast(RPC, Object)
			myRPCXform.DefineFromCoefficients(rpcCoeffs)

			Return myRPCXform
		Catch exc As Exception
			Dim [error] As String = exc.Message
			Throw exc
		End Try
	End Function

	''' <summary>
	''' Create new fields to add to the mosaic dataset attribute table.
	''' </summary>
	''' <param name="myFields">Fields to be added.</param>
	Private Sub AddFields(myFields As IFields)
		' Create a new field object
		Dim pField As IField = New FieldClass()
		' Set the field editor for this field
		Dim objectIDFieldEditor As IFieldEdit = DirectCast(pField, IFieldEdit)
		' Set the name and alias of the field
		objectIDFieldEditor.Name_2 = "SensorName"
		objectIDFieldEditor.AliasName_2 = "Sensor Name"
		' Set the type of the field
		objectIDFieldEditor.Type_2 = esriFieldType.esriFieldTypeString
		' Add the newly created field to list of existing fields
		Dim fieldsEditor As IFieldsEdit = DirectCast(myFields, IFieldsEdit)
		fieldsEditor.AddField(pField)

		' Create a new field object
		pField = New FieldClass()
		' Set the field editor for this field
		objectIDFieldEditor = DirectCast(pField, IFieldEdit)
		' Set the name and alias of the field
		objectIDFieldEditor.Name_2 = "AcquisitionDate"
		objectIDFieldEditor.AliasName_2 = "Acquisition Date"
		' Set the type of the field
		objectIDFieldEditor.Type_2 = esriFieldType.esriFieldTypeDate
		fieldsEditor.AddField(pField)

		' Create a new field object
		pField = New FieldClass()
		' Set the field editor for this field
		objectIDFieldEditor = DirectCast(pField, IFieldEdit)
		' Set the name and alias of the field
		objectIDFieldEditor.Name_2 = "SunAzimuth"
		objectIDFieldEditor.AliasName_2 = "Sun Azimuth"
		' Set the type of the field
		objectIDFieldEditor.Type_2 = esriFieldType.esriFieldTypeDouble
		fieldsEditor.AddField(pField)

		' Create a new field object
		pField = New FieldClass()
		' Set the field editor for this field
		objectIDFieldEditor = DirectCast(pField, IFieldEdit)
		' Set the name and alias of the field
		objectIDFieldEditor.Name_2 = "SunElevation"
		objectIDFieldEditor.AliasName_2 = "Sun Elevation"
		' Set the type of the field
		objectIDFieldEditor.Type_2 = esriFieldType.esriFieldTypeDouble
		fieldsEditor.AddField(pField)

		' Create a new field object
		pField = New FieldClass()
		' Set the field editor for this field
		objectIDFieldEditor = DirectCast(pField, IFieldEdit)
		' Set the name and alias of the field
		objectIDFieldEditor.Name_2 = "SatAzimuth"
		objectIDFieldEditor.AliasName_2 = "Satellite Azimuth"
		' Set the type of the field as Blob
		objectIDFieldEditor.Type_2 = esriFieldType.esriFieldTypeDouble
		fieldsEditor.AddField(pField)

		' Create a new field object
		pField = New FieldClass()
		' Set the field editor for this field
		objectIDFieldEditor = DirectCast(pField, IFieldEdit)
		' Set the name and alias of the field
		objectIDFieldEditor.Name_2 = "SatElevation"
		objectIDFieldEditor.AliasName_2 = "Satellite Elevation"
		' Set the type of the field as Blob
		objectIDFieldEditor.Type_2 = esriFieldType.esriFieldTypeDouble
		fieldsEditor.AddField(pField)
	End Sub
	#End Region

	#Region "IRasterBuilderInit Members"

	Public Property DefaultSpatialReference() As ISpatialReference Implements IRasterBuilderInit.DefaultSpatialReference, IRasterBuilderInit2.DefaultSpatialReference
		Get
			Return myDefaultSpatialReference
		End Get
		Set
			myDefaultSpatialReference = value
		End Set
	End Property

	Public Property MosaicDataset() As IMosaicDataset Implements IRasterBuilderInit.MosaicDataset, IRasterBuilderInit2.MosaicDataset
		Get
			Return myMosaicDataset
		End Get
		Set
			myMosaicDataset = value
		End Set
	End Property

	Public Property RasterTypeOperation() As IRasterTypeOperation Implements IRasterBuilderInit.RasterTypeOperation, IRasterBuilderInit2.RasterTypeOperation
		Get
			Return myRasterTypeOperation
		End Get
		Set
			myRasterTypeOperation = value
		End Set
	End Property

	Public Property TrackCancel() As ITrackCancel Implements IRasterBuilderInit.TrackCancel, IRasterBuilderInit2.TrackCancel
		Get
			Return myTrackCancel
		End Get
		Set
			myTrackCancel = value
		End Set
	End Property

	#End Region

	#Region "IPersistVariant Members"
	''' <summary>
	''' UID for the object implementing the Persist Variant
	''' </summary>
	Public ReadOnly Property ID() As UID Implements IPersistVariant.ID
		Get
			Return myUID
		End Get
	End Property

	''' <summary>
	''' Load the object from the stream provided
	''' </summary>
	''' <param name="Stream">Stream that represents the serialized Raster Type</param>
	Public Sub Load(Stream As IVariantStream) Implements IPersistVariant.Load
		Dim name As String = DirectCast(Stream.Read(), String)
		'if (innerRasterBuilder is IPersistVariant)
		'    ((IPersistVariant)innerRasterBuilder).Load(Stream);
		'innerRasterBuilder = (IRasterBuilder)Stream.Read(); // Load the innerRasterBuilder from the stream.
	End Sub

	''' <summary>
	''' Same the Raster Type to the stream provided
	''' </summary>
	''' <param name="Stream">Stream to serialize the Raster Type into</param>
	Public Sub Save(Stream As IVariantStream) Implements IPersistVariant.Save
		Stream.Write("CustomRasterType")
		'if (innerRasterBuilder is IPersistVariant)
		'    ((IPersistVariant)innerRasterBuilder).Save(Stream);
		'Stream.Write(innerRasterBuilder); // Save the innerRasterBuilder into the stream.
	End Sub

	#End Region

	#Region "IRasterBuilder2 Members"
	''' <summary>
	''' Check if the data source provided is a valid data source for the builder.
	''' </summary>
	''' <param name="vtDataSource">Data source (usually the path to a metadta file)</param>
	''' <returns>Flag to specify whether it is  valid source.</returns>
	Public Function CanBuild(vtDataSource As Object) As Boolean Implements IRasterBuilder2.CanBuild
		If Not (TypeOf vtDataSource Is String) Then
			Return False
		End If
		Dim dimFilePath As String = DirectCast(vtDataSource, String)
		If Not dimFilePath.ToLower().EndsWith(".dim") Then
			Return False
		End If
		Dim myDimParser As DiMapParser = Nothing
		Try
			myDimParser = New DiMapParser(dimFilePath)
			If myDimParser.MetadataProfile.ToLower() = "DMCII".ToLower() Then
				myDimParser = Nothing
				Return True
			Else
				myDimParser = Nothing
				Return False
			End If
		Catch exc As Exception
			myDimParser = Nothing
			Dim [error] As String = exc.Message
			Return False
		End Try
	End Function

	Public ReadOnly Property CanMergeItems() As Boolean Implements IRasterBuilder2.CanMergeItems
		Get
			Return myCanMergeItems
		End Get
	End Property

	Public Property MergeItems() As Boolean Implements IRasterBuilder2.MergeItems
		Get
			Return myMergeItems
		End Get
		Set
			myMergeItems = value
		End Set
	End Property

	''' <summary>
	''' Check to see if the properties provided to the raster type/builder 
	''' are sufficient for it to work. Usually used for UI validation.
	''' </summary>
	Public Sub Validate() Implements IRasterBuilder2.Validate
		Return
	End Sub
	#End Region

	#Region "IRasterBuilderInit2 Members"

	''' <summary>
	''' Helper object to store geographic transformations set on the mosaic dataset.
	''' </summary>
	Public Property GeoTransformationHelper() As IGeoTransformationHelper Implements IRasterBuilderInit2.GeoTransformationHelper
		Get
			Return myGeoTransformationHelper
		End Get
		Set
			myGeoTransformationHelper = Nothing
		End Set
	End Property
	Private Shared Function InlineAssignHelper(Of T)(ByRef target As T, value As T) As T
		target = value
		Return value
	End Function

	#End Region
End Class

''' <summary>
''' Class used to parse the dim file (xml format) and get relevant properties from it.
''' </summary>
Public Class DiMapParser
	Private myXmlPath As String
	Private myXmlDoc As XmlDocument
	Private bandInfo As XmlNodeList
	Private bandStats As XmlNodeList
	Private footprintInfo As XmlNodeList
	Private vertexCount As Integer

	Public Sub New()
		myXmlPath = Nothing
		bandInfo = Nothing
		bandStats = Nothing
		footprintInfo = Nothing
		vertexCount = 0
	End Sub

	Public Sub New(xmlPath As String)
		myXmlPath = xmlPath
		bandInfo = Nothing
		bandStats = Nothing
		footprintInfo = Nothing
		vertexCount = 0
		myXmlDoc = New XmlDocument()
		myXmlDoc.Load(myXmlPath)
	End Sub

	''' <summary>
	''' Flag to specify whether the footprint exists in the xml file.
	''' </summary>
	Public ReadOnly Property FootPrintExists() As Boolean
		Get
			If footprintInfo Is Nothing Then
				footprintInfo = myXmlDoc.SelectSingleNode("//Dataset_Frame").SelectNodes("Vertex")
			End If
			Return footprintInfo IsNot Nothing
		End Get
	End Property

	''' <summary>
	''' Reset the vertex count to get vertices of the footprint.
	''' </summary>
	Public Sub ResetVertexCount()
		vertexCount = 0
	End Sub

	Public Function GetNextVertex(ByRef x As String, ByRef y As String, ByRef col As String, ByRef row As String) As Boolean
		If footprintInfo Is Nothing Then
			footprintInfo = myXmlDoc.SelectSingleNode("//Dataset_Frame").SelectNodes("Vertex")
		End If
		x = footprintInfo(vertexCount).SelectSingleNode("FRAME_LON").InnerText
		y = footprintInfo(vertexCount).SelectSingleNode("FRAME_LAT").InnerText
		col = footprintInfo(vertexCount).SelectSingleNode("FRAME_COL").InnerText
		row = footprintInfo(vertexCount).SelectSingleNode("FRAME_ROW").InnerText
		vertexCount += 1

		If vertexCount >= footprintInfo.Count Then
			Return False
		Else
			Return True
		End If
	End Function

	''' <summary>
	''' Get next vertex from the footprint defined in the xml based on the vertex count and unit.
	''' </summary>
	''' <param name="x">The X value.</param>
	''' <param name="y">The Y value.</param>
	''' <param name="unit">Unit to check which parameter to get vertex from.</param>
	''' <returns>True if next vertex exists.</returns>
	Public Function GetNextVertex2(ByRef x As String, ByRef y As String, unit As String) As Boolean
		If unit = "Deg" Then
			If footprintInfo Is Nothing Then
				footprintInfo = myXmlDoc.SelectSingleNode("//Dataset_Frame").SelectNodes("Vertex")
			End If
			x = footprintInfo(vertexCount).SelectSingleNode("FRAME_LON").InnerText
				'col = footprintInfo[vertexCount].SelectSingleNode("FRAME_COL").InnerText;
				'row = footprintInfo[vertexCount].SelectSingleNode("FRAME_ROW").InnerText;
			y = footprintInfo(vertexCount).SelectSingleNode("FRAME_LAT").InnerText
		Else
			If footprintInfo Is Nothing Then
				footprintInfo = myXmlDoc.SelectSingleNode("//Dataset_Frame").SelectNodes("Vertex")
			End If
			x = footprintInfo(vertexCount).SelectSingleNode("FRAME_X").InnerText
				'col = footprintInfo[vertexCount].SelectSingleNode("FRAME_COL").InnerText;
				'row = footprintInfo[vertexCount].SelectSingleNode("FRAME_ROW").InnerText;
			y = footprintInfo(vertexCount).SelectSingleNode("FRAME_Y").InnerText
		End If
		vertexCount += 1

		If vertexCount >= footprintInfo.Count Then
			Return False
		Else
			Return True
		End If
	End Function

	''' <summary>
	''' The number of bands defined in the xml.
	''' </summary>
	Public ReadOnly Property NumBands() As Integer
		Get
			If bandInfo Is Nothing Then
				bandInfo = myXmlDoc.SelectNodes("//Spectral_Band_Info")
			End If
			If bandStats Is Nothing Then
				bandStats = myXmlDoc.SelectNodes("//Band_Statistics")
			End If
			Return bandInfo.Count
		End Get
	End Property

	''' <summary>
	''' Index of the band based on the counter.
	''' </summary>
	''' <param name="indexCounter">Counter (similar to vertexCount) to get the index for.</param>
	''' <returns>Index of the band as string.</returns>
	Public Function GetBandIndex(indexCounter As Integer) As String
		If bandInfo Is Nothing Then
			bandInfo = myXmlDoc.SelectNodes("//Spectral_Band_Info")
		End If
		Return bandInfo(indexCounter).SelectSingleNode("BAND_INDEX").InnerText
	End Function

	''' <summary>
	''' Get the name of the band.
	''' </summary>
	''' <param name="bandIndex">Index of the band for which to get the name.</param>
	''' <returns>Band name as string.</returns>
	Public Function GetBandDesc(bandIndex As Integer) As String
		If bandInfo Is Nothing Then
			bandInfo = myXmlDoc.SelectNodes("//Spectral_Band_Info")
		End If
		Return bandInfo(bandIndex - 1).SelectSingleNode("BAND_DESCRIPTION").InnerText
	End Function

	''' <summary>
	''' Get minimum value for the band.
	''' </summary>
	''' <param name="bandIndex">Index of the band for which to get the value.</param>
	''' <returns>Value requested as string.</returns>
	Public Function GetBandStatMin(bandIndex As Integer) As String
		If bandStats Is Nothing Then
			bandStats = myXmlDoc.SelectNodes("//Band_Statistics")
		End If
		Return bandStats(bandIndex - 1).SelectSingleNode("STX_LIN_MIN").InnerText
	End Function

	''' <summary>
	''' Get maximum value for the band.
	''' </summary>
	''' <param name="bandIndex">Index of the band for which to get the value.</param>
	''' <returns>Value requested as string.</returns>
	Public Function GetBandStatMax(bandIndex As Integer) As String
		If bandStats Is Nothing Then
			bandStats = myXmlDoc.SelectNodes("//Band_Statistics")
		End If
		Return bandStats(bandIndex - 1).SelectSingleNode("STX_LIN_MAX").InnerText
	End Function

	''' <summary>
	''' Get mean value for the band.
	''' </summary>
	''' <param name="bandIndex">Index of the band for which to get the value.</param>
	''' <returns>Value requested as string.</returns>
	Public Function GetBandStatMean(bandIndex As Integer) As String
		If bandStats Is Nothing Then
			bandStats = myXmlDoc.SelectNodes("//Band_Statistics")
		End If
		Return bandStats(bandIndex - 1).SelectSingleNode("STX_MEAN").InnerText
	End Function

	''' <summary>
	''' Get standard deviation value for the band.
	''' </summary>
	''' <param name="bandIndex">Index of the band for which to get the value.</param>
	''' <returns>Value requested as string.</returns>
	Public Function GetBandStatStdDev(bandIndex As Integer) As String
		If bandStats Is Nothing Then
			bandStats = myXmlDoc.SelectNodes("//Band_Statistics")
		End If
		Return bandStats(bandIndex - 1).SelectSingleNode("STX_STDV").InnerText
	End Function

	''' <summary>
	''' Get the product type for the dataset.
	''' </summary>
	Public ReadOnly Property ProductType() As String
		Get
			Return myXmlDoc.SelectSingleNode("//PRODUCT_TYPE").InnerText
		End Get
	End Property

	''' <summary>
	''' Get the sensor name for the dataset.
	''' </summary>
	Public ReadOnly Property MetadataProfile() As String
		Get
			Return myXmlDoc.SelectSingleNode("//METADATA_PROFILE").InnerText
		End Get
	End Property

	''' <summary>
	''' Get the geometric processing for the dataset.
	''' </summary>
	Public ReadOnly Property GeometricProcessing() As String
		Get
			Return myXmlDoc.SelectSingleNode("//GEOMETRIC_PROCESSING").InnerText
		End Get
	End Property

	''' <summary>
	''' Get the Acquisition Date for the dataset.
	''' </summary>
	Public ReadOnly Property AcquisitionDate() As String
		Get
			Return myXmlDoc.SelectSingleNode("//IMAGING_DATE").InnerText
		End Get
	End Property

	''' <summary>
	''' Get the Acquisition Time for the dataset.
	''' </summary>
	Public ReadOnly Property AcquisitionTime() As String
		Get
			Return myXmlDoc.SelectSingleNode("//IMAGING_TIME").InnerText
		End Get
	End Property

	''' <summary>
	''' Get the Sensor Angle for the dataset.
	''' </summary>
	Public ReadOnly Property IncidenceAngle() As String
		Get
			Return myXmlDoc.SelectSingleNode("//INCIDENCE_ANGLE").InnerText
		End Get
	End Property

	''' <summary>
	''' Get the Sun Azimuth for the dataset.
	''' </summary>
	Public ReadOnly Property SunAzimuth() As String
		Get
			Return myXmlDoc.SelectSingleNode("//SUN_AZIMUTH").InnerText
		End Get
	End Property

	''' <summary>
	''' Get the Sun Elevation for the dataset.
	''' </summary>
	Public ReadOnly Property SunElevation() As String
		Get
			Return myXmlDoc.SelectSingleNode("//SUN_ELEVATION").InnerText
		End Get
	End Property

	''' <summary>
	''' Get the epsg code for the spatial reference of the dataset.
	''' </summary>
	Public ReadOnly Property SrsCode() As String
		Get
			Return myXmlDoc.SelectSingleNode("//HORIZONTAL_CS_CODE").InnerText
		End Get
	End Property

	''' <summary>
	''' Get the Sensor Azimuth for the dataset.
	''' </summary>
	Public ReadOnly Property SensorAzimuth() As String
		Get
			Dim qualityAssessments As XmlNodeList = myXmlDoc.SelectNodes("//Quality_Assessment")
			Dim qualityPars As XmlNodeList = qualityAssessments(1).SelectNodes("Quality_Parameter")
			For i As Integer = 0 To qualityPars.Count - 1
				If qualityPars(i).SelectSingleNode("QUALITY_PARAMETER_CODE").InnerText.Contains("SENSOR_AZIMUTH") Then
					Return qualityPars(i).SelectSingleNode("QUALITY_PARAMETER_VALUE").InnerText
				End If
			Next
			Return ""
		End Get
	End Property
End Class