/// <summary> /// Opens a shapefile /// </summary> /// <param name="fileName">The string fileName of the point shapefile to load</param> /// <param name="progressHandler">Any valid implementation of the DotSpatial.Data.IProgressHandler</param> public void Open(string fileName, IProgressHandler progressHandler) { //JK - handle case when filename doesn't exist if (!File.Exists(fileName)) { Attributes = new AttributeTable(); Header = new ShapefileHeader { FileLength = 100, ShapeType = ShapeType.Point }; FeatureType = FeatureType.Point; return; } IndexMode = true; Filename = fileName; Header = new ShapefileHeader(fileName); CoordinateType = CoordinateType.Regular; if (Header.ShapeType == ShapeType.PointM) { CoordinateType = CoordinateType.M; } if (Header.ShapeType == ShapeType.PointZ) { CoordinateType = CoordinateType.Z; } MyExtent = Header.ToExtent(); Name = Path.GetFileNameWithoutExtension(fileName); Attributes.Open(fileName); FillPoints(fileName, progressHandler); ReadProjection(); }
/// <summary> /// Opens a shapefile /// </summary> /// <param name="fileName">The string fileName of the line shapefile to load</param> /// <param name="progressHandler">Any valid implementation of the DotSpatial.Data.IProgressHandler</param> public void Open(string fileName, IProgressHandler progressHandler) { if (!File.Exists(fileName)) return; Filename = fileName; IndexMode = true; Header = new ShapefileHeader(fileName); switch (Header.ShapeType) { case ShapeType.PolyLineM: CoordinateType = CoordinateType.M; break; case ShapeType.PolyLineZ: CoordinateType = CoordinateType.Z; break; default: CoordinateType = CoordinateType.Regular; break; } Extent = Header.ToExtent(); Name = Path.GetFileNameWithoutExtension(fileName); Attributes.Open(fileName); FillLines(fileName, progressHandler, this, FeatureType.Line); ReadProjection(); }
/// <summary> /// Opens a shapefile /// </summary> /// <param name="fileName">The string fileName of the line shapefile to load</param> /// <param name="progressHandler">Any valid implementation of the DotSpatial.Data.IProgressHandler</param> public void Open(string fileName, IProgressHandler progressHandler) { if (!File.Exists(fileName)) { Attributes = new AttributeTable(); Header = new ShapefileHeader { FileLength = 100, ShapeType = ShapeType.PolyLine }; FeatureType = FeatureType.Line; return; } Filename = fileName; FeatureType = FeatureType.Line; Header = new ShapefileHeader(fileName); CoordinateType = CoordinateType.Regular; IndexMode = true; if (Header.ShapeType == ShapeType.PolyLineM) { CoordinateType = CoordinateType.M; } if (Header.ShapeType == ShapeType.PolyLineZ) { CoordinateType = CoordinateType.Z; } MyExtent = Header.ToExtent(); Name = Path.GetFileNameWithoutExtension(fileName); Attributes.Open(fileName); FillLines(fileName, progressHandler); ReadProjection(); }
/// <summary> /// Creates a new instance of a shapefile based on a fileName /// </summary> /// <param name="fileName">File name</param> /// <param name="featureType">Feature type</param> protected Shapefile(string fileName, FeatureType featureType) : base(featureType) { Attributes = new AttributeTable(); Header = new ShapefileHeader(); Open(fileName, null); }
/// <inheritdocs/> protected override void AppendBasicGeometry(ShapefileHeader header, IBasicGeometry feature, int numFeatures) { FileInfo fi = new FileInfo(Filename); int offset = Convert.ToInt32(fi.Length / 2); FileStream shpStream = new FileStream(Filename, FileMode.Append, FileAccess.Write, FileShare.None, 10000); FileStream shxStream = new FileStream(header.ShxFilename, FileMode.Append, FileAccess.Write, FileShare.None, 100); Coordinate point = feature.Coordinates[0]; int contentLength = 6; if (header.ShapeType == ShapeType.PointM) { contentLength += 4; // one additional value (m) } if (header.ShapeType == ShapeType.PointZ) { contentLength += 8; // 2 additional values (m, z) } // Index File // --------------------------------------------------------- // Position Value Type Number Byte Order // --------------------------------------------------------- shxStream.WriteBe(offset); // Byte 0 Offset Integer 1 Big shxStream.WriteBe(contentLength); // Byte 4 Content Length Integer 1 Big shxStream.Flush(); shxStream.Close(); // X Y Points // --------------------------------------------------------- // Position Value Type Number Byte Order // --------------------------------------------------------- shpStream.WriteBe(numFeatures); // Byte 0 Record Number Integer 1 Big shpStream.WriteBe(contentLength); // Byte 4 Content Length Integer 1 Big shpStream.WriteLe((int)header.ShapeType); // Byte 8 Shape Type 3 Integer 1 Little if (header.ShapeType == ShapeType.NullShape) { return; } shpStream.WriteLe(point.X); // Byte 12 X Double 1 Little shpStream.WriteLe(point.Y); // Byte 20 Y Double 1 Little if (header.ShapeType == ShapeType.PointM) { shpStream.WriteLe(point.M); // Byte 28 M Double 1 Little } else if (header.ShapeType == ShapeType.PointZ) { shpStream.WriteLe(point.Z); // Byte 28 Z Double 1 Little shpStream.WriteLe(point.M); // Byte 36 M Double 1 Little } shpStream.Flush(); shpStream.Close(); offset += contentLength; Shapefile.WriteFileLength(Filename, offset); Shapefile.WriteFileLength(header.ShxFilename, 50 + numFeatures * 4); }
/// <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> /// Creates a new instance of a PolygonShapefile for in-ram handling only. /// </summary> public PolygonShapefile() : base(FeatureType.Polygon) { Attributes = new AttributeTable(); Header = new ShapefileHeader(); Header.FileLength = 100; Header.ShapeType = ShapeType.Polygon; FeatureType = FeatureType.Polygon; }
/// <summary> /// Creates a new ShapefileReader tailored to read a particular file. /// </summary> /// <param name="filename"></param> /// <returns></returns> public ShapefileReader(string filename) { Filename = filename; _attributeTable = new AttributeTable(filename); ShapefileHeader header = new ShapefileHeader(filename); //if (header.ShapeType == ShapeType.Polygon || // header.ShapeType == ShapeType.PolygonM || // header.ShapeType == ShapeType.PolygonZ) //{ // _shapeSource = new PolygonShapefileShapeSource(filename); //} // To Do: Implement alternate shape sources here. _fieldCount = -1; }
/// <summary> /// Opens a shapefile /// </summary> /// <param name="fileName">The string fileName of the point shapefile to load</param> /// <param name="progressHandler">Any valid implementation of the DotSpatial.Data.IProgressHandler</param> public void Open(string fileName, IProgressHandler progressHandler) { IndexMode = true; Filename = fileName; FeatureType = FeatureType.MultiPoint; Header = new ShapefileHeader(fileName); CoordinateType = CoordinateType.Regular; if (Header.ShapeType == ShapeType.MultiPointM) { CoordinateType = CoordinateType.M; } if (Header.ShapeType == ShapeType.MultiPointZ) { CoordinateType = CoordinateType.Z; } Name = Path.GetFileNameWithoutExtension(fileName); Attributes.Open(fileName); FillPoints(fileName, progressHandler); ReadProjection(); }
/// <summary> /// This tests the specified file in order to determine what type of vector the file contains. /// This returns unspecified if the file format is not supported by this provider. /// </summary> /// <param name="fileName">The string fileName to test</param> /// <returns>A FeatureType clarifying what sort of features are stored on the data type.</returns> public virtual FeatureType GetFeatureType(string fileName) { ShapefileHeader sh = new ShapefileHeader(fileName); if (sh.ShapeType == ShapeType.Polygon || sh.ShapeType == ShapeType.PolygonM || sh.ShapeType == ShapeType.PolygonZ) { return FeatureType.Polygon; } if (sh.ShapeType == ShapeType.PolyLine || sh.ShapeType == ShapeType.PolyLineM || sh.ShapeType == ShapeType.PolyLineZ) { return FeatureType.Line; } if (sh.ShapeType == ShapeType.Point || sh.ShapeType == ShapeType.PointM || sh.ShapeType == ShapeType.PointZ) { return FeatureType.Point; } if (sh.ShapeType == ShapeType.MultiPoint || sh.ShapeType == ShapeType.MultiPointM || sh.ShapeType == ShapeType.MultiPointZ) { return FeatureType.MultiPoint; } return FeatureType.Unspecified; }
/// <summary> /// Opens a shapefile /// </summary> /// <param name="fileName">The string fileName of the polygon shapefile to load</param> /// <param name="progressHandler">Any valid implementation of the DotSpatial.Data.IProgressHandler</param> public void Open(string fileName, IProgressHandler progressHandler) { //JK - handle case when filename doesn't exist if (!File.Exists(fileName)) return; IndexMode = true; Filename = fileName; Header = new ShapefileHeader(fileName); CoordinateType = CoordinateType.Regular; if (Header.ShapeType == ShapeType.PolygonM) { CoordinateType = CoordinateType.M; } if (Header.ShapeType == ShapeType.PolygonZ) { CoordinateType = CoordinateType.Z; } Name = Path.GetFileNameWithoutExtension(fileName); Attributes.Open(fileName); FillPolygons(fileName, progressHandler); ReadProjection(); }
/// <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); 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 if (null != Quadtree) { Quadtree.Remove(featureEnv, index); } } }
/// <summary> /// Opens a shapefile. /// </summary> /// <param name="fileName">The string fileName of the polygon shapefile to load.</param> /// <param name="progressHandler">Any valid implementation of the DotSpatial.Data.IProgressHandler.</param> public void Open(string fileName, IProgressHandler progressHandler) { if (!File.Exists(fileName)) { return; } Filename = fileName; IndexMode = true; Header = new ShapefileHeader(Filename); CoordinateType = Header.ShapeType switch { ShapeType.PolygonM => CoordinateType.M, ShapeType.PolygonZ => CoordinateType.Z, _ => CoordinateType.Regular, }; Extent = Header.ToExtent(); Name = Path.GetFileNameWithoutExtension(fileName); Attributes.Open(Filename); LineShapefile.FillLines(Filename, progressHandler, this, FeatureType.Polygon); ReadProjection(); }
/// <summary> /// Opens a shapefile /// </summary> /// <param name="fileName">The string fileName of the point shapefile to load</param> /// <param name="progressHandler">Any valid implementation of the DotSpatial.Data.IProgressHandler</param> private void Open(string fileName, IProgressHandler progressHandler) { if (!File.Exists(fileName)) return; Header = new ShapefileHeader(fileName); // Check to ensure that the fileName is the correct shape type switch (FeatureType) { case FeatureType.Line: if (Header.ShapeType != ShapeType.PolyLine && Header.ShapeType != ShapeType.PolyLineM && Header.ShapeType != ShapeType.PolyLineZ) { throw new ArgumentException(DataStrings.FileNotLines_S.Replace("%S", fileName)); } break; case FeatureType.Polygon: if (Header.ShapeType != ShapeType.Polygon && Header.ShapeType != ShapeType.PolygonM && Header.ShapeType != ShapeType.PolygonZ) { throw new ArgumentException(DataStrings.FileNotPolygons_S.Replace("%S", fileName)); } break; case FeatureType.Point: if (Header.ShapeType != ShapeType.Point && Header.ShapeType != ShapeType.PointM && Header.ShapeType != ShapeType.PointZ) { throw new ArgumentException(DataStrings.FileNotPoints_S.Replace("%S", fileName)); } break; case FeatureType.MultiPoint: if (Header.ShapeType != ShapeType.MultiPoint && Header.ShapeType != ShapeType.MultiPointM && Header.ShapeType != ShapeType.MultiPointZ) { throw new ArgumentException(DataStrings.FileNotMultipoints_S.Replace("%S", fileName)); } break; } switch (Header.ShapeType) { case ShapeType.Point: case ShapeType.MultiPoint: case ShapeType.PolyLine: case ShapeType.Polygon: CoordinateType = CoordinateType.Regular; break; case ShapeType.PointM: case ShapeType.MultiPointM: case ShapeType.PolyLineM: case ShapeType.PolygonM: CoordinateType = CoordinateType.M; break; case ShapeType.PointZ: case ShapeType.MultiPointZ: case ShapeType.PolyLineZ: case ShapeType.PolygonZ: CoordinateType = CoordinateType.Z; break; default: throw new Exception("Unsupported ShapeType"); } Filename = fileName; IndexMode = true; Extent = Header.ToExtent(); Name = Path.GetFileNameWithoutExtension(fileName); Attributes.Open(fileName); FillIndexes(fileName, progressHandler); ReadProjection(); }
private void Configure() { Attributes = new AttributeTable(); Header = new ShapefileHeader(); IndexMode = true; }
/// <summary> /// Obtains a typed list of ShapefilePoint structures with double values associated with the various coordinates. /// </summary> /// <param name="fileName">A string fileName</param> /// <param name="progressHandler">A progress indicator</param> private void FillPoints(string fileName, IProgressHandler progressHandler) { // Check to ensure the fileName is not null if (fileName == null) { throw new NullReferenceException(DataStrings.ArgumentNull_S.Replace("%S", fileName)); } if (File.Exists(fileName) == false) { throw new FileNotFoundException(DataStrings.FileNotFound_S.Replace("%S", fileName)); } // Reading the headers gives us an easier way to track the number of shapes and their overall length etc. List <ShapeHeader> shapeHeaders = ReadIndexFile(fileName); // Get the basic header information. ShapefileHeader header = new ShapefileHeader(fileName); MyExtent = header.ToExtent(); // Check to ensure that the fileName is the correct shape type if (header.ShapeType != ShapeType.Point && header.ShapeType != ShapeType.PointM && header.ShapeType != ShapeType.PointZ) { throw new ApplicationException(DataStrings.FileNotPoints_S.Replace("%S", fileName)); } // This will set up a reader so that we can read values in huge chunks, which is much // faster than one value at a time. BufferedBinaryReader bbReader = new BufferedBinaryReader(fileName, progressHandler); if (bbReader.FileLength == 100) { bbReader.Close(); // the file is empty so we are done reading return; } // Skip the shapefile header by skipping the first 100 bytes in the shapefile bbReader.Seek(100, SeekOrigin.Begin); int numShapes = shapeHeaders.Count; byte[] bigEndian = new byte[numShapes * 8]; byte[] allCoords = new byte[numShapes * 16]; bool isM = false; bool isZ = false; if (header.ShapeType == ShapeType.PointM || header.ShapeType == ShapeType.PointZ) { isM = true; } if (header.ShapeType == ShapeType.PointZ) { isZ = true; } byte[] allM = new byte[8]; if (isM) { allM = new byte[numShapes * 8]; } byte[] allZ = new byte[8]; if (isZ) { allZ = new byte[numShapes * 8]; } for (int shp = 0; shp < numShapes; shp++) { // Read from the index file because some deleted records // might still exist in the .shp file. long offset = (shapeHeaders[shp].ByteOffset); bbReader.Seek(offset, SeekOrigin.Begin); bbReader.Read(bigEndian, shp * 8, 8); bbReader.ReadInt32(); // Skip ShapeType. Null shapes may break this. //bbReader.Seek(4, SeekOrigin.Current); bbReader.Read(allCoords, shp * 16, 16); if (isZ) { bbReader.Read(allZ, shp * 8, 8); } if (isM) { bbReader.Read(allM, shp * 8, 8); } ShapeRange shape = new ShapeRange(FeatureType.Point) { StartIndex = shp, ContentLength = 8, NumPoints = 1, NumParts = 1 }; ShapeIndices.Add(shape); } double[] vert = new double[2 * numShapes]; Buffer.BlockCopy(allCoords, 0, vert, 0, numShapes * 16); Vertex = vert; if (isM) { double[] m = new double[numShapes]; Buffer.BlockCopy(allM, 0, m, 0, numShapes * 8); M = m; } if (isZ) { double[] z = new double[numShapes]; Buffer.BlockCopy(allZ, 0, z, 0, numShapes * 8); Z = z; } for (int shp = 0; shp < numShapes; shp++) { PartRange part = new PartRange(vert, shp, 0, FeatureType.Point); part.NumVertices = 1; ShapeRange shape = ShapeIndices[shp]; shape.Parts.Add(part); shape.Extent = new Extent(new[] { vert[shp * 2], vert[shp * 2 + 1], vert[shp * 2], vert[shp * 2 + 1] }); } bbReader.Dispose(); }
/// <summary> /// Creates a new instance of a LineShapefile for in-ram handling only. /// </summary> public LineShapefile() : base(FeatureType.Line) { Attributes = new AttributeTable(); Header = new ShapefileHeader { FileLength = 100, ShapeType = ShapeType.PolyLine }; }
/// <summary> /// Writes the current content to the specified file. /// </summary> /// <param name="header">The header to write</param> /// <param name="fileName">Basically the same code can be used for the shp and shx files</param> /// <param name="numShapes">The integer number of shapes to write to the file</param> private static void WriteHeader(ShapefileHeader header, string fileName, int numShapes) { string dir = Path.GetDirectoryName(fileName); if (dir != null) { if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } } FileStream bbWriter = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None, 100); bbWriter.WriteBe(header.FileCode); // Byte 0 File Code 9994 Integer Big byte[] bt = new byte[20]; bbWriter.Write(bt, 0, 20); // Bytes 4 - 20 are unused // This is overwritten later bbWriter.WriteBe(50 + 4 * numShapes); // Byte 24 File Length File Length Integer Big bbWriter.WriteLe(header.Version); // Byte 28 Version 1000 Integer Little bbWriter.WriteLe((int)header.ShapeType); // Byte 32 Shape Type Shape Type Integer Little bbWriter.WriteLe(header.Xmin); // Byte 36 Bounding Box Xmin Double Little bbWriter.WriteLe(header.Ymin); // Byte 44 Bounding Box Ymin Double Little bbWriter.WriteLe(header.Xmax); // Byte 52 Bounding Box Xmax Double Little bbWriter.WriteLe(header.Ymax); // Byte 60 Bounding Box Ymax Double Little bbWriter.WriteLe(header.Zmin); // Byte 68 Bounding Box Zmin Double Little bbWriter.WriteLe(header.Zmax); // Byte 76 Bounding Box Zmax Double Little bbWriter.WriteLe(header.Mmin); // Byte 84 Bounding Box Mmin Double Little bbWriter.WriteLe(header.Mmax); // Byte 92 Bounding Box Mmax Double Little // ------------ WRITE TO SHP FILE ------------------------- bbWriter.Close(); }
// X Y Poly Lines: Total Length = 28 Bytes // --------------------------------------------------------- // Position Value Type Number Byte Order // --------------------------------------------------------- // Byte 0 Record Number Integer 1 Big // Byte 4 Content Length Integer 1 Big // Byte 8 Shape Type 3 Integer 1 Little // Byte 12 Xmin Double 1 Little // Byte 20 Ymin Double 1 Little // Byte 28 Xmax Double 1 Little // Byte 36 Ymax Double 1 Little // Byte 44 NumParts Integer 1 Little // Byte 48 NumPoints Integer 1 Little // Byte 52 Parts Integer NumParts Little // Byte X Points Point NumPoints Little // X Y M Poly Lines: Total Length = 34 Bytes // --------------------------------------------------------- // Position Value Type Number Byte Order // --------------------------------------------------------- // Byte 0 Record Number Integer 1 Big // Byte 4 Content Length Integer 1 Big // Byte 8 Shape Type 23 Integer 1 Little // Byte 12 Box Double 4 Little // Byte 44 NumParts Integer 1 Little // Byte 48 NumPoints Integer 1 Little // Byte 52 Parts Integer NumParts Little // Byte X Points Point NumPoints Little // Byte Y* Mmin Double 1 Little // Byte Y + 8* Mmax Double 1 Little // Byte Y + 16* Marray Double NumPoints Little // X Y Z M Poly Lines: Total Length = 44 Bytes // --------------------------------------------------------- // Position Value Type Number Byte Order // --------------------------------------------------------- // Byte 0 Record Number Integer 1 Big // Byte 4 Content Length Integer 1 Big // Byte 8 Shape Type 13 Integer 1 Little // Byte 12 Box Double 4 Little // Byte 44 NumParts Integer 1 Little // Byte 48 NumPoints Integer 1 Little // Byte 52 Parts Integer NumParts Little // Byte X Points Point NumPoints Little // Byte Y Zmin Double 1 Little // Byte Y + 8 Zmax Double 1 Little // Byte Y + 16 Zarray Double NumPoints Little // Byte Z* Mmin Double 1 Little // Byte Z+8* Mmax Double 1 Little // Byte Z+16* Marray Double NumPoints Little private void FillPolygons(string fileName, IProgressHandler progressHandler) { // Check to ensure the fileName is not null if (fileName == null) { throw new NullReferenceException(DataStrings.ArgumentNull_S.Replace("%S", fileName)); } if (File.Exists(fileName) == false) { throw new FileNotFoundException(DataStrings.FileNotFound_S.Replace("%S", fileName)); } // Get the basic header information. ShapefileHeader header = new ShapefileHeader(fileName); Extent = new Extent(new[] { header.Xmin, header.Ymin, header.Xmax, header.Ymax }); // Check to ensure that the fileName is the correct shape type if (header.ShapeType != ShapeType.Polygon && header.ShapeType != ShapeType.PolygonM && header.ShapeType != ShapeType.PolygonZ) { throw new ArgumentException(DataStrings.FileNotLines_S.Replace("%S", fileName)); } // Reading the headers gives us an easier way to track the number of shapes and their overall length etc. List <ShapeHeader> shapeHeaders = ReadIndexFile(fileName); // TO DO: replace with a normal reader. We no longer need Buffered Binary reader as // the buffer can be set on the underlying file stream. BufferedBinaryReader bbReader = new BufferedBinaryReader(fileName, progressHandler); if (bbReader.FileLength == 100) { // The shapefile is empty so we can simply return here bbReader.Close(); return; } // Skip the shapefile header by skipping the first 100 bytes in the shapefile bbReader.Seek(100, SeekOrigin.Begin); int numShapes = shapeHeaders.Count; int[] partOffsets = new int[numShapes]; //byte[] allBounds = new byte[numShapes * 32]; // probably all will be in one block, but use a byteBlock just in case. ByteBlock allParts = new ByteBlock(BLOCKSIZE); ByteBlock allCoords = new ByteBlock(BLOCKSIZE); bool isM = (header.ShapeType == ShapeType.PolygonM || header.ShapeType == ShapeType.PolygonZ); bool isZ = (header.ShapeType == ShapeType.PolygonZ); ByteBlock allZ = null; ByteBlock allM = null; if (isZ) { allZ = new ByteBlock(BLOCKSIZE); } if (isM) { allM = new ByteBlock(BLOCKSIZE); } int pointOffset = 0; for (int shp = 0; shp < numShapes; shp++) { // Read from the index file because some deleted records // might still exist in the .shp file. long offset = (shapeHeaders[shp].ByteOffset); bbReader.Seek(offset, SeekOrigin.Begin); // Position Value Type Number Byte Order ShapeRange shape = new ShapeRange(FeatureType.Polygon); //------------------------------------ shape.RecordNumber = bbReader.ReadInt32(false); // Byte 0 Record Integer 1 Big shape.ContentLength = bbReader.ReadInt32(false); // Byte 4 Length Integer 1 Big // Setting shape type also controls extent class type. shape.ShapeType = (ShapeType)bbReader.ReadInt32(); // Byte 8 Type Integer 1 Little shape.StartIndex = pointOffset; if (shape.ShapeType == ShapeType.NullShape) { continue; } shape.Extent.MinX = bbReader.ReadDouble(); shape.Extent.MinY = bbReader.ReadDouble(); shape.Extent.MaxX = bbReader.ReadDouble(); shape.Extent.MaxY = bbReader.ReadDouble(); shape.NumParts = bbReader.ReadInt32(); // Byte 44 #Parts Integer 1 Little shape.NumPoints = bbReader.ReadInt32(); // Byte 48 #Points Integer 1 Little partOffsets[shp] = allParts.IntOffset(); allParts.Read(shape.NumParts * 4, bbReader); allCoords.Read(shape.NumPoints * 16, bbReader); pointOffset += shape.NumPoints; 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) { IExtentM mExt = (IExtentM)shape.Extent; mExt.MinM = bbReader.ReadDouble(); mExt.MaxM = bbReader.ReadDouble(); if (allM != null) { allM.Read(shape.NumPoints * 8, bbReader); } } } if (header.ShapeType == ShapeType.PolygonZ) { bool hasM = shape.ContentLength * 2 > 60 + 4 * shape.NumParts + 24 * shape.NumPoints; IExtentZ zExt = (IExtentZ)shape.Extent; zExt.MinZ = bbReader.ReadDouble(); zExt.MaxZ = bbReader.ReadDouble(); // For Z shapefiles, the Z part is not optional. if (allZ != null) { allZ.Read(shape.NumPoints * 8, bbReader); } // 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) { IExtentM mExt = (IExtentM)shape.Extent; mExt.MinM = bbReader.ReadDouble(); mExt.MaxM = bbReader.ReadDouble(); if (allM != null) { allM.Read(shape.NumPoints * 8, bbReader); } } } ShapeIndices.Add(shape); } double[] vert = allCoords.ToDoubleArray(); Vertex = vert; if (isM) { M = allM.ToDoubleArray(); } if (isZ) { Z = allZ.ToDoubleArray(); } List <ShapeRange> shapes = ShapeIndices; //double[] bounds = new double[numShapes * 4]; //Buffer.BlockCopy(allBounds, 0, bounds, 0, allBounds.Length); int[] parts = allParts.ToIntArray(); ProgressMeter = new ProgressMeter(ProgressHandler, "Testing Parts and Holes", shapes.Count); for (int shp = 0; shp < shapes.Count; shp++) { ShapeRange shape = shapes[shp]; //shape.Extent = new Extent(bounds, shp * 4); for (int part = 0; part < shape.NumParts; part++) { int offset = partOffsets[shp]; int endIndex = shape.NumPoints + shape.StartIndex; int startIndex = parts[offset + part] + shape.StartIndex; if (part < shape.NumParts - 1) { endIndex = parts[offset + part + 1] + shape.StartIndex; } int count = endIndex - startIndex; PartRange partR = new PartRange(vert, shape.StartIndex, parts[offset + part], FeatureType.Polygon); partR.NumVertices = count; shape.Parts.Add(partR); } ProgressMeter.CurrentValue = shp; } ProgressMeter.Reset(); }
private void Configure() { Attributes = new AttributeTable(); _header = new ShapefileHeader(); IndexMode = true; }
/// <summary> /// Update the header to include the feature extent /// </summary> /// <param name="header"></param> /// <param name="feature"></param> protected void UpdateHeader(ShapefileHeader header, IGeometry feature) { UpdateHeader(header, feature, true); }
/// <inheritdoc/> protected override void AppendGeometry(ShapefileHeader header, Geometry feature, int numFeatures) { FileInfo fi = new FileInfo(Filename); int offset = Convert.ToInt32(fi.Length / 2); FileStream shpStream = new FileStream(Filename, FileMode.Append, FileAccess.Write, FileShare.None, 10000); FileStream shxStream = new FileStream(header.ShxFilename, FileMode.Append, FileAccess.Write, FileShare.None, 100); List <int> parts = new List <int>(); List <Coordinate> points = new List <Coordinate>(); int contentLength = 22; for (int iPart = 0; iPart < feature.NumGeometries; iPart++) { parts.Add(points.Count); LineString pg = feature.GetGeometryN(iPart) as LineString; if (pg == null) { continue; } points.AddRange(pg.Coordinates); } contentLength += 2 * parts.Count; if (header.ShapeType == ShapeType.PolyLine) { contentLength += points.Count * 8; } if (header.ShapeType == ShapeType.PolyLineM) { contentLength += 8; // mmin mmax contentLength += points.Count * 12; // x, y, m } if (header.ShapeType == ShapeType.PolyLineZ) { contentLength += 16; // mmin, mmax, zmin, zmax contentLength += points.Count * 16; // x, y, m, z } //// Index File // ------------------------------------------------------------------- // Position Value Type Number Byte Order // ------------------------------------------------------------------- shxStream.WriteBe(offset); // Byte 0 Offset Integer 1 Big shxStream.WriteBe(contentLength); // Byte 4 Content Length Integer 1 Big shxStream.Flush(); shxStream.Close(); //// X Y Poly Lines // ------------------------------------------------------------------- // Position Value Type Number Byte Order // ------------------------------------------------------------------- shpStream.WriteBe(numFeatures); // Byte 0 Record Number Integer 1 Big shpStream.WriteBe(contentLength); // Byte 4 Content Length Integer 1 Big shpStream.WriteLe((int)header.ShapeType); // Byte 8 Shape Type 3 Integer 1 Little if (header.ShapeType == ShapeType.NullShape) { return; } shpStream.WriteLe(feature.EnvelopeInternal.MinX); // Byte 12 Xmin Double 1 Little shpStream.WriteLe(feature.EnvelopeInternal.MinY); // Byte 20 Ymin Double 1 Little shpStream.WriteLe(feature.EnvelopeInternal.MaxX); // Byte 28 Xmax Double 1 Little shpStream.WriteLe(feature.EnvelopeInternal.MaxY); // Byte 36 Ymax Double 1 Little shpStream.WriteLe(parts.Count); // Byte 44 NumParts Integer 1 Little shpStream.WriteLe(points.Count); // Byte 48 NumPoints Integer 1 Little foreach (int iPart in parts) { shpStream.WriteLe(iPart); // Byte 52 Parts Integer NumParts Little } double[] xyVals = new double[points.Count * 2]; for (var i = 0; i < points.Count; i++) { xyVals[i * 2] = points[i].X; xyVals[(i * 2) + 1] = points[i].Y; } shpStream.WriteLe(xyVals, 0, 2 * points.Count); if (header.ShapeType == ShapeType.PolyLineZ) { shpStream.WriteLe(feature.MinZ()); shpStream.WriteLe(feature.MaxZ()); double[] zVals = new double[points.Count]; for (int ipoint = 0; ipoint < points.Count; ipoint++) { zVals[ipoint] = points[ipoint].Z; } shpStream.WriteLe(zVals, 0, points.Count); } if (header.ShapeType == ShapeType.PolyLineM || header.ShapeType == ShapeType.PolyLineZ) { if (feature.Envelope == null) { shpStream.WriteLe(0.0); shpStream.WriteLe(0.0); } else { shpStream.WriteLe(feature.MinM()); shpStream.WriteLe(feature.MaxM()); } double[] mVals = new double[points.Count]; for (int ipoint = 0; ipoint < points.Count; ipoint++) { mVals[ipoint] = points[ipoint].M; } shpStream.WriteLe(mVals, 0, points.Count); } shpStream.Flush(); shpStream.Close(); offset += contentLength; Shapefile.WriteFileLength(Filename, offset + 4); // Add 4 for the record header Shapefile.WriteFileLength(header.ShxFilename, 50 + (numFeatures * 4)); }
protected override void InsertGeometry(ShapefileHeader header, int fid, IGeometry geometry) { var shapeHeaders = ReadIndexFile(header.ShxFilename); if (fid < shapeHeaders.Count) { var tmpShpPath = Path.GetTempFileName(); var tmpShxPath = Path.GetTempFileName(); FileStream tmpShpStream = new FileStream(tmpShpPath, FileMode.Create, FileAccess.ReadWrite); FileStream shpStream = new FileStream(Filename, FileMode.Open, FileAccess.ReadWrite, FileShare.Read, 10000); FileStream tmpShxStream = new FileStream(tmpShxPath, FileMode.Create, FileAccess.ReadWrite); FileStream shxStream = new FileStream(header.ShxFilename, FileMode.Open, FileAccess.ReadWrite, FileShare.Read, 100); long shpOffset = shapeHeaders[fid].ByteOffset; long shpRemainderOffset = shpOffset; long shpRemainderCount = shpStream.Length - shpRemainderOffset; if (shpRemainderCount > 0) { CopyTo(shpStream, tmpShpStream, shpRemainderOffset, shpRemainderCount); } long shxOffset = 100 + fid * 8; long shxRemainderOffset = shxOffset; long shxRemainderCount = shxStream.Length - shxRemainderOffset; if (shxRemainderCount > 0) { CopyTo(shxStream, tmpShxStream, shxRemainderOffset, shxRemainderCount); } shpStream.Seek(shpOffset, SeekOrigin.Begin); shxStream.Seek(shxOffset, SeekOrigin.Begin); int recordNumber = fid + 1; int contentLength = GetContentLength(header.ShapeType); //// Index File // --------------------------------------------------------- // Position Value Type Number Byte Order // --------------------------------------------------------- shxStream.WriteBe(shapeHeaders[fid].Offset); // Byte 0 Offset Integer 1 Big shxStream.WriteBe(contentLength); // Byte 4 Content Length Integer 1 Big //// X Y Points // --------------------------------------------------------- // Position Value Type Number Byte Order // --------------------------------------------------------- shpStream.WriteBe(recordNumber); // Byte 0 Record Number Integer 1 Big shpStream.WriteBe(contentLength); // Byte 4 Content Length Integer 1 Big shpStream.WriteLe((int)header.ShapeType); // Byte 8 Shape Type 3 Integer 1 Little if (header.ShapeType != ShapeType.NullShape) { WriteGeometryContent(shpStream, header.ShapeType, geometry); } if (shxRemainderCount > 0) { CopyTo(tmpShxStream, shxStream, 0, shxRemainderCount); } int dOffset = (int)((shpStream.Position - shpOffset) / 2); if (dOffset != 0) { long shpPosition = shpStream.Position; for (int i = fid; i < shapeHeaders.Count; i++) { shxStream.Seek(100 + (i + 1) * 8, SeekOrigin.Begin); shxStream.WriteBe(shapeHeaders[i].Offset + dOffset); shpStream.Seek((shapeHeaders[i].Offset + dOffset) * 2, SeekOrigin.Begin); shpStream.WriteBe(i + 2); } shpStream.Seek(shpPosition, SeekOrigin.Begin); } int shxLength = Convert.ToInt32(shxStream.Length / 2); shxStream.Flush(); tmpShxStream.Dispose(); shxStream.Dispose(); File.Delete(tmpShxPath); if (shpRemainderCount > 0) { CopyTo(tmpShpStream, shpStream, 0, shpRemainderCount); } if (shpStream.Length != shpStream.Position + shpRemainderCount) { shpStream.SetLength(shpStream.Position + shpRemainderCount); } ; int shpLength = Convert.ToInt32(shpStream.Length / 2); shpStream.Flush(); shpStream.Dispose(); Shapefile.WriteFileLength(Filename, shpLength); Shapefile.WriteFileLength(header.ShxFilename, shxLength); tmpShpStream.Dispose(); File.Delete(tmpShpPath); } }
/// <summary> /// Append the geometry to the shapefile. /// </summary> /// <param name="header">ShapefileHeader of this file.</param> /// <param name="geometry">Geometry that gets appended.</param> /// <param name="numFeatures">Number of the features in the shapefile including the one getting appended.</param> protected abstract void AppendGeometry(ShapefileHeader header, Geometry geometry, int numFeatures);
/// <summary> /// Updates the file with an additional feature. /// </summary> /// <param name="feature">Feature that gets added.</param> public void Add(IFeature feature) { if (feature.FeatureType != FeatureType) { throw new FeatureTypeMismatchException(); } string dir = Path.GetDirectoryName(Filename); if (dir != null && !Directory.Exists(dir)) { Directory.CreateDirectory(dir); } // We must add the dbf entry before changing the shx because if we already have one deleted record, the AttributeTable thinks we have none AttributeTable dbf = GetAttributeTable(Filename); dbf.AddRow(feature.DataRow); int numFeatures = 0; var header = new ShapefileHeader(); if (File.Exists(Filename)) { header.Open(Filename); UpdateHeader(header, feature.Geometry); numFeatures = (header.ShxLength - 50) / 4; } else { header.Xmin = feature.Geometry.EnvelopeInternal.MinX; header.Xmax = feature.Geometry.EnvelopeInternal.MaxX; header.Ymin = feature.Geometry.EnvelopeInternal.MinY; header.Ymax = feature.Geometry.EnvelopeInternal.MaxY; if (double.IsNaN(feature.Geometry.Coordinates[0].M)) { header.ShapeType = ShapeType; } else { if (double.IsNaN(feature.Geometry.Coordinates[0].Z)) { header.ShapeType = ShapeTypeM; } else { header.Zmin = feature.Geometry.MinZ(); header.Zmax = feature.Geometry.MaxZ(); header.ShapeType = ShapeTypeZ; } header.Mmin = feature.Geometry.MinM(); header.Mmax = feature.Geometry.MaxM(); } header.ShxLength = 4 + 50; header.SaveAs(Filename); } AppendGeometry(header, feature.Geometry, numFeatures); Quadtree?.Insert(feature.Geometry.EnvelopeInternal, numFeatures - 1); }
/// <summary> /// Append the geometry to the shapefile /// </summary> /// <param name="header"></param> /// <param name="feature"></param> /// <param name="numFeatures"></param> protected abstract void AppendBasicGeometry(ShapefileHeader header, IBasicGeometry feature, int numFeatures);
/// <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);
/// <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);
// X Y MultiPoints: Total Length = 28 Bytes // --------------------------------------------------------- // Position Value Type Number Byte Order // --------------------------------------------------------- // Byte 0 Record Number Integer 1 Big // Byte 4 Content Length Integer 1 Big // Byte 8 Shape Type 8 Integer 1 Little // Byte 12 Xmin Double 1 Little // Byte 20 Ymin Double 1 Little // Byte 28 Xmax Double 1 Little // Byte 36 Ymax Double 1 Little // Byte 48 NumPoints Integer 1 Little // Byte X Points Point NumPoints Little // X Y M MultiPoints: Total Length = 34 Bytes // --------------------------------------------------------- // Position Value Type Number Byte Order // --------------------------------------------------------- // Byte 0 Record Number Integer 1 Big // Byte 4 Content Length Integer 1 Big // Byte 8 Shape Type 28 Integer 1 Little // Byte 12 Box Double 4 Little // Byte 44 NumPoints Integer 1 Little // Byte X Points Point NumPoints Little // Byte Y* Mmin Double 1 Little // Byte Y + 8* Mmax Double 1 Little // Byte Y + 16* Marray Double NumPoints Little // X Y Z M MultiPoints: Total Length = 44 Bytes // --------------------------------------------------------- // Position Value Type Number Byte Order // --------------------------------------------------------- // Byte 0 Record Number Integer 1 Big // Byte 4 Content Length Integer 1 Big // Byte 8 Shape Type 18 Integer 1 Little // Byte 12 Box Double 4 Little // Byte 44 NumPoints Integer 1 Little // Byte X Points Point NumPoints Little // Byte Y Zmin Double 1 Little // Byte Y + 8 Zmax Double 1 Little // Byte Y + 16 Zarray Double NumPoints Little // Byte Z* Mmin Double 1 Little // Byte Z+8* Mmax Double 1 Little // Byte Z+16* Marray Double NumPoints Little private void FillPoints(string fileName, IProgressHandler progressHandler) { // Check to ensure the fileName is not null if (fileName == null) { throw new NullReferenceException(DataStrings.ArgumentNull_S.Replace("%S", fileName)); } if (File.Exists(fileName) == false) { throw new FileNotFoundException(DataStrings.FileNotFound_S.Replace("%S", fileName)); } // Get the basic header information. ShapefileHeader header = new ShapefileHeader(fileName); Extent = new Extent(new[] { header.Xmin, header.Ymin, header.Xmax, header.Ymax }); // Check to ensure that the fileName is the correct shape type if (header.ShapeType != ShapeType.MultiPoint && header.ShapeType != ShapeType.MultiPointM && header.ShapeType != ShapeType.MultiPointZ) { throw new ArgumentException(DataStrings.FileNotLines_S.Replace("%S", fileName)); } // Reading the headers gives us an easier way to track the number of shapes and their overall length etc. List<ShapeHeader> shapeHeaders = ReadIndexFile(fileName); // This will set up a reader so that we can read values in huge chunks, which is much faster than one value at a time. BufferedBinaryReader bbReader = new BufferedBinaryReader(fileName, progressHandler); if (bbReader.FileLength == 100) { // The shapefile is empty so we can simply return here bbReader.Close(); return; } // Skip the shapefile header by skipping the first 100 bytes in the shapefile bbReader.Seek(100, SeekOrigin.Begin); int numShapes = shapeHeaders.Count; byte[] bigEndians = new byte[numShapes * 8]; byte[] allBounds = new byte[numShapes * 32]; ByteBlock allCoords = new ByteBlock(BLOCKSIZE); bool isM = (header.ShapeType == ShapeType.MultiPointZ || header.ShapeType == ShapeType.MultiPointM); bool isZ = (header.ShapeType == ShapeType.PolyLineZ); ByteBlock allZ = null; ByteBlock allM = null; if (isZ) { allZ = new ByteBlock(BLOCKSIZE); } if (isM) { allM = new ByteBlock(BLOCKSIZE); } int pointOffset = 0; for (int shp = 0; shp < numShapes; shp++) { // Read from the index file because some deleted records // might still exist in the .shp file. long offset = (shapeHeaders[shp].ByteOffset); bbReader.Seek(offset, SeekOrigin.Begin); // time: 200 ms ShapeRange shape = new ShapeRange(FeatureType.MultiPoint) { RecordNumber = bbReader.ReadInt32(false), ContentLength = bbReader.ReadInt32(false), ShapeType = (ShapeType)bbReader.ReadInt32(), StartIndex = pointOffset }; //bbReader.Read(bigEndians, shp * 8, 8); if (shape.ShapeType == ShapeType.NullShape) { continue; } bbReader.Read(allBounds, shp * 32, 32); shape.NumParts = 1; shape.NumPoints = bbReader.ReadInt32(); allCoords.Read(shape.NumPoints * 16, bbReader); pointOffset += shape.NumPoints; if (header.ShapeType == ShapeType.MultiPointM) { // 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) { IExtentM mExt = (IExtentM)MyExtent; mExt.MinM = bbReader.ReadDouble(); mExt.MaxM = bbReader.ReadDouble(); if (allM != null) allM.Read(shape.NumPoints * 8, bbReader); } } if (header.ShapeType == ShapeType.MultiPointZ) { bool hasM = shape.ContentLength * 2 > 60 + 4 * shape.NumParts + 24 * shape.NumPoints; IExtentZ zExt = (IExtentZ)MyExtent; zExt.MinZ = bbReader.ReadDouble(); zExt.MaxZ = bbReader.ReadDouble(); // For Z shapefiles, the Z part is not optional. if (allZ != null) allZ.Read(shape.NumPoints * 8, bbReader); // 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) { IExtentM mExt = (IExtentM)MyExtent; mExt.MinM = bbReader.ReadDouble(); mExt.MaxM = bbReader.ReadDouble(); if (allM != null) allM.Read(shape.NumPoints * 8, bbReader); } } // Now that we have read all the values, create the geometries from the points and parts arrays. ShapeIndices.Add(shape); } double[] vert = allCoords.ToDoubleArray(); Vertex = vert; if (isM) M = allM.ToDoubleArray(); if (isZ) Z = allZ.ToDoubleArray(); Array.Reverse(bigEndians); List<ShapeRange> shapes = ShapeIndices; double[] bounds = new double[numShapes * 4]; Buffer.BlockCopy(allBounds, 0, bounds, 0, allBounds.Length); for (int shp = 0; shp < numShapes; shp++) { ShapeRange shape = shapes[shp]; shape.Extent = new Extent(bounds, shp * 4); int endIndex = shape.NumPoints + shape.StartIndex; int startIndex = shape.StartIndex; int count = endIndex - startIndex; PartRange partR = new PartRange(vert, shape.StartIndex, 0, FeatureType.MultiPoint) { NumVertices = count }; shape.Parts.Add(partR); } bbReader.Dispose(); }
// X Y Poly Lines: Total Length = 28 Bytes // --------------------------------------------------------- // Position Value Type Number Byte Order // --------------------------------------------------------- // Byte 0 Record Number Integer 1 Big // Byte 4 Content Length Integer 1 Big // Byte 8 Shape Type 3 Integer 1 Little // Byte 12 Xmin Double 1 Little // Byte 20 Ymin Double 1 Little // Byte 28 Xmax Double 1 Little // Byte 36 Ymax Double 1 Little // Byte 44 NumParts Integer 1 Little // Byte 48 NumPoints Integer 1 Little // Byte 52 Parts Integer NumParts Little // Byte X Points Point NumPoints Little // X Y M Poly Lines: Total Length = 34 Bytes // --------------------------------------------------------- // Position Value Type Number Byte Order // --------------------------------------------------------- // Byte 0 Record Number Integer 1 Big // Byte 4 Content Length Integer 1 Big // Byte 8 Shape Type 23 Integer 1 Little // Byte 12 Box Double 4 Little // Byte 44 NumParts Integer 1 Little // Byte 48 NumPoints Integer 1 Little // Byte 52 Parts Integer NumParts Little // Byte X Points Point NumPoints Little // Byte Y* Mmin Double 1 Little // Byte Y + 8* Mmax Double 1 Little // Byte Y + 16* Marray Double NumPoints Little // X Y Z M Poly Lines: Total Length = 44 Bytes // --------------------------------------------------------- // Position Value Type Number Byte Order // --------------------------------------------------------- // Byte 0 Record Number Integer 1 Big // Byte 4 Content Length Integer 1 Big // Byte 8 Shape Type 13 Integer 1 Little // Byte 12 Box Double 4 Little // Byte 44 NumParts Integer 1 Little // Byte 48 NumPoints Integer 1 Little // Byte 52 Parts Integer NumParts Little // Byte X Points Point NumPoints Little // Byte Y Zmin Double 1 Little // Byte Y + 8 Zmax Double 1 Little // Byte Y + 16 Zarray Double NumPoints Little // Byte Z* Mmin Double 1 Little // Byte Z+8* Mmax Double 1 Little // Byte Z+16* Marray Double NumPoints Little private void FillPolygons(string fileName, IProgressHandler progressHandler) { // Check to ensure the fileName is not null if (fileName == null) { throw new NullReferenceException(DataStrings.ArgumentNull_S.Replace("%S", fileName)); } if (File.Exists(fileName) == false) { throw new FileNotFoundException(DataStrings.FileNotFound_S.Replace("%S", fileName)); } // Get the basic header information. ShapefileHeader header = new ShapefileHeader(fileName); Extent = new Extent(new[] { header.Xmin, header.Ymin, header.Xmax, header.Ymax }); // Check to ensure that the fileName is the correct shape type if (header.ShapeType != ShapeType.Polygon && header.ShapeType != ShapeType.PolygonM && header.ShapeType != ShapeType.PolygonZ) { throw new ArgumentException(DataStrings.FileNotLines_S.Replace("%S", fileName)); } // Reading the headers gives us an easier way to track the number of shapes and their overall length etc. List<ShapeHeader> shapeHeaders = ReadIndexFile(fileName); // TO DO: replace with a normal reader. We no longer need Buffered Binary reader as // the buffer can be set on the underlying file stream. BufferedBinaryReader bbReader = new BufferedBinaryReader(fileName, progressHandler); if (bbReader.FileLength == 100) { // The shapefile is empty so we can simply return here bbReader.Close(); return; } // Skip the shapefile header by skipping the first 100 bytes in the shapefile bbReader.Seek(100, SeekOrigin.Begin); int numShapes = shapeHeaders.Count; int[] partOffsets = new int[numShapes]; //byte[] allBounds = new byte[numShapes * 32]; // probably all will be in one block, but use a byteBlock just in case. ByteBlock allParts = new ByteBlock(BLOCKSIZE); ByteBlock allCoords = new ByteBlock(BLOCKSIZE); bool isM = (header.ShapeType == ShapeType.PolygonM || header.ShapeType == ShapeType.PolygonZ); bool isZ = (header.ShapeType == ShapeType.PolygonZ); ByteBlock allZ = null; ByteBlock allM = null; if (isZ) { allZ = new ByteBlock(BLOCKSIZE); } if (isM) { allM = new ByteBlock(BLOCKSIZE); } int pointOffset = 0; for (int shp = 0; shp < numShapes; shp++) { // Read from the index file because some deleted records // might still exist in the .shp file. long offset = (shapeHeaders[shp].ByteOffset); bbReader.Seek(offset, SeekOrigin.Begin); // Position Value Type Number Byte Order ShapeRange shape = new ShapeRange(FeatureType.Polygon); //------------------------------------ shape.RecordNumber = bbReader.ReadInt32(false); // Byte 0 Record Integer 1 Big shape.ContentLength = bbReader.ReadInt32(false); // Byte 4 Length Integer 1 Big // Setting shape type also controls extent class type. shape.ShapeType = (ShapeType)bbReader.ReadInt32(); // Byte 8 Type Integer 1 Little shape.StartIndex = pointOffset; if (shape.ShapeType == ShapeType.NullShape) { continue; } shape.Extent.MinX = bbReader.ReadDouble(); shape.Extent.MinY = bbReader.ReadDouble(); shape.Extent.MaxX = bbReader.ReadDouble(); shape.Extent.MaxY = bbReader.ReadDouble(); shape.NumParts = bbReader.ReadInt32(); // Byte 44 #Parts Integer 1 Little shape.NumPoints = bbReader.ReadInt32(); // Byte 48 #Points Integer 1 Little partOffsets[shp] = allParts.IntOffset(); allParts.Read(shape.NumParts * 4, bbReader); allCoords.Read(shape.NumPoints * 16, bbReader); pointOffset += shape.NumPoints; 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) { IExtentM mExt = (IExtentM)shape.Extent; mExt.MinM = bbReader.ReadDouble(); mExt.MaxM = bbReader.ReadDouble(); if (allM != null) allM.Read(shape.NumPoints * 8, bbReader); } } if (header.ShapeType == ShapeType.PolygonZ) { bool hasM = shape.ContentLength * 2 > 60 + 4 * shape.NumParts + 24 * shape.NumPoints; IExtentZ zExt = (IExtentZ)shape.Extent; zExt.MinZ = bbReader.ReadDouble(); zExt.MaxZ = bbReader.ReadDouble(); // For Z shapefiles, the Z part is not optional. if (allZ != null) allZ.Read(shape.NumPoints * 8, bbReader); // 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) { IExtentM mExt = (IExtentM)shape.Extent; mExt.MinM = bbReader.ReadDouble(); mExt.MaxM = bbReader.ReadDouble(); if (allM != null) allM.Read(shape.NumPoints * 8, bbReader); } } ShapeIndices.Add(shape); } double[] vert = allCoords.ToDoubleArray(); Vertex = vert; if (isM) M = allM.ToDoubleArray(); if (isZ) Z = allZ.ToDoubleArray(); List<ShapeRange> shapes = ShapeIndices; //double[] bounds = new double[numShapes * 4]; //Buffer.BlockCopy(allBounds, 0, bounds, 0, allBounds.Length); int[] parts = allParts.ToIntArray(); ProgressMeter = new ProgressMeter(ProgressHandler, "Testing Parts and Holes", shapes.Count); for (int shp = 0; shp < shapes.Count; shp++) { ShapeRange shape = shapes[shp]; //shape.Extent = new Extent(bounds, shp * 4); for (int part = 0; part < shape.NumParts; part++) { int offset = partOffsets[shp]; int endIndex = shape.NumPoints + shape.StartIndex; int startIndex = parts[offset + part] + shape.StartIndex; if (part < shape.NumParts - 1) endIndex = parts[offset + part + 1] + shape.StartIndex; int count = endIndex - startIndex; PartRange partR = new PartRange(vert, shape.StartIndex, parts[offset + part], FeatureType.Polygon); partR.NumVertices = count; shape.Parts.Add(partR); } ProgressMeter.CurrentValue = shp; } ProgressMeter.Reset(); }
protected abstract void InsertGeometry(ShapefileHeader header, int fid, IGeometry geometry);
/// <summary> /// This will return the correct shapefile type by reading the fileName. /// </summary> /// <param name="fileName">A string specifying the file with the extension .shp to open.</param> /// <param name="progressHandler">receives progress messages and overrides the ProgressHandler on the DataManager.DefaultDataManager</param> /// <returns>A correct shapefile object which is exclusively for reading the .shp data</returns> public static new Shapefile OpenFile(string fileName, IProgressHandler progressHandler) { string ext = Path.GetExtension(fileName).ToLower(); if (ext != ".shp" && ext != ".shx" && ext != ".dbf") throw new ArgumentException(String.Format("The file extension {0} is not supported by Shapefile data provider.", ext)); string name = Path.ChangeExtension(fileName, ".shp"); ShapefileHeader head = new ShapefileHeader(); head.Open(name); PointShapefile psf; LineShapefile lsf; PolygonShapefile pgsf; MultiPointShapefile mpsf; switch (head.ShapeType) { case ShapeType.MultiPatch: throw new NotImplementedException("This shape type is not yet supported."); case ShapeType.MultiPoint: case ShapeType.MultiPointM: case ShapeType.MultiPointZ: mpsf = new MultiPointShapefile(); mpsf.Open(name, progressHandler); return mpsf; case ShapeType.NullShape: throw new NotImplementedException("This shape type is not yet supported."); case ShapeType.Point: case ShapeType.PointM: case ShapeType.PointZ: // Instantiate a new object to handle the point shapefile psf = new PointShapefile(); // Open the geometric components of the data (but not the dbf components) psf.Open(name, progressHandler); return psf; case ShapeType.Polygon: case ShapeType.PolygonM: case ShapeType.PolygonZ: pgsf = new PolygonShapefile(); pgsf.Open(name, progressHandler); return pgsf; case ShapeType.PolyLine: case ShapeType.PolyLineM: case ShapeType.PolyLineZ: lsf = new LineShapefile(); lsf.Open(name, progressHandler); return lsf; } return null; }
/// <summary> /// Opens the index file of the specified fileName. If the fileName is not the .shx extension, /// then the fileName will be changed too that extension first. /// </summary> /// <param name="fileName"></param> public void Open(string fileName) { Filename = Path.ChangeExtension(fileName, ".shx"); Header = new ShapefileHeader(Filename); Shapes = ReadIndexFile(Filename); }
/// <inheritdocs/> public void AddRange(IEnumerable <IFeature> features) { // Make sure the Output Directory exists string dir = Path.GetDirectoryName(Filename); if (dir != null && !Directory.Exists(dir)) { Directory.CreateDirectory(dir); } // Get the Attribute Table AttributeTable dbf = GetAttributeTable(Filename); // Open the Header if the Filename exists bool filenameExists = File.Exists(Filename); var header = new ShapefileHeader(); if (filenameExists) { header.Open(Filename); } foreach (var feature in features) { if (feature.FeatureType != FeatureType) { throw new FeatureTypeMismatchException(); } // We must add the dbf entry before changing the shx because if we already have one deleted record, the AttributeTable thinks we have none dbf.AddRow(feature.DataRow); int numFeatures = 0; if (filenameExists) { UpdateHeader(header, feature.Geometry, true); numFeatures = (header.ShxLength - 50) / 4; } else { header.Xmin = feature.Geometry.EnvelopeInternal.MinX; header.Xmax = feature.Geometry.EnvelopeInternal.MaxX; header.Ymin = feature.Geometry.EnvelopeInternal.MinY; header.Ymax = feature.Geometry.EnvelopeInternal.MaxY; if (double.IsNaN(feature.Geometry.Coordinates[0].M)) { header.ShapeType = ShapeType; } else { if (double.IsNaN(feature.Geometry.Coordinates[0].Z)) { header.ShapeType = ShapeTypeM; } else { header.Zmin = feature.Geometry.EnvelopeInternal.Minimum.Z; header.Zmax = feature.Geometry.EnvelopeInternal.Maximum.Z; header.ShapeType = ShapeTypeZ; } header.Mmin = feature.Geometry.EnvelopeInternal.Minimum.M; header.Mmax = feature.Geometry.EnvelopeInternal.Maximum.M; } header.ShxLength = 4 + 50; header.SaveAs(Filename); filenameExists = true; } AppendBasicGeometry(header, feature.Geometry, numFeatures); feature.RecordNumber = numFeatures; if (null != Quadtree) { Quadtree.Insert(feature.Geometry.EnvelopeInternal, numFeatures - 1); } } }
/// <summary> /// Creates a new instance of a PolygonShapefile for in-ram handling only. /// </summary> public PolygonShapefile() : base(FeatureType.Polygon) { Attributes = new AttributeTable(); Header = new ShapefileHeader { FileLength = 100, ShapeType = ShapeType.Polygon }; }
/// <summary> /// Append the geometry to the shapefile /// </summary> /// <param name="header"></param> /// <param name="feature"></param> /// <param name="numFeatures"></param> protected abstract void AppendBasicGeometry(ShapefileHeader header, IGeometry feature, int numFeatures);
/// <inheritdoc/> protected override void AppendGeometry(ShapefileHeader header, Geometry feature, int numFeatures) { FileInfo fi = new FileInfo(Filename); int offset = Convert.ToInt32(fi.Length / 2); FileStream shpStream = new FileStream(Filename, FileMode.Append, FileAccess.Write, FileShare.None, 10000); FileStream shxStream = new FileStream(header.ShxFilename, FileMode.Append, FileAccess.Write, FileShare.None, 100); List <int> parts = new List <int>(); List <Coordinate> points = new List <Coordinate>(); int contentLength = 22; for (int iPart = 0; iPart < feature.NumGeometries; iPart++) { parts.Add(points.Count); Polygon pg = feature.GetGeometryN(iPart) as Polygon; if (pg == null) { continue; } var bl = pg.Shell; IEnumerable <Coordinate> coords = bl.Coordinates; if (Orientation.IsCCW(bl.Coordinates)) { // Exterior rings need to be clockwise coords = coords.Reverse(); } points.AddRange(coords); foreach (LineString hole in pg.Holes) { parts.Add(points.Count); IEnumerable <Coordinate> holeCoords = hole.Coordinates; if (!Orientation.IsCCW(hole.Coordinates)) { // Interior rings need to be counter-clockwise holeCoords = holeCoords.Reverse(); } points.AddRange(holeCoords); } } contentLength += 2 * parts.Count; if (header.ShapeType == ShapeType.Polygon) { contentLength += points.Count * 8; } else if (header.ShapeType == ShapeType.PolygonM) { contentLength += 8; // mmin mmax contentLength += points.Count * 12; // x, y, m } else if (header.ShapeType == ShapeType.PolygonZ) { contentLength += 16; // mmin, mmax, zmin, zmax contentLength += points.Count * 16; // x, y, m, z } //// Index File // --------------------------------------------------------- // Position Value Type Number Byte Order // --------------------------------------------------------- shxStream.WriteBe(offset); // Byte 0 Offset Integer 1 Big shxStream.WriteBe(contentLength); // Byte 4 Content Length Integer 1 Big shxStream.Flush(); shxStream.Close(); //// X Y Poly Lines // --------------------------------------------------------- // Position Value Type Number Byte Order // --------------------------------------------------------- shpStream.WriteBe(numFeatures); // Byte 0 Record Number Integer 1 Big shpStream.WriteBe(contentLength); // Byte 4 Content Length Integer 1 Big shpStream.WriteLe((int)header.ShapeType); // Byte 8 Shape Type 3 Integer 1 Little if (header.ShapeType == ShapeType.NullShape) { return; } shpStream.WriteLe(feature.EnvelopeInternal.MinX); // Byte 12 Xmin Double 1 Little shpStream.WriteLe(feature.EnvelopeInternal.MinY); // Byte 20 Ymin Double 1 Little shpStream.WriteLe(feature.EnvelopeInternal.MaxX); // Byte 28 Xmax Double 1 Little shpStream.WriteLe(feature.EnvelopeInternal.MaxY); // Byte 36 Ymax Double 1 Little shpStream.WriteLe(parts.Count); // Byte 44 NumParts Integer 1 Little shpStream.WriteLe(points.Count); // Byte 48 NumPoints Integer 1 Little foreach (int iPart in parts) { shpStream.WriteLe(iPart); // Byte 52 Parts Integer NumParts Little } double[] xyVals = new double[points.Count * 2]; for (var i = 0; i < points.Count; i++) { xyVals[i * 2] = points[i].X; xyVals[i * 2 + 1] = points[i].Y; } shpStream.WriteLe(xyVals, 0, 2 * points.Count); if (header.ShapeType == ShapeType.PolygonZ) { // Pandell, 2020-06-23: "NetTopologySuite" version 1.7.5 doesn't have "NetTopologySuite.Geometries.Envelope.Minimum" property // shpStream.WriteLe(feature.EnvelopeInternal.Minimum.Z); // shpStream.WriteLe(feature.EnvelopeInternal.Maximum.Z); shpStream.WriteLe(double.NaN); shpStream.WriteLe(double.NaN); double[] zVals = new double[points.Count]; for (int ipoint = 0; ipoint < points.Count; ipoint++) { zVals[ipoint] = points[ipoint].Z; } shpStream.WriteLe(zVals, 0, points.Count); } if (header.ShapeType == ShapeType.PolygonM || header.ShapeType == ShapeType.PolygonZ) { if (feature.EnvelopeInternal == null) { shpStream.WriteLe(0.0); shpStream.WriteLe(0.0); } else { // Pandell, 2020-06-23: "NetTopologySuite" version 1.7.5 doesn't have "NetTopologySuite.Geometries.Envelope.Minimum" property // shpStream.WriteLe(feature.EnvelopeInternal.Minimum.M); // shpStream.WriteLe(feature.EnvelopeInternal.Maximum.M); shpStream.WriteLe(double.NaN); shpStream.WriteLe(double.NaN); } double[] mVals = new double[points.Count]; for (int ipoint = 0; ipoint < points.Count; ipoint++) { mVals[ipoint] = points[ipoint].M; } shpStream.WriteLe(mVals, 0, points.Count); } shpStream.Flush(); shpStream.Close(); offset += contentLength; Shapefile.WriteFileLength(Filename, offset + 4); // Add 4 for the record header Shapefile.WriteFileLength(header.ShxFilename, 50 + numFeatures * 4); }
/// <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); }
/// <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); }
/// <inheritdoc/> protected override void AppendBasicGeometry(ShapefileHeader header, IBasicGeometry feature, int numFeatures) { FileInfo fi = new FileInfo(Filename); int offset = Convert.ToInt32(fi.Length / 2); FileStream shpStream = new FileStream(Filename, FileMode.Append, FileAccess.Write, FileShare.None, 10000); FileStream shxStream = new FileStream(header.ShxFilename, FileMode.Append, FileAccess.Write, FileShare.None, 100); List <int> parts = new List <int>(); List <Coordinate> points = new List <Coordinate>(); int contentLength = 22; for (int iPart = 0; iPart < feature.NumGeometries; iPart++) { parts.Add(points.Count); IBasicPolygon pg = feature.GetBasicGeometryN(iPart) as IBasicPolygon; if (pg == null) { continue; } var bl = pg.Shell; var coords = bl.Coordinates; if (CgAlgorithms.IsCounterClockwise(coords)) { // Exterior rings need to be clockwise coords.Reverse(); } points.AddRange(coords); foreach (IBasicLineString hole in pg.Holes) { parts.Add(points.Count); var holeCoords = hole.Coordinates; if (CgAlgorithms.IsCounterClockwise(holeCoords) == false) { // Interior rings need to be counter-clockwise holeCoords.Reverse(); } points.AddRange(holeCoords); } } contentLength += 2 * parts.Count; if (header.ShapeType == ShapeType.Polygon) { contentLength += points.Count * 8; } if (header.ShapeType == ShapeType.PolygonM) { contentLength += 8; // mmin mmax contentLength += points.Count * 12; // x, y, m } if (header.ShapeType == ShapeType.PolygonZ) { contentLength += 16; // mmin, mmax, zmin, zmax contentLength += points.Count * 16; // x, y, m, z } // Index File // --------------------------------------------------------- // Position Value Type Number Byte Order // --------------------------------------------------------- shxStream.WriteBe(offset); // Byte 0 Offset Integer 1 Big shxStream.WriteBe(contentLength); // Byte 4 Content Length Integer 1 Big shxStream.Flush(); shxStream.Close(); // X Y Poly Lines // --------------------------------------------------------- // Position Value Type Number Byte Order // --------------------------------------------------------- shpStream.WriteBe(numFeatures); // Byte 0 Record Number Integer 1 Big shpStream.WriteBe(contentLength); // Byte 4 Content Length Integer 1 Big shpStream.WriteLe((int)header.ShapeType); // Byte 8 Shape Type 3 Integer 1 Little if (header.ShapeType == ShapeType.NullShape) { return; } shpStream.WriteLe(feature.Envelope.Minimum.X); // Byte 12 Xmin Double 1 Little shpStream.WriteLe(feature.Envelope.Minimum.Y); // Byte 20 Ymin Double 1 Little shpStream.WriteLe(feature.Envelope.Maximum.X); // Byte 28 Xmax Double 1 Little shpStream.WriteLe(feature.Envelope.Maximum.Y); // Byte 36 Ymax Double 1 Little shpStream.WriteLe(parts.Count); // Byte 44 NumParts Integer 1 Little shpStream.WriteLe(points.Count); // Byte 48 NumPoints Integer 1 Little // Byte 52 Parts Integer NumParts Little foreach (int iPart in parts) { shpStream.WriteLe(iPart); } double[] xyVals = new double[points.Count * 2]; int i = 0; foreach (Coordinate coord in points) { xyVals[i * 2] = coord.X; xyVals[i * 2 + 1] = coord.Y; i++; } shpStream.WriteLe(xyVals, 0, 2 * points.Count); if (header.ShapeType == ShapeType.PolygonZ) { shpStream.WriteLe(feature.Envelope.Minimum.Z); shpStream.WriteLe(feature.Envelope.Maximum.Z); double[] zVals = new double[points.Count]; for (int ipoint = 0; ipoint < points.Count; ipoint++) { zVals[ipoint] = points[ipoint].Z; } shpStream.WriteLe(zVals, 0, points.Count); } if (header.ShapeType == ShapeType.PolygonM || header.ShapeType == ShapeType.PolygonZ) { if (feature.Envelope == null) { shpStream.WriteLe(0.0); shpStream.WriteLe(0.0); } else { shpStream.WriteLe(feature.Envelope.Minimum.M); shpStream.WriteLe(feature.Envelope.Maximum.M); } double[] mVals = new double[points.Count]; for (int ipoint = 0; ipoint < points.Count; i++) { mVals[ipoint] = points[ipoint].M; ipoint++; } shpStream.WriteLe(mVals, 0, points.Count); } shpStream.Flush(); shpStream.Close(); offset += contentLength; Shapefile.WriteFileLength(Filename, offset); Shapefile.WriteFileLength(header.ShxFilename, 50 + numFeatures * 4); }
private void FillLines(string fileName, IProgressHandler progressHandler) { // Check to ensure the fileName is not null if (fileName == null) { throw new NullReferenceException(DataStrings.ArgumentNull_S.Replace("%S", "fileName")); } if (File.Exists(fileName) == false) { throw new FileNotFoundException(DataStrings.FileNotFound_S.Replace("%S", fileName)); } // Get the basic header information. ShapefileHeader header = new ShapefileHeader(fileName); Extent = new Extent(new[] { header.Xmin, header.Ymin, header.Xmax, header.Ymax }); // Check to ensure that the fileName is the correct shape type if (header.ShapeType != ShapeType.PolyLine && header.ShapeType != ShapeType.PolyLineM && header.ShapeType != ShapeType.PolyLineZ) { throw new ArgumentException(DataStrings.FileNotLines_S.Replace("%S", fileName)); } // Reading the headers gives us an easier way to track the number of shapes and their overall length etc. List <ShapeHeader> shapeHeaders = ReadIndexFile(fileName); // This will set up a reader so that we can read values in huge chunks, which is much faster // than one value at a time. BufferedBinaryReader bbReader = new BufferedBinaryReader(fileName, progressHandler); if (bbReader.FileLength == 100) { // We have reached the end of the file so we can close the file bbReader.Close(); return; } // Skip the shapefile header by skipping the first 100 bytes in the shapefile bbReader.Seek(100, SeekOrigin.Begin); int numShapes = shapeHeaders.Count; int[] partOffsets = new int[numShapes]; byte[] allBounds = new byte[numShapes * 32]; // probably all will be in one block, but use a byteBlock just in case. ByteBlock allParts = new ByteBlock(BLOCKSIZE); ByteBlock allCoords = new ByteBlock(BLOCKSIZE); bool isM = (header.ShapeType == ShapeType.PolyLineM || header.ShapeType == ShapeType.PolyLineZ); bool isZ = (header.ShapeType == ShapeType.PolyLineZ); ByteBlock allZ = null; ByteBlock allM = null; if (isZ) { allZ = new ByteBlock(BLOCKSIZE); } if (isM) { allM = new ByteBlock(BLOCKSIZE); } int pointOffset = 0; for (int shp = 0; shp < numShapes; shp++) { // Read from the index file because some deleted records // might still exist in the .shp file. long offset = (shapeHeaders[shp].ByteOffset); bbReader.Seek(offset, SeekOrigin.Begin); ShapeRange shape = new ShapeRange(FeatureType.Line) { RecordNumber = bbReader.ReadInt32(false), ContentLength = bbReader.ReadInt32(false), ShapeType = (ShapeType)bbReader.ReadInt32(), StartIndex = pointOffset }; if (shape.ShapeType == ShapeType.NullShape) { goto fin; } bbReader.Read(allBounds, shp * 32, 32); shape.NumParts = bbReader.ReadInt32(); // Byte 44 NumParts Integer 1 Little shape.NumPoints = bbReader.ReadInt32(); // Byte 48 NumPoints Integer 1 Little partOffsets[shp] = allParts.IntOffset(); allParts.Read(shape.NumParts * 4, bbReader); allCoords.Read(shape.NumPoints * 16, bbReader); pointOffset += shape.NumPoints; 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) { //mMin = bbReader.ReadDouble(); //mMax = bbReader.ReadDouble(); bbReader.Seek(16, SeekOrigin.Current); if (allM != null) { allM.Read(shape.NumPoints * 8, bbReader); } } } if (header.ShapeType == ShapeType.PolyLineZ) { bool hasM = shape.ContentLength * 2 > 60 + 4 * shape.NumParts + 24 * shape.NumPoints; IExtentZ zExt = (IExtentZ)shape.Extent; zExt.MinZ = bbReader.ReadDouble(); zExt.MaxZ = bbReader.ReadDouble(); if (allZ != null) { allZ.Read(shape.NumPoints * 8, bbReader); } // 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. IExtentM mExt = (IExtentM)shape.Extent; if (hasM) { mExt.MinM = bbReader.ReadDouble(); mExt.MaxM = bbReader.ReadDouble(); if (allM != null) { allM.Read(shape.NumPoints * 8, bbReader); } } } // Now that we have read all the values, create the geometries from the points and parts arrays. fin: ShapeIndices.Add(shape); } double[] vert = allCoords.ToDoubleArray(); Vertex = vert; if (isM) { M = allM.ToDoubleArray(); } if (isZ) { Z = allZ.ToDoubleArray(); } List <ShapeRange> shapes = ShapeIndices; double[] bounds = new double[numShapes * 4]; Buffer.BlockCopy(allBounds, 0, bounds, 0, allBounds.Length); int[] parts = allParts.ToIntArray(); for (int shp = 0; shp < numShapes; shp++) { ShapeRange shape = shapes[shp]; shape.Extent = new Extent(bounds, shp * 4); for (int part = 0; part < shape.NumParts; part++) { int offset = partOffsets[shp]; int endIndex = shape.NumPoints + shape.StartIndex; int startIndex = parts[offset + part] + shape.StartIndex; if (part < shape.NumParts - 1) { int prt = parts[offset + part + 1]; endIndex = prt + shape.StartIndex; } int count = endIndex - startIndex; PartRange partR = new PartRange(vert, shape.StartIndex, parts[offset + part], FeatureType.Line) { NumVertices = count }; shape.Parts.Add(partR); } } bbReader.Dispose(); }
/// <summary> /// Creates a new instance of a MultiPointShapefile for in-ram handling only. /// </summary> public MultiPointShapefile() : base(FeatureType.MultiPoint) { Attributes = new AttributeTable(); Header = new ShapefileHeader { FileLength = 100, ShapeType = ShapeType.MultiPoint }; }
/// <summary> /// Obtains a typed list of ShapefilePoint structures with double values associated with the various coordinates. /// </summary> /// <param name="fileName">A string fileName</param> private void FillPoints(string fileName) { // Check to ensure the fileName is not null if (fileName == null) { throw new NullReferenceException(DataStrings.ArgumentNull_S.Replace("%S", fileName)); } if (File.Exists(fileName) == false) { throw new FileNotFoundException(DataStrings.FileNotFound_S.Replace("%S", fileName)); } // Reading the headers gives us an easier way to track the number of shapes and their overall length etc. List<ShapeHeader> shapeHeaders = ReadIndexFile(fileName); // Get the basic header information. ShapefileHeader header = new ShapefileHeader(fileName); Extent = header.ToExtent(); // Check to ensure that the fileName is the correct shape type if (header.ShapeType != ShapeType.Point && header.ShapeType != ShapeType.PointM && header.ShapeType != ShapeType.PointZ) { throw new ApplicationException(DataStrings.FileNotPoints_S.Replace("%S", fileName)); } if (new FileInfo(fileName).Length == 100) { // the file is empty so we are done reading return; } var numShapes = shapeHeaders.Count; double[] m = null; double[] z = null; var vert = new double[2 * numShapes]; // X,Y if (header.ShapeType == ShapeType.PointM || header.ShapeType == ShapeType.PointZ) { m = new double[numShapes]; } if (header.ShapeType == ShapeType.PointZ) { z = new double[numShapes]; } using (var reader = new FileStream(fileName, FileMode.Open, FileAccess.Read)) { for (var shp = 0; shp < numShapes; shp++) { reader.Seek(shapeHeaders[shp].ByteOffset, SeekOrigin.Begin); var recordNumber = reader.ReadInt32(Endian.BigEndian); Debug.Assert(recordNumber == shp + 1); var contentLen = reader.ReadInt32(Endian.BigEndian); Debug.Assert(contentLen == shapeHeaders[shp].ContentLength); var shapeType = (ShapeType) reader.ReadInt32(Endian.LittleEndian); if (shapeType == ShapeType.NullShape) { if (m != null) { m[shp] = double.MinValue; } goto fin; } // Read X var ind = 4; vert[shp*2] = reader.ReadDouble(1, Endian.LittleEndian)[0]; ind += 8; // Read Y vert[shp*2 + 1] = reader.ReadDouble(1, Endian.LittleEndian)[0]; ind += 8; // Read Z if (z != null) { z[shp] = reader.ReadDouble(1, Endian.LittleEndian)[0]; ind += 8; } // Read M if (m != null) { if (shapeHeaders[shp].ByteLength <= ind) { m[shp] = double.MinValue; } else { m[shp] = reader.ReadDouble(1, Endian.LittleEndian)[0]; ind += 8; } } fin: var shape = new ShapeRange(FeatureType.Point) { StartIndex = shp, ContentLength = shapeHeaders[shp].ContentLength, NumPoints = 1, NumParts = 1 }; ShapeIndices.Add(shape); var part = new PartRange(vert, shp, 0, FeatureType.Point) {NumVertices = 1}; shape.Parts.Add(part); shape.Extent = new Extent(new[] {vert[shp*2], vert[shp*2 + 1], vert[shp*2], vert[shp*2 + 1]}); } } Vertex = vert; M = m; Z = z; }
/// <summary> /// Obtains a typed list of ShapefilePoint structures with double values associated with the various coordinates. /// </summary> /// <param name="fileName">A string fileName</param> /// <param name="progressHandler">Progress handler</param> private void FillPoints(string fileName, IProgressHandler progressHandler) { // Check to ensure the fileName is not null if (fileName == null) { throw new NullReferenceException(DataStrings.ArgumentNull_S.Replace("%S", "fileName")); } if (File.Exists(fileName) == false) { throw new FileNotFoundException(DataStrings.FileNotFound_S.Replace("%S", fileName)); } // Reading the headers gives us an easier way to track the number of shapes and their overall length etc. List <ShapeHeader> shapeHeaders = ReadIndexFile(fileName); // Get the basic header information. var header = new ShapefileHeader(fileName); Extent = header.ToExtent(); // Check to ensure that the fileName is the correct shape type if (header.ShapeType != ShapeType.Point && header.ShapeType != ShapeType.PointM && header.ShapeType != ShapeType.PointZ) { throw new ApplicationException(DataStrings.FileNotPoints_S.Replace("%S", fileName)); } if (new FileInfo(fileName).Length == 100) { // the file is empty so we are done reading return; } var numShapes = shapeHeaders.Count; double[] m = null; double[] z = null; var vert = new double[2 * numShapes]; // X,Y if (header.ShapeType == ShapeType.PointM || header.ShapeType == ShapeType.PointZ) { m = new double[numShapes]; } if (header.ShapeType == ShapeType.PointZ) { z = new double[numShapes]; } var progressMeter = new ProgressMeter(progressHandler, "Reading from " + Path.GetFileName(fileName)) { StepPercent = 5 }; using (var reader = new FileStream(fileName, FileMode.Open, FileAccess.Read)) { for (var shp = 0; shp < numShapes; shp++) { progressMeter.CurrentPercent = (int)(shp * 100.0 / numShapes); reader.Seek(shapeHeaders[shp].ByteOffset, SeekOrigin.Begin); var recordNumber = reader.ReadInt32(Endian.BigEndian); Debug.Assert(recordNumber == shp + 1); var contentLen = reader.ReadInt32(Endian.BigEndian); Debug.Assert(contentLen == shapeHeaders[shp].ContentLength); var shapeType = (ShapeType)reader.ReadInt32(); if (shapeType == ShapeType.NullShape) { if (m != null) { m[shp] = double.MinValue; } goto fin; } // Read X var ind = 4; vert[shp * 2] = reader.ReadDouble(); ind += 8; // Read Y vert[shp * 2 + 1] = reader.ReadDouble(); ind += 8; // Read Z if (z != null) { z[shp] = reader.ReadDouble(); ind += 8; } // Read M if (m != null) { if (shapeHeaders[shp].ByteLength <= ind) { m[shp] = double.MinValue; } else { m[shp] = reader.ReadDouble(); ind += 8; } } fin: var shape = new ShapeRange(FeatureType.Point) { RecordNumber = recordNumber, StartIndex = shp, ContentLength = shapeHeaders[shp].ContentLength, NumPoints = 1, NumParts = 1 }; ShapeIndices.Add(shape); var part = new PartRange(vert, shp, 0, FeatureType.Point) { NumVertices = 1 }; shape.Parts.Add(part); shape.Extent = new Extent(new[] { vert[shp * 2], vert[shp * 2 + 1], vert[shp * 2], vert[shp * 2 + 1] }); } } Vertex = vert; M = m; Z = z; progressMeter.Reset(); }
/// <inheritdocs/> protected override void AppendBasicGeometry(ShapefileHeader header, IBasicGeometry feature, int numFeatures) { FileInfo fi = new FileInfo(Filename); int offset = Convert.ToInt32(fi.Length / 2); FileStream shpStream = new FileStream(Filename, FileMode.Append, FileAccess.Write, FileShare.None, 10000); FileStream shxStream = new FileStream(header.ShxFilename, FileMode.Append, FileAccess.Write, FileShare.None, 100); List<int> parts = new List<int>(); List<Coordinate> points = new List<Coordinate>(); int contentLength = 22; for (int iPart = 0; iPart < feature.NumGeometries; iPart++) { parts.Add(points.Count); IBasicLineString pg = feature.GetBasicGeometryN(iPart) as IBasicLineString; if (pg == null) continue; points.AddRange(pg.Coordinates); } contentLength += 2 * parts.Count; if (header.ShapeType == ShapeType.PolyLine) { contentLength += points.Count * 8; } if (header.ShapeType == ShapeType.PolyLineM) { contentLength += 8; // mmin mmax contentLength += points.Count * 12; // x, y, m } if (header.ShapeType == ShapeType.PolyLineZ) { contentLength += 16; // mmin, mmax, zmin, zmax contentLength += points.Count * 16; // x, y, m, z } // Index File // --------------------------------------------------------- // Position Value Type Number Byte Order // --------------------------------------------------------- shxStream.WriteBe(offset); // Byte 0 Offset Integer 1 Big shxStream.WriteBe(contentLength); // Byte 4 Content Length Integer 1 Big shxStream.Flush(); shxStream.Close(); // X Y Poly Lines // --------------------------------------------------------- // Position Value Type Number Byte Order // --------------------------------------------------------- shpStream.WriteBe(numFeatures); // Byte 0 Record Number Integer 1 Big shpStream.WriteBe(contentLength); // Byte 4 Content Length Integer 1 Big shpStream.WriteLe((int)header.ShapeType); // Byte 8 Shape Type 3 Integer 1 Little if (header.ShapeType == ShapeType.NullShape) { return; } shpStream.WriteLe(feature.Envelope.Minimum.X); // Byte 12 Xmin Double 1 Little shpStream.WriteLe(feature.Envelope.Minimum.Y); // Byte 20 Ymin Double 1 Little shpStream.WriteLe(feature.Envelope.Maximum.X); // Byte 28 Xmax Double 1 Little shpStream.WriteLe(feature.Envelope.Maximum.Y); // Byte 36 Ymax Double 1 Little shpStream.WriteLe(parts.Count); // Byte 44 NumParts Integer 1 Little shpStream.WriteLe(points.Count); // Byte 48 NumPoints Integer 1 Little // Byte 52 Parts Integer NumParts Little foreach (int iPart in parts) { shpStream.WriteLe(iPart); } double[] xyVals = new double[points.Count * 2]; int i = 0; foreach (Coordinate coord in points) { xyVals[i * 2] = coord.X; xyVals[i * 2 + 1] = coord.Y; i++; } shpStream.WriteLe(xyVals, 0, 2 * points.Count); if (header.ShapeType == ShapeType.PolyLineZ) { shpStream.WriteLe(feature.Envelope.Minimum.Z); shpStream.WriteLe(feature.Envelope.Maximum.Z); double[] zVals = new double[points.Count]; for (int ipoint = 0; ipoint < points.Count; ipoint++) { zVals[ipoint] = points[ipoint].Z; } shpStream.WriteLe(zVals, 0, points.Count); } if (header.ShapeType == ShapeType.PolyLineM || header.ShapeType == ShapeType.PolyLineZ) { if (feature.Envelope == null) { shpStream.WriteLe(0.0); shpStream.WriteLe(0.0); } else { shpStream.WriteLe(feature.Envelope.Minimum.M); shpStream.WriteLe(feature.Envelope.Maximum.M); } double[] mVals = new double[points.Count]; for (int ipoint = 0; ipoint < points.Count; i++) { mVals[ipoint] = points[ipoint].M; ipoint++; } shpStream.WriteLe(mVals, 0, points.Count); } shpStream.Flush(); shpStream.Close(); offset += contentLength; Shapefile.WriteFileLength(Filename, offset + 4); // Add 4 for the record header Shapefile.WriteFileLength(header.ShxFilename, 50 + numFeatures * 4); }
/// <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); }
// X Y MultiPoints: Total Length = 28 Bytes // --------------------------------------------------------- // Position Value Type Number Byte Order // --------------------------------------------------------- // Byte 0 Record Number Integer 1 Big // Byte 4 Content Length Integer 1 Big // Byte 8 Shape Type 8 Integer 1 Little // Byte 12 Xmin Double 1 Little // Byte 20 Ymin Double 1 Little // Byte 28 Xmax Double 1 Little // Byte 36 Ymax Double 1 Little // Byte 48 NumPoints Integer 1 Little // Byte X Points Point NumPoints Little // X Y M MultiPoints: Total Length = 34 Bytes // --------------------------------------------------------- // Position Value Type Number Byte Order // --------------------------------------------------------- // Byte 0 Record Number Integer 1 Big // Byte 4 Content Length Integer 1 Big // Byte 8 Shape Type 28 Integer 1 Little // Byte 12 Box Double 4 Little // Byte 44 NumPoints Integer 1 Little // Byte X Points Point NumPoints Little // Byte Y* Mmin Double 1 Little // Byte Y + 8* Mmax Double 1 Little // Byte Y + 16* Marray Double NumPoints Little // X Y Z M MultiPoints: Total Length = 44 Bytes // --------------------------------------------------------- // Position Value Type Number Byte Order // --------------------------------------------------------- // Byte 0 Record Number Integer 1 Big // Byte 4 Content Length Integer 1 Big // Byte 8 Shape Type 18 Integer 1 Little // Byte 12 Box Double 4 Little // Byte 44 NumPoints Integer 1 Little // Byte X Points Point NumPoints Little // Byte Y Zmin Double 1 Little // Byte Y + 8 Zmax Double 1 Little // Byte Y + 16 Zarray Double NumPoints Little // Byte Z* Mmin Double 1 Little // Byte Z+8* Mmax Double 1 Little // Byte Z+16* Marray Double NumPoints Little private void FillPoints(string fileName, IProgressHandler progressHandler) { // Check to ensure the fileName is not null if (fileName == null) { throw new NullReferenceException(DataStrings.ArgumentNull_S.Replace("%S", "fileName")); } if (File.Exists(fileName) == false) { throw new FileNotFoundException(DataStrings.FileNotFound_S.Replace("%S", fileName)); } // Get the basic header information. ShapefileHeader header = new ShapefileHeader(fileName); Extent = header.ToExtent(); // Check to ensure that the fileName is the correct shape type if (header.ShapeType != ShapeType.MultiPoint && header.ShapeType != ShapeType.MultiPointM && header.ShapeType != ShapeType.MultiPointZ) { throw new ArgumentException(DataStrings.FileNotLines_S.Replace("%S", fileName)); } // Reading the headers gives us an easier way to track the number of shapes and their overall length etc. List <ShapeHeader> shapeHeaders = ReadIndexFile(fileName); // This will set up a reader so that we can read values in huge chunks, which is much faster than one value at a time. BufferedBinaryReader bbReader = new BufferedBinaryReader(fileName, progressHandler); if (bbReader.FileLength == 100) { // The shapefile is empty so we can simply return here bbReader.Close(); return; } // Skip the shapefile header by skipping the first 100 bytes in the shapefile bbReader.Seek(100, SeekOrigin.Begin); int numShapes = shapeHeaders.Count; byte[] bigEndians = new byte[numShapes * 8]; byte[] allBounds = new byte[numShapes * 32]; ByteBlock allCoords = new ByteBlock(BLOCKSIZE); bool isM = (header.ShapeType == ShapeType.MultiPointZ || header.ShapeType == ShapeType.MultiPointM); bool isZ = (header.ShapeType == ShapeType.MultiPointZ); ByteBlock allZ = null; ByteBlock allM = null; if (isZ) { allZ = new ByteBlock(BLOCKSIZE); } if (isM) { allM = new ByteBlock(BLOCKSIZE); } int pointOffset = 0; for (int shp = 0; shp < numShapes; shp++) { // Read from the index file because some deleted records // might still exist in the .shp file. long offset = (shapeHeaders[shp].ByteOffset); bbReader.Seek(offset, SeekOrigin.Begin); // time: 200 ms ShapeRange shape = new ShapeRange(FeatureType.MultiPoint) { RecordNumber = bbReader.ReadInt32(false), ContentLength = bbReader.ReadInt32(false), ShapeType = (ShapeType)bbReader.ReadInt32(), StartIndex = pointOffset }; //bbReader.Read(bigEndians, shp * 8, 8); if (shape.ShapeType == ShapeType.NullShape) { continue; } bbReader.Read(allBounds, shp * 32, 32); shape.NumParts = 1; shape.NumPoints = bbReader.ReadInt32(); allCoords.Read(shape.NumPoints * 16, bbReader); pointOffset += shape.NumPoints; if (header.ShapeType == ShapeType.MultiPointM) { // 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) { IExtentM mExt = (IExtentM)MyExtent; mExt.MinM = bbReader.ReadDouble(); mExt.MaxM = bbReader.ReadDouble(); if (allM != null) { allM.Read(shape.NumPoints * 8, bbReader); } } } if (header.ShapeType == ShapeType.MultiPointZ) { bool hasM = shape.ContentLength * 2 > 60 + 4 * shape.NumParts + 24 * shape.NumPoints; IExtentZ zExt = (IExtentZ)MyExtent; zExt.MinZ = bbReader.ReadDouble(); zExt.MaxZ = bbReader.ReadDouble(); // For Z shapefiles, the Z part is not optional. if (allZ != null) { allZ.Read(shape.NumPoints * 8, bbReader); } // 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) { IExtentM mExt = (IExtentM)MyExtent; mExt.MinM = bbReader.ReadDouble(); mExt.MaxM = bbReader.ReadDouble(); if (allM != null) { allM.Read(shape.NumPoints * 8, bbReader); } } } // Now that we have read all the values, create the geometries from the points and parts arrays. ShapeIndices.Add(shape); } double[] vert = allCoords.ToDoubleArray(); Vertex = vert; if (isM) { M = allM.ToDoubleArray(); } if (isZ) { Z = allZ.ToDoubleArray(); } Array.Reverse(bigEndians); List <ShapeRange> shapes = ShapeIndices; double[] bounds = new double[numShapes * 4]; Buffer.BlockCopy(allBounds, 0, bounds, 0, allBounds.Length); for (int shp = 0; shp < numShapes; shp++) { ShapeRange shape = shapes[shp]; shape.Extent = new Extent(bounds, shp * 4); int endIndex = shape.NumPoints + shape.StartIndex; int startIndex = shape.StartIndex; int count = endIndex - startIndex; PartRange partR = new PartRange(vert, shape.StartIndex, 0, FeatureType.MultiPoint) { NumVertices = count }; shape.Parts.Add(partR); } bbReader.Dispose(); }
/// <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; }
/// <summary> /// Obtains a typed list of ShapefilePoint structures with double values associated with the various coordinates. /// </summary> /// <param name="fileName">A string fileName</param> /// <param name="progressHandler">A progress indicator</param> private void FillPoints(string fileName, IProgressHandler progressHandler) { // Check to ensure the fileName is not null if (fileName == null) { throw new NullReferenceException(DataStrings.ArgumentNull_S.Replace("%S", fileName)); } if (File.Exists(fileName) == false) { throw new FileNotFoundException(DataStrings.FileNotFound_S.Replace("%S", fileName)); } // Reading the headers gives us an easier way to track the number of shapes and their overall length etc. List<ShapeHeader> shapeHeaders = ReadIndexFile(fileName); // Get the basic header information. ShapefileHeader header = new ShapefileHeader(fileName); MyExtent = header.ToExtent(); // Check to ensure that the fileName is the correct shape type if (header.ShapeType != ShapeType.Point && header.ShapeType != ShapeType.PointM && header.ShapeType != ShapeType.PointZ) { throw new ApplicationException(DataStrings.FileNotPoints_S.Replace("%S", fileName)); } // This will set up a reader so that we can read values in huge chunks, which is much // faster than one value at a time. BufferedBinaryReader bbReader = new BufferedBinaryReader(fileName, progressHandler); if (bbReader.FileLength == 100) { bbReader.Close(); // the file is empty so we are done reading return; } // Skip the shapefile header by skipping the first 100 bytes in the shapefile bbReader.Seek(100, SeekOrigin.Begin); int numShapes = shapeHeaders.Count; byte[] bigEndian = new byte[numShapes * 8]; byte[] allCoords = new byte[numShapes * 16]; bool isM = false; bool isZ = false; if (header.ShapeType == ShapeType.PointM || header.ShapeType == ShapeType.PointZ) { isM = true; } if (header.ShapeType == ShapeType.PointZ) { isZ = true; } byte[] allM = new byte[8]; if (isM) allM = new byte[numShapes * 8]; byte[] allZ = new byte[8]; if (isZ) allZ = new byte[numShapes * 8]; for (int shp = 0; shp < numShapes; shp++) { // Read from the index file because some deleted records // might still exist in the .shp file. long offset = (shapeHeaders[shp].ByteOffset); bbReader.Seek(offset, SeekOrigin.Begin); bbReader.Read(bigEndian, shp * 8, 8); bbReader.ReadInt32(); // Skip ShapeType. Null shapes may break this. //bbReader.Seek(4, SeekOrigin.Current); bbReader.Read(allCoords, shp * 16, 16); if (isZ) { bbReader.Read(allZ, shp * 8, 8); } if (isM) { bbReader.Read(allM, shp * 8, 8); } ShapeRange shape = new ShapeRange(FeatureType.Point) { StartIndex = shp, ContentLength = 8, NumPoints = 1, NumParts = 1 }; ShapeIndices.Add(shape); } double[] vert = new double[2 * numShapes]; Buffer.BlockCopy(allCoords, 0, vert, 0, numShapes * 16); Vertex = vert; if (isM) { double[] m = new double[numShapes]; Buffer.BlockCopy(allM, 0, m, 0, numShapes * 8); M = m; } if (isZ) { double[] z = new double[numShapes]; Buffer.BlockCopy(allZ, 0, z, 0, numShapes * 8); Z = z; } for (int shp = 0; shp < numShapes; shp++) { PartRange part = new PartRange(vert, shp, 0, FeatureType.Point); part.NumVertices = 1; ShapeRange shape = ShapeIndices[shp]; shape.Parts.Add(part); shape.Extent = new Extent(new[] { vert[shp * 2], vert[shp * 2 + 1], vert[shp * 2], vert[shp * 2 + 1] }); } bbReader.Dispose(); }
/// <summary> /// This will return the correct shapefile type by reading the fileName. /// </summary> /// <param name="fileName">A string specifying the file with the extension .shp to open.</param> /// <param name="progressHandler">receives progress messages and overrides the ProgressHandler on the DataManager.DefaultDataManager</param> /// <returns>A correct shapefile object which is exclusively for reading the .shp data</returns> public static new Shapefile OpenFile(string fileName, IProgressHandler progressHandler) { string ext = Path.GetExtension(fileName).ToLower(); if (ext != ".shp" && ext != ".shx" && ext != ".dbf") { throw new ArgumentException(String.Format("The file extension {0} is not supported by Shapefile data provider.", ext)); } string name = Path.ChangeExtension(fileName, ".shp"); ShapefileHeader head = new ShapefileHeader(); head.Open(name); PointShapefile psf; LineShapefile lsf; PolygonShapefile pgsf; MultiPointShapefile mpsf; switch (head.ShapeType) { case ShapeType.MultiPatch: throw new NotImplementedException("This shape type is not yet supported."); case ShapeType.MultiPoint: case ShapeType.MultiPointM: case ShapeType.MultiPointZ: mpsf = new MultiPointShapefile(); mpsf.Open(name, progressHandler); return(mpsf); case ShapeType.NullShape: throw new NotImplementedException("This shape type is not yet supported."); case ShapeType.Point: case ShapeType.PointM: case ShapeType.PointZ: // Instantiate a new object to handle the point shapefile psf = new PointShapefile(); // Open the geometric components of the data (but not the dbf components) psf.Open(name, progressHandler); return(psf); case ShapeType.Polygon: case ShapeType.PolygonM: case ShapeType.PolygonZ: pgsf = new PolygonShapefile(); pgsf.Open(name, progressHandler); return(pgsf); case ShapeType.PolyLine: case ShapeType.PolyLineM: case ShapeType.PolyLineZ: lsf = new LineShapefile(); lsf.Open(name, progressHandler); return(lsf); } return(null); }
/// <summary> /// Opens the index file of the specified fileName. If the fileName is not the .shx extension, /// then the fileName will be changed too that extension first. /// </summary> /// <param name="fileName">The file name.</param> public void Open(string fileName) { Filename = Path.ChangeExtension(fileName, ".shx"); Header = new ShapefileHeader(Filename); Shapes = ReadIndexFile(Filename); }
/// <summary> /// This will return the correct shapefile type by reading the fileName. /// </summary> /// <param name="fileName">A string specifying the file with the extension .shp to open.</param> /// <param name="progressHandler">receives progress messages and overrides the ProgressHandler on the DataManager.DefaultDataManager</param> /// <returns>A correct shapefile object which is exclusively for reading the .shp data</returns> public static new Shapefile OpenFile(string fileName, IProgressHandler progressHandler) { var ext = Path.GetExtension(fileName); if (ext != null) ext = ext.ToLower(); if (ext != ".shp" && ext != ".shx" && ext != ".dbf") throw new ArgumentException(String.Format("The file extension {0} is not supported by Shapefile data provider.", ext)); string name = Path.ChangeExtension(fileName, ".shp"); var head = new ShapefileHeader(); head.Open(name); switch (head.ShapeType) { case ShapeType.MultiPatch: throw new NotImplementedException("This shape type is not yet supported."); case ShapeType.MultiPoint: case ShapeType.MultiPointM: case ShapeType.MultiPointZ: var mpsf = new MultiPointShapefile(); mpsf.Open(name, progressHandler); return mpsf; case ShapeType.NullShape: throw new NotImplementedException("This shape type is not yet supported."); case ShapeType.Point: case ShapeType.PointM: case ShapeType.PointZ: var psf = new PointShapefile(); psf.Open(name, progressHandler); return psf; case ShapeType.Polygon: case ShapeType.PolygonM: case ShapeType.PolygonZ: var pgsf = new PolygonShapefile(); pgsf.Open(name, progressHandler); return pgsf; case ShapeType.PolyLine: case ShapeType.PolyLineM: case ShapeType.PolyLineZ: var lsf = new LineShapefile(); lsf.Open(name, progressHandler); return lsf; } return null; }
/// <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; }
/// <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(); } }
protected override void InsertGeometry(ShapefileHeader header, int fid, IGeometry geometry) { var shapeHeaders = ReadIndexFile(header.ShxFilename); if (fid < shapeHeaders.Count) { var tmpShpPath = Path.GetTempFileName(); var tmpShxPath = Path.GetTempFileName(); FileStream tmpShpStream = new FileStream(tmpShpPath, FileMode.Create, FileAccess.ReadWrite); FileStream shpStream = new FileStream(Filename, FileMode.Open, FileAccess.ReadWrite, FileShare.Read, 10000); FileStream tmpShxStream = new FileStream(tmpShxPath, FileMode.Create, FileAccess.ReadWrite); FileStream shxStream = new FileStream(header.ShxFilename, FileMode.Open, FileAccess.ReadWrite, FileShare.Read, 100); long shpOffset = shapeHeaders[fid].ByteOffset; long shpRemainderOffset = shpOffset; long shpRemainderCount = shpStream.Length - shpRemainderOffset; if (shpRemainderCount > 0) { CopyTo(shpStream, tmpShpStream, shpRemainderOffset, shpRemainderCount); } long shxOffset = 100 + fid * 8; long shxRemainderOffset = shxOffset; long shxRemainderCount = shxStream.Length - shxRemainderOffset; if (shxRemainderCount > 0) { CopyTo(shxStream, tmpShxStream, shxRemainderOffset, shxRemainderCount); } shpStream.Seek(shpOffset, SeekOrigin.Begin); shxStream.Seek(shxOffset, SeekOrigin.Begin); int recordNumber = fid + 1; List <int> parts = new List <int>(); List <Coordinate> points = new List <Coordinate>(); int contentLength = 22; for (int iPart = 0; iPart < geometry.NumGeometries; iPart++) { parts.Add(points.Count); ILineString pg = geometry.GetGeometryN(iPart) as ILineString; if (pg == null) { continue; } points.AddRange(pg.Coordinates); } contentLength += 2 * parts.Count; switch (header.ShapeType) { case ShapeType.PolyLine: contentLength += points.Count * 8; break; case ShapeType.PolyLineM: contentLength += 8; // mmin mmax contentLength += points.Count * 12; // x, y, m break; case ShapeType.PolyLineZ: contentLength += 16; // mmin, mmax, zmin, zmax contentLength += points.Count * 16; // x, y, m, z break; } //// Index File // --------------------------------------------------------- // Position Value Type Number Byte Order // --------------------------------------------------------- shxStream.WriteBe(shapeHeaders[fid].Offset); // Byte 0 Offset Integer 1 Big shxStream.WriteBe(contentLength); // Byte 4 Content Length Integer 1 Big //// X Y Points // --------------------------------------------------------- // Position Value Type Number Byte Order // --------------------------------------------------------- shpStream.WriteBe(recordNumber); // Byte 0 Record Number Integer 1 Big shpStream.WriteBe(contentLength); // Byte 4 Content Length Integer 1 Big shpStream.WriteLe((int)header.ShapeType); // Byte 8 Shape Type 3 Integer 1 Little if (header.ShapeType != ShapeType.NullShape) { shpStream.WriteLe(geometry.EnvelopeInternal.MinX); // Byte 12 Xmin Double 1 Little shpStream.WriteLe(geometry.EnvelopeInternal.MinY); // Byte 20 Ymin Double 1 Little shpStream.WriteLe(geometry.EnvelopeInternal.MaxX); // Byte 28 Xmax Double 1 Little shpStream.WriteLe(geometry.EnvelopeInternal.MaxY); // Byte 36 Ymax Double 1 Little shpStream.WriteLe(parts.Count); // Byte 44 NumParts Integer 1 Little shpStream.WriteLe(points.Count); // Byte 48 NumPoints Integer 1 Little foreach (int iPart in parts) { shpStream.WriteLe(iPart); // Byte 52 Parts Integer NumParts Little } double[] xyVals = new double[points.Count * 2]; for (var i = 0; i < points.Count; i++) { xyVals[i * 2] = points[i].X; xyVals[i * 2 + 1] = points[i].Y; } shpStream.WriteLe(xyVals, 0, 2 * points.Count); if (header.ShapeType == ShapeType.PolyLineZ) { shpStream.WriteLe(geometry.EnvelopeInternal.Minimum.Z); shpStream.WriteLe(geometry.EnvelopeInternal.Maximum.Z); double[] zVals = new double[points.Count]; for (int ipoint = 0; ipoint < points.Count; ipoint++) { zVals[ipoint] = points[ipoint].Z; } shpStream.WriteLe(zVals, 0, points.Count); } if (header.ShapeType == ShapeType.PolyLineM || header.ShapeType == ShapeType.PolyLineZ) { if (geometry.Envelope == null) { shpStream.WriteLe(0.0); shpStream.WriteLe(0.0); } else { shpStream.WriteLe(geometry.EnvelopeInternal.Minimum.M); shpStream.WriteLe(geometry.EnvelopeInternal.Maximum.M); } double[] mVals = new double[points.Count]; for (int ipoint = 0; ipoint < points.Count; ipoint++) { mVals[ipoint] = points[ipoint].M; } shpStream.WriteLe(mVals, 0, points.Count); } } if (shxRemainderCount > 0) { CopyTo(tmpShxStream, shxStream, 0, shxRemainderCount); } int dOffset = (int)((shpStream.Position - shpOffset) / 2); if (dOffset != 0) { long shpPosition = shpStream.Position; for (int i = fid; i < shapeHeaders.Count; i++) { shxStream.Seek(100 + (i + 1) * 8, SeekOrigin.Begin); shxStream.WriteBe(shapeHeaders[i].Offset + dOffset); shpStream.Seek((shapeHeaders[i].Offset + dOffset) * 2, SeekOrigin.Begin); shpStream.WriteBe(i + 2); } shpStream.Seek(shpPosition, SeekOrigin.Begin); } int shxLength = Convert.ToInt32(shxStream.Length / 2); shxStream.Flush(); tmpShxStream.Dispose(); shxStream.Dispose(); File.Delete(tmpShxPath); if (shpRemainderCount > 0) { CopyTo(tmpShpStream, shpStream, 0, shpRemainderCount); } if (shpStream.Length != shpStream.Position + shpRemainderCount) { shpStream.SetLength(shpStream.Position + shpRemainderCount); } ; int shpLength = Convert.ToInt32(shpStream.Length / 2); shpStream.Flush(); shpStream.Dispose(); Shapefile.WriteFileLength(Filename, shpLength); Shapefile.WriteFileLength(header.ShxFilename, shxLength); tmpShpStream.Dispose(); File.Delete(tmpShpPath); } }