BitConverter methods that allow a different source byte order (only a subset of BitConverter)
예제 #1
0
        /// <summary>
        /// A Shapefile MultiPoint Shape
        /// </summary>
        /// <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(byte[] shapeData)
            : base(ShapeType.MultiPoint)
        {
            // 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);
            Debug.Log($"Extents: {_boundingBox.west}, {_boundingBox.east}, {_boundingBox.north}, {_boundingBox.south}");
            int numPoints = EndianBitConverter.ToInt32(shapeData, 44, ProvidedOrder.Little);

            Debug.Log($"numPoints: {numPoints}");

            // 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));
            }
        }
예제 #2
0
        /// <summary>
        /// A Shapefile Point 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 ShapePoint(int recordNumber, StringDictionary metadata, IDataRecord dataRecord, byte[] shapeData)
            : base(ShapeType.Point, 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  1       Integer     1       Little
            // Byte 4       X           X       Double      1       Little
            // Byte 12      Y           Y       Double      1       Little

            // validation - shapedata should be 8 + 4 + 8 + 8 = 28 bytes long
            if (shapeData.Length != 28)
            {
                throw new InvalidOperationException("Invalid shape data");
            }

            _point = new PointD(EndianBitConverter.ToDouble(shapeData, 12, ProvidedOrder.Little),
                                EndianBitConverter.ToDouble(shapeData, 20, ProvidedOrder.Little));
        }
예제 #3
0
        /// <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, StringDictionary 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);
        }
예제 #4
0
        /// <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);
            Debug.Log($"Extents: {boundingBox.west}, {boundingBox.east}, {boundingBox.north}, {boundingBox.south}");
            int numParts = EndianBitConverter.ToInt32(shapeData, 44, ProvidedOrder.Little);

            Debug.Log($"numParts: {numParts}");
            int numPoints = EndianBitConverter.ToInt32(shapeData, 48, ProvidedOrder.Little);

            Debug.Log($"numPoints: {numPoints}");

            // 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);
            }
        }
예제 #5
0
        /// <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);
            }
        }