RWFlag mode;              // file open mode: Read or Write

        /// <summary>
        /// Create a file given specs
        /// </summary>
        public ErdasImageFile(string filename,
                              Dimensions dimensions,
                              int bandCount,
                              System.TypeCode bandType,
                              IMetadata metadata)
        {
            // set instance variables
            this.open        = false;
            this.mode        = RWFlag.Write;
            this.dimensions  = dimensions;
            this.bandType    = bandType;
            this.bandCount   = bandCount;
            this.currPixel   = 0;
            this.totalPixels = dimensions.Rows * dimensions.Columns;
            this.metadata    = metadata;

            // if filename does not end in .gis or .lan throw exception
            string extension = Path.GetExtension(filename).ToLower();

            if (!(extension.Equals(".gis")) && !(extension.Equals(".lan")))
            {
                throw new System.ApplicationException("Erdas file must have either GIS or LAN as extension");
            }

            // if dimensions are messed up throw exception
            if ((dimensions.Rows < 1) || (dimensions.Columns < 1))
            {
                throw new System.ApplicationException("Erdas file given invalid dimensions");
            }

            // if bandCount messed up throw exception
            if ((bandCount < 1) || (bandCount > 0xffff))
            {
                throw new System.ApplicationException("Erdas file given invalid band count");
            }

            // more bandCount checking
            if (extension.Equals(".gis"))
            {
                if (bandCount > 1)
                {
                    throw new System.ApplicationException("Erdas GIS files cannot support multiband images");
                }
                if (bandType != System.TypeCode.Byte)
                {
                    throw new System.ApplicationException("Erdas GIS files only suupport byte for bandtype");
                }
            }

            // if bandType not System.Byte or System.UInt16 throw exception
            if (bandType == System.TypeCode.Byte)
            {
                this.bandSize = 1;
            }
            else if (bandType == System.TypeCode.UInt16)
            {
                this.bandSize = 2;
            }
            else
            {
                throw new System.ApplicationException("Erdas file given unsupported band type");
            }

            // open file for writing
            this.file       = new FileStream(filename, FileMode.OpenOrCreate);
            this.fileWriter = new BinaryWriter(this.file);
            this.open       = true;

            // write header (using metadata whenever possible)

            try
            {
                // sentinel
                byte[] sentinel = new byte[6];
                sentinel[0] = (byte)'H';
                sentinel[1] = (byte)'E';
                sentinel[2] = (byte)'A';
                sentinel[3] = (byte)'D';
                sentinel[4] = (byte)'7';
                sentinel[5] = (byte)'4';
                this.fileWriter.Write(sentinel);

                // packing
                System.UInt16 ipack;
                if (bandType == System.TypeCode.Byte)
                {
                    ipack = 0;
                }
                else
                {
                    ipack = 2;
                }
                this.fileWriter.Write(ipack);

                // nbands
                System.UInt16 nbands = (System.UInt16)bandCount;
                this.fileWriter.Write(nbands);

                // unused
                for (int i = 0; i < 6; i++)
                {
                    this.fileWriter.Write((byte)0);
                }

                // icols
                System.UInt32 icols = (System.UInt32)dimensions.Columns;
                this.fileWriter.Write(icols);

                // irows
                System.UInt32 irows = (System.UInt32)dimensions.Rows;
                this.fileWriter.Write(irows);

                // xstart
                System.Int32 xstart = 0;
                if ((metadata != null) &&
                    (metadata.TryGetValue <System.Int32>(RASTER_ULX, ref xstart)))
                {
                }
                this.fileWriter.Write(xstart);

                // ystart
                System.Int32 ystart = 0;
                if ((metadata != null) &&
                    (metadata.TryGetValue <System.Int32>(RASTER_ULY, ref ystart)))
                {
                }
                this.fileWriter.Write(ystart);

                // unused
                for (int i = 0; i < 56; i++)
                {
                    this.fileWriter.Write((byte)0);
                }

                // maptyp
                System.UInt16 maptyp     = 99; // 99 means NONE
                string        projection = null;
                if ((metadata != null) &&
                    (metadata.TryGetValue <string>(PROJECTION, ref projection)))
                {
                    int projNum = Projections.find(projection);
                    if (projNum != -1)
                    {
                        maptyp = (System.UInt16)projNum;
                    }
                }
                this.fileWriter.Write(maptyp);

                // nclass : calc if needed but never has been in past
                System.UInt16 nclass = 0;
                this.fileWriter.Write(nclass);

                // unused
                for (int i = 0; i < 14; i++)
                {
                    this.fileWriter.Write((byte)0);
                }

                // iautyp : first need xcell and ycell and then acre
                System.Single xcell = 0;
                if ((metadata != null) &&
                    (metadata.TryGetValue <System.Single>(X_SCALE, ref xcell)))
                {
                }
                if (maptyp == 99)
                {
                    xcell = 0;
                }
                System.Single ycell = 0;
                if ((metadata != null) &&
                    (metadata.TryGetValue <System.Single>(Y_SCALE, ref ycell)))
                {
                }
                if (maptyp == 99)
                {
                    ycell = 0;
                }
                System.UInt16 iautyp = 0;
                System.Single acre   = 0;
                switch (maptyp) // iautyp depends upon maptyp indirectly
                {
                case 0:         // Lat/Long -> dist unit == degrees
                    iautyp = 0; // default to no area unit
                    break;

                case 2:         // State Plane ->  dist unit == feet
                    iautyp = 1; // default to acres
                    acre   = xcell * ycell;
                    // acre = sq.feet at this pt
                    //   so now convert to acres
                    acre = (float)((double)acre * 0.0000229568411386593);
                    break;

                default:        //  dist unit == meters
                    iautyp = 2; // default to hectares
                    acre   = xcell * ycell;
                    // acre = sq.meters at this pt
                    //   so now convert to hectares
                    acre *= 0.0001f;
                    break;
                }
                this.fileWriter.Write(iautyp);

                // acre
                this.fileWriter.Write(acre);

                // xmap
                System.Single xmap = 0;
                if ((metadata != null) &&
                    (metadata.TryGetValue <System.Single>(WORLD_ULX, ref xmap)))
                {
                }
                this.fileWriter.Write(xmap);

                // ymap
                System.Single ymap = 0;
                if ((metadata != null) &&
                    (metadata.TryGetValue <System.Single>(WORLD_ULY, ref ymap)))
                {
                }
                this.fileWriter.Write(ymap);

                // xcell
                this.fileWriter.Write(xcell);

                // ycell
                this.fileWriter.Write(ycell);

                // now create pixel data as zeroes for now
                // many nested for loops avoids index calc overflows
                for (int row = 0; row < dimensions.Rows; row++)
                {
                    for (int bandNum = 0; bandNum < this.bandCount; bandNum++)
                    {
                        for (int col = 0; col < dimensions.Columns; col++)
                        {
                            for (int byteNum = 0; byteNum < this.bandSize; byteNum++)
                            {
                                this.fileWriter.Write((byte)0);
                            }
                        }
                    }
                }
            }
            catch (System.Exception)
            {
                Close();
                throw;
            }
        }
        /// <summary>
        /// Open an existing file
        /// </summary>
        public ErdasImageFile(string filename, RWFlag mode)
        {
            this.open = false;
            this.mode = mode;

            // if filename does not end in .gis or .lan throw exception
            string extension = Path.GetExtension(filename).ToLower();

            if (!(extension.Equals(".gis")) && !(extension.Equals(".lan")))
            {
                throw new System.ApplicationException("Erdas file must have either GIS or LAN as extension");
            }

            // open file
            this.file       = new FileStream(filename, FileMode.Open);
            this.fileReader = new BinaryReader(this.file);
            this.open       = true;

            try
            {
                // prepare to build metadata while reading
                Metadata metadata = new Metadata();

                // Read Header

                // if not start with "HEAD74" throw exception
                byte[] sentinel = fileReader.ReadBytes(6);
                if ((sentinel[0] != (byte)'H') ||
                    (sentinel[1] != (byte)'E') ||
                    (sentinel[2] != (byte)'A') ||
                    (sentinel[3] != (byte)'D') ||
                    (sentinel[4] != (byte)'7') ||
                    (sentinel[5] != (byte)'4'))
                {
                    throw new System.ApplicationException(filename + " is not an ERDAS 7.4 compatible image file");
                }

                // packing
                System.UInt16 ipack = this.fileReader.ReadUInt16();
                if ((ipack != 0) && (ipack != 2))
                {
                    throw new System.ApplicationException("Only 8 and 16 bit bands are supported by Erdas reader");
                }

                // nbands
                System.UInt16 nbands = this.fileReader.ReadUInt16();

                // unused
                byte[] unused = this.fileReader.ReadBytes(6);

                // icols
                System.UInt32 icols = this.fileReader.ReadUInt32();

                // irows
                System.UInt32 irows = this.fileReader.ReadUInt32();

                // xstart
                System.Int32 xstart = this.fileReader.ReadInt32();
                metadata[RASTER_ULX] = xstart;

                // ystart
                System.Int32 ystart = this.fileReader.ReadInt32();
                metadata[RASTER_ULY] = ystart;

                // unused
                unused = this.fileReader.ReadBytes(56);

                // maptyp
                System.UInt16 maptyp     = this.fileReader.ReadUInt16();
                string        projection = Projections.find(maptyp);
                if (projection != null)
                {
                    metadata[PROJECTION] = projection;
                }
                if (maptyp == 0)
                {
                    metadata[SCALE_UNITS] = "degrees";
                }
                else if (maptyp == 2)
                {
                    metadata[SCALE_UNITS] = "feet";
                }
                else
                {
                    metadata[SCALE_UNITS] = "meters";
                }

                // nclass : calc if needed but never has been in past
                System.UInt16 nclass = this.fileReader.ReadUInt16();

                // unused
                unused = this.fileReader.ReadBytes(14);

                // iautyp
                System.UInt16 iautyp = this.fileReader.ReadUInt16();

                // acre
                System.Single acre = this.fileReader.ReadSingle();

                // xmap
                System.Single xmap = this.fileReader.ReadSingle();
                metadata[WORLD_ULX] = xmap;

                // ymap
                System.Single ymap = this.fileReader.ReadSingle();
                metadata[WORLD_ULY] = ymap;

                // xcell
                System.Single xcell = this.fileReader.ReadSingle();
                metadata[X_SCALE] = xcell;

                // ycell
                System.Single ycell = this.fileReader.ReadSingle();
                metadata[Y_SCALE] = ycell;

                // construct instance variables based upon hedaer info
                this.dimensions = new Dimensions((int)irows, (int)icols);
                if (ipack == 0)
                {
                    this.bandType = System.TypeCode.Byte;
                    this.bandSize = 1;
                }
                else  // ipack == 2 due to earlier screening
                {
                    this.bandType = System.TypeCode.UInt16;
                    this.bandSize = 2;
                }
                this.bandCount   = nbands;
                this.currPixel   = 0;
                this.totalPixels = (int)irows * (int)icols;
                this.metadata    = metadata;

                if (mode == RWFlag.Write)
                {
                    this.fileReader.Close();
                    this.fileReader = null;
                    // need to reopen stream - fileReader.Close() shuts it
                    this.file       = new FileStream(filename, FileMode.Open);
                    this.fileWriter = new BinaryWriter(this.file);
                }
            }
            catch (System.Exception)
            {
                Close();
                throw;
            }
        }