using System; using System.Collections; using System.IO; using GeoAPI.Geometries; using GisSharpBlog.NetTopologySuite.Geometries; using GisSharpBlog.NetTopologySuite.IO.Handlers; namespace GisSharpBlog.NetTopologySuite.IO { /// /// This class represnts an ESRI Shapefile. /// public class ShapefileReader : IEnumerable { /// /// Summary description for ShapefileEnumerator. /// private class ShapefileEnumerator : IEnumerator, IDisposable { private ShapefileReader _parent; private IGeometry _geometry; private ShapeHandler _handler; private BigEndianBinaryReader _shpBinaryReader = null; /// /// Initializes a new instance of the class. /// /// public ShapefileEnumerator(ShapefileReader shapefile) { _parent = shapefile; // create a file stream for each enumerator that is given out. This allows the same file // to have one or more enumerator. If we used the parents stream - than only one IEnumerator // could be given out. FileStream stream = new FileStream(_parent._filename, FileMode.Open, FileAccess.Read, FileShare.Read); _shpBinaryReader = new BigEndianBinaryReader(stream); // skip header - since parent has already read this. _shpBinaryReader.ReadBytes(100); ShapeGeometryType type = _parent._mainHeader.ShapeType; _handler = Shapefile.GetShapeHandler(type); if (_handler == null) throw new NotSupportedException("Unsupported shape type:" + type); } /// /// Sets the enumerator to its initial position, which is /// before the first element in the collection. /// /// /// The collection was modified after the enumerator was created. /// public void Reset() { _shpBinaryReader.BaseStream.Seek(100, SeekOrigin.Begin); //throw new InvalidOperationException(); } /// /// Advances the enumerator to the next element of the collection. /// /// /// true if the enumerator was successfully advanced to the next element; /// false if the enumerator has passed the end of the collection. /// /// The collection was modified after the enumerator was created. public bool MoveNext() { if (_shpBinaryReader.PeekChar() != -1) { // Mark Jacquin: add a try catch when some shapefile have extra char at the end but no record try { int recordNumber = _shpBinaryReader.ReadInt32BE(); int contentLength = _shpBinaryReader.ReadInt32BE(); _geometry = _handler.Read(_shpBinaryReader, _parent._geometryFactory); } catch (Exception) { return false; } return true; } else { // Reached end of file, so close the reader. //_shpBinaryReader.Close(); return false; } } /// /// Gets the current element in the collection. /// /// /// The current element in the collection. /// /// The enumerator is positioned before the first element /// of the collection or after the last element. /// public object Current { get { return _geometry; } } #region IDisposable Members /// /// Performs application-defined tasks associated with freeing, /// releasing, or resetting unmanaged resources. /// public void Dispose() { _shpBinaryReader.Close(); } #endregion } private ShapefileHeader _mainHeader = null; private IGeometryFactory _geometryFactory = null; private string _filename; /// /// Initializes a new instance of the Shapefile class with the given parameters. /// /// The filename of the shape file to read (with .shp). /// The GeometryFactory to use when creating Geometry objects. public ShapefileReader(string filename, IGeometryFactory geometryFactory) { if (filename == null) throw new ArgumentNullException("filename"); if (geometryFactory == null) throw new ArgumentNullException("geometryFactory"); _filename = filename; _geometryFactory = geometryFactory; // read header information. note, we open the file, read the header information and then // close the file. This means the file is not opened again until GetEnumerator() is requested. // For each call to GetEnumerator() a new BinaryReader is created. FileStream stream = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.Read); BigEndianBinaryReader shpBinaryReader = new BigEndianBinaryReader(stream); _mainHeader = new ShapefileHeader(shpBinaryReader); shpBinaryReader.Close(); } /// /// Initializes a new instance of the Shapefile class with the given parameter /// and a standard GeometryFactory. /// /// The filename of the shape file to read (with .shp). public ShapefileReader(string filename) : this(filename, new GeometryFactory()) { } /// /// Gets the bounds of the shape file. /// public ShapefileHeader Header { get { return _mainHeader; } } /// /// Reads the shapefile and returns a GeometryCollection representing all the records in the shapefile. /// /// GeometryCollection representing every record in the shapefile. public IGeometryCollection ReadAll() { ArrayList list = new ArrayList(); ShapeGeometryType type = _mainHeader.ShapeType; ShapeHandler handler = Shapefile.GetShapeHandler(type); if (handler == null) throw new NotSupportedException("Unsupported shape type:" + type); int i = 0; foreach (IGeometry geometry in this) { list.Add(geometry); i++; } IGeometry[] geomArray = GeometryFactory.ToGeometryArray(list); return _geometryFactory.CreateGeometryCollection(geomArray); } /// /// Returns an enumerator that iterates through a collection. /// /// /// An object /// that can be used to iterate through the collection. /// public IEnumerator GetEnumerator() { return new ShapefileEnumerator(this); } } }