SubsetRasterTool.cs
Multithreaded raster subset
SubsetRasterTool.cs
// 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.

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Threading;
using System.IO;
using ESRI.ArcGIS.ADF.BaseClasses;
using ESRI.ArcGIS.ADF.CATIDs;
using ESRI.ArcGIS.Controls;
using System.Windows.Forms;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Display;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.DataSourcesRaster;
using ESRI.ArcGIS.DataSourcesGDB;
using ESRI.ArcGIS.esriSystem;


namespace SubsetRasterLayer
{
  /// <summary>
  /// Class used to pass the task information to the working thread
  /// </summary>
  public class TaskInfo
  {
    //defauld constructor
    public TaskInfo()
    {

    }

    public TaskInfo(int BandID, ManualResetEvent doneEvent)
    {
      m_bandID = BandID;
      m_doneEvent = doneEvent;
    }

    //public RegisteredWaitHandle Handle = null;
    private int m_bandID = 0;

    //the even used to signal the main thread that the thread has finished its task
    private ManualResetEvent m_doneEvent;

    private int m_rows;
    private int m_columns;
    private int m_i0;
    private int m_j0;
    private string m_outputRasterName;
    private string m_outputRasterPath;
    private string m_inputRasterName;
    private string m_inputRasterWSConnectionProps;

    private esriWorkspaceType m_workspaceType;


    public int BandID
    {
      get { return m_bandID;  }
      set { m_bandID = value; }
    }

    public ManualResetEvent DoneEvent
    {
      get { return m_doneEvent;  }
      set { m_doneEvent = value; }
    }

    public string InputRasterName
    {
      get { return m_inputRasterName; }
      set { m_inputRasterName = value; }
    }

    public string OutputRasterPath
    {
      get { return m_outputRasterPath; }
      set { m_outputRasterPath = value; }
    }

    public string OutputRasterName
    {
      get { return m_outputRasterName; }
      set { m_outputRasterName = value; }
    }

    public int Columns
    {
      get { return m_columns; }
      set { m_columns = value; }
    }

    public int Rows
    {
      get { return m_rows; }
      set { m_rows = value; }
    }

    public esriWorkspaceType WorkspaceType
    {
      get { return m_workspaceType; }
      set { m_workspaceType = value; }
    }

    public string InputRasterWSConnectionProps
    {
      get { return m_inputRasterWSConnectionProps; }
      set { m_inputRasterWSConnectionProps = value; }
    }

    public int I0
    {
      get { return m_i0; }
      set { m_i0 = value; }
    }

    public int J0
    {
      get { return m_j0; }
      set { m_j0 = value; }
    }
  }


  /// <summary>
  /// Summary description for SubsetRasterTool.
  /// </summary>
  [Guid("9587dd55-595f-4feb-99f9-56f1bdb4d09c")]
  [ClassInterface(ClassInterfaceType.None)]
  [ProgId("SubsetRasterLayer.SubsetRasterTool")]
  public sealed class SubsetRasterTool : BaseTool
  {
    #region COM Registration Function(s)
    [ComRegisterFunction()]
    [ComVisible(false)]
    static void RegisterFunction(Type registerType)
    {
      // Required for ArcGIS Component Category Registrar support
      ArcGISCategoryRegistration(registerType);

      //
      // TODO: Add any COM registration code here
      //
    }

    [ComUnregisterFunction()]
    [ComVisible(false)]
    static void UnregisterFunction(Type registerType)
    {
      // Required for ArcGIS Component Category Registrar support
      ArcGISCategoryUnregistration(registerType);

      //
      // TODO: Add any COM unregistration code here
      //
    }

    #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 static void ArcGISCategoryRegistration(Type registerType)
    {
      string regKey = string.Format("HKEY_CLASSES_ROOT\\CLSID\\{{{0}}}", registerType.GUID);
      ControlsCommands.Register(regKey);
      MxCommands.Register(regKey);

    }
    /// <summary>
    /// Required method for ArcGIS Component Category unregistration -
    /// Do not modify the contents of this method with the code editor.
    /// </summary>
    private static void ArcGISCategoryUnregistration(Type registerType)
    {
      string regKey = string.Format("HKEY_CLASSES_ROOT\\CLSID\\{{{0}}}", registerType.GUID);
      ControlsCommands.Unregister(regKey);
      MxCommands.Unregister(regKey);

    }

    #endregion
    #endregion

    #region class members

    private enum FormatType
    {
      IMG = 0,
      TIFF,
      BMP,
      GRID
    }

    private IHookHelper         m_pHookHelper                               = null;
    private IRasterLayer        m_rasterLayer                               = null;
    private frmProgress         m_progressDlg                               = null;
    private string              m_outputRasterPath                          = string.Empty;
    private string              m_outputRasterName                          = string.Empty;
    private string              m_strInputRasterWorkspaceConnectionProps    = string.Empty;
    private string              m_inputRasterName                           = string.Empty;
    private int                 m_intRows                                   = 0;
    private int                 m_intCols                                   = 0;
    private int                 m_intBandCount                              = 0;
    private int                 m_i0                                        = 0;
    private int                 m_j0                                        = 0;
    private esriWorkspaceType   m_workSpaceType;
    private FormatType          m_outputFormat;

    //used in order to block access to the shared resource (raster dataset)
    private static AutoResetEvent m_autoEvent = new AutoResetEvent(false);

    private object m_lockObject = null;

    #endregion

    #region class constructor
    public SubsetRasterTool()
    {
      base.m_category = ".NET Samples";
      base.m_caption = "Subset Raster MT";
      base.m_message = "Subset Raster using multi threading";
      base.m_toolTip = "Subset Raster MT";
      base.m_name = base.m_category + "_" + base.m_caption;

      try
      {
        string bitmapResourceName = GetType().Name + ".bmp";
        base.m_bitmap = new Bitmap(GetType(), bitmapResourceName);
        base.m_cursor = new System.Windows.Forms.Cursor(GetType(), GetType().Name + ".cur");
      }
      catch (Exception ex)
      {
        System.Diagnostics.Trace.WriteLine(ex.Message, "Invalid Bitmap");
      }
    }
    #endregion

    #region Overriden Class Methods

    /// <summary>
    /// Occurs when this command is created
    /// </summary>
    /// <param name="hook">Instance of the application</param>
    public override void OnCreate(object hook)
    {
      if (m_pHookHelper == null)
        m_pHookHelper = new HookHelperClass();

      m_pHookHelper.Hook = hook;
    }

    public override void OnMouseDown(int Button, int Shift, int X, int Y)
    {
      try
      {
        //use rubber-band in order to getthe subset area from the user
        IRubberBand2 rubberBand = new RubberEnvelopeClass();
        IEnvelope envelope = (IEnvelope)rubberBand.TrackNew(m_pHookHelper.ActiveView.ScreenDisplay, null);
        if (null == envelope || envelope.IsEmpty)
          return;

        // verify that there are layers in the map
        if (m_pHookHelper.FocusMap.LayerCount == 0)
          return;

        // get the top raster layer
        m_rasterLayer = null;
        IEnumLayer layers = m_pHookHelper.FocusMap.get_Layers(null, false);
        layers.Reset();
        ILayer layer = layers.Next();
        while (layer != null)
        {
          if (layer is IRasterLayer)
          {
            m_rasterLayer = (IRasterLayer)layer;
            break;
          }
          layer = layers.Next();
        }

        if (m_rasterLayer == null)
        {
          MessageBox.Show("There is no raster layer in the map");
          return;
        }

        // assert if there is no overlap between the user envelope and the top raster
        envelope.Intersect(m_rasterLayer.AreaOfInterest);
        if (envelope.IsEmpty)
        {
          MessageBox.Show("The delineated envelope does not intersect the topmost raster in the map");
          return;
        }

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

        IDataset dataset = (IDataset)m_rasterLayer;        
        
        //get the workspace connection props since we'll need to connect it from each of the worker threads
        IPropertySet connectionProps = 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
        IXMLSerializer xmlSerializer = new XMLSerializerClass();
        m_strInputRasterWorkspaceConnectionProps = xmlSerializer.SaveToString(connectionProps, null, null);       

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

        //open a folder-browser in order to get the output location of the subset raster
        FolderBrowserDialog folderDlg = new FolderBrowserDialog();
        folderDlg.Description = "Select output folder for the subset raster";
        folderDlg.ShowNewFolderButton = true;

        if (DialogResult.OK != folderDlg.ShowDialog())
          return;
        //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)
        string inputRasterLayerName = m_rasterLayer.Name;
        if (inputRasterLayerName.IndexOf(".") != -1)
          inputRasterLayerName = inputRasterLayerName.Substring(0, inputRasterLayerName.IndexOf("."));

        //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).
        Thread t = new Thread(new ThreadStart(SubsetProc));
        t.Start();
      }
      catch (Exception ex)
      {
        System.Diagnostics.Trace.WriteLine(ex.Message);
      }
    }

    public override void OnMouseMove(int Button, int Shift, int X, int Y)
    {
      // TODO:  Add SubsetRasterTool.OnMouseMove implementation
    }

    public override void OnMouseUp(int Button, int Shift, int X, int Y)
    {
      // TODO:  Add SubsetRasterTool.OnMouseUp implementation
    }
    #endregion

    /// <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 void 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
        ManualResetEvent[] doneEvents = new ManualResetEvent[m_intBandCount];

        //create the subset threads
        Thread[] threadTask = new Thread[m_intBandCount];

        //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
        for (int i = 0; i < m_intBandCount; i++)
        {
          //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
          TaskInfo ti = 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(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((object)ti);
        }

        //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
        for (int i = 0; i < m_intBandCount; i++)
        {
          threadTask[i].Join();
        }
        threadTask = null;

        // spin a new thread in order to claculate statistics and pyramid layers
        Thread additionalDataTask = new Thread(new ParameterizedThreadStart(CalculateStatsAndPyramids));
        additionalDataTask.SetApartmentState(ApartmentState.STA);
        additionalDataTask.Name = "calc_aux_data";
        additionalDataTask.Start(System.IO.Path.Combine(m_outputRasterPath, m_outputRasterName));       
      }
      catch (Exception ex)
      {
        System.Diagnostics.Trace.WriteLine(ex.Message);
      }
    }

    /// <summary>
    /// Opens the new raster dataset, calculate statistics and build pyramids
    /// </summary>
    /// <param name="data"></param>
    private void CalculateStatsAndPyramids(object data)
    {
      string path = System.IO.Path.GetDirectoryName((string)data);
      string fileName = System.IO.Path.GetFileName((string)data);
      IRasterDataset rasterDataset = OpenOutputRasterDataset(path, fileName);

      IRasterBandCollection rasterBandColl = (IRasterBandCollection)rasterDataset;

      // calculate statistics and histogram
      for (int i = 0; i < rasterBandColl.Count; i++)
      {
        m_progressDlg.SetStatusBarMessage("Computing statistics for band #" + (i + 1));
        IRasterBand newRasterBand = rasterBandColl.Item(i);
        newRasterBand.ComputeStatsAndHist();
      }
     
      // build pyramids
      m_progressDlg.SetStatusBarMessage("Building pyramids");
      ((IRasterPyramid)rasterDataset).Create();

      //set the message on the progress dialog
      m_progressDlg.SetStatusBarMessage("Finished subsetting raster " + m_inputRasterName);
      //close the progress dialog
      m_progressDlg.CloseDlg();
    }

    /// <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 void CreateRasterDataset(IRasterLayer rasterLayer, string path, string rasterName,IEnvelope extent)
    {
      try
      {
        //cache the name of the output subset raster
        m_outputRasterName = rasterName;

        //create a new workspace-factory
        IWorkspaceFactory workspaceFact = new RasterWorkspaceFactoryClass();

        //open a raster workspace
        IRasterWorkspace2 rasterWorkspace = (IRasterWorkspace2)workspaceFact.OpenFromFile(path, 0);

        //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)))
        {

          IDataset dataset = rasterWorkspace.OpenRasterDataset(rasterName) as IDataset;
          dataset.Delete();
        }

        //get the raster layer properties
        IRasterProps rasterProps = (IRasterProps)rasterLayer.Raster;

        //the input raster cellsize (please note that it is possible that it will not be square)
        double cellSizeX = rasterProps.MeanCellSize().X;
        double cellSizeY = 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
        double llx = rasterProps.Extent.XMin + m_i0 * cellSizeX;
        double lly = rasterProps.Extent.YMin + (rasterProps.Height - (m_intRows + m_j0)) * cellSizeY;
        IPoint point = new PointClass();
        point.PutCoords(llx, lly);

        string formatType = string.Empty;
        switch (m_outputFormat)
        {
          case FormatType.IMG:
            formatType = "IMAGINE Image";
            break;
          case FormatType.TIFF:
            formatType = "TIFF";
            break;
          case FormatType.GRID:
            formatType = "GRID";
            break;
          case FormatType.BMP:
            formatType = "BMP";
            break;
        }

        //create the subset raster dataset
        rasterWorkspace.CreateRasterDataset(rasterName,
                                             formatType,
                                             point,
                                             m_intCols,
                                             m_intRows,
                                             cellSizeX,
                                             cellSizeY,
                                             rasterLayer.BandCount,
                                             rasterProps.PixelType,
                                             rasterProps.SpatialReference,
                                             true);
      }
      catch (Exception ex)
      {
        System.Diagnostics.Trace.WriteLine(ex.Message);
      }
    }

    /// <summary>
    /// subset task method
    /// </summary>
    /// <param name="state"></param>
    private void SubsetRasterBand(object state)
    {
      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.
        TaskInfo ti = (TaskInfo)state;

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


        IRasterDataset rasterataset = OpenRasterDataset(ti);

        //cast the input raster bandCollection
        IRasterBandCollection rasterBaneCollection = (IRasterBandCollection)rasterataset;
        
        //get the raster band from which to copy the data
        IRasterBand rasterBand = rasterBaneCollection.Item(ti.BandID);
        IRawPixels rawPixels = (IRawPixels)rasterBand;

        //open the newly created raster dataset
        IRasterDataset newRasterDataset = OpenOutputRasterDataset(ti.OutputRasterPath, ti.OutputRasterName);

        //get the raster-band into which data will be copied
        IRasterBandCollection newRasterBandCollection = (IRasterBandCollection)newRasterDataset;
        IRasterBand newRasterBand = newRasterBandCollection.Item(ti.BandID);
        IRawPixels newRasterPixels = (IRawPixels)newRasterBand;
                
        //set the size of the pixelBlock
        int pixBlockSizeX = Math.Min(128, m_intCols);
        int pixBlockSizeY = Math.Min(128, m_intRows);

        //calculate the progressbar MaxValue
        int hBlocks = Convert.ToInt32(m_intCols / pixBlockSizeX + 0.5);
        int vBlocks = Convert.ToInt32(m_intRows / pixBlockSizeY + 0.5);

        //set the maximum value of the progress controls of the progress dialog
        if (ti.BandID == 0)
          m_progressDlg.SetProgressbarMaximum(hBlocks * vBlocks);

        //set the upper left pixel from which the subset starts
        IPnt pnt = 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
        IPnt tlc = new PntClass();

        Monitor.Enter(m_lockObject);
        //create the pixelBlock for the copying process
        IPixelBlock pixelBlock = rawPixels.CreatePixelBlock(pnt);
        Monitor.Exit(m_lockObject);

        int p = 0;
        int q = 0;

        //read the raster band info from the input raster-band and write it to the new raster-band
        for (int j = ti.J0; j < (ti.J0 + m_intRows); j += pixBlockSizeY)
        {
          for (int i = ti.I0; i < (ti.I0 + m_intCols); i += pixBlockSizeX)
          {
            //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, (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, (PixelBlock)pixelBlock);

            m_autoEvent.Set();

            //increment the horizontal coordinate of the output rasterband
            p += pixBlockSizeX;

            //increment the progressbars
            if (ti.BandID < 3)
            {
              m_progressDlg.IncrementProgressBar(ti.BandID + 1);
            }
          }
          //reset the horizontal coordinate of the output rasterband
          p = 0;
          //increment the vertical coordinate of the output rasterband
          q += pixBlockSizeY;
        }

        //// 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 (Exception ex)
      {
        //m_autoEvent.Set();
        System.Diagnostics.Trace.WriteLine(ex.Message);
      }
    }

    /// <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 IRasterDataset OpenOutputRasterDataset(string rasterPath, string rasterName)
    {
      IWorkspaceFactory2 newRasterWorkspaceFactory = new RasterWorkspaceFactoryClass();
      Monitor.Enter(m_lockObject);
      IRasterWorkspace2 newRasterWorkspace = newRasterWorkspaceFactory.OpenFromFile(rasterPath, 0) as IRasterWorkspace2;
      IRasterDataset newRasterDataset = newRasterWorkspace.OpenRasterDataset(rasterName);
      Monitor.Exit(m_lockObject);

      return newRasterDataset;
    }

    /// <summary>
    /// given the task information, opens the raster dataset
    /// </summary>
    /// <param name="ti"></param>
    /// <returns></returns>
    private IRasterDataset OpenRasterDataset(TaskInfo ti)
    {
      if (null == ti)
        return null;

      //deserialize the workspace connection properties
      IXMLSerializer xmlSerializer = new XMLSerializerClass();
      object obj = xmlSerializer.LoadFromString(ti.InputRasterWSConnectionProps, null, null);
      if (!(obj is IPropertySet))
      {
        System.Diagnostics.Trace.WriteLine("object is not a PropertySet...");
        return null;
      }
      IPropertySet workspaceProperties = (IPropertySet)obj;

      //open the input raster workspace factory
      IWorkspaceFactory2 workspaceFactory = CreateWorkspaceFactory(ti);

      //open the workspace of the input raster
      Monitor.Enter(m_lockObject);
      IRasterWorkspace2 rasterWorkSpace = workspaceFactory.Open(workspaceProperties, 0) as IRasterWorkspace2;
      if (null == rasterWorkSpace)
      {
        System.Diagnostics.Trace.WriteLine("error opening raster workspace...");
        return null;
      }

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

      return rasterataset;
    }

    /// <summary>
    /// create an instance of the right workspace factory
    /// </summary>
    /// <param name="ti"></param>
    /// <returns></returns>
    IWorkspaceFactory2 CreateWorkspaceFactory(TaskInfo ti)
    {
      //test the type of input raster workspace
      switch (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)
            return new AccessWorkspaceFactoryClass();
          else
            return new FileGDBWorkspaceFactoryClass();
        case esriWorkspaceType.esriRemoteDatabaseWorkspace:
          return new SdeWorkspaceFactoryClass();
      }

      return null;
    }
  }
}