SubsetRasterTool.vb
Multithreaded raster subset
SubsetRasterTool.vb
' Copyright 2006 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 Microsoft.VisualBasic
Imports System
Imports System.Drawing
Imports System.Runtime.InteropServices
Imports System.Threading
Imports System.IO
Imports ESRI.ArcGIS.ADF.BaseClasses
Imports ESRI.ArcGIS.ADF.CATIDs
Imports ESRI.ArcGIS.Controls
Imports System.Windows.Forms
Imports ESRI.ArcGIS.Carto
Imports ESRI.ArcGIS.Display
Imports ESRI.ArcGIS.Geometry
Imports ESRI.ArcGIS.Geodatabase
Imports ESRI.ArcGIS.DataSourcesRaster
Imports ESRI.ArcGIS.DataSourcesGDB
Imports ESRI.ArcGIS.esriSystem


  ''' <summary>
  ''' Class used to pass the task information to the working thread
  ''' </summary>
Public Class TaskInfo
  'defauld constructor
  Public Sub New()

  End Sub

  Public Sub New(ByVal BandID As Integer, ByVal doneEvent As ManualResetEvent)
    m_bandID = BandID
    m_doneEvent = doneEvent
  End Sub

  'public RegisteredWaitHandle Handle = null;
  Private m_bandID As Integer = 0

  'the even used to signal the main thread that the thread has finished its task
  Private m_doneEvent As ManualResetEvent

  Private m_rows As Integer
  Private m_columns As Integer
  Private m_i0 As Integer
  Private m_j0 As Integer
  Private m_outputRasterName As String
  Private m_outputRasterPath As String
  Private m_inputRasterName As String
  Private m_inputRasterWSConnectionProps As String

  Private m_workspaceType As esriWorkspaceType


  Public Property BandID() As Integer
    Get
      Return m_bandID
    End Get
    Set(ByVal value As Integer)
      m_bandID = value
    End Set
  End Property

  Public Property DoneEvent() As ManualResetEvent
    Get
      Return m_doneEvent
    End Get
    Set(ByVal value As ManualResetEvent)
      m_doneEvent = value
    End Set
  End Property

  Public Property InputRasterName() As String
    Get
      Return m_inputRasterName
    End Get
    Set(ByVal value As String)
      m_inputRasterName = value
    End Set
  End Property

  Public Property OutputRasterPath() As String
    Get
      Return m_outputRasterPath
    End Get
    Set(ByVal value As String)
      m_outputRasterPath = value
    End Set
  End Property

  Public Property OutputRasterName() As String
    Get
      Return m_outputRasterName
    End Get
    Set(ByVal value As String)
      m_outputRasterName = value
    End Set
  End Property

  Public Property Columns() As Integer
    Get
      Return m_columns
    End Get
    Set(ByVal value As Integer)
      m_columns = value
    End Set
  End Property

  Public Property Rows() As Integer
    Get
      Return m_rows
    End Get
    Set(ByVal value As Integer)
      m_rows = value
    End Set
  End Property

  Public Property WorkspaceType() As esriWorkspaceType
    Get
      Return m_workspaceType
    End Get
    Set(ByVal value As esriWorkspaceType)
      m_workspaceType = value
    End Set
  End Property

  Public Property InputRasterWSConnectionProps() As String
    Get
      Return m_inputRasterWSConnectionProps
    End Get
    Set(ByVal value As String)
      m_inputRasterWSConnectionProps = value
    End Set
  End Property

  Public Property I0() As Integer
    Get
      Return m_i0
    End Get
    Set(ByVal value As Integer)
      m_i0 = value
    End Set
  End Property

  Public Property J0() As Integer
    Get
      Return m_j0
    End Get
    Set(ByVal value As Integer)
      m_j0 = value
    End Set
  End Property
End Class


  ''' <summary>
  ''' Summary description for SubsetRasterTool.
  ''' </summary>
<Guid("9587dd55-595f-4feb-99f9-56f1bdb4d09c"), ClassInterface(ClassInterfaceType.None), ProgId("SubsetRasterLayer.SubsetRasterTool")> _
Public NotInheritable Class SubsetRasterTool
  Inherits BaseTool
#Region "COM Registration Function(s)"
  <ComRegisterFunction(), ComVisible(False)> _
  Private Shared Sub RegisterFunction(ByVal registerType As Type)
    ' Required for ArcGIS Component Category Registrar support
    ArcGISCategoryRegistration(registerType)

    '
    ' TODO: Add any COM registration code here
    '
  End Sub

  <ComUnregisterFunction(), ComVisible(False)> _
  Private Shared Sub UnregisterFunction(ByVal registerType As Type)
    ' Required for ArcGIS Component Category Registrar support
    ArcGISCategoryUnregistration(registerType)

    '
    ' TODO: Add any COM unregistration code here
    '
  End Sub

#Region "ArcGIS Component Category Registrar generated code"
  ''' <summary>
  ''' Required method for ArcGIS Component Category registration -
  ''' Do not modify the contents of this method with the code editor.
  ''' </summary>
  Private Shared Sub ArcGISCategoryRegistration(ByVal registerType As Type)
    Dim regKey As String = String.Format("HKEY_CLASSES_ROOT\CLSID\{{{0}}}", registerType.GUID)
    ControlsCommands.Register(regKey)
    MxCommands.Register(regKey)

  End Sub
  ''' <summary>
  ''' Required method for ArcGIS Component Category unregistration -
  ''' Do not modify the contents of this method with the code editor.
  ''' </summary>
  Private Shared Sub ArcGISCategoryUnregistration(ByVal registerType As Type)
    Dim regKey As String = String.Format("HKEY_CLASSES_ROOT\CLSID\{{{0}}}", registerType.GUID)
    ControlsCommands.Unregister(regKey)
    MxCommands.Unregister(regKey)

  End Sub

#End Region
#End Region

#Region "class members"

  Private Enum FormatType
    IMG = 0
    TIFF
    BMP
    GRID
  End Enum

  Private m_pHookHelper As IHookHelper = Nothing
  Private m_rasterLayer As IRasterLayer = Nothing
  Private m_progressDlg As frmProgress = Nothing
  Private m_outputRasterPath As String = String.Empty
  Private m_outputRasterName As String = String.Empty
  Private m_strInputRasterWorkspaceConnectionProps As String = String.Empty
  Private m_inputRasterName As String = String.Empty
  Private m_intRows As Integer = 0
  Private m_intCols As Integer = 0
  Private m_intBandCount As Integer = 0
  Private m_i0 As Integer = 0
  Private m_j0 As Integer = 0
  Private m_workSpaceType As esriWorkspaceType
  Private m_outputFormat As FormatType

  'used in order to block access to the shared resource (raster dataset)
  Private Shared m_autoEvent As AutoResetEvent = New AutoResetEvent(False)

  Private m_lockObject As Object = Nothing

#End Region

#Region "class constructor"
  Public Sub New()
    MyBase.m_category = ".NET Samples"
    MyBase.m_caption = "Subset Raster MT"
    MyBase.m_message = "Subset Raster using multi threading"
    MyBase.m_toolTip = "Subset Raster MT"
    MyBase.m_name = MyBase.m_category & "_" & MyBase.m_caption

    Try
      Dim bitmapResourceName As String = Me.GetType().Name & ".bmp"
      MyBase.m_bitmap = New Bitmap(Me.GetType(), bitmapResourceName)
      MyBase.m_cursor = New System.Windows.Forms.Cursor(Me.GetType(), Me.GetType().Name & ".cur")
    Catch ex As Exception
      System.Diagnostics.Trace.WriteLine(ex.Message, "Invalid Bitmap")
    End Try
  End Sub
#End Region

#Region "Overriden Class Methods"

  ''' <summary>
  ''' Occurs when this command is created
  ''' </summary>
  ''' <param name="hook">Instance of the application</param>
  Public Overrides Sub OnCreate(ByVal hook As Object)
    If m_pHookHelper Is Nothing Then
      m_pHookHelper = New HookHelperClass()
    End If

    m_pHookHelper.Hook = hook
  End Sub

  Public Overrides Sub OnMouseDown(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Integer, ByVal Y As Integer)
    Try
      'use rubber-band in order to getthe subset area from the user
      Dim rubberBand As IRubberBand2 = New RubberEnvelopeClass()
      Dim envelope As IEnvelope = CType(rubberBand.TrackNew(m_pHookHelper.ActiveView.ScreenDisplay, Nothing), IEnvelope)
      If Nothing Is envelope OrElse envelope.IsEmpty Then
        Return
      End If

      ' verify that there are layers in the map
      If m_pHookHelper.FocusMap.LayerCount = 0 Then
        Return
      End If

      ' get the top raster layer
      m_rasterLayer = Nothing
      Dim layers As IEnumLayer = m_pHookHelper.FocusMap.Layers(Nothing, False)
      layers.Reset()
      Dim layer As ILayer = layers.Next()
      Do While Not layer Is Nothing
        If TypeOf layer Is IRasterLayer Then
          m_rasterLayer = CType(layer, IRasterLayer)
          Exit Do
        End If
        layer = layers.Next()
      Loop

      If m_rasterLayer Is Nothing Then
        MessageBox.Show("There is no raster layer in the map")
        Return
      End If

      ' assert if there is no overlap between the user envelope and the top raster
      envelope.Intersect(m_rasterLayer.AreaOfInterest)
      If envelope.IsEmpty Then
        MessageBox.Show("The delineated envelope does not intersect the topmost raster in the map")
        Return
      End If

      'query number of bands in the input raster
      m_intBandCount = m_rasterLayer.BandCount

      Dim dataset As IDataset = CType(m_rasterLayer, IDataset)

      'get the workspace connection props since we'll need to connect it from each of the worker threads
      Dim connectionProps As IPropertySet = dataset.Workspace.ConnectionProperties

      'get the workspace type
      m_workSpaceType = dataset.Workspace.Type

      'serialize the connection properties into a string in order to avoid cross apartment calls
      Dim xmlSerializer As IXMLSerializer = New XMLSerializerClass()
      m_strInputRasterWorkspaceConnectionProps = xmlSerializer.SaveToString(connectionProps, Nothing, Nothing)

      'm_inputRasterName = dataset.Name;
      m_inputRasterName = m_rasterLayer.FilePath

      'open a folder-browser in order to get the output location of the subset raster
      Dim folderDlg As FolderBrowserDialog = New FolderBrowserDialog()
      folderDlg.Description = "Select output folder for the subset raster"
      folderDlg.ShowNewFolderButton = True

      If System.Windows.Forms.DialogResult.OK <> folderDlg.ShowDialog() Then
        Return
      End If
      'get the output folder that the user had chosen
      m_outputRasterPath = folderDlg.SelectedPath

      'get the input raster layer name (will be used in order to create the output raster)
      Dim inputRasterLayerName As String = m_rasterLayer.Name
      If inputRasterLayerName.IndexOf(".") <> -1 Then
        inputRasterLayerName = inputRasterLayerName.Substring(0, inputRasterLayerName.IndexOf("."))
      End If

      'set the output format
      m_outputFormat = FormatType.IMG

      'create the output raster dataset
      CreateRasterDataset(m_rasterLayer, m_outputRasterPath, inputRasterLayerName & "_Subset.img", envelope)

      'show the output progress form
      m_progressDlg = New frmProgress()
      'force the control creation in order to make sure that the form has a handle
      m_progressDlg.CreateControl()
      'set the statusbar message of the progress dialog
      m_progressDlg.SetStatusBarMessage("Subsetting raster " & m_rasterLayer.Name)
      'show the progress dialog
      m_progressDlg.Show()


      'run the subset thread. The subset process uses WaitHandle in order to
      'wait on the subsetting threads to finish their task. In order to use WaitHandle.WaitAll()
      'the call must be made from a MTA. The current command is STA and therefore a thread must be created 
      'in order to allow this(.NET threads gets created as MTA by default).
      Dim t As Thread = New Thread(AddressOf SubsetProc)
      t.Start()
    Catch ex As Exception
      System.Diagnostics.Trace.WriteLine(ex.Message)
    End Try
  End Sub

  Public Overrides Sub OnMouseMove(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Integer, ByVal Y As Integer)
    ' TODO:  Add SubsetRasterTool.OnMouseMove implementation
  End Sub

  Public Overrides Sub OnMouseUp(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Integer, ByVal Y As Integer)
    ' TODO:  Add SubsetRasterTool.OnMouseUp implementation
  End Sub
#End Region

  ''' <summary>
  ''' main subset method
  ''' </summary>
  ''' <remarks>Please note that this sample does not copy colormap
  ''' in case where it is present. The outcome will be black and white</remarks>
  Private Sub SubsetProc()
    Try
      m_lockObject = New Object()

      'set the message on the progress dialog statusbar  
      m_progressDlg.SetStatusBarMessage("starting subset process")

      'create ManualResetEvent in order to notify the main threads that all threads 
      'are done with their task
      Dim doneEvents As ManualResetEvent() = New ManualResetEvent(m_intBandCount - 1) {}

      'create the subset threads
      Dim threadTask As Thread() = New Thread(m_intBandCount - 1) {}

      'create a thread for each of the bands of the input raster
      'each task will subset a different raster-band. The information required for the 
      'subsetting the raster-band will be passed to the task by the user-defined 
      'class TaskInfo
      Dim i As Integer = 0
      Do While i < m_intBandCount
        'create the ManualResetEvent flad for the task in order to signal the waiting thread
        'that the task had been completed
        doneEvents(i) = New ManualResetEvent(False)

        'create the Task-Information for the thread and pass in the relevant information
        'Pleas note that all the information is passed as simple types
        Dim ti As TaskInfo = New TaskInfo(i, doneEvents(i))
        ti.WorkspaceType = m_workSpaceType
        ti.Rows = m_intRows
        ti.Columns = m_intCols
        ti.I0 = m_i0
        ti.J0 = m_j0
        ti.InputRasterName = m_inputRasterName
        ti.OutputRasterPath = m_outputRasterPath
        ti.OutputRasterName = m_outputRasterName
        ti.InputRasterWSConnectionProps = m_strInputRasterWorkspaceConnectionProps

        ' assign the subsetting thread for the rasterband.
        threadTask(i) = New Thread(New ParameterizedThreadStart(AddressOf SubsetRasterBand))
        ' Note the STA apartment which is required to run ArcObjects
        threadTask(i).SetApartmentState(ApartmentState.STA)
        threadTask(i).Name = "Subset_" & (i + 1).ToString()
        ' start the task and pass the task information
        threadTask(i).Start(CObj(ti))
        i += 1
      Loop

      'Sets the state of the event to signaled, thus allowing one or more of the waiting threads to proceed
      m_autoEvent.Set()

      ' Wait for all threads to complete their task...
      WaitHandle.WaitAll(doneEvents)

      ' verify that all tasks are done and that the memory is cleaned
      i = 0
      Do While i < m_intBandCount
        threadTask(i).Join()
        i += 1
      Loop
      threadTask = Nothing

      ' spin a new thread in order to claculate statistics and pyramid layers
      Dim additionalDataTask As Thread = New Thread(New ParameterizedThreadStart(AddressOf CalculateStatsAndPyramids))
      additionalDataTask.SetApartmentState(ApartmentState.STA)
      additionalDataTask.Name = "calc_aux_data"
      additionalDataTask.Start(System.IO.Path.Combine(m_outputRasterPath, m_outputRasterName))
    Catch ex As Exception
      System.Diagnostics.Trace.WriteLine(ex.Message)
    End Try
  End Sub

  ''' <summary>
  ''' Opens the new raster dataset, calculate statistics and build pyramids
  ''' </summary>
  ''' <param name="data"></param>
  Private Sub CalculateStatsAndPyramids(ByVal data As Object)
    Dim path As String = System.IO.Path.GetDirectoryName(CStr(data))
    Dim fileName As String = System.IO.Path.GetFileName(CStr(data))
    Dim rasterDataset As IRasterDataset = OpenOutputRasterDataset(path, fileName)

    Dim rasterBandColl As IRasterBandCollection = CType(rasterDataset, IRasterBandCollection)

    ' calculate statistics and histogram
    Dim i As Integer = 0
    Do While i < rasterBandColl.Count
      m_progressDlg.SetStatusBarMessage("Computing statistics for band #" & (i + 1))
      Dim newRasterBand As IRasterBand = rasterBandColl.Item(i)
      newRasterBand.ComputeStatsAndHist()
      i += 1
    Loop

    ' build pyramids
    m_progressDlg.SetStatusBarMessage("Building pyramids")
    CType(rasterDataset, IRasterPyramid).Create()

    'set the message on the progress dialog
    m_progressDlg.SetStatusBarMessage("Finished subsetting raster " & m_inputRasterName)
    'close the progress dialog
    m_progressDlg.CloseDlg()
  End Sub

  ''' <summary>
  ''' create a new raster dataset based on input rasterLayer and a given extent
  ''' </summary>
  ''' <param name="rasterLayer">The input raster layer</param>
  ''' <param name="path">path for the output subser raster</param>
  ''' <param name="rasterName">name of the output subset raster</param>
  ''' <param name="extent">extent of the subset raster</param>
  Private Sub CreateRasterDataset(ByVal rasterLayer As IRasterLayer, ByVal path As String, ByVal rasterName As String, ByVal extent As IEnvelope)
    Try
      'cache the name of the output subset raster
      m_outputRasterName = rasterName

      'create a new workspace-factory
      Dim workspaceFact As IWorkspaceFactory = New RasterWorkspaceFactoryClass()

      'open a raster workspace
      Dim rasterWorkspace As IRasterWorkspace2 = CType(workspaceFact.OpenFromFile(path, 0), IRasterWorkspace2)

      'in case that there is already an existing raster with the raster name, try to delete it
      If System.IO.File.Exists(System.IO.Path.Combine(path, rasterName)) Then

        Dim dataset As IDataset = TryCast(rasterWorkspace.OpenRasterDataset(rasterName), IDataset)
        dataset.Delete()
      End If

      'get the raster layer properties
      Dim rasterProps As IRasterProps = CType(rasterLayer.Raster, IRasterProps)

      'the input raster cellsize (please note that it is possible that it will not be square)
      Dim cellSizeX As Double = rasterProps.MeanCellSize().X
      Dim cellSizeY As Double = rasterProps.MeanCellSize().Y

      'the stating indices (in pixels) for the upper left corner of the subset raster
      m_i0 = Convert.ToInt32((extent.XMin - rasterProps.Extent.XMin) / cellSizeX)
      m_j0 = Convert.ToInt32(rasterProps.Height - ((extent.YMax - rasterProps.Extent.YMin) / cellSizeY))

      'calculate the number of rows and columns for subset
      m_intCols = Convert.ToInt32(extent.Width / cellSizeX + 0.5)
      m_intRows = Convert.ToInt32(extent.Height / cellSizeY + 0.5)


      'need to shift the point of origin so that it will allign with the origonal raster pixels
      Dim llx As Double = rasterProps.Extent.XMin + m_i0 * cellSizeX
      Dim lly As Double = rasterProps.Extent.YMin + (rasterProps.Height - (m_intRows + m_j0)) * cellSizeY
      Dim point As IPoint = New PointClass()
      point.PutCoords(llx, lly)

      Dim format_Type As String = String.Empty
      Select Case m_outputFormat
        Case FormatType.IMG
          format_Type = "IMAGINE Image"
        Case FormatType.TIFF
          format_Type = "TIFF"
        Case FormatType.GRID
          format_Type = "GRID"
        Case FormatType.BMP
          format_Type = "BMP"
      End Select

      'create the subset raster dataset
      rasterWorkspace.CreateRasterDataset(rasterName, format_Type, point, m_intCols, m_intRows, cellSizeX, cellSizeY, rasterLayer.BandCount, rasterProps.PixelType, rasterProps.SpatialReference, True)
    Catch ex As Exception
      System.Diagnostics.Trace.WriteLine(ex.Message)
    End Try
  End Sub

  ''' <summary>
  ''' subset task method
  ''' </summary>
  ''' <param name="state"></param>
  Private Sub SubsetRasterBand(ByVal state As Object)
    Try
      System.Diagnostics.Trace.WriteLine(Thread.CurrentThread.GetApartmentState())
      ' The state object must be cast to the correct type, because the
      ' signature of the WaitOrTimerCallback delegate specifies type
      ' Object.
      Dim ti As TaskInfo = CType(state, TaskInfo)

      'set the message on the progress dialog statusbar
      m_progressDlg.SetStatusBarMessage("Copy RasterBand #" & (ti.BandID + 1).ToString())


      Dim rasterataset As IRasterDataset = OpenRasterDataset(ti)

      'cast the input raster bandCollection
      Dim rasterBaneCollection As IRasterBandCollection = CType(rasterataset, IRasterBandCollection)

      'get the raster band from which to copy the data
      Dim rasterBand As IRasterBand = rasterBaneCollection.Item(ti.BandID)
      Dim rawPixels As IRawPixels = CType(rasterBand, IRawPixels)

      'open the newly created raster dataset
      Dim newRasterDataset As IRasterDataset = OpenOutputRasterDataset(ti.OutputRasterPath, ti.OutputRasterName)

      'get the raster-band into which data will be copied
      Dim newRasterBandCollection As IRasterBandCollection = CType(newRasterDataset, IRasterBandCollection)
      Dim newRasterBand As IRasterBand = newRasterBandCollection.Item(ti.BandID)
      Dim newRasterPixels As IRawPixels = CType(newRasterBand, IRawPixels)

      'set the size of the pixelBlock
      Dim pixBlockSizeX As Integer = Math.Min(128, m_intCols)
      Dim pixBlockSizeY As Integer = Math.Min(128, m_intRows)

      'calculate the progressbar MaxValue
      Dim hBlocks As Integer = Convert.ToInt32(m_intCols / pixBlockSizeX + 0.5)
      Dim vBlocks As Integer = Convert.ToInt32(m_intRows / pixBlockSizeY + 0.5)

      'set the maximum value of the progress controls of the progress dialog
      If ti.BandID = 0 Then
        m_progressDlg.SetProgressbarMaximum(hBlocks * vBlocks)
      End If

      'set the upper left pixel from which the subset starts
      Dim pnt As IPnt = New PntClass()
      pnt.SetCoords(Convert.ToDouble(pixBlockSizeX), Convert.ToDouble(pixBlockSizeY))

      'instantiate the Top-Left-Corner to be used with the pixel-block for the copying process
      Dim tlc As IPnt = New PntClass()

      Monitor.Enter(m_lockObject)
      'create the pixelBlock for the copying process
      Dim pixelBlock As IPixelBlock = rawPixels.CreatePixelBlock(pnt)
      Monitor.Exit(m_lockObject)

      Dim p As Integer = 0
      Dim q As Integer = 0

      'read the raster band info from the input raster-band and write it to the new raster-band
      Dim j As Integer = ti.J0
      Do While j < (ti.J0 + m_intRows)
        Dim i As Integer = ti.I0
        Do While i < (ti.I0 + m_intCols)
          'set the upper-left corner coordinate
          tlc.SetCoords(Convert.ToDouble(i), Convert.ToDouble(j))

          ' block the other thread while reading from the raster
          m_autoEvent.WaitOne()

          'read the pixels from the input raster-band into the pixel-block
          rawPixels.Read(tlc, CType(pixelBlock, PixelBlock))

          m_autoEvent.Set()

          'set the upper-left corner coordinate in order to write into the subset raster
          tlc.SetCoords(Convert.ToDouble(p), Convert.ToDouble(q))


          ' block the other thread while writing to the output raster
          m_autoEvent.WaitOne()

          'write the pixels to the output rasterband
          newRasterPixels.Write(tlc, CType(pixelBlock, PixelBlock))

          m_autoEvent.Set()

          'increment the horizontal coordinate of the output rasterband
          p += pixBlockSizeX

          'increment the progressbars
          If ti.BandID < 3 Then
            m_progressDlg.IncrementProgressBar(ti.BandID + 1)
          End If
          i += pixBlockSizeX
        Loop
        'reset the horizontal coordinate of the output rasterband
        p = 0
        'increment the vertical coordinate of the output rasterband
        q += pixBlockSizeY
        j += pixBlockSizeY
      Loop

      '// wait untill all copy thraeds are done copying
      '//calculate statistics
      '//need to lock all other threads in order to write to the rasterband
      'm_autoEvent.WaitOne();
      '//Monitor.Enter(m_lockObject);

      'm_progressDlg.SetStatusBarMessage("compute statistics for band #" + (ti.BandID + 1).ToString());
      'newRasterBand.ComputeStatsAndHist();

      '//set the message on the progress dialog statusbar
      'm_progressDlg.SetStatusBarMessage("done subsetting band #" + (ti.BandID + 1).ToString());

      '////signal the next available thread to write to the subset dataset
      '//Monitor.Exit(m_lockObject);
      'm_autoEvent.Set();

      'signal the main thread the that thread has finished its task
      ti.DoneEvent.Set()
    Catch ex As Exception
      'm_autoEvent.Set();
      System.Diagnostics.Trace.WriteLine(ex.Message)
    End Try
  End Sub

  ''' <summary>
  ''' Opens the new raster dataset
  ''' </summary>
  ''' <param name="rasterPath">path of new raster dataset</param>
  ''' <param name="rasterName">name of new raster dataset</param>
  ''' <returns></returns>
  Private Function OpenOutputRasterDataset(ByVal rasterPath As String, ByVal rasterName As String) As IRasterDataset
    Dim newRasterWorkspaceFactory As IWorkspaceFactory2 = New RasterWorkspaceFactoryClass()
    Monitor.Enter(m_lockObject)
    Dim newRasterWorkspace As IRasterWorkspace2 = TryCast(newRasterWorkspaceFactory.OpenFromFile(rasterPath, 0), IRasterWorkspace2)
    Dim newRasterDataset As IRasterDataset = newRasterWorkspace.OpenRasterDataset(rasterName)
    Monitor.Exit(m_lockObject)

    Return newRasterDataset
  End Function

  ''' <summary>
  ''' given the task information, opens the raster dataset
  ''' </summary>
  ''' <param name="ti"></param>
  ''' <returns></returns>
  Private Function OpenRasterDataset(ByVal ti As TaskInfo) As IRasterDataset
    If Nothing Is ti Then
      Return Nothing
    End If

    'deserialize the workspace connection properties
    Dim xmlSerializer As IXMLSerializer = New XMLSerializerClass()
    Dim obj As Object = xmlSerializer.LoadFromString(ti.InputRasterWSConnectionProps, Nothing, Nothing)
    If Not (TypeOf obj Is IPropertySet) Then
      System.Diagnostics.Trace.WriteLine("object is not a PropertySet...")
      Return Nothing
    End If
    Dim workspaceProperties As IPropertySet = CType(obj, IPropertySet)

    'open the input raster workspace factory
    Dim workspaceFactory As IWorkspaceFactory2 = CreateWorkspaceFactory(ti)

    'open the workspace of the input raster
    Monitor.Enter(m_lockObject)
    Dim rasterWorkSpace As IRasterWorkspace2 = TryCast(workspaceFactory.Open(workspaceProperties, 0), IRasterWorkspace2)
    If Nothing Is rasterWorkSpace Then
      System.Diagnostics.Trace.WriteLine("error opening raster workspace...")
      Return Nothing
    End If

    'open the input raster dataset
    Dim rasterataset As IRasterDataset = rasterWorkSpace.OpenRasterDataset(System.IO.Path.GetFileName(ti.InputRasterName))
    Monitor.Exit(m_lockObject)

    Return rasterataset
  End Function

  ''' <summary>
  ''' create an instance of the right workspace factory
  ''' </summary>
  ''' <param name="ti"></param>
  ''' <returns></returns>
  Private Function CreateWorkspaceFactory(ByVal ti As TaskInfo) As IWorkspaceFactory2
    'test the type of input raster workspace
    Select Case ti.WorkspaceType
      Case esriWorkspaceType.esriFileSystemWorkspace
        Return New RasterWorkspaceFactoryClass()
        'in the case of a local-workspace it can be FileBased GDB or an access DB    
      Case esriWorkspaceType.esriLocalDatabaseWorkspace
        If ti.InputRasterName.IndexOf(".mdb") <> -1 Then
          Return New AccessWorkspaceFactoryClass()
        Else
          Return New FileGDBWorkspaceFactoryClass()
        End If
      Case esriWorkspaceType.esriRemoteDatabaseWorkspace
        Return New SdeWorkspaceFactoryClass()
    End Select

    Return Nothing
  End Function
End Class