Create a custom raster type from the ground up for DMCII data
DMCIIRasterType\DMCIIRasterType.vb
' Copyright 2012 ESRI
' 
' All rights reserved under the copyright laws of the United States
' and applicable international laws, treaties, and conventions.
' 
' You may freely redistribute and use this sample code, with or
' without modification, provided you include the original copyright
' notice and use restrictions.
' 
' See the use restrictions.
' 

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