/// <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, StringDictionary 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> ///// 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, Dictionary 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> /// 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="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, Dictionary <string, string> metadata, byte[] shapeData) : base(ShapeType.MultiPoint, recordNumber, metadata) { // 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> /// Create a new Shapefile object and open a Shapefile. Note that three files are required - /// the main file (.shp), the index file (.shx) and the dBASE table (.dbf). The three files /// must all have the same filename (i.e. shapes.shp, shapes.shx and shapes.dbf). Set path /// to any one of these three files to open the Shapefile. /// </summary> /// <param name="path">Path to the .shp, .shx or .dbf file for this Shapefile</param> /// <exception cref="ObjectDisposedException">Thrown if the Shapefile has been disposed</exception> /// <exception cref="ArgumentNullException">Thrown if the path parameter is null</exception> /// <exception cref="ArgumentException">Thrown if the path parameter is empty</exception> /// <exception cref="FileNotFoundException">Thrown if one of the three required files is not found</exception> /// <exception cref="InvalidOperationException">Thrown if an error occurs parsing file headers</exception> public void Open(string path) { if (_disposed) { throw new ObjectDisposedException("Shapefile"); } if (path == null) { throw new ArgumentNullException("path"); } if (path.Length <= 0) { throw new ArgumentException("path parameter is empty", "path"); } //.shp file path _shapefileMainPath = Path.ChangeExtension(path, MainPathExtension); //.shx file path _shapefileIndexPath = Path.ChangeExtension(path, IndexPathExtension); //.dbf file path _shapefileDbasePath = Path.ChangeExtension(path, DbasePathExtension); //파일 존재 유무 확인 if (!File.Exists(_shapefileMainPath)) { throw new FileNotFoundException("Shapefile main file not found", _shapefileMainPath); } if (!File.Exists(_shapefileIndexPath)) { throw new FileNotFoundException("Shapefile index file not found", _shapefileIndexPath); } if (!File.Exists(_shapefileDbasePath)) { throw new FileNotFoundException("Shapefile dBase file not found", _shapefileDbasePath); } _mainStream = File.Open(_shapefileMainPath, FileMode.Open, FileAccess.Read, FileShare.Read); _indexStream = File.Open(_shapefileIndexPath, FileMode.Open, FileAccess.Read, FileShare.Read); if (_mainStream.Length < Header.HeaderLength) { throw new InvalidOperationException("Shapefile main file does not contain a valid header"); } if (_indexStream.Length < Header.HeaderLength) { throw new InvalidOperationException("Shapefile index file does not contain a valid header"); } // read in and parse the headers byte[] headerBytes = new byte[Header.HeaderLength]; _mainStream.Read(headerBytes, 0, Header.HeaderLength); _mainHeader = new Header(headerBytes); _indexStream.Read(headerBytes, 0, Header.HeaderLength); _indexHeader = new Header(headerBytes); // set properties from the main header _type = _mainHeader.ShapeType; _boundingBox = new RectangleD(_mainHeader.XMin, _mainHeader.YMin, _mainHeader.XMax, _mainHeader.YMax); // index header length is in 16-bit words, including the header - number of // shapes is the number of records (each 4 workds long) after subtracting the header bytes _count = (_indexHeader.FileLength - (Header.HeaderLength / 2)) / 4; // open the metadata database OpenDb(); _opened = true; }
/// <summary> /// The PolyLine and Polygon shapes share the same structure, this method parses the bounding box /// and list of parts for both /// </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> protected void ParsePolyLineOrPolygon(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 3 or 5 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 // // Note: X = 44 + 4 * NumParts // 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 * numPoints + 52 bytes total if (shapeData.Length != 52 + (4 * numParts) + (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; } 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); } }
/// <summary> /// The PolygonZ shape share the same structure, this method parses the bounding box /// and list of parts for both /// </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> protected void ParsePolygonZ(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 25 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 Zmin Zmin Double 1 Little // Byte Y+8 Zmax Zmax Double 1 Little // Byte Y+16 Zarray Zarray Double NumPoints Little // Byte Z* Mmin Mmin Double 1 Little // Byte Z+8* Mmax Mmax Double 1 Little // Byte Z+16* Marray Marray Double NumPoints Little // Byte M* Mmin Mmin Double 1 Little // Byte Z+8* Mmax Mmax Double 1 Little // Byte Z+16* Marray Marray Double NumPoints Little // // Note: X = 44 + (4 * NumParts), Y = X + (16 * NumPoints), Z = Y + 16 + (8 * NumPoints) // * 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 * numPoints + 52 bytes total if (shapeData.Length != 52 + (4 * numParts) + (16 * numPoints) && shapeData.Length != 52 + (4 * numParts) + (16 * numPoints) + 16 + (8 * numPoints) && shapeData.Length != 52 + (4 * numParts) + (16 * numPoints) + 16 + (8 * numPoints) + 16 + (8 * 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; } 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 = Math.Min(numBytes / 16, numPoints); 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); } }