context: // ********************************************************************************************************
  // Product Name: DotSpatial.Data.dll
  // Description:  The data access libraries for the DotSpatial project.
  // ********************************************************************************************************
  // The contents of this file are subject to the MIT License (MIT)
  // you may not use this file except in compliance with the License. You may obtain a copy of the License at
  // http://dotspatial.codeplex.com/license
  //
  // Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF
  // ANY KIND, either expressed or implied. See the License for the specific language governing rights and
  // limitations under the License.
  //
  // The Original Code is from MapWindow.dll version 6.0
  //
  // The Initial Developer of this Original Code is Ted Dunsford. Created 3/2/2010 10:07:31 AM
  //
  // Contributor(s): (Open source contributors should list themselves and their modifications here).
  //
  // ********************************************************************************************************
  
  using System;
  using System.Collections.Generic;
  using System.IO;
  using DotSpatial.Topology;
  using DotSpatial.Topology.Utilities;
  
  namespace DotSpatial.Data
  {
      /// <summary>
      /// WKBPointReader
      /// </summary>
      public static class WkbFeatureReader
      {
          private static ByteOrder _endian;
  
          /// <summary>
          /// Given the array of bytes, this reverses the bytes based on size.  So if size if 4, the
          /// reversal will flip uints of 4 bytes at a time.
          /// </summary>
          /// <param name="data"></param>
          /// <param name="size"></param>
          public static void CheckEndian(byte[] data, int size)
          {
              if ((_endian == ByteOrder.LittleEndian) == BitConverter.IsLittleEndian) return;
              int count = data.Length / size;
              for (int i = 0; i < count; i++)
              {
                  byte[] temp = new byte[size];
                  Array.Copy(data, i * size, temp, 0, size);
                  Array.Reverse(temp);
                  Array.Copy(temp, 0, data, i * size, size);
              }
          }
  
          /// <summary>
          ///
          /// </summary>
          /// <param name="data">The raw byte data.</param>
          /// <returns></returns>
          public static int ReadInt32(Stream data)
          {
              byte[] vals = new byte[4];
              data.Read(vals, 0, 4);
              CheckEndian(vals, 4);
              return BitConverter.ToInt32(vals, 0);
          }
  
          /// <summary>
          ///
          /// </summary>
          /// <param name="data">The raw byte data.</param>
          /// <param name="count">The count of integers, not bytes.</param>
          /// <returns></returns>
          public static int[] ReadInt32(Stream data, int count)
          {
              byte[] vals = new byte[4 * count];
              data.Read(vals, 0, 4 * count);
              CheckEndian(vals, 4);
              int[] result = new int[count];
              Buffer.BlockCopy(vals, 0, result, 0, count * 4);
              return result;
          }
  
          /// <summary>
          /// Reads the specified number of doubles
          /// </summary>
          /// <param name="data"></param>
          /// <param name="count"></param>
          /// <returns></returns>
          public static double[] ReadDouble(Stream data, int count)
          {
              byte[] vals = new byte[8 * count];
              data.Read(vals, 0, count * 8);
              CheckEndian(vals, 8);
              double[] result = new double[count];
              Buffer.BlockCopy(vals, 0, result, 0, count * 8);
              return result;
          }
  
          /// <summary>
          /// Since WKB can in fact store different kinds of shapes, this will split out
          /// each type of shape into a different featureset.  If all the shapes are
          /// the same kind of feature, thre will only be one list of feature types.
          /// </summary>
          /// <param name="data"></param>
          /// <returns></returns>
          public static FeatureSetPack GetFeatureSets(byte[] data)
          {
              MemoryStream ms = new MemoryStream(data);
              return GetFeatureSets(ms);
          }
  
          /// <summary>
          /// Gets a FeatureSetPack from the wkb
          /// </summary>
          /// <param name="data"></param>
          /// <returns></returns>
          public static FeatureSetPack GetFeatureSets(Stream data)
          {
              FeatureSetPack result = new FeatureSetPack();
  
              while (data.Position < data.Length)
              {
                  ReadFeature(data, result);
              }
              result.StopEditing();
              return result;
          }
  
          /// <summary>
          /// Reads only a single geometry into a feature.  This may be a multi-part geometry.
          /// </summary>
          /// <param name="data"></param>
          /// <param name="results"></param>
          public static void ReadFeature(Stream data, FeatureSetPack results)
          {
              _endian = (ByteOrder)data.ReadByte();
              WkbGeometryType type = (WkbGeometryType)ReadInt32(data);
  
              switch (type)
              {
                  case WkbGeometryType.Point:
                      ReadPoint(data, results);
                      return;
                  case WkbGeometryType.LineString:
                      ReadLineString(data, results);
                      return;
                  case WkbGeometryType.Polygon:
                      ReadPolygon(data, results);
                      break;
                  case WkbGeometryType.MultiPoint:
                      ReadMultiPoint(data, results);
                      break;
                  case WkbGeometryType.MultiLineString:
                      ReadMultiLineString(data, results);
                      break;
                  case WkbGeometryType.MultiPolygon:
                      ReadMultiPolygon(data, results);
                      break;
                  case WkbGeometryType.GeometryCollection:
                      ReadGeometryCollection(data, results);
                      break;
              }
          }
  
          /// <summary>
          /// Reads only a single geometry into a feature.  This may be a multi-part geometry,
          /// but cannot be a mixed part geometry.  Anything that registers as "geometryCollection"
          /// will trigger an exception.
          /// </summary>
          /// <param name="data"></param>
          /// <returns></returns>
          public static Shape ReadShape(Stream data)
          {
              return ReadShape(data, FeatureType.Unspecified);
          }
  
          /// <summary>
          /// Attempts to read in an entry to the specified feature type.  If the feature type does not match
          /// the geometry type, this will return null.  (A Point geometry will be accepted by MultiPoint
          /// feature type, but not the other way arround.  Either way, this will advance the reader
          /// through the shape feature.  Using the Unspecified will always return the shape it reads,
          /// or null in the case of mixed feature collections which are not supported.
          /// </summary>
          /// <param name="data"></param>
          /// <param name="featureType"></param>
          /// <returns></returns>
          public static Shape ReadShape(Stream data, FeatureType featureType)
          {
              _endian = (ByteOrder)data.ReadByte();
              WkbGeometryType type = (WkbGeometryType)ReadInt32(data);
              Shape result;
              switch (type)
              {
                  case WkbGeometryType.Point:
                      result = ReadPoint(data);
                      if (featureType == FeatureType.Point || featureType == FeatureType.MultiPoint || featureType == FeatureType.Unspecified)
                      {
                          return result;
                      }
                      return null;
                  case WkbGeometryType.LineString:
                      result = ReadLineString(data);
                      if (featureType == FeatureType.Line || featureType == FeatureType.Unspecified)
                      {
                          return result;
                      }
                      return null;
                  case WkbGeometryType.Polygon:
                      result = ReadPolygon(data);
                      if (featureType == FeatureType.Polygon || featureType == FeatureType.Unspecified)
                      {
                          return result;
                      }
                      return null;
                  case WkbGeometryType.MultiPoint:
                      result = ReadMultiPoint(data);
                      if (featureType == FeatureType.MultiPoint || featureType == FeatureType.Unspecified)
                      {
                          return result;
                      }
                      return null;
                  case WkbGeometryType.MultiLineString:
                      result = ReadMultiLineString(data);
                      if (featureType == FeatureType.Line || featureType == FeatureType.Unspecified)
                      {
                          return result;
                      }
                      return null;
                  case WkbGeometryType.MultiPolygon:
                      result = ReadMultiPolygon(data);
                      if (featureType == FeatureType.Polygon || featureType == FeatureType.Unspecified)
                      {
                          return result;
                      }
                      return null;
                  case WkbGeometryType.GeometryCollection:
                      throw new ArgumentException("Mixed shape type collections are not supported by this method.");
              }
              return null;
          }
  
          /// <summary>
          /// This assumes that the byte order and shapetype have already been read.
          /// </summary>
          /// <param name="data"></param>
          public static Shape ReadPoint(Stream data)
          {
              Shape result = new Shape();
              result.Range = new ShapeRange(FeatureType.Point);
              PartRange prt = new PartRange(FeatureType.Point);
              prt.NumVertices = 1;
              result.Range.Parts.Add(prt);
              result.Vertices = ReadDouble(data, 2);
              return result;
          }
  
          private static void ReadGeometryCollection(Stream data, FeatureSetPack results)
          {
              int numGeometries = ReadInt32(data);
              // Don't worry about "multi-parting" these.  Simply create a separate shape
              // entry for every single geometry here since we have to split out the features
              // based on feature type.  (currently we don't have a mixed feature type for drawing.)
              for (int i = 0; i < numGeometries; i++)
              {
                  _endian = (ByteOrder)data.ReadByte();
                  WkbGeometryType type = (WkbGeometryType)ReadInt32(data);
                  switch (type)
                  {
                      case WkbGeometryType.Point:
                          ReadPoint(data, results);
                          return;
                      case WkbGeometryType.LineString:
                          ReadLineString(data, results);
                          return;
                      case WkbGeometryType.Polygon:
                          ReadPolygon(data, results);
                          break;
                      case WkbGeometryType.MultiPoint:
                          ReadMultiPoint(data, results);
                          break;
                      case WkbGeometryType.MultiLineString:
                          ReadMultiLineString(data, results);
                          break;
                      case WkbGeometryType.MultiPolygon:
                          ReadMultiPolygon(data, results);
                          break;
                      case WkbGeometryType.GeometryCollection:
                          ReadGeometryCollection(data, results);
                          break;
                  }
              }
          }
  
          /// <summary>
          /// This assumes that the byte order and shapetype have already been read.
          /// </summary>
          /// <param name="data"></param>
          /// <param name="results"></param>
          private static void ReadPoint(Stream data, FeatureSetPack results)
          {
              ShapeRange sr = new ShapeRange(FeatureType.MultiPoint);
              PartRange prt = new PartRange(FeatureType.MultiPoint);
              prt.NumVertices = 1;
              sr.Parts.Add(prt);
              double[] coord = ReadDouble(data, 2);
              results.Add(coord, sr);
          }
  
          /// <summary>
          /// Reads one multipoint shape from a data stream.
          /// (this assumes that the two bytes (endian and type) have already been read.
          /// </summary>
          /// <param name="data"></param>
          /// <returns></returns>
          private static Shape ReadMultiPoint(Stream data)
          {
              Shape result = new Shape(FeatureType.MultiPoint);
              int count = ReadInt32(data);
              PartRange prt = new PartRange(FeatureType.MultiPoint);
              prt.NumVertices = count;
              result.Range.Parts.Add(prt);
              double[] vertices = new double[count * 2];
              for (int iPoint = 0; iPoint < count; iPoint++)
              {
                  data.ReadByte(); // ignore endian
                  ReadInt32(data); // ignore geometry type
                  double[] coord = ReadDouble(data, 2);
  
gt:                 Array.Copy(coord, 0, vertices, iPoint * 2, 2);