/// <summary>
 /// handles the attributes while adding a shape
 /// </summary>
 /// <param name="shape"></param>
 /// <returns>A data row, but only if attributes are populated</returns>
 private DataRow AddAttributes(Shape shape)
 {
     // Handle attributes if the array is not null.  Assumes compatible schema.
     if (shape.Attributes != null)
     {
         DataColumn[] columns = GetColumns();
         Dictionary<string, object> rowContent = new Dictionary<string, object>();
         object[] fixedContent = new object[columns.Length];
         DataRow addedRow;
         if (shape.Attributes.Length != columns.Length)
         {
             throw new ArgumentException("Attribute column count mismatch.");
         }
         for (int iField = 0; iField < columns.Length; iField++)
         {
             object value = shape.Attributes[iField];
             if (value != null)
             {
                 if (columns[iField].DataType != value.GetType())
                 {
                     // this may throw an exception if the type casting fails
                     value = Convert.ChangeType(value, columns[iField].DataType);
                 }
             }
             fixedContent[iField] = value;
             rowContent.Add(columns[iField].ColumnName, value);
         }
         if (AttributesPopulated)
         {
             // just add a new datarow
             addedRow = _dataTable.NewRow();
             addedRow.ItemArray = fixedContent;
             return addedRow;
         }
         // Insert a new row in the source 
         AddRow(rowContent);
     }
     return null;
 }
        /// <summary>
        /// Gets a shape at the specified shape index.  If the featureset is in 
        /// indexmode, this returns a copy of the shape.  If not, it will create
        /// a new shape based on the specified feature.
        /// </summary>
        /// <param name="index">The zero based integer index of the shape.</param>
        /// <param name="getAttributes">If getAttributes is true, then this also try to get attributes for that shape.
        /// If attributes are loaded, then it will use the existing datarow.  Otherwise, it will read the attributes
        /// from the file.  (This second option is not recommended for large repeats.  In such a case, the attributes
        /// can be set manually from a larger bulk query of the source attributes.)</param>
        /// <returns>The Shape object</returns>
        public virtual Shape GetShape(int index, bool getAttributes)
        {
            if (InternalDataSet != null)
            {
                return InternalDataSet.GetShape(index, getAttributes);
            }
            if (_indexMode == false) return new Shape(Features[index]);
            Shape result = new Shape(FeatureType);
            // This will also deep copy the parts, attributes and vertices
            ShapeRange range = ShapeIndices[index];
            result.Range = range.Copy();
            int start = range.StartIndex;
            int numPoints = range.NumPoints;
            double[] vertices = new double[numPoints * 2];
            
            double[] m = new double[numPoints];
            Array.Copy(_vertices, start*2, vertices, 0, numPoints * 2);
            if (_z != null && (_z.Length - start) >= numPoints)
            {
                result.Z = new double[numPoints];
                Array.Copy(_z, start, result.Z, 0, numPoints);
            }
            if(_m != null && (_m.Length - start) >= numPoints)
            {
                result.M = new double[numPoints];
                Array.Copy(_m, start, result.M, 0, numPoints);
            }
            result.Vertices = vertices;
         

            // There is presumed to be only a single shape in the output array.
            result.Range.StartIndex = 0;
            if(AttributesPopulated)
            {
                if (getAttributes) result.Attributes = DataTable.Rows[index].ItemArray;
            }
            else
            {
                DataTable dt = GetAttributes(index, 1);
                if(dt != null && dt.Rows.Count > 0)
                {
                    result.Attributes = dt.Rows[0].ItemArray;
                }
            }
            
            return result;
        }
 public void FinishShape(object sender, EventArgs e)
 {
     Feature f = null;
     if (_featureSet.FeatureType == FeatureTypes.MultiPoint)
     {
        f = new Feature(new MultiPoint(_coordinates));
     }
     if (_featureSet.FeatureType == FeatureTypes.Line || _featureSet.FeatureType == FeatureTypes.Polygon)
     {
         FinishPart(sender, e);
         Shape shp = new Shape(_featureSet.FeatureType);
         foreach (List<Coordinate> part in _parts)
         {
             shp.AddPart(part, _featureSet.CoordinateType);
         }
         f = new Feature(shp);
     }
     if (f != null)
     {
         
         _featureSet.Features.Add(f);
         _featureSet.UpdateEnvelopes();
     }
     _featureSet.InvalidateVertices();
     _coordinates = new List<Coordinate>();
     _parts = new List<List<Coordinate>>();
 }
        /// <summary>
        /// If this featureset is in index mode, this will append the vertices and shapeindex of the shape.
        /// Otherwise, this will add a new feature based on this shape.  If the attributes of the shape are not null,
        /// this will attempt to append a new datarow It is up to the developer
        /// to ensure that the object array of attributes matches the this featureset.  If the Attributes of this feature are loaded,
        /// this will add the attributes in ram only.  Otherwise, this will attempt to insert the attributes as a 
        /// new record using the "AddRow" method.  The schema of the object array should match this featureset's column schema.
        /// </summary>
        /// <param name="shape">The shape to add to this featureset.</param>
        public void AddShape(Shape shape)
        {
            if(InternalDataSet != null)
            {
                InternalDataSet.AddShape(shape);
                return;
            }
            IFeature addedFeature = null;
            
            // This first section controls the indices which need to happen regardless
            // because drawing uses indices even if editors like working with features.
            int count = (_vertices != null) ? _vertices.Length / 2 : 0; // Original number of points
            int totalCount = shape.Range.NumPoints + count;
            int start = shape.Range.StartIndex;
            int num = shape.Range.NumPoints;

            double[] vertices = new double[totalCount*2];
            if (_vertices != null) Array.Copy(_vertices, 0, vertices, 0, _vertices.Length);
            if(shape.Vertices != null) Array.Copy(shape.Vertices, start * 2, vertices, count * 2, num * 2);

            if(_m != null || shape.M != null)
            {
                double[] m = new double[totalCount];
                if (_m != null) Array.Copy(_m, 0, m, 0, _m.Length);
                if (shape.M != null) Array.Copy(shape.Vertices, start, m, count, num);
                _m = m;
            }
            if(_z != null || shape.Z != null)
            {
                double[] z = new double[totalCount];
                if (_z != null) Array.Copy(_z, 0, z, 0, _z.Length);
                if (shape.Z != null) Array.Copy(shape.Vertices, start, z, count, num);
                _z = z;
            }
            
            shape.Range.StartIndex = count;
            ShapeIndices.Add(shape.Range);
            Vertex = vertices;
            shape.Vertices = vertices; 
            if (Extent == null) Extent = new Extent();
            Extent.ExpandToInclude(shape.Range.Extent);
            Envelope = Extent.ToEnvelope();
            
            if(!_indexMode)
            {
                // Just add a new feature
                addedFeature = new Feature(shape);
                
                Features.Add(addedFeature);
                addedFeature.DataRow = AddAttributes(shape);
                if(Extent == null)Extent = new Extent();
                if (!shape.Range.Extent.IsEmpty()) Extent.ExpandToInclude(new Extent(addedFeature.Envelope));
                Envelope = Extent.ToEnvelope();
                
            }

            
            
            

            
        }
        private static Shape ReadMultiPolygon(Stream data)
        {
            Shape result = new Shape(FeatureTypes.Polygon);
            int numPolygons = ReadInt32(data);
            List<double[]> rings = new List<double[]>();
            int partOffset = 0;
            for (int iPoly = 0; iPoly < numPolygons; iPoly++)
            {
                data.Seek(5, SeekOrigin.Current); // endian and geometry type
                int numRings = ReadInt32(data);
                for (int iRing = 0; iRing < numRings; iRing++)
                {
                    int numPoints = ReadInt32(data); // ring structures are like a linestring without the final point.
                    double[] coords = ReadDouble(data, 2 * numPoints);
                    if (iRing == 0)
                    {
                        // By shapefile standard, the shell should be clockwise
                        if (IsCounterClockwise(coords)) ReverseCoords(coords);
                    }
                    else
                    {
                        // By shapefile standard, the holes should be counter clockwise.
                        if (!IsCounterClockwise(coords)) ReverseCoords(coords);
                    }
                    PartRange lPrt = new PartRange(FeatureTypes.Polygon);
                    lPrt.PartOffset = partOffset;
                    lPrt.NumVertices = numPoints;
                    result.Range.Parts.Add(lPrt);
                    partOffset += coords.Length / 2;
                    rings.Add(coords);
                }
            }

            double[] allVertices = new double[partOffset * 2];
            int offset = 0;
            foreach (double[] ring in rings)
            {
                Array.Copy(ring, 0, allVertices, offset, ring.Length);
                offset += ring.Length;
            }
            result.Vertices = allVertices;
            return result;
        }
 private static Shape ReadMultiLineString(Stream data)
 {
     Shape result = new Shape(FeatureTypes.Line);
     int numLineStrings = ReadInt32(data);
     List<double[]> strings = new List<double[]>();
     int partOffset = 0;
     for (int iString = 0; iString < numLineStrings; iString++)
     {
         // Each of these needs to read a full WKBLineString
         data.Seek(5, SeekOrigin.Current); //ignore header
         int numPoints = ReadInt32(data);
         double[] coords = ReadDouble(data, 2 * numPoints);
         PartRange lPrt = new PartRange(FeatureTypes.Line);
         lPrt.PartOffset = partOffset;
         lPrt.NumVertices = numPoints;
         result.Range.Parts.Add(lPrt);
         partOffset += coords.Length / 2;
         strings.Add(coords);
     }
     double[] allVertices = new double[partOffset * 2];
     int offset = 0;
     foreach (double[] ring in strings)
     {
         Array.Copy(ring, 0, allVertices, offset, ring.Length);
         offset += ring.Length;
     }
     result.Vertices = allVertices;
     return result;
 }
 private static Shape ReadLineString(Stream data)
 {
     Shape result = new Shape(FeatureTypes.Line);
     int count = ReadInt32(data);
     double[] coords = ReadDouble(data, 2 * count);
     PartRange lPrt = new PartRange(FeatureTypes.Line);
     lPrt.NumVertices = count;
     result.Range.Parts.Add(lPrt);
     result.Vertices = coords;
     return result;
 }
 /// <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(FeatureTypes.MultiPoint);
     int count = ReadInt32(data);
     PartRange prt = new PartRange(FeatureTypes.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);
         Array.Copy(coord, 0, vertices, iPoint * 2, 2);
     }
     result.Vertices = vertices;
     return result;
 }
 /// <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(FeatureTypes.Point);
     PartRange prt = new PartRange(FeatureTypes.Point);
     prt.NumVertices = 1;
     result.Range.Parts.Add(prt);
     result.Vertices = ReadDouble(data, 2);
     return result;
 }