Ejemplo n.º 1
0
 /// <summary>
 /// Forces each of the parts to adopt an extent equal to a calculated extents.
 /// The extents for the shape will expand to include those.
 /// </summary>
 public Extent CalculateExtents()
 {
     Extent ext = new Extent();
     foreach (PartRange part in Parts)
     {
         ext.ExpandToInclude(part.CalculateExtent());
     }
     Extent = ext;
     return ext;
 }
Ejemplo n.º 2
0
        /// <summary>
        /// Obtains a typed list of ShapefilePoint structures with double values associated with the various coordinates.
        /// </summary>
        /// <param name="filename">A string filename</param>
        /// <param name="progressHandler">A progress indicator</param>
        private void FillPoints(string filename, IProgressHandler progressHandler)
        {
            

            // Check to ensure the filename is not null
            if (filename == null)
            {
                throw new NullReferenceException(MessageStrings.ArgumentNull_S.Replace("%S",filename));
            }

            if (File.Exists(filename) == false)
            {
                throw new FileNotFoundException(MessageStrings.FileNotFound_S.Replace("%S", filename));
            }

            // Reading the headers gives us an easier way to track the number of shapes and their overall length etc.
            List<ShapeHeader> shapeHeaders = ReadIndexFile(filename);

            // Get the basic header information.  
            ShapefileHeader header = new ShapefileHeader(filename);
            Extent = new Extent(new[] { header.Xmin, header.Ymin, header.Xmax, header.Ymax });
            Envelope = Extent.ToEnvelope();
            // Check to ensure that the filename is the correct shape type
            if (header.ShapeType != ShapeTypes.Point && header.ShapeType != ShapeTypes.PointM && header.ShapeType != ShapeTypes.PointZ)
            {
                throw new ApplicationException(MessageStrings.FileNotPoints_S.Replace("%S", filename));
            }

            // This will set up a reader so that we can read values in huge chunks, which is much faster than one value at a time.
            IO.BufferedBinaryReader bbReader = new IO.BufferedBinaryReader(filename, progressHandler);

            if (bbReader.FileLength == 100)
            {
                bbReader.Close();
                // the file is empty so we are done reading
                return;
            }

            // Skip the shapefile header by skipping the first 100 bytes in the shapefile
            bbReader.Seek(100, SeekOrigin.Begin);
            int numShapes = shapeHeaders.Count;
            byte[] bigEndian = new byte[numShapes * 8];
            byte[] allCoords = new byte[numShapes * 16];
            bool isM = false;
            bool isZ = false;
            if(header.ShapeType == ShapeTypes.PointM || header.ShapeType == ShapeTypes.PointZ)
            {
                isM = true;
            }
            if (header.ShapeType == ShapeTypes.PointZ)
            {
                isZ = true;
            }
            byte[] allM = new byte[8];
            if(isM) allM = new byte[numShapes * 8];
            byte[] allZ = new byte[8];
            if(isZ) allZ = new byte[numShapes * 8];
            for (int shp = 0; shp < numShapes; shp++)
            {
                // Read from the index file because some deleted records
                // might still exist in the .shp file.
                long offset = (shapeHeaders[shp].ByteOffset);
                bbReader.Seek(offset, SeekOrigin.Begin);
                bbReader.Read(bigEndian, shp * 8, 8);
                ShapeTypes type = (ShapeTypes)bbReader.ReadInt32();
                //bbReader.Seek(4, SeekOrigin.Current);
                bbReader.Read(allCoords, shp * 16, 16);
                if(isZ)
                {
                    bbReader.Read(allZ, shp*8, 8);
                }
                if (isM)
                {
                    bbReader.Read(allM, shp*8, 8);
                }
                ShapeRange shape = new ShapeRange(FeatureTypes.Point);
                shape.StartIndex = shp;
                shape.ContentLength = 8;
                shape.NumPoints = 1;
                shape.NumParts = 1;
                ShapeIndices.Add(shape);
            }
            double[] vert = new double[2 * numShapes];
            Buffer.BlockCopy(allCoords, 0, vert, 0, numShapes*16);
            Vertex = vert;
            if(isM)
            {
                double[] m = new double[numShapes];
                Buffer.BlockCopy(allM, 0, m, 0, numShapes * 8);
                M = m;
            }
            if(isZ)
            {
                double[] z = new double[numShapes];
                Buffer.BlockCopy(allZ, 0, z, 0, numShapes * 8);
                Z = z;
            }
            for (int shp = 0; shp < numShapes; shp++)
            {
                PartRange part = new PartRange(vert, shp, 0, FeatureTypes.Point);
                part.NumVertices = 1;
                ShapeRange shape = ShapeIndices[shp];
                shape.Parts.Add(part);
                shape.Extent = new Extent(new[] {vert[shp*2], vert[shp*2 + 1], vert[shp*2], vert[shp*2 + 1]});
            }

                bbReader.Dispose();
            
            
            
  
        }
Ejemplo n.º 3
0
        /// <summary>
        /// This creates a polygon shape from an extent. 
        /// </summary>
        /// <param name="ext">The extent to turn into a polygon shape.</param>
        public ShapeRange(Extent ext)
        {
            Extent = ext;
            Parts = new List<PartRange>();
            _numParts = -1;
            // Counter clockwise
            // 1 2
            // 4 3
            double[] coords = new double[8];
            // C1
            coords[0] = ext.XMin;
            coords[1] = ext.YMax;
            // C2
            coords[2] = ext.XMax;
            coords[3] = ext.YMax;
            // C3
            coords[4] = ext.XMax;
            coords[5] = ext.YMin;
            // C4
            coords[6] = ext.XMin;
            coords[7] = ext.YMin;

            FeatureType = FeatureTypes.Polygon;
            ShapeType = ShapeTypes.Polygon;
            PartRange pr = new PartRange(coords, 0,0, FeatureTypes.Polygon);
            pr.NumVertices = 4;
            Parts.Add(pr);
            
        }
Ejemplo n.º 4
0
        /// <summary>
        /// This tests each feature of the input  
        /// </summary>
        /// <param name="self">This featureSet</param>
        /// <param name="other">The featureSet to perform intersection with</param>
        /// <param name="joinType">The attribute join type</param>
        /// <param name="progHandler">A progress handler for status messages</param>
        /// <returns>An IFeatureSet with the intersecting features, broken down based on the join Type</returns>
        public static IFeatureSet Intersection(this IFeatureSet self, IFeatureSet other, FieldJoinType joinType, IProgressHandler progHandler)
        {
          
            IFeatureSet result = null;
            ProgressMeter pm = new ProgressMeter(progHandler, "Calculating Intersection", self.Features.Count);
            if (joinType == FieldJoinType.All)
            {
                result = CombinedFields(self, other);
                // Intersection is symetric, so only consider I X J where J <= I
                int i=0;
                foreach(IFeature selfFeature in self.Features)
                {
                    List<IFeature> potentialOthers = other.Select(selfFeature.Envelope);
                    foreach (IFeature otherFeature in potentialOthers)
                    {
                        selfFeature.Intersection(otherFeature, result, joinType);
                        
                    }
                    pm.CurrentValue = i;
                    i++;
                }
                pm.Reset();
            }
            if (joinType == FieldJoinType.LocalOnly)
            {
                result = new FeatureSet();
                result.CopyTableSchema(self);
                result.FeatureType = self.FeatureType;
                IFeature union;
                pm = new ProgressMeter(progHandler, "Calculating Union", other.Features.Count);
                if(other.Features != null && other.Features.Count > 0)
                {
                    union = other.Features[0];
                    for(int i = 1; i < other.Features.Count; i++)
                    {
                        union = union.Union(other.Features[i]);
                        pm.CurrentValue = i;
                    }
                    pm.Reset();
                    pm = new ProgressMeter(progHandler, "Calculating Intersections", self.NumRows());
                    Extent otherEnvelope = new Extent(union.Envelope);
                    for (int shp = 0; shp < self.ShapeIndices.Count; shp++)
                    {
                        if (!self.ShapeIndices[shp].Extent.Intersects(otherEnvelope)) continue;
                        IFeature selfFeature = self.GetFeature(shp);
                        selfFeature.Intersection(union, result, joinType);
                        pm.CurrentValue = shp;
                    }
                    pm.Reset();
                }

                
            }
            if (joinType == FieldJoinType.ForeignOnly)
            {
                result = new FeatureSet();
                result.CopyTableSchema(other);
                IFeature union;
                if (self.Features != null && self.Features.Count > 0)
                {
                    pm = new ProgressMeter(progHandler, "Calculating Union", self.Features.Count);
                    union = self.Features[0];
                    for (int i = 1; i < self.Features.Count; i++)
                    {
                        union = union.Union(self.Features[i]);
                        pm.CurrentValue = i;
                    }
                    pm.Reset();
                    if (other.Features != null)
                    {
                        pm = new ProgressMeter(progHandler, "Calculating Intersection", other.Features.Count);
                        int j = 0;
                        foreach (IFeature otherFeature in other.Features)
                        {
                            IFeature test = otherFeature.Intersection(union, result, joinType);
                            if (test.BasicGeometry != null)
                            {
                                result.Features.Add(test);
                            }
                            pm.CurrentValue = j;
                            j++;
                        }
                    }
                    pm.Reset();
                }
                
                
            }
            return result;

        }
Ejemplo n.º 5
0
 /// <summary>
 /// This creates a new extent and cylces through the coordinates to calculate what it is.
 /// Since the vertices may change so easilly, this is not cached.
 /// </summary>
 /// <returns></returns>
 public Extent CalculateExtent()
 {
     // Create an extent for faster checking in most cases
     Extent ext = new Extent();
    
     // Do this once, and then we can re-use it for each other part.
     foreach (Vertex point in this)
     {
         if (ext.XMax < point.X) ext.XMax = point.X;
         if (ext.XMin > point.X) ext.XMin = point.X;
         if (ext.YMin > point.Y) ext.YMin = point.Y;
         if (ext.YMax < point.Y) ext.YMax = point.Y;
     }
     return ext;
 }
Ejemplo n.º 6
0
        ///// <summary>
        ///// Obsolete and slow.  First get the X and Y array once, and then access the members directly from that array.
        ///// </summary>
        //public double[][] Vertices
        //{
        //    get
        //    {
        //        int len = _vertices.Length/2;
        //        double[][] result = new double[len][];
        //        for (int i = 0; i < len; i++)
        //        {
        //            result[i] = new[] { _vertices[i*2], _vertices[i*2+1] };
        //        }
        //        return result;
        //    }
        //    protected set
        //    {
        //        for(int i = 0; i < value.Length; i++)
        //        {
        //            _vertices[i*2] = value[i][0];
        //            _vertices[i*2 + 1] = value[i][1];
        //        }
        //    }
        //}

        /// <summary>
        /// After changing coordinates, this will force the re-calculation of envelopes on a feature
        /// level as well as on the featureset level.  If this featureset is in index mode, 
        /// this will update the shape extents instead.
        /// </summary>
        public void UpdateEnvelopes()
        {
            if (InternalDataSet != null)
            {
                InternalDataSet.UpdateEnvelopes();
                return;
            }
            _envelope = new Envelope();
            _extent = new Extent();
            if(!_indexMode)
            {
                if (Features == null || Features.Count <= 0)
                {
                    _extent = new Extent(-180, -90, 180, 90);
                    _envelope = _extent.ToEnvelope();
                    return;
                }
                for (int shp = 0; shp < Features.Count; shp++)
                {
                    Features[shp].UpdateEnvelope();
                    _envelope.ExpandToInclude(Features[shp].Envelope);
                }
                _extent = new Extent(_envelope);
            }
            else
            {
                if(_shapeIndices == null || _shapeIndices.Count == 0)
                {
                    _extent = new Extent(-180, -90, 180, 90);
                    _envelope = _extent.ToEnvelope();
                    return;
                }
                foreach (ShapeRange range in _shapeIndices)
                {
                    _extent.ExpandToInclude(range.Extent);
                }
                _envelope = _extent.ToEnvelope();
            }
            
        }
        // X Y MultiPoints: Total Length = 28 Bytes
        // ---------------------------------------------------------
        // Position     Value               Type        Number      Byte Order
        // ---------------------------------------------------------
        // Byte 0       Record Number       Integer     1           Big
        // Byte 4       Content Length      Integer     1           Big
        // Byte 8       Shape Type 8        Integer     1           Little
        // Byte 12      Xmin                Double      1           Little
        // Byte 20      Ymin                Double      1           Little
        // Byte 28      Xmax                Double      1           Little
        // Byte 36      Ymax                Double      1           Little
        // Byte 48      NumPoints           Integer     1           Little
        // Byte X       Points              Point       NumPoints   Little


        // X Y M MultiPoints: Total Length = 34 Bytes
        // ---------------------------------------------------------
        // Position     Value               Type        Number      Byte Order
        // ---------------------------------------------------------
        // Byte 0       Record Number       Integer     1           Big
        // Byte 4       Content Length      Integer     1           Big
        // Byte 8       Shape Type 28       Integer     1           Little
        // Byte 12      Box                 Double      4           Little
        // Byte 44      NumPoints           Integer     1           Little
        // Byte X       Points              Point       NumPoints   Little
        // Byte Y*      Mmin                Double      1           Little
        // Byte Y + 8*  Mmax                Double      1           Little
        // Byte Y + 16* Marray              Double      NumPoints   Little
       
       
        
        // X Y Z M MultiPoints: Total Length = 44 Bytes
        // ---------------------------------------------------------
        // Position     Value               Type        Number  Byte Order
        // ---------------------------------------------------------
        // Byte 0       Record Number       Integer     1           Big
        // Byte 4       Content Length      Integer     1           Big       
        // Byte 8       Shape Type 18       Integer     1           Little
        // Byte 12      Box                 Double      4           Little
        // Byte 44      NumPoints           Integer     1           Little
        // Byte X       Points              Point       NumPoints   Little
        // Byte Y       Zmin                Double      1           Little
        // Byte Y + 8   Zmax                Double      1           Little
        // Byte Y + 16  Zarray              Double      NumPoints   Little
        // Byte Z*      Mmin                Double      1           Little
        // Byte Z+8*    Mmax                Double      1           Little
        // Byte Z+16*   Marray              Double      NumPoints   Little


        private void FillPoints(string filename, IProgressHandler progressHandler)
        {
           
            // Check to ensure the filename is not null
            if (filename == null)
            {
                throw new NullReferenceException(MessageStrings.ArgumentNull_S.Replace("%S", filename));
            }

            if (File.Exists(filename) == false)
            {
                throw new FileNotFoundException(MessageStrings.FileNotFound_S.Replace("%S", filename));
            }

            // Get the basic header information.  
            ShapefileHeader header = new ShapefileHeader(filename);
            Extent = new Extent(new[] { header.Xmin, header.Ymin, header.Xmax, header.Ymax });
            // Check to ensure that the filename is the correct shape type
            if (header.ShapeType != ShapeTypes.MultiPoint &&
                 header.ShapeType != ShapeTypes.MultiPointM &&
                 header.ShapeType != ShapeTypes.MultiPointZ)
            {
                throw new ArgumentException(MessageStrings.FileNotLines_S.Replace("%S", filename));

            }

            // Reading the headers gives us an easier way to track the number of shapes and their overall length etc.
            List<ShapeHeader> shapeHeaders = ReadIndexFile(filename);


            // This will set up a reader so that we can read values in huge chunks, which is much faster than one value at a time.
            IO.BufferedBinaryReader bbReader = new IO.BufferedBinaryReader(filename, progressHandler);

            if (bbReader.FileLength == 100)
            {
                // The shapefile is empty so we can simply return here
                bbReader.Close();
                return;
            }


            // Skip the shapefile header by skipping the first 100 bytes in the shapefile
            bbReader.Seek(100, SeekOrigin.Begin);

            int numShapes = shapeHeaders.Count;

            byte[] bigEndians = new byte[numShapes * 8];
            byte[] allBounds = new byte[numShapes * 32];
        
            ByteBlock allCoords = new ByteBlock(BLOCKSIZE);
            bool isM = (header.ShapeType == ShapeTypes.MultiPointZ || header.ShapeType == ShapeTypes.MultiPointM);
            bool isZ = (header.ShapeType == ShapeTypes.PolyLineZ);
            ByteBlock allZ = null;
            ByteBlock allM = null;
            if (isZ)
            {
                allZ = new ByteBlock(BLOCKSIZE);
            }
            if (isM)
            {
                allM = new ByteBlock(BLOCKSIZE);
            }
            int pointOffset = 0;
            for (int shp = 0; shp < numShapes; shp++)
            {

                // Read from the index file because some deleted records
                // might still exist in the .shp file.
                long offset = (shapeHeaders[shp].ByteOffset);
                bbReader.Seek(offset, SeekOrigin.Begin);

                // time: 200 ms
                ShapeRange shape = new ShapeRange(FeatureTypes.MultiPoint);


                shape.RecordNumber = bbReader.ReadInt32(false);       // Byte 0       Record Number       Integer     1           Big
                shape.ContentLength = bbReader.ReadInt32(false);      // Byte 4       Content Length      Integer     1           Big
                //bbReader.Read(bigEndians, shp * 8, 8);
                shape.ShapeType = (ShapeTypes)bbReader.ReadInt32();     
                shape.StartIndex = pointOffset;
                if (shape.ShapeType == ShapeTypes.NullShape)
                {
                    continue;
                }
                bbReader.Read(allBounds, shp * 32, 32);
                shape.NumParts = 1;                                    
                shape.NumPoints = bbReader.ReadInt32();                 
                allCoords.Read(shape.NumPoints * 16, bbReader);
                pointOffset += shape.NumPoints;

                if (header.ShapeType == ShapeTypes.MultiPointM)
                {
                    // These are listed as "optional" but there isn't a good indicator of how to determine if they were added.
                    // To handle the "optional" M values, check the contentLength for the feature.
                    // The content length does not include the 8-byte record header and is listed in 16-bit words.
                    if (shape.ContentLength * 2 > 44 + 4 * shape.NumParts + 16 * shape.NumPoints)
                    {
                        double mMin = bbReader.ReadDouble();
                        double mMax = bbReader.ReadDouble();
                        //bbReader.Seek(16, SeekOrigin.Current);
                        if (allM != null) allM.Read(shape.NumPoints * 8, bbReader);
                    }
                }
                if (header.ShapeType == ShapeTypes.MultiPointZ)
                {
                    bool hasM = shape.ContentLength * 2 > 60 + 4 * shape.NumParts + 24 * shape.NumPoints;
                    double zMin = bbReader.ReadDouble();
                    double zMax = bbReader.ReadDouble();
                    // For Z shapefiles, the Z part is not optional.
                    if (allZ != null) allZ.Read(shape.NumPoints * 8, bbReader);

                    // These are listed as "optional" but there isn't a good indicator of how to determine if they were added.
                    // To handle the "optional" M values, check the contentLength for the feature.
                    // The content length does not include the 8-byte record header and is listed in 16-bit words.
                    if (hasM)
                    {
                        double mMin = bbReader.ReadDouble();
                        double mMax = bbReader.ReadDouble();
                        
                        if (allM != null) allM.Read(shape.NumPoints * 8, bbReader);
                    }
                }
                // Now that we have read all the values, create the geometries from the points and parts arrays.
                ShapeIndices.Add(shape);
            }
            double[] vert = allCoords.ToDoubleArray();
            Vertex = vert;
            if (isM) M = allM.ToDoubleArray();
            if (isZ) Z = allZ.ToDoubleArray();
            Array.Reverse(bigEndians);
            List<ShapeRange> shapes = ShapeIndices;
            double[] bounds = new double[numShapes * 4];
            Buffer.BlockCopy(allBounds, 0, bounds, 0, allBounds.Length);
            for (int shp = 0; shp < numShapes; shp++)
            {
                ShapeRange shape = shapes[shp];
                shape.Extent = new Extent(bounds, shp * 4);
                int endIndex = shape.NumPoints + shape.StartIndex;
                int startIndex = shape.StartIndex;
                int count = endIndex - startIndex;
                PartRange partR = new PartRange(vert, shape.StartIndex, 0, FeatureTypes.MultiPoint);
                partR.NumVertices = count;
                shape.Parts.Add(partR);
            }
            GC.Collect();
            bbReader.Dispose();
        }
Ejemplo n.º 8
0
 private void BuildPaths(MapArgs e, IEnumerable<int> indices, out List<GraphicsPath> paths)
 {
     paths = new List<GraphicsPath>();     
     Extent drawExtents = new Extent(e.PixelToProj(_drawingBounds));
     Dictionary<FastDrawnState, GraphicsPath> borders = new Dictionary<FastDrawnState, GraphicsPath>();
     for (int selectState = 0; selectState < 2; selectState++)
     {
         foreach (IPolygonCategory category in Symbology.Categories)
         {
             FastDrawnState state = new FastDrawnState(selectState == 1, category);
             
             GraphicsPath border = new GraphicsPath();
             borders.Add(state, border);
             paths.Add(border);
         }
     }
     List<ShapeRange> shapes = DataSet.ShapeIndices;
     double[] vertices = DataSet.Vertex;
     if(!DrawnStatesNeeded)
     {
         FastDrawnState state = new FastDrawnState(false, Symbology.Categories[0]);
         foreach (int shp in indices)
         {
             ShapeRange shape = shapes[shp];
             if (!shape.Extent.Intersects(e.GeographicExtents)) continue;
             if(shp >= shapes.Count) continue;
             if (!borders.ContainsKey(state)) continue;
             if (drawExtents.Contains(shape.Extent))
             {
                 BuildPolygon(vertices, shapes[shp], borders[state], e, false);
             }
             else
             {
                 BuildPolygon(vertices, shapes[shp], borders[state], e, true);
             }
         }
     }
     else
     {
         FastDrawnState[] states = DrawnStates;
         foreach (int shp in indices)
         {
             if (shp >= shapes.Count) continue;
             if (shp >= states.Length)
             {
                 AssignFastDrawnStates();
                 states = DrawnStates;
             }
             if (states[shp].Visible == false) continue;
             ShapeRange shape = shapes[shp];
             if (!shape.Extent.Intersects(e.GeographicExtents)) continue;
             if (drawExtents.Contains(shape.Extent))
             {
                 FastDrawnState state = states[shp];
                 if (!borders.ContainsKey(state)) continue;
                 BuildPolygon(vertices, shapes[shp], borders[state], e, false);
             }
             else
             {
                 FastDrawnState state = states[shp];
                 if (!borders.ContainsKey(state)) continue;
                 BuildPolygon(vertices, shapes[shp], borders[state], e, true);
             }
             
         }
        
     }
     
 }
Ejemplo n.º 9
0
 /// <summary>
 /// This will draw any features that intersect this region.  To specify the features
 /// directly, use OnDrawFeatures.  This will not clear existing buffer content.
 /// For that call Initialize instead.
 /// </summary>
 /// <param name="args">A GeoArgs clarifying the transformation from geographic to image space</param>
 /// <param name="regions">The geographic regions to draw</param>
 public void DrawRegions(MapArgs args, List<IEnvelope> regions)
 {
     if (FeatureSet == null) return;
     if(FeatureSet.IndexMode)
     {
         // First determine the number of features we are talking about based on region.
         List<int> drawIndices = new List<int>();
         foreach (IEnvelope region in regions)
         {
             if (region != null)
             {
                 // We need to consider labels that go off the screen.  figure a region
                 // that is larger.
                 IEnvelope sur = new Envelope(region.Copy());
                 sur.ExpandBy(region.Width, region.Height);
                 Extent r = new Extent(sur);
                 // Use union to prevent duplicates.  No sense in drawing more than we have to.
                 drawIndices = drawIndices.Union(FeatureSet.SelectIndices(r)).ToList();
             }
         }
         List<Rectangle> clips = args.ProjToPixel(regions);
         DrawFeatures(args, drawIndices, clips, true);
     }
     else
     {
         // First determine the number of features we are talking about based on region.
         List<IFeature> drawList = new List<IFeature>();
         foreach (IEnvelope region in regions)
         {
             if (region != null)
             {
                 // We need to consider labels that go off the screen.  figure a region
                 // that is larger.
                 IEnvelope r = region.Copy();
                 r.ExpandBy(region.Width, region.Height);
                 // Use union to prevent duplicates.  No sense in drawing more than we have to.
                 drawList = drawList.Union(FeatureSet.Select(r)).ToList();
             }
         }
         List<Rectangle> clipRects = args.ProjToPixel(regions);
         DrawFeatures(args, drawList, clipRects, true);
     }
     
 }
Ejemplo n.º 10
0
 /// <summary>
 /// Tests if this envelope is contained by the specified envelope
 /// </summary>
 /// <param name="ext"></param>
 /// <returns></returns>
 public bool Contains(Extent ext)
 {
     if (XMin > ext.XMin) return false;
     if (XMax < ext.XMax) return false;
     if (YMin > ext.YMin) return false;
     if (YMax < ext.YMax) return false;
     return true;
 }
Ejemplo n.º 11
0
        /// <summary>
        /// Draws the GraphicsPaths.  Before we were effectively "re-creating" the same geometric
        /// </summary>
        /// <param name="e"></param>
        /// <param name="paths"></param>
        private void DrawPaths(MapArgs e, IList<GraphicsPath> paths)
        {
            Graphics g = e.Device ?? Graphics.FromImage(_backBuffer);
            int numCategories = Symbology.Categories.Count;
            
            if(!DrawnStatesNeeded && !EditMode)
            {
                IPolygonSymbolizer ps = Symbolizer;
                g.SmoothingMode = ps.Smoothing ? SmoothingMode.AntiAlias : SmoothingMode.None;
                Extent catBounds = new Extent(DataSet.Envelope);
                RectangleF bounds = new RectangleF();
                bounds.X = Convert.ToSingle((catBounds.XMin - e.MinX) * e.Dx);
                bounds.Y = Convert.ToSingle((e.MaxY - catBounds.YMax) * e.Dy);
                float r = Convert.ToSingle((catBounds.XMax - e.MinX) * e.Dx);
                bounds.Width = r - bounds.X;
                float b = Convert.ToSingle((e.MaxY - catBounds.YMin) * e.Dy);
                bounds.Height = b - bounds.Y;

                foreach (IPattern pattern in ps.Patterns)
                {
                    IGradientPattern gp = pattern as IGradientPattern;
                    if (gp != null)
                    {
                        gp.Bounds = bounds;
                    }

                    pattern.FillPath(g, paths[0]);
                }

                double scale = 1;
                if (ps.ScaleMode == ScaleModes.Geographic)
                {
                    scale = e.ImageRectangle.Width / e.GeographicExtents.Width;
                }
                foreach (IPattern pattern in ps.Patterns)
                {
                    if (pattern.UseOutline)
                    {
                        pattern.DrawPath(g, paths[0], scale);
                    }
                }
            }
            else
            {
                for (int selectState = 0; selectState < 2; selectState++)
                {
                    int iCategory = 0;
                    foreach (IPolygonCategory category in Symbology.Categories)
                    {
                        Extent catBounds;
                        if (CategoryExtents.Keys.Contains(category))
                        {
                            catBounds = CategoryExtents[category];
                        }
                        else
                        {
                            catBounds = CalculateCategoryExtent(category);
                        }
                        if (catBounds == null) catBounds = new Extent(Envelope);
                        RectangleF bounds = new RectangleF();
                        bounds.X = Convert.ToSingle((catBounds.XMin - e.MinX) * e.Dx);
                        bounds.Y = Convert.ToSingle((e.MaxY - catBounds.YMax) * e.Dy);
                        float r = Convert.ToSingle((catBounds.XMax - e.MinX) * e.Dx);
                        bounds.Width = r - bounds.X;
                        float b = Convert.ToSingle((e.MaxY - catBounds.YMin) * e.Dy);
                        bounds.Height = b - bounds.Y;

                        int index = selectState * numCategories + iCategory;
                        // Define the symbology based on the category and selection state
                        IPolygonSymbolizer ps = category.Symbolizer;
                        if (selectState == SELECTED) ps = category.SelectionSymbolizer;

                        g.SmoothingMode = ps.Smoothing ? SmoothingMode.AntiAlias : SmoothingMode.None;

                        foreach (IPattern pattern in ps.Patterns)
                        {
                            IGradientPattern gp = pattern as IGradientPattern;
                            if (gp != null)
                            {
                                gp.Bounds = bounds;
                            }
                            paths[index].FillMode = FillMode.Winding;
                            pattern.FillPath(g, paths[index]);
                        }

                        double scale = 1;
                        if (ps.ScaleMode == ScaleModes.Geographic)
                        {
                            scale = e.ImageRectangle.Width / e.GeographicExtents.Width;
                        }
                        foreach (IPattern pattern in ps.Patterns)
                        {
                            if (pattern.UseOutline)
                            {
                                pattern.DrawPath(g, paths[index], scale);
                            }
                        }
                        iCategory++;
                    } // category
                } // selectState
            }

            for (int i = 0; i < Symbology.Categories.Count; i++)
            {
                paths[i].Dispose();
            }
            if(e.Device == null) g.Dispose();
        
        }
Ejemplo n.º 12
0
 /// <summary>
 /// Tests if this envelope is contained by the specified envelope
 /// </summary>
 /// <param name="ext"></param>
 /// <returns></returns>
 public bool Within(Extent ext)
 {
     if(XMin < ext.XMin) return false;
     if (XMax > ext.XMax) return false;
     if (YMin < ext.YMin) return false;
     if (YMax > ext.YMax) return false;
     return true;
 }
Ejemplo n.º 13
0
 /// <summary>
 /// Expands this extent to include the domain of the specified extent
 /// </summary>
 /// <param name="ext">The extent to expand to include</param>
 public void ExpandToInclude(Extent ext)
 {
     if (ext.XMin < XMin) XMin = ext.XMin;
     if (ext.YMin < YMin) YMin = ext.YMin;
     if (ext.XMax > XMax) XMax = ext.XMax;
     if (ext.YMax > YMax) YMax = ext.YMax;
 }
Ejemplo n.º 14
0
 /// <summary>
 /// Tests for an intersection with the specified extent
 /// </summary>
 /// <param name="ext">The other extent</param>
 /// <returns>Boolean, true if they overlap anywhere, or even touch</returns>
 public bool Intersects(Extent ext)
 {
     if (ext.XMax < XMin) return false;
     if (ext.YMax < YMin) return false;
     if (ext.XMin > XMax) return false;
     if (ext.YMin > YMax) return false;
     return true;
 }
Ejemplo n.º 15
0
 /// <summary>
 /// Skips the features themselves and uses the shapeindicies instead.
 /// </summary>
 /// <param name="region">The region to select members from</param>
 /// <returns>A list of integer valued shape indices that are selected.</returns>
 public virtual List<int> SelectIndices(Extent region)
 {
     List<int> result = new List<int>();
     List<ShapeRange> shapes = ShapeIndices; // get from internal dataset if necessary
     if(shapes != null)
     {
         for (int shp = 0; shp < shapes.Count; shp++)
         {
             if (_shapeIndices[shp].Extent.Intersects(region)) result.Add(shp);
         }
     }
     return result;
 }
Ejemplo n.º 16
0
        private void DrawFeatures(MapArgs e, IEnumerable<int> indices)
        {
            Graphics g;
            if (e.Device != null)
            {
                g = e.Device; // A device on the MapArgs is optional, but overrides the normal buffering behaviors.
            }
            else
            {
                g = Graphics.FromImage(_backBuffer);
            }
            // Only draw features that are currently visible.
            Extent drawExtents = new Extent(e.PixelToProj(_drawingBounds));
            
            double minX = e.MinX;
            double maxY = e.MaxY;
            double dx = e.Dx;
            double dy = e.Dy;

            if(DrawnStatesNeeded)
            {
                FastDrawnState[] states = DrawnStates;
                for (int selectState = 0; selectState < 2; selectState++)
                {
                    foreach (ILineCategory category in Symbology.Categories)
                    {
                        // Define the symbology based on the category and selection state
                        ILineSymbolizer ls = category.Symbolizer;
                        if (selectState == SELECTED) ls = category.SelectionSymbolizer;
                        if (ls.Smoothing)
                        {
                            g.SmoothingMode = SmoothingMode.AntiAlias;
                        }
                        else
                        {
                            g.SmoothingMode = SmoothingMode.None;
                        }


                        // Determine the subset of the specified features that are visible and match the category
                        ILineCategory lineCategory = category;
                        int i = selectState;
                        Func<FastDrawnState, bool> isMember = state =>
                            state.Category == lineCategory &&
                            state.Selected == (i == 1) &&
                            state.Visible == true;

                        var drawnFeatures = from feature in indices
                                            where isMember(states[feature])
                                            select feature;


                        GraphicsPath graphPath = new GraphicsPath();
                        foreach (int shp in drawnFeatures)
                        {
                            ShapeRange shape = DataSet.ShapeIndices[shp];
                            if (drawExtents.Contains(shape.Extent))
                            {
                                // This optimization only works if we are zoomed out far enough
                                // that the whole linestring fits in the short integer
                                // drawing window.  Otherwise, we need to crop the line.
                                FastBuildLine(graphPath, DataSet.Vertex, shape, minX, maxY, dx, dy);
                            }
                            else
                            {
                                for (int iPart = 0; iPart < shape.Parts.Count; iPart++)
                                {
                                    IBasicGeometry geom = DataSet.GetFeature(shp).GetBasicGeometryN(iPart);
                                    if (drawExtents.Contains(shape.Extent) == false)
                                    {
                                        geom = e.GeographicExtents.Intersection(geom);
                                    }
                                    // Cropped geometries can be either linestrings or multi-linestrings
                                    IBasicLineString bls = geom as IBasicLineString;
                                    if (bls != null)
                                    {
                                        // the result is definitely in the linestring category
                                        BuildLineString(graphPath, bls, minX, maxY, dx, dy);
                                    }
                                    else
                                    {
                                        IMultiLineString intersect = geom as MultiLineString;
                                        if (intersect == null) continue;
                                        for (int iLine = 0; iLine < intersect.NumGeometries; iLine++)
                                        {
                                            BuildLineString(graphPath, intersect.GetBasicGeometryN(iLine) as IBasicLineString, minX, maxY, dx, dy);
                                        }
                                    }
                                }
                            }
                        }
                        double scale = 1;
                        if (ls.ScaleMode == ScaleModes.Geographic)
                        {
                            scale = e.ImageRectangle.Width / e.GeographicExtents.Width;
                        }

                        foreach (IStroke stroke in ls.Strokes)
                        {

                            stroke.DrawPath(g, graphPath, scale);
                        }

                        graphPath.Dispose();

                    }
                }
            }
            else
            {
                // Selection state is disabled
                // Category is only the very first category
                ILineCategory category = Symbology.Categories[0];
                ILineSymbolizer ls = category.Symbolizer;
                
                if (ls.Smoothing)
                {
                    g.SmoothingMode = SmoothingMode.AntiAlias;
                }
                else
                {
                    g.SmoothingMode = SmoothingMode.None;
                }


                // Determine the subset of the specified features that are visible and match the category
                ILineCategory lineCategory = category;
                GraphicsPath graphPath = new GraphicsPath();
                foreach (int shp in indices)
                {
                    ShapeRange shape = DataSet.ShapeIndices[shp];
                    if (drawExtents.Contains(shape.Extent))
                    {
                        // This optimization only works if we are zoomed out far enough
                        // that the whole linestring fits in the short integer
                        // drawing window.  Otherwise, we need to crop the line.
                        FastBuildLine(graphPath, DataSet.Vertex, shape, minX, maxY, dx, dy);
                    }
                    else
                    {
                        for (int iPart = 0; iPart < shape.Parts.Count; iPart++)
                        {
                            IBasicGeometry geom = DataSet.GetFeature(shp).GetBasicGeometryN(iPart);
                            if (drawExtents.Contains(shape.Extent) == false)
                            {
                                geom = e.GeographicExtents.Intersection(geom);
                            }
                            // Cropped geometries can be either linestrings or multi-linestrings
                            IBasicLineString bls = geom as IBasicLineString;
                            if (bls != null)
                            {
                                // the result is definitely in the linestring category
                                BuildLineString(graphPath, bls, minX, maxY, dx, dy);
                            }
                            else
                            {
                                IMultiLineString intersect = geom as MultiLineString;
                                if (intersect == null) continue;
                                for (int iLine = 0; iLine < intersect.NumGeometries; iLine++)
                                {
                                    BuildLineString(graphPath, intersect.GetBasicGeometryN(iLine) as IBasicLineString, minX, maxY, dx, dy);
                                }
                            }
                        }
                    }
                }
                double scale = 1;
                if (ls.ScaleMode == ScaleModes.Geographic)
                {
                    scale = e.ImageRectangle.Width / e.GeographicExtents.Width;
                }

                foreach (IStroke stroke in ls.Strokes)
                {

                    stroke.DrawPath(g, graphPath, scale);
                }

                graphPath.Dispose();

            }
  
           
            if (e.Device == null) g.Dispose();
        }
Ejemplo n.º 17
0
        /// <summary>
        /// returns only the features that have envelopes that
        /// intersect with the specified envelope.  If in indexMode, this uses the ShapeIndices to create
        /// features on demand, rather than loading all the features.  It is much faster to use selectIndices
        /// when in index mode.
        /// </summary>
        /// <param name="region">The specified region to test for intersect with</param>
        /// <param name="affectedRegion">This returns the geographic extents that contains the modified contents.</param>
        /// <returns>A List of the IFeature elements that are contained in this region</returns>
        public virtual List<IFeature> Select(IEnvelope region, out IEnvelope affectedRegion)
        {
            List<IFeature> result = new List<IFeature>();
            if(IndexMode)
            {
                ShapeRange aoi = new ShapeRange(new Extent(region));
                IEnvelope env = region.Copy();
                Extent affected = new Extent();
                List<ShapeRange> shapes = ShapeIndices;
                if(shapes != null)
                {
                    for(int shp = 0; shp < shapes.Count; shp++)
                    {
                        if (!shapes[shp].Intersects(aoi)) continue;
                        IFeature f = GetFeature(shp);
                        affected.ExpandToInclude(shapes[shp].Extent);
                        result.Add(f);
                    }
                }
                affectedRegion = affected.ToEnvelope();
                return result;
            }

            affectedRegion = new Envelope();

            foreach (IFeature feature in Features)
            {
                if (!feature.Envelope.Intersects(region)) continue;
                result.Add(feature);
                affectedRegion.ExpandToInclude(feature.Envelope);
            }
            return result;

        }
Ejemplo n.º 18
0
        /// <summary>
        /// This calculates the extent for the category and caches it in the extents collection
        /// </summary>
        /// <param name="category"></param>
        protected virtual Extent CalculateCategoryExtent(IFeatureCategory category)
        {
            Extent ext = new Extent(new[]{double.MaxValue, double.MaxValue, double.MinValue, double.MinValue});
            if(_editMode)
            {
                IDictionary<IFeature, IDrawnState> features = _drawingFilter.DrawnStates;

                foreach (IFeature f in DataSet.Features)
                {
                    if (category == features[f].SchemeCategory)
                    {
                        ext.ExpandToInclude(new Extent(f.Envelope));
                    }
                }
                if (_categoryExtents.Keys.Contains(category))
                {
                    _categoryExtents[category] = ext.Copy();
                }
                else
                {
                    _categoryExtents.Add(category, ext.Copy());
                }
            }
            else
            {
                FastDrawnState[] states = DrawnStates;
                List<ShapeRange> ranges = DataSet.ShapeIndices;
                for(int shp = 0; shp < DrawnStates.Length; shp++)
                {
                    if(!_categoryExtents.ContainsKey(states[shp].Category))
                    {
                        _categoryExtents.Add(states[shp].Category, ranges[shp].Extent.Copy());
                    }
                    else
                    {
                        _categoryExtents[states[shp].Category].ExpandToInclude(ranges[shp].Extent);
                    }
                }
            }
            
            return ext;
        }
Ejemplo n.º 19
0
        /// <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();
                
            }

            
            
            

            
        }
Ejemplo n.º 20
0
        // X Y Poly Lines: Total Length = 28 Bytes
        // ---------------------------------------------------------
        // Position     Value               Type        Number      Byte Order
        // ---------------------------------------------------------
        // Byte 0       Record Number       Integer     1           Big
        // Byte 4       Content Length      Integer     1           Big
        // Byte 8       Shape Type 3        Integer     1           Little
        // Byte 12      Xmin                Double      1           Little
        // Byte 20      Ymin                Double      1           Little
        // Byte 28      Xmax                Double      1           Little
        // Byte 36      Ymax                Double      1           Little
        // Byte 44      NumParts            Integer     1           Little
        // Byte 48      NumPoints           Integer     1           Little
        // Byte 52      Parts               Integer     NumParts    Little
        // Byte X       Points              Point       NumPoints   Little


        // X Y M Poly Lines: Total Length = 34 Bytes
        // ---------------------------------------------------------
        // Position     Value               Type        Number      Byte Order
        // ---------------------------------------------------------
        // Byte 0       Record Number       Integer     1           Big
        // Byte 4       Content Length      Integer     1           Big
        // Byte 8       Shape Type 23       Integer     1           Little
        // Byte 12      Box                 Double      4           Little
        // Byte 44      NumParts            Integer     1           Little
        // Byte 48      NumPoints           Integer     1           Little
        // Byte 52      Parts               Integer     NumParts    Little
        // Byte X       Points              Point       NumPoints   Little
        // Byte Y*      Mmin                Double      1           Little
        // Byte Y + 8*  Mmax                Double      1           Little
        // Byte Y + 16* Marray              Double      NumPoints   Little
       
       
        
        // X Y Z M Poly Lines: Total Length = 44 Bytes
        // ---------------------------------------------------------
        // Position     Value               Type        Number  Byte Order
        // ---------------------------------------------------------
        // Byte 0       Record Number       Integer     1           Big
        // Byte 4       Content Length      Integer     1           Big       
        // Byte 8       Shape Type 13       Integer     1           Little
        // Byte 12      Box                 Double      4           Little
        // Byte 44      NumParts            Integer     1           Little
        // Byte 48      NumPoints           Integer     1           Little
        // Byte 52      Parts               Integer     NumParts    Little
        // Byte X       Points              Point       NumPoints   Little
        // Byte Y       Zmin                Double      1           Little
        // Byte Y + 8   Zmax                Double      1           Little
        // Byte Y + 16  Zarray              Double      NumPoints   Little
        // Byte Z*      Mmin                Double      1           Little
        // Byte Z+8*    Mmax                Double      1           Little
        // Byte Z+16*   Marray              Double      NumPoints   Little

       


        private void FillPolygons(string filename, IProgressHandler progressHandler)
        {
           
            // Check to ensure the filename is not null
            if (filename == null)
            {
                throw new NullReferenceException(MessageStrings.ArgumentNull_S.Replace("%S", filename));
            }

            if (File.Exists(filename) == false)
            {
                throw new FileNotFoundException(MessageStrings.FileNotFound_S.Replace("%S", filename));
            }

            // Get the basic header information.  
            ShapefileHeader header = new ShapefileHeader(filename);
            Extent = new Extent(new[]{header.Xmin, header.Ymin, header.Xmax, header.Ymax});

            // Check to ensure that the filename is the correct shape type
            if (header.ShapeType != ShapeTypes.Polygon &&
                 header.ShapeType != ShapeTypes.PolygonM &&
                 header.ShapeType != ShapeTypes.PolygonZ)
            {
                throw new ArgumentException(MessageStrings.FileNotLines_S.Replace("%S", filename));

            }

            // Reading the headers gives us an easier way to track the number of shapes and their overall length etc.
            List<ShapeHeader> shapeHeaders = ReadIndexFile(filename);


            // This will set up a reader so that we can read values in huge chunks, which is much faster than one value at a time.
            IO.BufferedBinaryReader bbReader = new IO.BufferedBinaryReader(filename, progressHandler);

            if (bbReader.FileLength == 100)
            {
                // The shapefile is empty so we can simply return here
                bbReader.Close();
                return;
            }

            // Skip the shapefile header by skipping the first 100 bytes in the shapefile
            bbReader.Seek(100, SeekOrigin.Begin);

            int numShapes = shapeHeaders.Count;
            
            int [] partOffsets = new int[numShapes];
            byte[] bigEndians = new byte[numShapes * 8];
            byte[] allBounds = new byte[numShapes * 32];

            ByteBlock allParts = new ByteBlock(BLOCKSIZE); // probably all will be in one block, but use a byteBlock just in case.
            ByteBlock allCoords = new ByteBlock(BLOCKSIZE);
            bool isM = (header.ShapeType == ShapeTypes.PolyLineM || header.ShapeType == ShapeTypes.PolyLineZ);
            bool isZ = (header.ShapeType == ShapeTypes.PolyLineZ);
            ByteBlock allZ = null;
            ByteBlock allM = null;
            if (isZ)
            {
                allZ = new ByteBlock(BLOCKSIZE);
            }
            if (isM)
            {
                allM = new ByteBlock(BLOCKSIZE);
            }

            int pointOffset = 0;
            for (int shp = 0; shp < numShapes; shp++)
            {
                // Read from the index file because some deleted records
                // might still exist in the .shp file.
                long offset = (shapeHeaders[shp].ByteOffset);
                bbReader.Seek(offset, SeekOrigin.Begin);

                                                                    // Position     Value               Type        Number      Byte Order
                ShapeRange shape = new ShapeRange(FeatureTypes.Polygon); //-------------------------------------------------------------------- 
                shape.RecordNumber = bbReader.ReadInt32(false);     // Byte 0       Record Number       Integer     1           Big
                shape.ContentLength = bbReader.ReadInt32(false);    // Byte 4       Content Length      Integer     1           Big
                shape.ShapeType = (ShapeTypes)bbReader.ReadInt32(); // Byte 8       Shape Type          Integer     1           Little
                shape.StartIndex = pointOffset;
                if (shape.ShapeType == ShapeTypes.NullShape)
                {
                    continue;
                }
                bbReader.Read(allBounds, shp*32, 32);
                //double xMin = bbReader.ReadDouble();                // Byte 12      Xmin                Double      1           Little
               // double yMin = bbReader.ReadDouble();                // Byte 20      Ymin                Double      1           Little
                //double xMax = bbReader.ReadDouble();                // Byte 28      Xmax                Double      1           Little
                //double yMax = bbReader.ReadDouble();                // Byte 36      Ymax                Double      1           Little
                shape.NumParts = bbReader.ReadInt32();              // Byte 44      NumParts            Integer     1           Little
                //feature.NumPoints = bbReader.ReadInt32();             // Byte 48      NumPoints           Integer     1           Little
                shape.NumPoints = bbReader.ReadInt32();                                             
                
                // Create an envelope from the extents box in the file.
                //feature.Envelope = new Envelope(xMin, xMax, yMin, yMax);

                partOffsets[shp] = allParts.IntOffset();
                allParts.Read(shape.NumParts * 4, bbReader);
                allCoords.Read(shape.NumPoints * 16, bbReader);
                pointOffset += shape.NumPoints;



                if (header.ShapeType == ShapeTypes.PolygonM)
                {
                    // These are listed as "optional" but there isn't a good indicator of how to determine if they were added.
                    // To handle the "optional" M values, check the contentLength for the feature.
                    // The content length does not include the 8-byte record header and is listed in 16-bit words.
                    if (shape.ContentLength * 2 > 44 + 4 * shape.NumParts + 16 * shape.NumPoints)
                    {
                        double mMin = bbReader.ReadDouble();
                        double mMax = bbReader.ReadDouble();
                        
                        if(allM != null)allM.Read(shape.NumPoints * 8, bbReader);
                    }
                }

                if (header.ShapeType == ShapeTypes.PolygonZ)
                {
                    bool hasM = shape.ContentLength * 2 > 60 + 4 * shape.NumParts + 24 * shape.NumPoints;
                    double zMin = bbReader.ReadDouble();
                    double zMax = bbReader.ReadDouble();
                   
                    
                    // For Z shapefiles, the Z part is not optional.  
                    if (allZ != null) allZ.Read(shape.NumPoints * 8, bbReader);
                   
                    // These are listed as "optional" but there isn't a good indicator of how to determine if they were added.
                    // To handle the "optional" M values, check the contentLength for the feature.
                    // The content length does not include the 8-byte record header and is listed in 16-bit words.
                    if (hasM)
                    {
                        double mMin = bbReader.ReadDouble();
                        double mMax = bbReader.ReadDouble();
                        if (allM != null) allM.Read(shape.NumPoints * 8, bbReader);
                    }
                    
                }
                ShapeIndices.Add(shape);
            }

            double[] vert = allCoords.ToDoubleArray();
            Vertex = vert;
            if (isM) M = allM.ToDoubleArray();
            if (isZ) Z = allZ.ToDoubleArray();
            List<ShapeRange> shapes = ShapeIndices;
            double[] bounds = new double[numShapes * 4];
            Buffer.BlockCopy(allBounds, 0, bounds, 0, allBounds.Length);
            int[] parts = allParts.ToIntArray();
            ProgressMeter = new ProgressMeter(ProgressHandler, "Testing Parts and Holes", numShapes);
            for (int shp = 0; shp < numShapes; shp++)
            {
                ShapeRange shape = shapes[shp];
                shape.Extent = new Extent(bounds, shp * 4);
                for (int part = 0; part < shape.NumParts; part++)
                {
                    int offset = partOffsets[shp];
                    int endIndex = shape.NumPoints + shape.StartIndex;
                    int startIndex = parts[offset + part] + shape.StartIndex;
                    if (part < shape.NumParts - 1) endIndex = parts[offset + part + 1] + shape.StartIndex;
                    int count = endIndex - startIndex;
                    PartRange partR = new PartRange(vert, shape.StartIndex, parts[offset + part], FeatureTypes.Polygon);
                    partR.NumVertices = count;
                    shape.Parts.Add(partR);
                }
                ProgressMeter.CurrentValue = shp;
            }
            ProgressMeter.Reset();
            GC.Collect();
        }