/// <summary>
        /// This creates a window from this raster.  The window will still save to the same
        /// source file, but only has access to a small window of data, so it can be loaded like a buffer.
        /// The georeferenced extents will be for the new window, not the original raster.  startRow and endRow
        /// will exist in the new raster, however, so that it knows how to copy values back to the original raster.
        /// </summary>
        /// <param name="startRow">The 0 based integer index of the top row to get from this raster.  If this raster is itself a window, 0 represents the startRow from the file.</param>
        /// <param name="endRow">The integer index of the bottom row to get from this raster.  The largest allowed value is NumRows - 1.</param>
        /// <param name="startColumn">The 0 based integer index of the leftmost column to get from this raster.  If this raster is a window, 0 represents the startColumn from the file.</param>
        /// <param name="endColumn">The 0 based integer index of the rightmost column to get from this raster.  The largest allowed value is NumColumns - 1</param>
        /// <param name="inRam">Boolean.  If this is true and the window is small enough, a copy of the values will be loaded into memory.</param>
        /// <returns>An implementation of IRaster</returns>
        public new IRaster GetWindow(int startRow, int endRow, int startColumn, int endColumn, bool inRam)
        {
            int numCols = endColumn - startColumn + 1;
            int numRows = endRow - startRow + 1;
            BinaryShortRaster result = new BinaryShortRaster();
            result.Filename = Filename;
            result.Projection = Projection;
            result.NumRows = endRow - startRow + 1;
            result.NumColumns = endColumn - startColumn + 1;
            result.NumRowsInFile = NumRowsInFile;
            result.NumColumnsInFile = NumColumnsInFile;
            result.NoDataValue = NoDataValue;
            result.StartColumn = startColumn + StartColumn;
            result.StartRow = startRow + StartRow;
            result.EndColumn = endColumn + StartColumn;
            result.EndRow = EndRow + StartRow;

            // Reposition the "raster" so that it matches the window, not the whole raster
            // X = [0] + [1] * column + [2] * row;
            // Y = [3] + [4] * column + [5] * row;
            result.Bounds = new RasterBounds(result.NumRows, result.NumColumns, new double[6]);
            result.Bounds.AffineCoefficients[0] = Bounds.AffineCoefficients[0] +
                                                  Bounds.AffineCoefficients[1]*startColumn +
                                                  Bounds.AffineCoefficients[2]*startRow;
            result.Bounds.AffineCoefficients[1] = Bounds.AffineCoefficients[1];
            result.Bounds.AffineCoefficients[2] = Bounds.AffineCoefficients[2];
            result.Bounds.AffineCoefficients[3] = Bounds.AffineCoefficients[3] +
                                                  Bounds.AffineCoefficients[4]*startColumn +
                                                  Bounds.AffineCoefficients[5]*startRow;
            result.Bounds.AffineCoefficients[4] = Bounds.AffineCoefficients[4];
            result.Bounds.AffineCoefficients[5] = Bounds.AffineCoefficients[5];


            // Now we can copy any values currently in memory.
            if (IsInRam)
            {
                //result.ReadHeader(Filename);
                result.Data = new short[numRows][];
                ProgressMeter pm = new ProgressMeter(ProgressHandler, MessageStrings.CopyingValues, endRow);
                pm.StartValue = startRow;
                // copy values directly using both data structures
                for (int row = 0; row < numRows; row++)
                {
                    result.Data[row] = new short[numCols];
                    for (int col = 0; col < numCols; col++)
                    {
                        result.Data[row][col] = Data[startRow + row][startColumn + col];
                    }
                    pm.CurrentValue = row;
                }
                pm.Reset();
            }
            else
                result.OpenWindow(Filename, startRow, endRow, startColumn, endColumn, inRam);
            result.Value = new ShortValueGrid(result);
            return result;
        }
        /// <summary>
        /// This creates a completely new raster from the windowed domain on the original raster.  This new raster
        /// will have a separate source file, and values like NumRowsInFile will correspond to the newly created file.
        /// All the values will be copied to the new source file.  If inRam = true and the new raster is small enough,
        /// the raster values will be loaded into memory.
        /// </summary>
        /// <param name="filename"></param>
        /// <param name="startRow">The 0 based integer index of the top row to copy from this raster.  If this raster is itself a window, 0 represents the startRow from the file.</param>
        /// <param name="endRow">The integer index of the bottom row to copy from this raster.  The largest allowed value is NumRows - 1.</param>
        /// <param name="startColumn">The 0 based integer index of the leftmost column to copy from this raster.  If this raster is a window, 0 represents the startColumn from the file.</param>
        /// <param name="endColumn">The 0 based integer index of the rightmost column to copy from this raster.  The largest allowed value is NumColumns - 1</param>
        /// <param name="copyValues">If this is true, the valeus are saved to the file.  If this is false and the data can be loaded into Ram, no file handling is done.  Otherwise, a file of NoData values is created.</param>
        /// <param name="inRam">Boolean.  If this is true and the window is small enough, a copy of the values will be loaded into memory.</param>
        /// <returns>An implementation of IRaster</returns>
        public new IRaster CopyWindow(string filename, int startRow, int endRow, int startColumn, int endColumn,
                                  bool copyValues, bool inRam)
        {
            int numCols = endColumn - startColumn + 1;
            int numRows = endRow - startRow + 1;


            BinaryShortRaster result = new BinaryShortRaster(filename, numCols, numRows, inRam);

            result.Projection = Projection;

            // The affine coefficients defining the world file are the same except that they are translated over.  Only the position of the
            // upper left corner changes.  Everything else is the same as the previous raster.

            // X = [0] + [1] * column + [2] * row;
            // Y = [3] + [4] * column + [5] * row;
            result.Bounds = new RasterBounds(result.NumRows, result.NumColumns, new double[6]);
            result.Bounds.AffineCoefficients[0] = Bounds.AffineCoefficients[0] +
                                                  Bounds.AffineCoefficients[1]*startColumn +
                                                  Bounds.AffineCoefficients[2]*startRow;
            result.Bounds.AffineCoefficients[1] = Bounds.AffineCoefficients[1];
            result.Bounds.AffineCoefficients[2] = Bounds.AffineCoefficients[2];
            result.Bounds.AffineCoefficients[3] = Bounds.AffineCoefficients[3] +
                                                  Bounds.AffineCoefficients[4]*startColumn +
                                                  Bounds.AffineCoefficients[5]*startRow;
            result.Bounds.AffineCoefficients[4] = Bounds.AffineCoefficients[4];
            result.Bounds.AffineCoefficients[5] = Bounds.AffineCoefficients[5];

            if (IsInRam)
            {
                ProgressMeter pm = new ProgressMeter(ProgressHandler, MessageStrings.CopyingValues, numRows);
                // copy values directly using both data structures
                for (int row = 0; row < numRows; row++)
                {
                    for (int col = 0; col < numCols; col++)
                    {
                        result.Data[row][col] = Data[startRow + row][startColumn + col];
                    }
                    pm.CurrentValue = row;
                }
                pm.Reset();

                if (result.IsInRam == false)
                {
                    // Force the result raster to write itself to a file and then purge its memory.
                    result.Write(filename);
                    result.Data = null;
                }
            }
            else
            {
                if (result.IsInRam)
                {
                    // the source is not in memory, so we just read the values from the file as if opening it directly from the file.
                    result.OpenWindow(Filename, startRow, endRow, startColumn, endColumn, true);
                }
                else
                {
                    // Both sources are file based so we basically copy rows of bytes from one to the other.
                    FileStream source = new FileStream(Filename, FileMode.Open, FileAccess.Read, FileShare.Read);
                    result.WriteHeader(filename);
                    FileStream dest = new FileStream(filename, FileMode.Append, FileAccess.Write, FileShare.None);
                    source.Seek(HeaderSize, SeekOrigin.Begin);
                    BinaryReader bReader = new BinaryReader(source);
                    BinaryWriter bWriter = new BinaryWriter(dest);
                    ProgressMeter pm = new ProgressMeter(ProgressHandler, MessageStrings.CopyingValues, numRows);
                    // copy values directly using both data structures
                    source.Seek(NumColumnsInFile*startRow*ByteSize, SeekOrigin.Current);
                    for (int row = 0; row < numRows; row++)
                    {
                        source.Seek(numCols*ByteSize, SeekOrigin.Current);
                        byte[] rowData = bReader.ReadBytes(ByteSize*numCols);
                        bWriter.Write(rowData);
                        source.Seek(NumColumnsInFile - endColumn + 1, SeekOrigin.Current);
                        bWriter.Flush();
                        pm.CurrentValue = row;
                    }
                    pm.Reset();
                }
            }
            return result;
        }