/// <summary> /// The header data for a Shapefile main file or Index file /// </summary> /// <param name="headerBytes">The first 100 bytes of the Shapefile main file or Index file</param> /// <exception cref="ArgumentNullException">Thrown if headerBytes is null</exception> /// <exception cref="InvalidOperationException">Thrown if an error occurs parsing the header</exception> public Header(byte[] headerBytes) { if (headerBytes == null) { throw new ArgumentNullException("headerBytes"); } if (headerBytes.Length != HeaderLength) { throw new InvalidOperationException(string.Format("headerBytes must be {0} bytes long", HeaderLength)); } //Position Field Value Type Order //Byte 0 File Code 9994 Integer Big //Byte 4 Unused 0 Integer Big //Byte 8 Unused 0 Integer Big //Byte 12 Unused 0 Integer Big //Byte 16 Unused 0 Integer Big //Byte 20 Unused 0 Integer Big //Byte 24 File Length File Length Integer Big //Byte 28 Version 1000 Integer Little //Byte 32 Shape Type Shape Type Integer Little //Byte 36 Bounding Box Xmin Double Little //Byte 44 Bounding Box Ymin Double Little //Byte 52 Bounding Box Xmax Double Little //Byte 60 Bounding Box Ymax Double Little //Byte 68* Bounding Box Zmin Double Little //Byte 76* Bounding Box Zmax Double Little //Byte 84* Bounding Box Mmin Double Little //Byte 92* Bounding Box Mmax Double Little _fileCode = EndianBitConverter.ToInt32(headerBytes, 0, ProvidedOrder.Big); if (_fileCode != ExpectedFileCode) { throw new InvalidOperationException(string.Format("Header File code is {0}, expected {1}", _fileCode, ExpectedFileCode)); } _version = EndianBitConverter.ToInt32(headerBytes, 28, ProvidedOrder.Little); if (_version != ExpectedVersion) { throw new InvalidOperationException(string.Format("Header version is {0}, expected {1}", _version, ExpectedVersion)); } _fileLength = EndianBitConverter.ToInt32(headerBytes, 24, ProvidedOrder.Big); _shapeType = (ShapeType)EndianBitConverter.ToInt32(headerBytes, 32, ProvidedOrder.Little); _xMin = EndianBitConverter.ToDouble(headerBytes, 36, ProvidedOrder.Little); _yMin = EndianBitConverter.ToDouble(headerBytes, 44, ProvidedOrder.Little); _xMax = EndianBitConverter.ToDouble(headerBytes, 52, ProvidedOrder.Little); _yMax = EndianBitConverter.ToDouble(headerBytes, 60, ProvidedOrder.Little); _zMin = EndianBitConverter.ToDouble(headerBytes, 68, ProvidedOrder.Little); _zMax = EndianBitConverter.ToDouble(headerBytes, 76, ProvidedOrder.Little); _mMin = EndianBitConverter.ToDouble(headerBytes, 84, ProvidedOrder.Little); _mMax = EndianBitConverter.ToDouble(headerBytes, 92, ProvidedOrder.Little); }
/// <summary> /// A Shapefile MultiPoint Shape /// </summary> /// <param name="recordNumber">The record number in the Shapefile</param> /// <param name="metadata">Metadata about the shape</param> /// <param name="dataRecord">IDataRecord associated with the metadata</param> /// <param name="shapeData">The shape record as a byte array</param> /// <exception cref="ArgumentNullException">Thrown if shapeData is null</exception> /// <exception cref="InvalidOperationException">Thrown if an error occurs parsing shapeData</exception> protected internal ShapeMultiPoint(int recordNumber, ArrayList metadata, IDataRecord dataRecord, byte[] shapeData) : base(ShapeType.MultiPoint, recordNumber, metadata, dataRecord) { // metadata is validated by the base class if (shapeData == null) { throw new ArgumentNullException("shapeData"); } // Note, shapeData includes an 8 byte header so positions below are +8 // Position Field Value Type Number Order // Byte 0 Shape Type 8 Integer 1 Little // Byte 4 Box Box Double 4 Little // Byte 36 NumPoints Num Points Integer 1 Little // Byte 40 Points Points Point NumPoints Little // validation step 1 - must have at least 8 + 4 + (4*8) + 4 bytes = 48 if (shapeData.Length < 48) { throw new InvalidOperationException("Invalid shape data"); } // extract bounding box and points _boundingBox = ParseBoundingBox(shapeData, 12, ProvidedOrder.Little); int numPoints = EndianBitConverter.ToInt32(shapeData, 44, ProvidedOrder.Little); // validation step 2 - we're expecting 16 * numPoints + 48 bytes total if (shapeData.Length != 48 + (16 * numPoints)) { throw new InvalidOperationException("Invalid shape data"); } // now extract the points _points = new PointD[numPoints]; for (int pointNum = 0; pointNum < numPoints; pointNum++) { _points[pointNum] = new PointD(EndianBitConverter.ToDouble(shapeData, 48 + (16 * pointNum), ProvidedOrder.Little), EndianBitConverter.ToDouble(shapeData, 56 + (16 * pointNum), ProvidedOrder.Little)); } }
/// <summary> /// Function is basically the same as Shape.ParsePolyLineOrPolygon, it is just /// extended to handle the M extreme values /// </summary> /// <param name="shapeData">The shape record as a byte array</param> /// <param name="boundingBox">Returns the bounding box</param> /// <param name="parts">Returns the list of parts</param> private void ParsePolyLineM(byte[] shapeData, out RectangleD boundingBox, out List <PointD[]> parts) { boundingBox = new RectangleD(); parts = null; // metadata is validated by the base class if (shapeData == null) { throw new ArgumentNullException("shapeData"); } // Note, shapeData includes an 8 byte header so positions below are +8 // Position Field Value Type Number Order // Byte 0 Shape Type 23 Integer 1 Little // Byte 4 Box Box Double 4 Little // Byte 36 NumParts NumParts Integer 1 Little // Byte 40 NumPoints NumPoints Integer 1 Little // Byte 44 Parts Parts Integer NumParts Little // Byte X Points Points Point NumPoints Little // Byte Y* Mmin Mmin Double 1 Little // Byte Y + 8* Mmax Mmax Double 1 Little // Byte Y + 16* Marray Marray Double NumPoints Little // // *optional // validation step 1 - must have at least 8 + 4 + (4*8) + 4 + 4 bytes = 52 if (shapeData.Length < 44) { throw new InvalidOperationException("Invalid shape data"); } // extract bounding box, number of parts and number of points boundingBox = ParseBoundingBox(shapeData, 12, ProvidedOrder.Little); int numParts = EndianBitConverter.ToInt32(shapeData, 44, ProvidedOrder.Little); int numPoints = EndianBitConverter.ToInt32(shapeData, 48, ProvidedOrder.Little); // validation step 2 - we're expecting 4 * numParts + (16 + 8 * numPoints for m extremes and values) + 16 * numPoints + 52 bytes total if (shapeData.Length != 52 + (4 * numParts) + 16 + 8 * numPoints + (16 * numPoints)) { throw new InvalidOperationException("Invalid shape data"); } // now extract the parts int partsOffset = 52 + (4 * numParts); parts = new List <PointD[]>(numParts); for (int part = 0; part < numParts; part++) { // this is the index of the start of the part in the points array int startPart = (EndianBitConverter.ToInt32(shapeData, 52 + (4 * part), ProvidedOrder.Little) * 16) + partsOffset; int numBytes; if (part == numParts - 1) { // it's the last part so we go to the end of the point array numBytes = shapeData.Length - startPart; // remove bytes for M extreme block numBytes -= numPoints * 8 + 16; } else { // we need to get the next part int nextPart = (EndianBitConverter.ToInt32(shapeData, 52 + (4 * (part + 1)), ProvidedOrder.Little) * 16) + partsOffset; numBytes = nextPart - startPart; } // the number of 16-byte points to read for this segment int numPointsInPart = (numBytes) / 16; PointD[] points = new PointD[numPointsInPart]; for (int point = 0; point < numPointsInPart; point++) { points[point] = new PointD(EndianBitConverter.ToDouble(shapeData, startPart + (16 * point), ProvidedOrder.Little), EndianBitConverter.ToDouble(shapeData, startPart + 8 + (16 * point), ProvidedOrder.Little)); } parts.Add(points); } // parse M information Mmin = EndianBitConverter.ToDouble(shapeData, 52 + (4 * numParts) + (16 * numPoints), ProvidedOrder.Little); Mmax = EndianBitConverter.ToDouble(shapeData, 52 + 8 + (4 * numParts) + (16 * numPoints), ProvidedOrder.Little); M.Clear(); for (int i = 0; i < numPoints; i++) { double _m = EndianBitConverter.ToDouble(shapeData, 52 + 16 + (4 * numParts) + (16 * numPoints) + i * 8, ProvidedOrder.Little); M.Add(_m); } }
/// <summary> /// Creates a Shape object (or derived object) from a shape record /// </summary> /// <param name="shapeData">The shape record as a byte array</param> /// <param name="metadata">Metadata associated with this shape (optional)</param> /// <param name="dataRecord">IDataRecord associated with the metadata</param> /// <returns>A Shape, or derived class</returns> /// <exception cref="ArgumentNullException">Thrown if shapeData or metadata are null</exception> /// <exception cref="ArgumentException">Thrown if shapeData is less than 12 bytes long</exception> /// <exception cref="InvalidOperationException">Thrown if an error occurs parsing shapeData</exception> public static Shape ParseShape(byte[] shapeData, ArrayList metadata, IDataRecord dataRecord) { if (shapeData == null) { throw new ArgumentNullException("shapeData"); } if (shapeData.Length < 12) { throw new ArgumentException("shapeData must be at least 12 bytes long"); } // shape data contains a header (shape number and content length) // the first field in each shape is the shape type //Position Field Value Type Order //Byte 0 Record Number Record Number Integer Big //Byte 4 Content Length Content Length Integer Big //Position Field Value Type Number Order //Byte 0 Shape Type Shape Type Integer 1 Little int recordNumber = EndianBitConverter.ToInt32(shapeData, 0, ProvidedOrder.Big); int contentLengthInWords = EndianBitConverter.ToInt32(shapeData, 4, ProvidedOrder.Big); ShapeType shapeType = (ShapeType)EndianBitConverter.ToInt32(shapeData, 8, ProvidedOrder.Little); // test that we have the expected amount of data - need to take the 8 byte header into account if (shapeData.Length != (contentLengthInWords * 2) + 8) { throw new InvalidOperationException("Shape data length does not match shape header length"); } Shape shape = null; switch (shapeType) { case ShapeType.Null: shape = new Shape(shapeType, recordNumber, metadata, dataRecord); break; case ShapeType.Point: shape = new ShapePoint(recordNumber, metadata, dataRecord, shapeData); break; case ShapeType.MultiPoint: shape = new ShapeMultiPoint(recordNumber, metadata, dataRecord, shapeData); break; case ShapeType.PolyLine: shape = new ShapePolyLine(recordNumber, metadata, dataRecord, shapeData); break; case ShapeType.PolyLineM: shape = new ShapePolyLineM(recordNumber, metadata, dataRecord, shapeData); break; case ShapeType.Polygon: shape = new ShapePolygon(recordNumber, metadata, dataRecord, shapeData); break; default: throw new NotImplementedException(string.Format("Shapetype {0} is not implemented", shapeType)); } return(shape); }