/// <summary> /// Removes the feature with the specified index. /// </summary> /// <param name="index">Removes the feature from the specified index location.</param> public void RemoveAt(int index) { // Get shape range so we can update header smartly int startIndex = index; IFeatureSet ifs = Select(null, null, ref startIndex, 1); if (ifs.NumRows() > 0) { var shx = new ShapefileIndexFile(); shx.Open(Filename); shx.Shapes.RemoveAt(index); shx.Save(); AttributeTable dbf = GetAttributeTable(Filename); dbf.RemoveRowAt(index); if (_trackDeletedRows) { _deletedRows = dbf.DeletedRows; } // Update extent in header if feature being deleted is NOT completely contained var hdr = new ShapefileHeader(Filename); Envelope featureEnv = ifs.GetFeature(0).Geometry.EnvelopeInternal; if (featureEnv.MinX <= hdr.Xmin || featureEnv.MaxX >= hdr.Xmax || featureEnv.MaxY >= hdr.Ymax || featureEnv.MinY <= hdr.Ymin) { UpdateExtents(); } // Update the Quadtree Quadtree?.Remove(featureEnv, index); } }
/// <summary> /// Cache Index File in memory so we don't have to read it in every call to GetShapes /// </summary> /// <returns></returns> protected ShapefileIndexFile CacheShapeIndexFile() { if (null == _shx) { _shx = new ShapefileIndexFile(); _shx.Open(Filename); } return(_shx); }
/// <inheritdocs/> protected override Shape GetShapeAtIndex(FileStream fs, ShapefileIndexFile shx, ShapefileHeader header, int shp, IEnvelope envelope) { // Read from the index file because some deleted records // might still exist in the .shp file. long offset = (shx.Shapes[shp].ByteOffset); fs.Seek(offset, SeekOrigin.Begin); Shape myShape = new Shape(); // Position Value Type Number Byte Order ShapeRange shape = new ShapeRange(FeatureType.Point); //-------------------------------------------------------------------- shape.RecordNumber = fs.ReadInt32(Endian.BigEndian); // Byte 0 Record Number Integer 1 Big shape.ContentLength = fs.ReadInt32(Endian.BigEndian); // Byte 4 Content Length Integer 1 Big ShapeType shapeType = (ShapeType)fs.ReadInt32(); // Byte 8 Shape Type Integer 1 Little if (shapeType == ShapeType.NullShape) { return null; } double[] vertices = fs.ReadDouble(2); double x = vertices[0], y = vertices[1]; // Don't add this shape to the result if (envelope != null) { if (!envelope.Contains(new Coordinate(x, y))) { return null; } } shape.StartIndex = 0; shape.NumParts = 1; shape.NumPoints = 1; shape.ShapeType = shapeType; shape.Extent = new Extent(x, y, x, y); myShape.Range = shape; myShape.Vertices = vertices; if (header.ShapeType == ShapeType.PointM) { myShape.M = fs.ReadDouble(1); myShape.MinM = myShape.MaxM = myShape.M[0]; shape.Extent = new ExtentM(shape.Extent, myShape.MinM, myShape.MaxM); } else if (header.ShapeType == ShapeType.PointZ) { // For Z shapefiles, the Z part is not optional. myShape.Z = fs.ReadDouble(1); myShape.MinZ = myShape.MaxZ = myShape.Z[0]; myShape.M = fs.ReadDouble(1); myShape.MinM = myShape.MaxM = myShape.M[0]; shape.Extent = new ExtentMZ(shape.Extent.MinX, shape.Extent.MinY, myShape.MinM, myShape.MinZ, shape.Extent.MaxX, shape.Extent.MaxY, myShape.MaxM, myShape.MaxZ); } PartRange partR = new PartRange(myShape.Vertices, 0, 0, FeatureType.Point) { NumVertices = 1 }; shape.Parts.Add(partR); myShape.Range = shape; return myShape; }
/// <inheritdocs/> public Shape[] GetShapes(int[] indices) { Shape[] result = new Shape[indices.Length]; ShapefileIndexFile shx = CacheShapeIndexFile(); // Check to ensure the fileName is not null if (Filename == null) { throw new NullReferenceException(Filename); } if (File.Exists(Filename) == false) { throw new FileNotFoundException(Filename); } // Get the basic header information. ShapefileHeader header = new ShapefileHeader(Filename); // Check to ensure that the fileName is the correct shape type if (header.ShapeType != ShapeType && header.ShapeType != ShapeTypeM && header.ShapeType != ShapeTypeZ) { throw new ArgumentException("Wrong feature type."); } FileStream fs = new FileStream(Filename, FileMode.Open, FileAccess.Read, FileShare.Read); try { if (fs.Length == 100) { // The shapefile is empty so we can simply return here return(result); } int numShapes = shx.Shapes.Count; for (int j = 0; j < indices.Length; j++) { int index = indices[j]; if (index < numShapes) { result[j] = GetShapeAtIndex(fs, shx, header, index, null); } } return(result); } finally { fs.Close(); } }
/// <summary> /// Creates a new instance of the ShapefileShapeSource with the specified /// shapefile as the source and supplied spatial and shape indices. /// </summary> /// <param name="fileName"></param> /// <param name="spatialIndex"></param> /// <param name="shx"></param> protected ShapefileShapeSource(string fileName, ISpatialIndex spatialIndex, ShapefileIndexFile shx) { Filename = fileName; _spatialIndex = spatialIndex; _shx = shx; }
/// <summary> /// Get the shape at the specified index. Must be implemented by all shape sources derived from ShapefileShapeSource /// </summary> /// <param name="fs"></param> /// <param name="shx"></param> /// <param name="header"></param> /// <param name="shp"></param> /// <param name="envelope"></param> /// <returns></returns> protected abstract Shape GetShapeAtIndex(FileStream fs, ShapefileIndexFile shx, ShapefileHeader header, int shp, IEnvelope envelope);
/// <inheritdocs/> public void EndGetShapesSession() { _shx = null; }
/// <inheritdocs/> public void RemoveAt(int index) { // Get shape range so we can update header smartly int startIndex = index; IFeatureSet ifs = Select(null, null, ref startIndex, 1); if (ifs.NumRows() > 0) { var shx = new ShapefileIndexFile(); shx.Open(Filename); shx.Shapes.RemoveAt(index); shx.Save(); AttributeTable dbf = GetAttributeTable(Filename); dbf.RemoveRowAt(index); if (_trackDeletedRows) _deletedRows = dbf.DeletedRows; // Update extent in header if feature being deleted is NOT completely contained var hdr = new ShapefileHeader(Filename); IEnvelope featureEnv = ifs.GetFeature(0).Envelope; if (featureEnv.Left() <= hdr.Xmin || featureEnv.Right() >= hdr.Xmax || featureEnv.Top() >= hdr.Ymax || featureEnv.Bottom() <= hdr.Ymin) { UpdateExtents(); } // Update the Quadtree if (null != Quadtree) { Quadtree.Remove(featureEnv, index); } } }
/// <summary> /// Creates a new instance of the PolygonShapefileShapeSource with the specified polygon shapefile as the source and provided indices /// </summary> /// <param name="fileName"></param> /// <param name="spatialIndex"></param> /// <param name="shx"></param> public PolygonShapefileShapeSource(string fileName, ISpatialIndex spatialIndex, ShapefileIndexFile shx) : base(fileName, spatialIndex, shx) { }
/// <summary> /// Cache Index File in memory so we don't have to read it in every call to GetShapes /// </summary> /// <returns></returns> protected ShapefileIndexFile CacheShapeIndexFile() { if (null == _shx) { _shx = new ShapefileIndexFile(); _shx.Open(Filename); } return _shx; }
/// <inheritdocs/> protected override Shape GetShapeAtIndex(FileStream fs, ShapefileIndexFile shx, ShapefileHeader header, int shp, IEnvelope envelope) { // Read from the index file because some deleted records // might still exist in the .shp file. long offset = (shx.Shapes[shp].ByteOffset); fs.Seek(offset, SeekOrigin.Begin); Shape myShape = new Shape(); // Position Value Type Number Byte Order ShapeRange shape = new ShapeRange(FeatureType.Point); //-------------------------------------------------------------------- shape.RecordNumber = fs.ReadInt32(Endian.BigEndian); // Byte 0 Record Number Integer 1 Big shape.ContentLength = fs.ReadInt32(Endian.BigEndian); // Byte 4 Content Length Integer 1 Big ShapeType shapeType = (ShapeType)fs.ReadInt32(); // Byte 8 Shape Type Integer 1 Little if (shapeType == ShapeType.NullShape) { return(null); } double[] vertices = fs.ReadDouble(2); double x = vertices[0], y = vertices[1]; // Don't add this shape to the result if (envelope != null) { if (!envelope.Contains(new Coordinate(x, y))) { return(null); } } shape.StartIndex = 0; shape.NumParts = 1; shape.NumPoints = 1; shape.ShapeType = shapeType; shape.Extent = new Extent(x, y, x, y); myShape.Range = shape; myShape.Vertices = vertices; if (header.ShapeType == ShapeType.PointM) { myShape.M = fs.ReadDouble(1); myShape.MinM = myShape.MaxM = myShape.M[0]; shape.Extent = new ExtentM(shape.Extent, myShape.MinM, myShape.MaxM); } else if (header.ShapeType == ShapeType.PointZ) { // For Z shapefiles, the Z part is not optional. myShape.Z = fs.ReadDouble(1); myShape.MinZ = myShape.MaxZ = myShape.Z[0]; myShape.M = fs.ReadDouble(1); myShape.MinM = myShape.MaxM = myShape.M[0]; shape.Extent = new ExtentMZ(shape.Extent.MinX, shape.Extent.MinY, myShape.MinM, myShape.MinZ, shape.Extent.MaxX, shape.Extent.MaxY, myShape.MaxM, myShape.MaxZ); } PartRange partR = new PartRange(myShape.Vertices, 0, 0, FeatureType.Point) { NumVertices = 1 }; shape.Parts.Add(partR); myShape.Range = shape; return(myShape); }
/// <summary> /// Initializes a new instance of the <see cref="PointShapefileShapeSource"/> class with the specified polygon shapefile as the source and provided indices /// </summary> /// <param name="fileName">The file name.</param> /// <param name="spatialIndex">The spatial index.</param> /// <param name="shx">The shapefile index file.</param> public PointShapefileShapeSource(string fileName, ISpatialIndex <int> spatialIndex, ShapefileIndexFile shx) : base(fileName, spatialIndex, shx) { }
/// <summary> /// Creates a new instance of the LineShapefileShapeSource with the specified polygon shapefile as the source and provided indices /// </summary> /// <param name="fileName"></param> /// <param name="spatialIndex"></param> /// <param name="shx"></param> public LineShapefileShapeSource(string fileName, ISpatialIndex spatialIndex, ShapefileIndexFile shx) : base(fileName, spatialIndex, shx) { }
/// <inheritdocs/> protected override Shape GetShapeAtIndex(FileStream fs, ShapefileIndexFile shx, ShapefileHeader header, int shp, IEnvelope envelope) { // Read from the index file because some deleted records // might still exist in the .shp file. long offset = (shx.Shapes[shp].ByteOffset); fs.Seek(offset, SeekOrigin.Begin); Shape myShape = new Shape(); // Position Value Type Number Byte Order ShapeRange shape = new ShapeRange(FeatureType.Line); //-------------------------------------------------------------------- shape.RecordNumber = fs.ReadInt32(Endian.BigEndian); // Byte 0 Record Number Integer 1 Big shape.ContentLength = fs.ReadInt32(Endian.BigEndian); // Byte 4 Content Length Integer 1 Big shape.ShapeType = (ShapeType)fs.ReadInt32(); // Byte 8 Shape Type Integer 1 Little shape.StartIndex = 0; if (shape.ShapeType == ShapeType.NullShape) { return null; } myShape.Range = shape; //bbReader.Read(allBounds, shp*32, 32); double xMin = fs.ReadDouble(); // Byte 12 Xmin Double 1 Little double yMin = fs.ReadDouble(); // Byte 20 Ymin Double 1 Little double xMax = fs.ReadDouble(); // Byte 28 Xmax Double 1 Little double yMax = fs.ReadDouble(); // Byte 36 Ymax Double 1 Little shape.Extent = new Extent(xMin, yMin, xMax, yMax); // Don't add this shape to the result if (envelope != null) { if (!myShape.Range.Extent.Intersects(envelope)) { return null; } } shape.NumParts = fs.ReadInt32(); // Byte 44 NumParts Integer 1 Little //feature.NumPoints = bbReader.ReadInt32(); // Byte 48 NumPoints Integer 1 Little shape.NumPoints = fs.ReadInt32(); // Create an envelope from the extents box in the file. //feature.Envelope = new Envelope(xMin, xMax, yMin, yMax); int[] partIndices = fs.ReadInt32(shape.NumParts); myShape.Vertices = fs.ReadDouble(shape.NumPoints * 2); if (header.ShapeType == ShapeType.PolyLineM) { // These are listed as "optional" but there isn't a good indicator of how to determine if they were added. // To handle the "optional" M values, check the contentLength for the feature. // The content length does not include the 8-byte record header and is listed in 16-bit words. if (shape.ContentLength * 2 > 44 + 4 * shape.NumParts + 16 * shape.NumPoints) { myShape.MinM = fs.ReadDouble(); myShape.MaxM = fs.ReadDouble(); myShape.M = fs.ReadDouble(shape.NumPoints); shape.Extent = new ExtentM(shape.Extent, myShape.MinM, myShape.MaxM); } } else if (header.ShapeType == ShapeType.PolyLineZ) { bool hasM = shape.ContentLength * 2 > 60 + 4 * shape.NumParts + 24 * shape.NumPoints; myShape.MinZ = fs.ReadDouble(); myShape.MaxZ = fs.ReadDouble(); // For Z shapefiles, the Z part is not optional. myShape.Z = fs.ReadDouble(shape.NumPoints); // These are listed as "optional" but there isn't a good indicator of how to determine if they were added. // To handle the "optional" M values, check the contentLength for the feature. // The content length does not include the 8-byte record header and is listed in 16-bit words.) if (hasM) { myShape.MinM = fs.ReadDouble(); myShape.MaxM = fs.ReadDouble(); myShape.M = fs.ReadDouble(shape.NumPoints); shape.Extent = new ExtentMZ(shape.Extent.MinX, shape.Extent.MinY, myShape.MinM, myShape.MinZ, shape.Extent.MaxX, shape.Extent.MaxY, myShape.MaxM, myShape.MaxZ); } else shape.Extent = new ExtentMZ(shape.Extent.MinX, shape.Extent.MinY, double.MaxValue, myShape.MinZ, shape.Extent.MaxX, shape.Extent.MaxY, double.MinValue, myShape.MaxZ); } myShape.Range = shape; for (int part = 0; part < shape.NumParts; part++) { int partOff = partIndices[part]; int pointCount = shape.NumPoints - partOff; if (part < shape.NumParts - 1) { pointCount = partIndices[part + 1] - partOff; } PartRange partR = new PartRange(myShape.Vertices, 0, partOff, FeatureType.Line) { NumVertices = pointCount }; shape.Parts.Add(partR); } return myShape; }
/// <inheritdocs/> protected override Shape GetShapeAtIndex(FileStream fs, ShapefileIndexFile shx, ShapefileHeader header, int shp, IEnvelope envelope) { // Read from the index file because some deleted records // might still exist in the .shp file. long offset = (shx.Shapes[shp].ByteOffset); fs.Seek(offset, SeekOrigin.Begin); // Position Value Type Number Byte Order ShapeRange shape = new ShapeRange(FeatureType.Polygon); //-------------------------------------------------------------------- shape.RecordNumber = fs.ReadInt32(Endian.BigEndian); // Byte 0 Record Number Integer 1 Big shape.ContentLength = fs.ReadInt32(Endian.BigEndian); // Byte 4 Content Length Integer 1 Big shape.ShapeType = (ShapeType)fs.ReadInt32(); // Byte 8 Shape Type Integer 1 Little shape.StartIndex = 0; if (shape.ShapeType == ShapeType.NullShape) { return(null); } Shape myShape = new Shape(); myShape.Range = shape; //bbReader.Read(allBounds, shp*32, 32); double xMin = fs.ReadDouble(); // Byte 12 Xmin Double 1 Little double yMin = fs.ReadDouble(); // Byte 20 Ymin Double 1 Little double xMax = fs.ReadDouble(); // Byte 28 Xmax Double 1 Little double yMax = fs.ReadDouble(); // Byte 36 Ymax Double 1 Little shape.Extent = new Extent(xMin, yMin, xMax, yMax); // Don't add this shape to the result if (envelope != null) { if (!myShape.Range.Extent.Intersects(envelope)) { return(null); } } shape.NumParts = fs.ReadInt32(); // Byte 44 NumParts Integer 1 Little //feature.NumPoints = bbReader.ReadInt32(); // Byte 48 NumPoints Integer 1 Little shape.NumPoints = fs.ReadInt32(); // Create an envelope from the extents box in the file. //feature.Envelope = new Envelope(xMin, xMax, yMin, yMax); int[] partIndices = fs.ReadInt32(shape.NumParts); myShape.Vertices = fs.ReadDouble(shape.NumPoints * 2); if (header.ShapeType == ShapeType.PolygonM) { // These are listed as "optional" but there isn't a good indicator of how to determine if they were added. // To handle the "optional" M values, check the contentLength for the feature. // The content length does not include the 8-byte record header and is listed in 16-bit words. if (shape.ContentLength * 2 > 44 + 4 * shape.NumParts + 16 * shape.NumPoints) { myShape.MinM = fs.ReadDouble(); myShape.MaxM = fs.ReadDouble(); myShape.M = fs.ReadDouble(shape.NumPoints); shape.Extent = new ExtentM(shape.Extent, myShape.MinM, myShape.MaxM); } } else if (header.ShapeType == ShapeType.PolygonZ) { bool hasM = shape.ContentLength * 2 > 60 + 4 * shape.NumParts + 24 * shape.NumPoints; myShape.MinZ = fs.ReadDouble(); myShape.MaxZ = fs.ReadDouble(); // For Z shapefiles, the Z part is not optional. myShape.Z = fs.ReadDouble(shape.NumPoints); // These are listed as "optional" but there isn't a good indicator of how to determine if they were added. // To handle the "optional" M values, check the contentLength for the feature. // The content length does not include the 8-byte record header and is listed in 16-bit words.) if (hasM) { myShape.MinM = fs.ReadDouble(); myShape.MaxM = fs.ReadDouble(); myShape.M = fs.ReadDouble(shape.NumPoints); shape.Extent = new ExtentMZ(shape.Extent.MinX, shape.Extent.MinY, myShape.MinM, myShape.MinZ, shape.Extent.MaxX, shape.Extent.MaxY, myShape.MaxM, myShape.MaxZ); } else { shape.Extent = new ExtentMZ(shape.Extent.MinX, shape.Extent.MinY, double.MaxValue, myShape.MinZ, shape.Extent.MaxX, shape.Extent.MaxY, double.MinValue, myShape.MaxZ); } } myShape.Range = shape; for (int part = 0; part < shape.NumParts; part++) { int partOff = partIndices[part]; int pointCount = shape.NumPoints - partOff; if (part < shape.NumParts - 1) { pointCount = partIndices[part + 1] - partOff; } PartRange partR = new PartRange(myShape.Vertices, 0, partOff, FeatureType.Polygon) { NumVertices = pointCount }; shape.Parts.Add(partR); } return(myShape); }
/// <inheritdocs/> public Dictionary <int, Shape> GetShapes(ref int startIndex, int count, IEnvelope envelope) { Dictionary <int, Shape> result = new Dictionary <int, Shape>(); ShapefileIndexFile shx = CacheShapeIndexFile(); // Check to ensure the fileName is not null if (Filename == null) { throw new NullReferenceException(Filename); } if (File.Exists(Filename) == false) { throw new FileNotFoundException(Filename); } // Get the basic header information. ShapefileHeader header = new ShapefileHeader(Filename); Extent ext = new Extent(new[] { header.Xmin, header.Ymin, header.Xmax, header.Ymax }); if (envelope != null) { if (!ext.Intersects(envelope)) { return(result); } } // Check to ensure that the fileName is the correct shape type if (header.ShapeType != ShapeType && header.ShapeType != ShapeTypeM && header.ShapeType != ShapeTypeZ) { throw new ArgumentException("Wrong feature type."); } FileStream fs = new FileStream(Filename, FileMode.Open, FileAccess.Read, FileShare.Read); if (fs.Length == 100) { // The shapefile is empty so we can simply return here fs.Close(); return(result); } int shapesTested = 0; int shapesReturned = 0; // Use spatial index if we have one if (null != _spatialIndex && null != envelope) { IList spatialQueryResults = _spatialIndex.Query(envelope); // Sort the results from low to high index var sqra = new int[spatialQueryResults.Count]; spatialQueryResults.CopyTo(sqra, 0); Array.Sort(sqra); foreach (int shp in sqra) { if (shp >= startIndex) { Shape myShape = GetShapeAtIndex(fs, shx, header, shp, envelope); shapesTested++; if (null != myShape) { shapesReturned++; result.Add(shp, myShape); if (shapesReturned >= count) { break; } } } } } else { int numShapes = shx.Shapes.Count; for (int shp = startIndex; shp < numShapes; shp++) { Shape myShape = GetShapeAtIndex(fs, shx, header, shp, envelope); shapesTested++; if (null != myShape) { shapesReturned++; result.Add(shp, myShape); if (shapesReturned >= count) { break; } } } } startIndex += shapesTested; fs.Close(); return(result); }