/// <summary> /// Calculates the intersection of a polygon shape without relying on the NTS geometry /// </summary> /// <param name="polygonShape"></param> /// <param name="otherShape"></param> /// <returns></returns> public static bool Intersects(ShapeRange polygonShape, ShapeRange otherShape) { if (polygonShape.FeatureType != FeatureType.Polygon) { throw new ArgumentException("The First parameter should be a point shape, but it was featuretype:" + polygonShape.FeatureType); } // Extents will have already been tested by this point. // Regardless of what the other shapetype is, a coordinate inside this polygon indicates a hit. if (ContainsVertex(polygonShape, otherShape)) return true; // There is no other way for this polygon to intersect the other points if (otherShape.FeatureType == FeatureType.Point || otherShape.FeatureType == FeatureType.MultiPoint) return false; // For lines and polygons, if any segment intersects any segment of this polygon shape, return true. // This is essentially looking for the rare case of crossing in and crossing out again with // no actual points inside this polygonShape. if (LineShape.SegmentsIntersect(polygonShape, otherShape)) return true; // There is no other way for this polygon to intersect the other lines if (otherShape.FeatureType == FeatureType.Line) return false; // If the other polygon completely contains this polygon every other test has returned false, // but this will test for that case with only a single point. Vertex v = polygonShape.First(); ShapeRange onlyFirstPoint = new ShapeRange(v); return ContainsVertex(otherShape, onlyFirstPoint); }
/// <summary> /// Calculates the intersection of a polygon shape without relying on the NTS geometry /// </summary> /// <param name="lineShape"></param> /// <param name="otherShape"></param> /// <returns></returns> public static bool Intersects(ShapeRange lineShape, ShapeRange otherShape) { if (lineShape.FeatureType != FeatureType.Line) { throw new ArgumentException("The First parameter should be a point shape, but it was featuretype:" + lineShape.FeatureType); } // Implmented in PolygonShape if (otherShape.FeatureType == FeatureType.Polygon) { return otherShape.Intersects(lineShape); } // Test for point on a line if (otherShape.FeatureType == FeatureType.Point || otherShape.FeatureType == FeatureType.MultiPoint) { return IntersectsVertex(lineShape, otherShape); } // There is no other way for this polygon to intersect the other points if (otherShape.FeatureType == FeatureType.Point || otherShape.FeatureType == FeatureType.MultiPoint) return false; // For lines and polygons, if any segment intersects any segment of this polygon shape, return true. // This is essentially looking for the rare case of crossing in and crossing out again with // no actual points inside this polygonShape. return SegmentsIntersect(lineShape, otherShape); }
/// <summary> /// Tests to see if any vertices from the other shape are coincident with a segment from this line shape. /// </summary> /// <param name="lineShape"></param> /// <param name="otherShape"></param> /// <returns></returns> public static bool IntersectsVertex(ShapeRange lineShape, ShapeRange otherShape) { return lineShape.Parts .Any(part => part.Segments .Any(segment => otherShape.Parts .Any(oPart => oPart .Any(segment.IntersectsVertex)))); }
/// <summary> /// Returns true if any vertices overlap /// </summary> /// <returns></returns> public static bool VerticesIntersect(ShapeRange pointShape, ShapeRange otherPointShape) { return pointShape.Parts .Any(part => otherPointShape.Parts .Any(oPart => part .Any(v1 => oPart .Any(v2 => v1 == v2)))); }
/// <summary> /// This cycles through all the vertices, which are stored as {X1, Y1, X2, Y2...Xn, Yn} and tests /// if any of those vertices falls within the polygon shape. /// </summary> /// <param name="polygonShape"></param> /// <param name="otherShape"></param> /// <returns></returns> public static bool ContainsVertex(ShapeRange polygonShape, ShapeRange otherShape) { foreach (PartRange otherPart in otherShape.Parts) { if (ContainsVertex(polygonShape, otherPart)) { return true; } } return false; }
/// <summary> /// Returns true if any segment from a line or polygon shape interesect any segments from the line or polygon shape. /// </summary> /// <param name="lineShape">A Line or Polygon shape</param> /// <param name="otherShape">Another line or polygon shape</param> /// <returns></returns> public static bool SegmentsIntersect(ShapeRange lineShape, ShapeRange otherShape) { if (lineShape.FeatureType != FeatureType.Line && lineShape.FeatureType != FeatureType.Polygon) throw new ArgumentException("Expected lineShape to be a line or polygon feature type, got " + lineShape.FeatureType); if (otherShape.FeatureType != FeatureType.Line && otherShape.FeatureType != FeatureType.Polygon) throw new ArgumentException("Expected otherShape to be a line or polygon feature type, got " + otherShape.FeatureType); return lineShape.Parts .Any(part => part.Segments .Any(segment => otherShape.Parts .Any(oPart => oPart.Segments .Any(segment.Intersects)))); }
/// <summary> /// Calculates the intersection of a polygon shape without relying on the NTS geometry /// </summary> /// <param name="pointShape"></param> /// <param name="otherShape"></param> /// <returns></returns> public static bool Intersects(ShapeRange pointShape, ShapeRange otherShape) { if (pointShape.FeatureType != FeatureType.Point && pointShape.FeatureType != FeatureType.MultiPoint) { throw new ArgumentException("The First parameter should be a point shape, but it was featuretype:" + pointShape.FeatureType); } // Implmented in PolygonShape or line shape. Point shape is the simplest and just looks for overlapping coordinates. if (otherShape.FeatureType == FeatureType.Polygon || otherShape.FeatureType == FeatureType.Line) { return otherShape.Intersects(pointShape); } // For two point-type shapes, test if any vertex from one overlaps with any vertex of the other within Epsilon tollerance return VerticesIntersect(pointShape, otherShape); }
/// <summary> /// Tests to see if any vertices from the other shape are coincident with a segment from this line shape. /// </summary> /// <param name="lineShape"></param> /// <param name="otherShape"></param> /// <returns></returns> public static bool IntersectsVertex(ShapeRange lineShape, ShapeRange otherShape) { foreach (PartRange part in lineShape.Parts) { foreach (Segment segment in part.Segments) { foreach (PartRange oPart in otherShape.Parts) { foreach (Vertex v in oPart) { if (segment.IntersectsVertex(v)) return true; } } } } return false; }
/// <summary> /// Returns true if any vertices overlap /// </summary> /// <returns></returns> public static bool VerticesIntersect(ShapeRange pointShape, ShapeRange otherPointShape) { foreach (PartRange part in pointShape.Parts) { foreach (PartRange oPart in otherPointShape.Parts) { foreach (Vertex v1 in part) { foreach (Vertex v2 in oPart) { if (v1 == v2) return true; } } } } return false; }
/// <summary> /// Calculates the intersection of a polygon shape without relying on the NTS geometry. /// </summary> /// <param name="polygonShape">Polygon shape used for calculation.</param> /// <param name="otherShape">Other shape of any feature type.</param> /// <returns>True, if the given shape ranges intersect.</returns> public static bool Intersects(ShapeRange polygonShape, ShapeRange otherShape) { if (polygonShape.FeatureType != FeatureType.Polygon) { throw new ArgumentException(string.Format(DataStrings.Shape_WrongFeatureType, "polygonShape", "polygon", polygonShape.FeatureType)); } // Extents will have already been tested by this point. // Regardless of what the other shapetype is, a coordinate inside this polygon indicates a hit. if (ContainsVertex(polygonShape, otherShape)) { return(true); } // There is no other way for this polygon to intersect the other points if (otherShape.FeatureType == FeatureType.Point || otherShape.FeatureType == FeatureType.MultiPoint) { return(false); } // For lines and polygons, if any segment intersects any segment of this polygon shape, return true. // This is essentially looking for the rare case of crossing in and crossing out again with // no actual points inside this polygonShape. if (LineShape.SegmentsIntersect(polygonShape, otherShape)) { return(true); } // There is no other way for this polygon to intersect the other lines if (otherShape.FeatureType == FeatureType.Line) { return(false); } // If the other polygon completely contains this polygon every other test has returned false, // but this will test for that case with only a single point. Vertex v = polygonShape.First(); ShapeRange onlyFirstPoint = new(v); return(ContainsVertex(otherShape, onlyFirstPoint)); }
/// <summary> /// Tests to see if any vertices from the other shape are coincident with a segment from this line shape. /// </summary> /// <param name="lineShape">LineShape used for calculation.</param> /// <param name="otherShape">Other shape of any feature type.</param> /// <returns>True, if the shapes intersect.</returns> public static bool IntersectsVertex(ShapeRange lineShape, ShapeRange otherShape) { foreach (PartRange part in lineShape.Parts) { foreach (Segment segment in part.Segments) { foreach (PartRange oPart in otherShape.Parts) { foreach (Vertex v in oPart) { if (segment.IntersectsVertex(v)) { return(true); } } } } } return(false); }
private static void ReadMultiPoint(Stream data, FeatureSetPack results) { int count = ReadInt32(data); ShapeRange sr = new ShapeRange(FeatureType.MultiPoint); PartRange prt = new PartRange(FeatureType.MultiPoint) { NumVertices = count }; sr.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); } results.Add(vertices, sr); }
/// <summary> /// Returns true if any vertices overlap. /// </summary> /// <param name="pointShape">First point shape used for checking.</param> /// <param name="otherPointShape">Second point shape used for checking.</param> /// <returns>True if any vertices overlap.</returns> public static bool VerticesIntersect(ShapeRange pointShape, ShapeRange otherPointShape) { foreach (PartRange part in pointShape.Parts) { foreach (PartRange oPart in otherPointShape.Parts) { foreach (Vertex v1 in part) { foreach (Vertex v2 in oPart) { if (v1 == v2) { return(true); } } } } } return(false); }
/// <summary> /// Creates a new multi-part polygon shape. Winding order should control holes. /// </summary> /// <param name="allParts">The list of all the lists of coordinates.</param> /// <returns>A new Multi-Polygon Shape.</returns> public Shape CreateMultiPolygonFromCoordinates(IEnumerable <IEnumerable <Coordinate> > allParts) { Shape shp = new Shape(FeatureType.Polygon); List <Coordinate> allCoords = new List <Coordinate>(); List <int> offsets = new List <int>(); List <int> counts = new List <int>(); int count = 0; int numParts = 0; foreach (var part in allParts) { int coordinatecount = 0; foreach (Coordinate c in part) { allCoords.Add(c); count++; coordinatecount++; } offsets.Add(count); counts.Add(coordinatecount); numParts++; } shp.Vertices = new double[allCoords.Count * 2]; for (int i = 0; i < allCoords.Count; i++) { shp.Vertices[i * 2] = allCoords[i].X; shp.Vertices[i * 2 + 1] = allCoords[i].Y; } ShapeRange result = new ShapeRange(FeatureType.Polygon); for (int i = 0; i < numParts; i++) { PartRange prt = new PartRange(shp.Vertices, 0, offsets[i], FeatureType.Polygon); prt.NumVertices = counts[i]; result.Parts.Add(prt); } return(shp); }
/// <summary> /// Creates a shape based on the specified geometry. This shape will be standing alone, /// all by itself. The attributes will be null. /// </summary> /// <param name="geometry">The geometry to create a shape from.</param> public Shape(IGeometry geometry) { if (Equals(geometry, null)) { throw new ArgumentNullException("geometry"); } IList <Coordinate> coords = geometry.Coordinates; _vertices = new double[geometry.NumPoints * 2]; _z = new double[geometry.NumPoints]; _m = new double[geometry.NumPoints]; for (int i = 0; i < coords.Count; i++) { Coordinate c = coords[i]; _vertices[i * 2] = c.X; _vertices[i * 2 + 1] = c.Y; _z[i] = c.Z; _m[i] = c.M; } _shapeRange = ShapeRangeFromGeometry(geometry, _vertices, 0); }
/// <summary> /// Creates a new multi-part polygon shape. Winding order should control holes. /// </summary> /// <param name="allParts">The list of all the lists of coordinates.</param> /// <returns>A new Multi-Polygon Shape.</returns> public Shape CreateMultiPolygonFromCoordinates(IEnumerable<IEnumerable<Coordinate>> allParts) { Shape shp = new Shape(FeatureType.Polygon); List<Coordinate> allCoords = new List<Coordinate>(); List<int> offsets = new List<int>(); List<int> counts = new List<int>(); int count = 0; int numParts = 0; foreach (List<Coordinate> part in allParts) { int coordinatecount = 0; foreach (Coordinate c in part) { allCoords.Add(c); count++; coordinatecount++; } offsets.Add(count); counts.Add(coordinatecount); numParts++; } shp.Vertices = new double[allCoords.Count * 2]; for (int i = 0; i < allCoords.Count; i++) { shp.Vertices[i * 2] = allCoords[i].X; shp.Vertices[i * 2 + 1] = allCoords[i].Y; } ShapeRange result = new ShapeRange(FeatureType.Polygon); for (int i = 0; i < numParts; i++) { PartRange prt = new PartRange(shp.Vertices, 0, offsets[i], FeatureType.Polygon); prt.NumVertices = counts[i]; result.Parts.Add(prt); } return shp; }
/// <summary> /// Creates a point shape from a coordinate. /// </summary> /// <param name="coord">Coordinate used for creating the point shape.</param> public Shape(Coordinate coord) { if (Equals(coord, null)) { throw new ArgumentNullException("coord"); } if (!double.IsNaN(coord.Z)) { _z = new[] { coord.Z }; } if (!double.IsNaN(coord.M)) { _m = new[] { coord.M }; } _shapeRange = new ShapeRange(FeatureType.Point); _vertices = new[] { coord.X, coord.Y }; _shapeRange.Parts.Add(new PartRange(_vertices, 0, 0, FeatureType.Point) { NumVertices = 1 }); _shapeRange.Extent = new Extent(coord.X, coord.Y, coord.X, coord.Y); }
/// <summary> /// Adds the shape. Assumes that the "part" indices are created with a 0 base, and the number of /// vertices is specified. The start range of each part will be updated with the new shape range. /// The vertices array itself iwll be updated during hte stop editing step. /// </summary> /// <param name="shapeVertices"></param> /// <param name="shape"></param> public void Add(double[] shapeVertices, ShapeRange shape) { if (shape.FeatureType == FeatureType.Point || shape.FeatureType == FeatureType.MultiPoint) { _pointVertices.Add(shapeVertices); shape.StartIndex = _pointLength / 2; // point offset, not double offset Points.ShapeIndices.Add(shape); _pointLength += shapeVertices.Length; } if (shape.FeatureType == FeatureType.Line) { _lineVertices.Add(shapeVertices); shape.StartIndex = _lineLength / 2; // point offset Lines.ShapeIndices.Add(shape); _lineLength += shapeVertices.Length; } if (shape.FeatureType == FeatureType.Polygon) { _polygonVertices.Add(shapeVertices); shape.StartIndex = _polygonLength / 2; // point offset Polygons.ShapeIndices.Add(shape); _polygonLength += shapeVertices.Length / 2; } }
/// <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">Progress handler</param> private void FillPoints(string fileName, IProgressHandler progressHandler) { // Check to ensure the fileName is not null if (fileName == null) { throw new NullReferenceException(DataStrings.ArgumentNull_S.Replace("%S", "fileName")); } if (File.Exists(fileName) == false) { throw new FileNotFoundException(DataStrings.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. var header = new ShapefileHeader(fileName); Extent = header.ToExtent(); // Check to ensure that the fileName is the correct shape type if (header.ShapeType != ShapeType.Point && header.ShapeType != ShapeType.PointM && header.ShapeType != ShapeType.PointZ) { throw new ApplicationException(DataStrings.FileNotPoints_S.Replace("%S", fileName)); } if (new FileInfo(fileName).Length == 100) { // the file is empty so we are done reading return; } var numShapes = shapeHeaders.Count; double[] m = null; double[] z = null; var vert = new double[2 * numShapes]; // X,Y if (header.ShapeType == ShapeType.PointM || header.ShapeType == ShapeType.PointZ) { m = new double[numShapes]; } if (header.ShapeType == ShapeType.PointZ) { z = new double[numShapes]; } var progressMeter = new ProgressMeter(progressHandler, "Reading from " + Path.GetFileName(fileName)) { StepPercent = 5 }; using (var reader = new FileStream(fileName, FileMode.Open, FileAccess.Read)) { for (var shp = 0; shp < numShapes; shp++) { progressMeter.CurrentPercent = (int)(shp * 100.0 / numShapes); reader.Seek(shapeHeaders[shp].ByteOffset, SeekOrigin.Begin); var recordNumber = reader.ReadInt32(Endian.BigEndian); Debug.Assert(recordNumber == shp + 1); var contentLen = reader.ReadInt32(Endian.BigEndian); Debug.Assert(contentLen == shapeHeaders[shp].ContentLength); var shapeType = (ShapeType)reader.ReadInt32(); if (shapeType == ShapeType.NullShape) { if (m != null) { m[shp] = double.MinValue; } goto fin; } // Read X var ind = 4; vert[shp * 2] = reader.ReadDouble(); ind += 8; // Read Y vert[shp * 2 + 1] = reader.ReadDouble(); ind += 8; // Read Z if (z != null) { z[shp] = reader.ReadDouble(); ind += 8; } // Read M if (m != null) { if (shapeHeaders[shp].ByteLength <= ind) { m[shp] = double.MinValue; } else { m[shp] = reader.ReadDouble(); ind += 8; } } fin: var shape = new ShapeRange(FeatureType.Point) { RecordNumber = recordNumber, StartIndex = shp, ContentLength = shapeHeaders[shp].ContentLength, NumPoints = 1, NumParts = 1 }; ShapeIndices.Add(shape); var part = new PartRange(vert, shp, 0, FeatureType.Point) { NumVertices = 1 }; shape.Parts.Add(part); shape.Extent = new Extent(new[] { vert[shp * 2], vert[shp * 2 + 1], vert[shp * 2], vert[shp * 2 + 1] }); } } Vertex = vert; M = m; Z = z; progressMeter.Reset(); }
/// <inheritdoc/> public virtual List<IFeature> Select(Extent region, out Extent affectedRegion) { var result = new List<IFeature>(); affectedRegion = new Extent(); if (IndexMode) { var aoi = new ShapeRange(region); var shapes = ShapeIndices; for (var shp = 0; shp < shapes.Count; shp++) { if (!shapes[shp].Intersects(aoi)) { continue; } var feature = GetFeature(shp); affectedRegion.ExpandToInclude(feature.Envelope.ToExtent()); result.Add(feature); } } else { foreach (var feature in Features) { if (!region.Intersects(feature.Envelope) || !feature.Intersects(region.ToEnvelope())) { continue; } result.Add(feature); affectedRegion.ExpandToInclude(feature.Envelope.ToExtent()); } } return result; }
// 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(DataStrings.ArgumentNull_S.Replace("%S", fileName)); } if (File.Exists(fileName) == false) { throw new FileNotFoundException(DataStrings.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 != ShapeType.Polygon && header.ShapeType != ShapeType.PolygonM && header.ShapeType != ShapeType.PolygonZ) { throw new ArgumentException(DataStrings.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); // TO DO: replace with a normal reader. We no longer need Buffered Binary reader as // the buffer can be set on the underlying file stream. BufferedBinaryReader bbReader = new 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[] allBounds = new byte[numShapes * 32]; // probably all will be in one block, but use a byteBlock just in case. ByteBlock allParts = new ByteBlock(BLOCKSIZE); ByteBlock allCoords = new ByteBlock(BLOCKSIZE); bool isM = (header.ShapeType == ShapeType.PolygonM || header.ShapeType == ShapeType.PolygonZ); bool isZ = (header.ShapeType == ShapeType.PolygonZ); 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(FeatureType.Polygon); //------------------------------------ shape.RecordNumber = bbReader.ReadInt32(false); // Byte 0 Record Integer 1 Big shape.ContentLength = bbReader.ReadInt32(false); // Byte 4 Length Integer 1 Big // Setting shape type also controls extent class type. shape.ShapeType = (ShapeType)bbReader.ReadInt32(); // Byte 8 Type Integer 1 Little shape.StartIndex = pointOffset; if (shape.ShapeType == ShapeType.NullShape) { continue; } shape.Extent.MinX = bbReader.ReadDouble(); shape.Extent.MinY = bbReader.ReadDouble(); shape.Extent.MaxX = bbReader.ReadDouble(); shape.Extent.MaxY = bbReader.ReadDouble(); shape.NumParts = bbReader.ReadInt32(); // Byte 44 #Parts Integer 1 Little shape.NumPoints = bbReader.ReadInt32(); // Byte 48 #Points Integer 1 Little partOffsets[shp] = allParts.IntOffset(); allParts.Read(shape.NumParts * 4, bbReader); allCoords.Read(shape.NumPoints * 16, bbReader); pointOffset += shape.NumPoints; if (header.ShapeType == ShapeType.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) { IExtentM mExt = (IExtentM)shape.Extent; mExt.MinM = bbReader.ReadDouble(); mExt.MaxM = bbReader.ReadDouble(); if (allM != null) { allM.Read(shape.NumPoints * 8, bbReader); } } } if (header.ShapeType == ShapeType.PolygonZ) { bool hasM = shape.ContentLength * 2 > 60 + 4 * shape.NumParts + 24 * shape.NumPoints; IExtentZ zExt = (IExtentZ)shape.Extent; zExt.MinZ = bbReader.ReadDouble(); zExt.MaxZ = 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) { IExtentM mExt = (IExtentM)shape.Extent; mExt.MinM = bbReader.ReadDouble(); mExt.MaxM = 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", shapes.Count); for (int shp = 0; shp < shapes.Count; 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], FeatureType.Polygon); partR.NumVertices = count; shape.Parts.Add(partR); } ProgressMeter.CurrentValue = shp; } ProgressMeter.Reset(); }
/// <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(DataStrings.ArgumentNull_S.Replace("%S", fileName)); } if (File.Exists(fileName) == false) { throw new FileNotFoundException(DataStrings.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); MyExtent = header.ToExtent(); // Check to ensure that the fileName is the correct shape type if (header.ShapeType != ShapeType.Point && header.ShapeType != ShapeType.PointM && header.ShapeType != ShapeType.PointZ) { throw new ApplicationException(DataStrings.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. BufferedBinaryReader bbReader = new 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 == ShapeType.PointM || header.ShapeType == ShapeType.PointZ) { isM = true; } if (header.ShapeType == ShapeType.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); bbReader.ReadInt32(); // Skip ShapeType. Null shapes may break this. //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(FeatureType.Point) { StartIndex = shp, ContentLength = 8, NumPoints = 1, 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, FeatureType.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(); }
/// <summary> /// Creates a shape based on the specified feature. This shape will be standing alone, /// all by itself. The fieldnames and field types will be null. /// </summary> /// <param name="feature"></param> public Shape(IFeature feature) { if (Equals(feature, null)) throw new ArgumentNullException("feature"); if (feature.NumPoints == 0) throw new ArgumentOutOfRangeException("The IFeature.NumPoints of the parameter feature must be greater than 0."); _attributes = feature.DataRow.ItemArray; IList<Coordinate> coords = feature.Coordinates; _vertices = new double[feature.NumPoints * 2]; _z = new double[feature.NumPoints]; _m = new double[feature.NumPoints]; for (int i = 0; i < coords.Count; i++) { Coordinate c = coords[i]; _vertices[i * 2] = c.X; _vertices[i * 2 + 1] = c.Y; _z[i] = c.Z; _m[i] = c.M; } _shapeRange = ShapeRangeFromFeature(feature, _vertices); }
// X Y MultiPoints // --------------------------------------------------------- // 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 44 NumPoints Integer 1 Little // Byte 48 Points Point NumPoints Little // X Y M MultiPoints // --------------------------------------------------------- // 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 (Xmin - Ymax) Double 4 Little // Byte 44 NumPoints Integer 1 Little // Byte 48 Points Point NumPoints Little // Byte X* Mmin Double 1 Little // Byte X+8* Mmax Double 1 Little // Byte X+16* Marray Double NumPoints Little // X = 48 + (16 * NumPoints) // * = optional // X Y Z M MultiPoints // --------------------------------------------------------- // 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 48 Points Point NumPoints Little // Byte X Zmin Double 1 Little // Byte X+8 Zmax Double 1 Little // Byte X+16 Zarray Double NumPoints Little // Byte Y* Mmin Double 1 Little // Byte Y+8* Mmax Double 1 Little // Byte Y+16* Marray Double NumPoints Little // X = 48 + (16 * NumPoints) // Y = X + 16 + (8 * NumPoints) // * = optional /// <summary> /// Obtains a typed list of MultiPoint structures with double values associated with the various coordinates. /// </summary> /// <param name="fileName">Name of the file that gets loaded.</param> /// <param name="progressHandler">Progress handler</param> private void FillPoints(string fileName, IProgressHandler progressHandler) { // Check whether file is empty or not all parameters are set correctly. if (!CanBeRead(fileName, this, ShapeType.MultiPoint, ShapeType.MultiPointM, ShapeType.MultiPointZ)) { return; } // Reading the headers gives us an easier way to track the number of shapes and their overall length etc. List <ShapeHeader> shapeHeaders = ReadIndexFile(fileName); int numShapes = shapeHeaders.Count; bool isM = Header.ShapeType == ShapeType.MultiPointZ || Header.ShapeType == ShapeType.MultiPointM; bool isZ = Header.ShapeType == ShapeType.MultiPointZ; int totalPointsCount = 0; int totalPartsCount = 0; var shapeIndices = new List <ShapeRange>(numShapes); var progressMeter = new ProgressMeter(progressHandler, "Reading from " + Path.GetFileName(fileName)) { StepPercent = 5 }; using (var reader = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read, 65536)) { var boundsBytes = new byte[4 * 8]; var bounds = new double[4]; for (int shp = 0; shp < numShapes; shp++) { progressMeter.CurrentPercent = (int)(shp * 50.0 / numShapes); // Read from the index file because some deleted records might still exist in the .shp file. long offset = shapeHeaders[shp].ByteOffset; reader.Seek(offset, SeekOrigin.Begin); var shape = new ShapeRange(FeatureType.MultiPoint, CoordinateType) { RecordNumber = reader.ReadInt32(Endian.BigEndian), ContentLength = reader.ReadInt32(Endian.BigEndian), ShapeType = (ShapeType)reader.ReadInt32(), StartIndex = totalPointsCount, NumParts = 1 }; Debug.Assert(shape.RecordNumber == shp + 1, "shape.RecordNumber == shp + 1"); if (shape.ShapeType != ShapeType.NullShape) { // Bounds reader.Read(boundsBytes, 0, boundsBytes.Length); Buffer.BlockCopy(boundsBytes, 0, bounds, 0, boundsBytes.Length); shape.Extent.MinX = bounds[0]; shape.Extent.MinY = bounds[1]; shape.Extent.MaxX = bounds[2]; shape.Extent.MaxY = bounds[3]; //// Num Parts totalPartsCount += 1; // Num Points shape.NumPoints = reader.ReadInt32(); totalPointsCount += shape.NumPoints; } shapeIndices.Add(shape); } var vert = new double[totalPointsCount * 2]; var vertInd = 0; var parts = new int[totalPartsCount]; int mArrayInd = 0, zArrayInd = 0; double[] mArray = null, zArray = null; if (isM) { mArray = new double[totalPointsCount]; } if (isZ) { zArray = new double[totalPointsCount]; } int partsOffset = 0; for (int shp = 0; shp < numShapes; shp++) { progressMeter.CurrentPercent = (int)(50 + shp * 50.0 / numShapes); var shape = shapeIndices[shp]; if (shape.ShapeType == ShapeType.NullShape) { continue; } reader.Seek(shapeHeaders[shp].ByteOffset, SeekOrigin.Begin); reader.Seek(3 * 4 + 32 + 4, SeekOrigin.Current); // Skip first bytes (Record Number, Content Length, Shapetype + BoundingBox + NumPoints) // Read points var pointsBytes = reader.ReadBytes(8 * 2 * shape.NumPoints); // Numpoints * Point (X(8) + Y(8)) Buffer.BlockCopy(pointsBytes, 0, vert, vertInd, pointsBytes.Length); vertInd += 8 * 2 * shape.NumPoints; // Fill parts shape.Parts.Capacity = shape.NumParts; for (int p = 0; p < shape.NumParts; p++) { int endIndex = shape.NumPoints + shape.StartIndex; int startIndex = parts[partsOffset + p] + shape.StartIndex; if (p < shape.NumParts - 1) { endIndex = parts[partsOffset + p + 1] + shape.StartIndex; } int count = endIndex - startIndex; var part = new PartRange(vert, shape.StartIndex, parts[partsOffset + p], FeatureType.MultiPoint) { NumVertices = count }; shape.Parts.Add(part); } partsOffset += shape.NumParts; // Fill M and Z arrays switch (Header.ShapeType) { case ShapeType.MultiPointM: if (shape.ContentLength * 2 > 44 + 4 * shape.NumParts + 16 * shape.NumPoints) { var mExt = (IExtentM)shape.Extent; mExt.MinM = reader.ReadDouble(); mExt.MaxM = reader.ReadDouble(); var mBytes = reader.ReadBytes(8 * shape.NumPoints); Buffer.BlockCopy(mBytes, 0, mArray, mArrayInd, mBytes.Length); mArrayInd += 8 * shape.NumPoints; } break; case ShapeType.MultiPointZ: var zExt = (IExtentZ)shape.Extent; zExt.MinZ = reader.ReadDouble(); zExt.MaxZ = reader.ReadDouble(); var zBytes = reader.ReadBytes(8 * shape.NumPoints); Buffer.BlockCopy(zBytes, 0, zArray, zArrayInd, zBytes.Length); zArrayInd += 8 * shape.NumPoints; // 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 > 60 + 4 * shape.NumParts + 24 * shape.NumPoints) { goto case ShapeType.MultiPointM; } break; } } if (isM) { M = mArray; } if (isZ) { Z = zArray; } ShapeIndices = shapeIndices; Vertex = vert; } progressMeter.Reset(); }
private void FillLines(string fileName, IProgressHandler progressHandler) { // Check to ensure the fileName is not null if (fileName == null) { throw new NullReferenceException(DataStrings.ArgumentNull_S.Replace("%S", "fileName")); } if (File.Exists(fileName) == false) { throw new FileNotFoundException(DataStrings.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 != ShapeType.PolyLine && header.ShapeType != ShapeType.PolyLineM && header.ShapeType != ShapeType.PolyLineZ) { throw new ArgumentException(DataStrings.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. BufferedBinaryReader bbReader = new BufferedBinaryReader(fileName, progressHandler); if (bbReader.FileLength == 100) { // We have reached the end of the file so we can close the file 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[] allBounds = new byte[numShapes * 32]; // probably all will be in one block, but use a byteBlock just in case. ByteBlock allParts = new ByteBlock(BLOCKSIZE); ByteBlock allCoords = new ByteBlock(BLOCKSIZE); bool isM = (header.ShapeType == ShapeType.PolyLineM || header.ShapeType == ShapeType.PolyLineZ); bool isZ = (header.ShapeType == ShapeType.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); ShapeRange shape = new ShapeRange(FeatureType.Line) { RecordNumber = bbReader.ReadInt32(false), ContentLength = bbReader.ReadInt32(false), ShapeType = (ShapeType)bbReader.ReadInt32(), StartIndex = pointOffset }; if (shape.ShapeType == ShapeType.NullShape) { goto fin; } bbReader.Read(allBounds, shp * 32, 32); shape.NumParts = bbReader.ReadInt32(); // Byte 44 NumParts Integer 1 Little shape.NumPoints = bbReader.ReadInt32(); // Byte 48 NumPoints Integer 1 Little partOffsets[shp] = allParts.IntOffset(); allParts.Read(shape.NumParts * 4, bbReader); allCoords.Read(shape.NumPoints * 16, bbReader); pointOffset += shape.NumPoints; if (header.ShapeType == ShapeType.PolyLineM) { // 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) { //mMin = bbReader.ReadDouble(); //mMax = bbReader.ReadDouble(); bbReader.Seek(16, SeekOrigin.Current); if (allM != null) { allM.Read(shape.NumPoints * 8, bbReader); } } } if (header.ShapeType == ShapeType.PolyLineZ) { bool hasM = shape.ContentLength * 2 > 60 + 4 * shape.NumParts + 24 * shape.NumPoints; IExtentZ zExt = (IExtentZ)shape.Extent; zExt.MinZ = bbReader.ReadDouble(); zExt.MaxZ = bbReader.ReadDouble(); 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. IExtentM mExt = (IExtentM)shape.Extent; if (hasM) { mExt.MinM = bbReader.ReadDouble(); mExt.MaxM = 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. fin: 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(); 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) { int prt = parts[offset + part + 1]; endIndex = prt + shape.StartIndex; } int count = endIndex - startIndex; PartRange partR = new PartRange(vert, shape.StartIndex, parts[offset + part], FeatureType.Line) { NumVertices = count }; shape.Parts.Add(partR); } } bbReader.Dispose(); }
/// <inheritdocs/> protected override Shape GetShapeAtIndex(FileStream fs, ShapefileIndexFile shx, ShapefileHeader header, int shp, IEnvelope envelope) { // Read from the index file because some deleted records // might still exist in the .shp file. long offset = (shx.Shapes[shp].ByteOffset); fs.Seek(offset, SeekOrigin.Begin); Shape myShape = new Shape(); // Position Value Type Number Byte Order ShapeRange shape = new ShapeRange(FeatureType.Point); //-------------------------------------------------------------------- shape.RecordNumber = fs.ReadInt32(Endian.BigEndian); // Byte 0 Record Number Integer 1 Big shape.ContentLength = fs.ReadInt32(Endian.BigEndian); // Byte 4 Content Length Integer 1 Big ShapeType shapeType = (ShapeType)fs.ReadInt32(); // Byte 8 Shape Type Integer 1 Little if (shapeType == ShapeType.NullShape) { return(null); } double[] vertices = fs.ReadDouble(2); double x = vertices[0], y = vertices[1]; // Don't add this shape to the result if (envelope != null) { if (!envelope.Contains(new Coordinate(x, y))) { return(null); } } shape.StartIndex = 0; shape.NumParts = 1; shape.NumPoints = 1; shape.ShapeType = shapeType; shape.Extent = new Extent(x, y, x, y); myShape.Range = shape; myShape.Vertices = vertices; if (header.ShapeType == ShapeType.PointM) { myShape.M = fs.ReadDouble(1); myShape.MinM = myShape.MaxM = myShape.M[0]; shape.Extent = new ExtentM(shape.Extent, myShape.MinM, myShape.MaxM); } else if (header.ShapeType == ShapeType.PointZ) { // For Z shapefiles, the Z part is not optional. myShape.Z = fs.ReadDouble(1); myShape.MinZ = myShape.MaxZ = myShape.Z[0]; myShape.M = fs.ReadDouble(1); myShape.MinM = myShape.MaxM = myShape.M[0]; shape.Extent = new ExtentMZ(shape.Extent.MinX, shape.Extent.MinY, myShape.MinM, myShape.MinZ, shape.Extent.MaxX, shape.Extent.MaxY, myShape.MaxM, myShape.MaxZ); } PartRange partR = new PartRange(myShape.Vertices, 0, 0, FeatureType.Point) { NumVertices = 1 }; shape.Parts.Add(partR); myShape.Range = shape; return(myShape); }
/// <summary> /// Initializes a new instance of the <see cref="Shape"/> class where the shaperange exists and has a type specified. /// </summary> /// <param name="featureType">Feature type of the shape range.</param> public Shape(FeatureType featureType) { Range = new ShapeRange(featureType); }
/// <summary> /// Creates a new shape type where the shaperange exists and has a type specified. /// </summary> /// <param name="type"></param> public Shape(FeatureType type) { _shapeRange = new ShapeRange(type); }
internal static void BuildLineString(GraphicsPath path, double[] vertices, ShapeRange shpx, MapArgs args, Rectangle clipRect) { double minX = args.MinX; double maxY = args.MaxY; double dx = args.Dx; double dy = args.Dy; for (int prt = 0; prt < shpx.Parts.Count; prt++) { PartRange prtx = shpx.Parts[prt]; int start = prtx.StartIndex; int end = prtx.EndIndex; List<double[]> points = new List<double[]>(); for (int i = start; i <= end; i++) { double[] pt = new double[2]; pt[X] = (vertices[i * 2] - minX) * dx; pt[Y] = (maxY - vertices[i * 2 + 1]) * dy; points.Add(pt); } List<List<double[]>> multiLinestrings; if (!shpx.Extent.Within(args.GeographicExtents)) { multiLinestrings = CohenSutherland.ClipLinestring(points, clipRect.Left, clipRect.Top, clipRect.Right, clipRect.Bottom); } else { multiLinestrings = new List<List<double[]>>(); multiLinestrings.Add(points); } foreach (List<double[]> linestring in multiLinestrings) { List<Point> intPoints = DuplicationPreventer.Clean(linestring); if (intPoints.Count < 2) { points.Clear(); continue; } path.StartFigure(); Point[] pointArray = intPoints.ToArray(); path.AddLines(pointArray); } } }
/// <inheritdocs/> protected override Shape GetShapeAtIndex(FileStream fs, ShapefileIndexFile shx, ShapefileHeader header, int shp, IEnvelope envelope) { // Read from the index file because some deleted records // might still exist in the .shp file. long offset = (shx.Shapes[shp].ByteOffset); fs.Seek(offset, SeekOrigin.Begin); Shape myShape = new Shape(); // Position Value Type Number Byte Order ShapeRange shape = new ShapeRange(FeatureType.Line); //-------------------------------------------------------------------- shape.RecordNumber = fs.ReadInt32(Endian.BigEndian); // Byte 0 Record Number Integer 1 Big shape.ContentLength = fs.ReadInt32(Endian.BigEndian); // Byte 4 Content Length Integer 1 Big shape.ShapeType = (ShapeType)fs.ReadInt32(); // Byte 8 Shape Type Integer 1 Little shape.StartIndex = 0; if (shape.ShapeType == ShapeType.NullShape) { return null; } myShape.Range = shape; //bbReader.Read(allBounds, shp*32, 32); double xMin = fs.ReadDouble(); // Byte 12 Xmin Double 1 Little double yMin = fs.ReadDouble(); // Byte 20 Ymin Double 1 Little double xMax = fs.ReadDouble(); // Byte 28 Xmax Double 1 Little double yMax = fs.ReadDouble(); // Byte 36 Ymax Double 1 Little shape.Extent = new Extent(xMin, yMin, xMax, yMax); // Don't add this shape to the result if (envelope != null) { if (!myShape.Range.Extent.Intersects(envelope)) { return null; } } shape.NumParts = fs.ReadInt32(); // Byte 44 NumParts Integer 1 Little //feature.NumPoints = bbReader.ReadInt32(); // Byte 48 NumPoints Integer 1 Little shape.NumPoints = fs.ReadInt32(); // Create an envelope from the extents box in the file. //feature.Envelope = new Envelope(xMin, xMax, yMin, yMax); int[] partIndices = fs.ReadInt32(shape.NumParts); myShape.Vertices = fs.ReadDouble(shape.NumPoints * 2); if (header.ShapeType == ShapeType.PolyLineM) { // 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) { myShape.MinM = fs.ReadDouble(); myShape.MaxM = fs.ReadDouble(); myShape.M = fs.ReadDouble(shape.NumPoints); shape.Extent = new ExtentM(shape.Extent, myShape.MinM, myShape.MaxM); } } else if (header.ShapeType == ShapeType.PolyLineZ) { bool hasM = shape.ContentLength * 2 > 60 + 4 * shape.NumParts + 24 * shape.NumPoints; myShape.MinZ = fs.ReadDouble(); myShape.MaxZ = fs.ReadDouble(); // For Z shapefiles, the Z part is not optional. myShape.Z = fs.ReadDouble(shape.NumPoints); // 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) { myShape.MinM = fs.ReadDouble(); myShape.MaxM = fs.ReadDouble(); myShape.M = fs.ReadDouble(shape.NumPoints); shape.Extent = new ExtentMZ(shape.Extent.MinX, shape.Extent.MinY, myShape.MinM, myShape.MinZ, shape.Extent.MaxX, shape.Extent.MaxY, myShape.MaxM, myShape.MaxZ); } else shape.Extent = new ExtentMZ(shape.Extent.MinX, shape.Extent.MinY, double.MaxValue, myShape.MinZ, shape.Extent.MaxX, shape.Extent.MaxY, double.MinValue, myShape.MaxZ); } myShape.Range = shape; for (int part = 0; part < shape.NumParts; part++) { int partOff = partIndices[part]; int pointCount = shape.NumPoints - partOff; if (part < shape.NumParts - 1) { pointCount = partIndices[part + 1] - partOff; } PartRange partR = new PartRange(myShape.Vertices, 0, partOff, FeatureType.Line) { NumVertices = pointCount }; shape.Parts.Add(partR); } return myShape; }
/// <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(DataStrings.ArgumentNull_S.Replace("%S", fileName)); } if (File.Exists(fileName) == false) { throw new FileNotFoundException(DataStrings.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); MyExtent = header.ToExtent(); // Check to ensure that the fileName is the correct shape type if (header.ShapeType != ShapeType.Point && header.ShapeType != ShapeType.PointM && header.ShapeType != ShapeType.PointZ) { throw new ApplicationException(DataStrings.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. BufferedBinaryReader bbReader = new 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 == ShapeType.PointM || header.ShapeType == ShapeType.PointZ) { isM = true; } if (header.ShapeType == ShapeType.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); bbReader.ReadInt32(); // Skip ShapeType. Null shapes may break this. //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(FeatureType.Point) { StartIndex = shp, ContentLength = 8, NumPoints = 1, 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, FeatureType.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(); }
/// <summary> /// Creates a new shape type where the shaperange exists and has a type specified /// </summary> /// <param name="type"></param> public Shape(FeatureType type) { _shapeRange = new ShapeRange(type); }
/// <summary> /// For each coordinate in the other part, if it falls in the extent of this polygon, a /// ray crossing test is used for point in polygon testing. If it is not in the extent, /// it is skipped. /// </summary> /// <param name="polygonShape">The part of the polygon to analyze polygon</param> /// <param name="otherPart">The other part</param> /// <returns>Boolean, true if any coordinate falls inside the polygon</returns> private static bool ContainsVertex(ShapeRange polygonShape, PartRange otherPart) { // Create an extent for faster checking in most cases Extent ext = polygonShape.Extent; foreach (Vertex point in otherPart) { // This extent check shortcut should help speed things up for large polygon parts if (!ext.Intersects(point)) continue; // Imagine a ray on the horizontal starting from point.X -> infinity. (In practice this can be ext.XMax) // Count the intersections of segments with that line. If the resulting count is odd, the point is inside. Segment ray = new Segment(point.X, point.Y, ext.MaxX, point.Y); int[] numCrosses = new int[polygonShape.NumParts]; // A cross is a complete cross. Coincident doesn't count because it is either 0 or 2 crosses. int totalCrosses = 0; int iPart = 0; foreach (PartRange ring in polygonShape.Parts) { foreach (Segment segment in ring.Segments) { if (segment.IntersectionCount(ray) != 1) continue; numCrosses[iPart]++; totalCrosses++; } iPart++; } // If we didn't actually have any polygons we cant intersect with anything if (polygonShape.NumParts < 1) return false; // For shapes with only one part, we don't need to test part-containment. if (polygonShape.NumParts == 1 && totalCrosses % 2 == 1) return true; // This used to check to see if totalCrosses == 1, but now checks to see if totalCrosses is an odd number. // This change was made to solve the issue described in HD Issue 8593 (http://hydrodesktop.codeplex.com/workitem/8593). if (totalCrosses % 2 == 1) return true; totalCrosses = 0; for (iPart = 0; iPart < numCrosses.Length; iPart++) { int count = numCrosses[iPart]; // If this part does not contain the point, don't bother trying to figure out if the part is a hole or not. if (count % 2 == 0) continue; // If this particular part is a hole, subtract the total crosses by 1, otherwise add one. // This takes time, so we want to do this as few times as possible. if (polygonShape.Parts[iPart].IsHole()) { totalCrosses--; } else { totalCrosses++; } } return totalCrosses > 0; } return false; }
/// <summary> /// Creates a shape based on the specified geometry. This shape will be standing alone, /// all by itself. The attributes will be null. /// </summary> /// <param name="geometry">The geometry to create a shape from.</param> public Shape(IGeometry geometry) { if (Equals(geometry, null)) throw new ArgumentNullException("geometry"); IList<Coordinate> coords = geometry.Coordinates; _vertices = new double[geometry.NumPoints * 2]; _z = new double[geometry.NumPoints]; _m = new double[geometry.NumPoints]; for (int i = 0; i < coords.Count; i++) { Coordinate c = coords[i]; _vertices[i * 2] = c.X; _vertices[i * 2 + 1] = c.Y; _z[i] = c.Z; _m[i] = c.M; } _shapeRange = ShapeRangeFromGeometry(geometry, _vertices, 0); }
/// <inheritdoc/> public virtual List<IFeature> Select(Extent region, out Extent affectedRegion) { List<IFeature> result = new List<IFeature>(); if (IndexMode) { ShapeRange aoi = new ShapeRange(region); Extent affected = new Extent(); List<ShapeRange> shapes = ShapeIndices; if (shapes != null) { //ProgressMeter = new ProgressMeter(ProgressHandler, "Selecting shapes", shapes.Count); for (int shp = 0; shp < shapes.Count; shp++) { //ProgressMeter.Next(); if (!shapes[shp].Intersects(aoi)) { continue; } IFeature f = GetFeature(shp); affected.ExpandToInclude(shapes[shp].Extent); result.Add(f); } //ProgressMeter.Reset(); } affectedRegion = affected; return result; } affectedRegion = new Extent(); bool useProgress = (Features.Count > 10000); //ProgressMeter = new ProgressMeter(ProgressHandler, "Selecting Features", Features.Count); foreach (IFeature feature in Features) { //if (useProgress) // ProgressMeter.Next(); if (!region.Intersects(feature.Envelope)) { continue; } if (!feature.Intersects(region.ToEnvelope())) { continue; } result.Add(feature); affectedRegion.ExpandToInclude(feature.Envelope.ToExtent()); } //if (useProgress) // ProgressMeter.Reset(); return result; }
public Shape(ShapeRange shapeRange) { Range = shapeRange; }
/// <summary> /// Appends the specified polygon to the graphics path. /// </summary> private static void BuildPolygon(double[] vertices, ShapeRange shpx, GraphicsPath borderPath, MapArgs args, SoutherlandHodgman shClip) { double minX = args.MinX; double maxY = args.MaxY; double dx = args.Dx; double dy = args.Dy; for (int prt = 0; prt < shpx.Parts.Count; prt++) { PartRange prtx = shpx.Parts[prt]; int start = prtx.StartIndex; int end = prtx.EndIndex; var points = new List<double[]>(end - start + 1); for (int i = start; i <= end; i++) { var pt = new[] { (vertices[i*2] - minX)*dx, (maxY - vertices[i*2 + 1])*dy }; points.Add(pt); } if (null != shClip) { points = shClip.Clip(points); } var intPoints = DuplicationPreventer.Clean(points).ToArray(); if (intPoints.Length < 2) { continue; } borderPath.StartFigure(); borderPath.AddLines(intPoints); } }
// X Y Points: 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 1 Integer 1 Little // Byte 12 X Double 1 Little // Byte 20 Y Double 1 Little // X Y M Points: Total Length = 36 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 21 Integer 1 Little // Byte 12 X Double 1 Little // Byte 20 Y Double 1 Little // Byte 28 M Double 1 Little // X Y Z M Points: 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 11 Integer 1 Little // Byte 12 X Double 1 Little // Byte 20 Y Double 1 Little // Byte 28 Z Double 1 Little // Byte 36 M Double 1 Little /// <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">Progress handler</param> private void FillPoints(string fileName, IProgressHandler progressHandler) { if (!CanBeRead(fileName, this, ShapeType.Point, ShapeType.PointM, ShapeType.PointZ)) { return; } // Reading the headers gives us an easier way to track the number of shapes and their overall length etc. List <ShapeHeader> shapeHeaders = ReadIndexFile(fileName); var numShapes = shapeHeaders.Count; var shapeIndices = new List <ShapeRange>(numShapes); int totalPointsCount = 0; var progressMeter = new ProgressMeter(progressHandler, "Reading from " + Path.GetFileName(fileName)) { StepPercent = 5 }; using (var reader = new FileStream(fileName, FileMode.Open, FileAccess.Read)) { for (var shp = 0; shp < numShapes; shp++) { progressMeter.CurrentPercent = (int)(shp * 50.0 / numShapes); reader.Seek(shapeHeaders[shp].ByteOffset, SeekOrigin.Begin); var shape = new ShapeRange(FeatureType.Point, CoordinateType) { RecordNumber = reader.ReadInt32(Endian.BigEndian), ContentLength = reader.ReadInt32(Endian.BigEndian), StartIndex = totalPointsCount, ShapeType = (ShapeType)reader.ReadInt32() }; Debug.Assert(shape.RecordNumber == shp + 1, "The record number should equal " + shp + 1); Debug.Assert(shape.ContentLength == shapeHeaders[shp].ContentLength, "The shapes content length should equals the shapeHeaders content length."); if (shape.ShapeType == ShapeType.NullShape) { shape.NumPoints = 0; shape.NumParts = 0; } else { totalPointsCount += 1; shape.NumPoints = 1; shape.NumParts = 1; } shapeIndices.Add(shape); } double[] m = null; double[] z = null; var vert = new double[2 * totalPointsCount]; // X,Y if (Header.ShapeType == ShapeType.PointM || Header.ShapeType == ShapeType.PointZ) { m = new double[totalPointsCount]; } if (Header.ShapeType == ShapeType.PointZ) { z = new double[totalPointsCount]; } int i = 0; for (var shp = 0; shp < numShapes; shp++) { progressMeter.CurrentPercent = (int)(50 + (shp * 50.0 / numShapes)); var shape = shapeIndices[shp]; if (shape.ShapeType == ShapeType.NullShape) { continue; } reader.Seek(shapeHeaders[shp].ByteOffset, SeekOrigin.Begin); reader.Seek(3 * 4, SeekOrigin.Current); // Skip first bytes (Record Number, Content Length, Shapetype) // Read X var ind = 4; vert[i * 2] = reader.ReadDouble(); ind += 8; // Read Y vert[(i * 2) + 1] = reader.ReadDouble(); ind += 8; // Read Z if (z != null) { z[i] = reader.ReadDouble(); ind += 8; } // Read M if (m != null) { if (shapeHeaders[shp].ByteLength <= ind) { m[i] = double.MinValue; } else { m[i] = reader.ReadDouble(); } } var part = new PartRange(vert, shape.StartIndex, 0, FeatureType.Point) { NumVertices = 1 }; shape.Parts.Add(part); shape.Extent = new Extent(new[] { vert[i * 2], vert[(i * 2) + 1], vert[i * 2], vert[(i * 2) + 1] }); i++; } Vertex = vert; M = m; Z = z; ShapeIndices = shapeIndices; } progressMeter.Reset(); }
/// <summary> /// Loads the shapes from the given file into the given shapefile. /// </summary> /// <param name="fileName">Name of the file whose shapes should get loaded.</param> /// <param name="progressHandler">ProgressHandler that shows the progress.</param> /// <param name="shapefile">Shapefile the shapes are loaded into.</param> /// <param name="featureType">FeatureType that should be inside the file.</param> /// <exception cref="ArgumentNullException">Throws an ArgumentNullException, if the shapefile is null.</exception> /// <exception cref="ArgumentException">Throws an ArgumentException, if the FeatureType is Line but the files doesn't contain lines or the FeatureType is Polygon and the file doesn't contain polygons.</exception> /// <exception cref="NotSupportedException">Throws a NotSupportedException, if a FeatureType other than Line or Polygon is passed.</exception> /// <exception cref="FileNotFoundException">Throws a FileNotFoundException, if the file whith the path from fileName doesn't exist.</exception> /// <exception cref="NullReferenceException">Throws a NullReferenceException, if the fileName is null.</exception> internal static void FillLines(string fileName, IProgressHandler progressHandler, Shapefile shapefile, FeatureType featureType) { // Check to ensure that the fileName is the correct shape type switch (featureType) { case FeatureType.Line: if (!CanBeRead(fileName, shapefile, ShapeType.PolyLine, ShapeType.PolyLineM, ShapeType.PolyLineZ)) { return; } break; case FeatureType.Polygon: if (!CanBeRead(fileName, shapefile, ShapeType.Polygon, ShapeType.PolygonM, ShapeType.PolygonZ)) { return; } break; default: throw new NotSupportedException(DataStrings.ShapeType0NotSupported); } // Reading the headers gives us an easier way to track the number of shapes and their overall length etc. var header = shapefile.Header; var shapeHeaders = shapefile.ReadIndexFile(fileName); int numShapes = shapeHeaders.Count; bool isM = false, isZ = false; switch (header.ShapeType) { case ShapeType.PolyLineM: case ShapeType.PolygonM: isM = true; break; case ShapeType.PolyLineZ: case ShapeType.PolygonZ: isZ = true; isM = true; break; } int totalPointsCount = 0; int totalPartsCount = 0; var shapeIndices = new List <ShapeRange>(numShapes); var progressMeter = new ProgressMeter(progressHandler, "Reading from " + Path.GetFileName(fileName)) { StepPercent = 5 }; using (var reader = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read, 65536)) { var boundsBytes = new byte[4 * 8]; var bounds = new double[4]; for (int shp = 0; shp < numShapes; shp++) { progressMeter.CurrentPercent = (int)(shp * 50.0 / numShapes); // Read from the index file because some deleted records // might still exist in the .shp file. long offset = shapeHeaders[shp].ByteOffset; reader.Seek(offset, SeekOrigin.Begin); var shape = new ShapeRange(featureType, shapefile.CoordinateType) { RecordNumber = reader.ReadInt32(Endian.BigEndian), ContentLength = reader.ReadInt32(Endian.BigEndian), ShapeType = (ShapeType)reader.ReadInt32(), StartIndex = totalPointsCount }; Debug.Assert(shape.RecordNumber == shp + 1, "The shapes record number should equal" + shp + 1); if (shape.ShapeType != ShapeType.NullShape) { // Bounds reader.Read(boundsBytes, 0, boundsBytes.Length); Buffer.BlockCopy(boundsBytes, 0, bounds, 0, boundsBytes.Length); shape.Extent.MinX = bounds[0]; shape.Extent.MinY = bounds[1]; shape.Extent.MaxX = bounds[2]; shape.Extent.MaxY = bounds[3]; // Num Parts shape.NumParts = reader.ReadInt32(); totalPartsCount += shape.NumParts; // Num Points shape.NumPoints = reader.ReadInt32(); totalPointsCount += shape.NumPoints; } shapeIndices.Add(shape); } var vert = new double[totalPointsCount * 2]; var vertInd = 0; var parts = new int[totalPartsCount]; var partsInd = 0; double[] mArray = null, zArray = null; if (isM) { mArray = new double[totalPointsCount]; } int mArrayInd = 0; if (isZ) { zArray = new double[totalPointsCount]; } int zArrayInd = 0; int partsOffset = 0; for (int shp = 0; shp < numShapes; shp++) { progressMeter.CurrentPercent = (int)(50 + (shp * 50.0 / numShapes)); var shape = shapeIndices[shp]; if (shape.ShapeType == ShapeType.NullShape) { continue; } reader.Seek(shapeHeaders[shp].ByteOffset, SeekOrigin.Begin); reader.Seek((3 * 4) + 32 + (2 * 4), SeekOrigin.Current); // Skip first bytes (Record Number, Content Length, Shapetype + BoundingBox + NumParts, NumPoints) // Read parts var partsBytes = reader.ReadBytes(4 * shape.NumParts); // Numparts * Integer(4) = existing Parts Buffer.BlockCopy(partsBytes, 0, parts, partsInd, partsBytes.Length); partsInd += 4 * shape.NumParts; // Read points var pointsBytes = reader.ReadBytes(8 * 2 * shape.NumPoints); // Numpoints * Point (X(8) + Y(8)) Buffer.BlockCopy(pointsBytes, 0, vert, vertInd, pointsBytes.Length); vertInd += 8 * 2 * shape.NumPoints; // Fill parts shape.Parts.Capacity = shape.NumParts; for (int part = 0; part < shape.NumParts; part++) { int endIndex = shape.NumPoints + shape.StartIndex; int startIndex = parts[partsOffset + part] + shape.StartIndex; if (part < shape.NumParts - 1) { endIndex = parts[partsOffset + part + 1] + shape.StartIndex; } int count = endIndex - startIndex; var partR = new PartRange(vert, shape.StartIndex, parts[partsOffset + part], featureType) { NumVertices = count }; shape.Parts.Add(partR); } partsOffset += shape.NumParts; // Fill M and Z arrays switch (header.ShapeType) { case ShapeType.PolyLineM: case ShapeType.PolygonM: if (shape.ContentLength * 2 > 44 + (4 * shape.NumParts) + (16 * shape.NumPoints)) { var mExt = (IExtentM)shape.Extent; mExt.MinM = reader.ReadDouble(); mExt.MaxM = reader.ReadDouble(); var mBytes = reader.ReadBytes(8 * shape.NumPoints); Buffer.BlockCopy(mBytes, 0, mArray, mArrayInd, mBytes.Length); mArrayInd += 8 * shape.NumPoints; } break; case ShapeType.PolyLineZ: case ShapeType.PolygonZ: var zExt = (IExtentZ)shape.Extent; zExt.MinZ = reader.ReadDouble(); zExt.MaxZ = reader.ReadDouble(); var zBytes = reader.ReadBytes(8 * shape.NumPoints); Buffer.BlockCopy(zBytes, 0, zArray, zArrayInd, zBytes.Length); zArrayInd += 8 * shape.NumPoints; // 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 > 60 + (4 * shape.NumParts) + (24 * shape.NumPoints)) { goto case ShapeType.PolyLineM; } break; } } if (isM) { shapefile.M = mArray; } if (isZ) { shapefile.Z = zArray; } shapefile.ShapeIndices = shapeIndices; shapefile.Vertex = vert; } progressMeter.Reset(); }
// 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(DataStrings.ArgumentNull_S.Replace("%S", fileName)); } if (File.Exists(fileName) == false) { throw new FileNotFoundException(DataStrings.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 != ShapeType.Polygon && header.ShapeType != ShapeType.PolygonM && header.ShapeType != ShapeType.PolygonZ) { throw new ArgumentException(DataStrings.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); // TO DO: replace with a normal reader. We no longer need Buffered Binary reader as // the buffer can be set on the underlying file stream. BufferedBinaryReader bbReader = new 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[] allBounds = new byte[numShapes * 32]; // probably all will be in one block, but use a byteBlock just in case. ByteBlock allParts = new ByteBlock(BLOCKSIZE); ByteBlock allCoords = new ByteBlock(BLOCKSIZE); bool isM = (header.ShapeType == ShapeType.PolygonM || header.ShapeType == ShapeType.PolygonZ); bool isZ = (header.ShapeType == ShapeType.PolygonZ); 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(FeatureType.Polygon); //------------------------------------ shape.RecordNumber = bbReader.ReadInt32(false); // Byte 0 Record Integer 1 Big shape.ContentLength = bbReader.ReadInt32(false); // Byte 4 Length Integer 1 Big // Setting shape type also controls extent class type. shape.ShapeType = (ShapeType)bbReader.ReadInt32(); // Byte 8 Type Integer 1 Little shape.StartIndex = pointOffset; if (shape.ShapeType == ShapeType.NullShape) { continue; } shape.Extent.MinX = bbReader.ReadDouble(); shape.Extent.MinY = bbReader.ReadDouble(); shape.Extent.MaxX = bbReader.ReadDouble(); shape.Extent.MaxY = bbReader.ReadDouble(); shape.NumParts = bbReader.ReadInt32(); // Byte 44 #Parts Integer 1 Little shape.NumPoints = bbReader.ReadInt32(); // Byte 48 #Points Integer 1 Little partOffsets[shp] = allParts.IntOffset(); allParts.Read(shape.NumParts * 4, bbReader); allCoords.Read(shape.NumPoints * 16, bbReader); pointOffset += shape.NumPoints; if (header.ShapeType == ShapeType.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) { IExtentM mExt = (IExtentM)shape.Extent; mExt.MinM = bbReader.ReadDouble(); mExt.MaxM = bbReader.ReadDouble(); if (allM != null) allM.Read(shape.NumPoints * 8, bbReader); } } if (header.ShapeType == ShapeType.PolygonZ) { bool hasM = shape.ContentLength * 2 > 60 + 4 * shape.NumParts + 24 * shape.NumPoints; IExtentZ zExt = (IExtentZ)shape.Extent; zExt.MinZ = bbReader.ReadDouble(); zExt.MaxZ = 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) { IExtentM mExt = (IExtentM)shape.Extent; mExt.MinM = bbReader.ReadDouble(); mExt.MaxM = 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", shapes.Count); for (int shp = 0; shp < shapes.Count; 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], FeatureType.Polygon); partR.NumVertices = count; shape.Parts.Add(partR); } ProgressMeter.CurrentValue = shp; } ProgressMeter.Reset(); }
// 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(DataStrings.ArgumentNull_S.Replace("%S", "fileName")); } if (File.Exists(fileName) == false) { throw new FileNotFoundException(DataStrings.FileNotFound_S.Replace("%S", fileName)); } // Get the basic header information. ShapefileHeader header = new ShapefileHeader(fileName); Extent = header.ToExtent(); // Check to ensure that the fileName is the correct shape type if (header.ShapeType != ShapeType.MultiPoint && header.ShapeType != ShapeType.MultiPointM && header.ShapeType != ShapeType.MultiPointZ) { throw new ArgumentException(DataStrings.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. BufferedBinaryReader bbReader = new 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 == ShapeType.MultiPointZ || header.ShapeType == ShapeType.MultiPointM); bool isZ = (header.ShapeType == ShapeType.MultiPointZ); 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(FeatureType.MultiPoint) { RecordNumber = bbReader.ReadInt32(false), ContentLength = bbReader.ReadInt32(false), ShapeType = (ShapeType)bbReader.ReadInt32(), StartIndex = pointOffset }; //bbReader.Read(bigEndians, shp * 8, 8); if (shape.ShapeType == ShapeType.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 == ShapeType.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) { IExtentM mExt = (IExtentM)MyExtent; mExt.MinM = bbReader.ReadDouble(); mExt.MaxM = bbReader.ReadDouble(); if (allM != null) { allM.Read(shape.NumPoints * 8, bbReader); } } } if (header.ShapeType == ShapeType.MultiPointZ) { bool hasM = shape.ContentLength * 2 > 60 + 4 * shape.NumParts + 24 * shape.NumPoints; IExtentZ zExt = (IExtentZ)MyExtent; zExt.MinZ = bbReader.ReadDouble(); zExt.MaxZ = 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) { IExtentM mExt = (IExtentM)MyExtent; mExt.MinM = bbReader.ReadDouble(); mExt.MaxM = 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, FeatureType.MultiPoint) { NumVertices = count }; shape.Parts.Add(partR); } bbReader.Dispose(); }
// 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 44 NumPoints Integer 1 Little // Byte 48 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 (Xmin - Ymax) Double 4 Little // Byte 44 NumPoints Integer 1 Little // Byte 48 Points Point NumPoints Little // Byte X* Mmin Double 1 Little // Byte X+8* Mmax Double 1 Little // Byte X+16* Marray Double NumPoints Little // X = 48 + (16 * NumPoints) // * = optional // 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 48 Points Point NumPoints Little // Byte X Zmin Double 1 Little // Byte X+8 Zmax Double 1 Little // Byte X+16 Zarray Double NumPoints Little // Byte Y* Mmin Double 1 Little // Byte Y+8* Mmax Double 1 Little // Byte Y+16* Marray Double NumPoints Little // X = 48 + (16 * NumPoints) // Y = X + 16 + (8 * NumPoints) // * = optional private void FillPoints(string fileName, IProgressHandler progressHandler) { // Check to ensure the fileName is not null if (fileName == null) { throw new NullReferenceException(DataStrings.ArgumentNull_S.Replace("%S", "fileName")); } if (File.Exists(fileName) == false) { throw new FileNotFoundException(DataStrings.FileNotFound_S.Replace("%S", fileName)); } // Get the basic header information. var header = this.Header; // Check to ensure that the fileName is the correct shape type if (header.ShapeType != ShapeType.MultiPoint && header.ShapeType != ShapeType.MultiPointM && header.ShapeType != ShapeType.MultiPointZ) { throw new ArgumentException(DataStrings.FileNotLines_S.Replace("%S", fileName)); } if (new FileInfo(fileName).Length == 100) { // the file is empty so we are done reading return; } // Reading the headers gives us an easier way to track the number of shapes and their overall length etc. List <ShapeHeader> shapeHeaders = ReadIndexFile(fileName); int numShapes = shapeHeaders.Count; bool isM = (header.ShapeType == ShapeType.MultiPointZ || header.ShapeType == ShapeType.MultiPointM); bool isZ = (header.ShapeType == ShapeType.MultiPointZ); int totalPointsCount = 0; int totalPartsCount = 0; var shapeIndices = new List <ShapeRange>(numShapes); using (var reader = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read, 65536)) { var boundsBytes = new byte[4 * 8]; var bounds = new double[4]; 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); reader.Seek(offset, SeekOrigin.Begin); var shape = new ShapeRange(FeatureType.MultiPoint, CoordinateType) { RecordNumber = reader.ReadInt32(Endian.BigEndian), ContentLength = reader.ReadInt32(Endian.BigEndian), ShapeType = (ShapeType)reader.ReadInt32(), StartIndex = totalPointsCount, NumParts = 1 }; Debug.Assert(shape.RecordNumber == shp + 1); if (shape.ShapeType != ShapeType.NullShape) { // Bounds reader.Read(boundsBytes, 0, boundsBytes.Length); Buffer.BlockCopy(boundsBytes, 0, bounds, 0, boundsBytes.Length); shape.Extent.MinX = bounds[0]; shape.Extent.MinY = bounds[1]; shape.Extent.MaxX = bounds[2]; shape.Extent.MaxY = bounds[3]; //// Num Parts //shape.NumParts = reader.ReadInt32(); totalPartsCount += 1; // Num Points shape.NumPoints = reader.ReadInt32(); totalPointsCount += shape.NumPoints; } shapeIndices.Add(shape); } var vert = new double[totalPointsCount * 2]; var vertInd = 0; var parts = new int[totalPartsCount]; //var partsInd = 0; int mArrayInd = 0, zArrayInd = 0; double[] mArray = null, zArray = null; if (isM) { mArray = new double[totalPointsCount]; } if (isZ) { zArray = new double[totalPointsCount]; } int partsOffset = 0; for (int shp = 0; shp < numShapes; shp++) { var shape = shapeIndices[shp]; if (shape.ShapeType == ShapeType.NullShape) { continue; } reader.Seek(shapeHeaders[shp].ByteOffset, SeekOrigin.Begin); reader.Seek(3 * 4 + 32 + 4, SeekOrigin.Current); // Skip first bytes (Record Number, Content Length, Shapetype + BoundingBox + NumPoints) //// Read parts //var partsBytes = reader.ReadBytes(4 * shape.NumParts); //Numparts * Integer(4) = existing Parts //Buffer.BlockCopy(partsBytes, 0, parts, partsInd, partsBytes.Length); //partsInd += 4 * shape.NumParts; // Read points var pointsBytes = reader.ReadBytes(8 * 2 * shape.NumPoints); //Numpoints * Point (X(8) + Y(8)) Buffer.BlockCopy(pointsBytes, 0, vert, vertInd, pointsBytes.Length); vertInd += 8 * 2 * shape.NumPoints; // Fill parts shape.Parts.Capacity = shape.NumParts; for (int part = 0; part < shape.NumParts; part++) { int endIndex = shape.NumPoints + shape.StartIndex; int startIndex = parts[partsOffset + part] + shape.StartIndex; if (part < shape.NumParts - 1) { endIndex = parts[partsOffset + part + 1] + shape.StartIndex; } int count = endIndex - startIndex; var partR = new PartRange(vert, shape.StartIndex, parts[partsOffset + part], FeatureType.MultiPoint) { NumVertices = count }; shape.Parts.Add(partR); } partsOffset += shape.NumParts; // Fill M and Z arrays switch (header.ShapeType) { case ShapeType.MultiPointM: if (shape.ContentLength * 2 > 44 + 4 * shape.NumParts + 16 * shape.NumPoints) { var mExt = (IExtentM)shape.Extent; mExt.MinM = reader.ReadDouble(); mExt.MaxM = reader.ReadDouble(); var mBytes = reader.ReadBytes(8 * shape.NumPoints); Buffer.BlockCopy(mBytes, 0, mArray, mArrayInd, mBytes.Length); mArrayInd += 8 * shape.NumPoints; } break; case ShapeType.MultiPointZ: var zExt = (IExtentZ)shape.Extent; zExt.MinZ = reader.ReadDouble(); zExt.MaxZ = reader.ReadDouble(); var zBytes = reader.ReadBytes(8 * shape.NumPoints); Buffer.BlockCopy(zBytes, 0, zArray, zArrayInd, zBytes.Length); zArrayInd += 8 * shape.NumPoints; // 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 > 60 + 4 * shape.NumParts + 24 * shape.NumPoints) { goto case ShapeType.MultiPointM; } break; } } if (isM) { M = mArray; } if (isZ) { Z = zArray; } ShapeIndices = shapeIndices; Vertex = vert; } }
/// <summary> /// Creates a point shape from a coordinate /// </summary> /// <param name="coord"></param> public Shape(Coordinate coord) { if (Equals(coord, null)) throw new ArgumentNullException("coord"); _shapeRange = new ShapeRange(FeatureType.Point); _vertices = new double[2]; _vertices[0] = coord.X; _vertices[1] = coord.Y; if (!double.IsNaN(coord.Z)) { _z = new double[1]; _z[0] = coord.Z; } if (!double.IsNaN(coord.M)) { _z = new double[1]; _m[0] = coord.M; } const int offset = 0; PartRange part = new PartRange(_vertices, 0, offset, FeatureType.Point); part.NumVertices = 1; _shapeRange.Parts.Add(part); _shapeRange.Extent = new Extent(coord.X, coord.Y, coord.X, coord.Y); }
/// <summary> /// Occurs when the vertices are being re-calculated. /// </summary> protected virtual void OnInitializeVertices() { if (_features == null) return; int count = _features.Sum(f => f.NumPoints); _vertices = new double[count * 2]; int i = 0; foreach (IFeature f in _features) { // this should be all the coordinates, for all parts of the geometry. IList<Coordinate> coords = f.Coordinates; if (coords == null) { continue; } foreach (Coordinate c in coords) { _vertices[i * 2] = c.X; _vertices[i * 2 + 1] = c.Y; // essentially add a reference pointer to the internal array of values i++; } } // not sure, but I bet arrays a smidge faster at indexed access than lists _shapeIndices = new List<ShapeRange>(); int vIndex = 0; foreach (IFeature f in _features) { ShapeRange shx = new ShapeRange(FeatureType) { Extent = new Extent(f.Envelope) }; _shapeIndices.Add(shx); f.ShapeIndex = shx; // for simplicity in looping, there is always at least one part. // That way, the shape range can be ignored and the parts loop used instead. int shapeStart = vIndex; for (int part = 0; part < f.NumGeometries; part++) { PartRange prtx = new PartRange(_vertices, shapeStart, vIndex - shapeStart, FeatureType); IBasicPolygon bp = f.GetBasicGeometryN(part) as IBasicPolygon; if (bp != null) { // Account for the Shell prtx.NumVertices = bp.Shell.NumPoints; vIndex += bp.Shell.NumPoints; // The part range should be adjusted to no longer include the holes foreach (var hole in bp.Holes) { PartRange holex = new PartRange(_vertices, shapeStart, vIndex - shapeStart, FeatureType) { NumVertices = hole.NumPoints }; shx.Parts.Add(holex); vIndex += hole.NumPoints; } } else { int numPoints = f.GetBasicGeometryN(part).NumPoints; // This is not a polygon, so just add the number of points. vIndex += numPoints; prtx.NumVertices = numPoints; } shx.Parts.Add(prtx); } } _verticesAreValid = true; }
/// <summary> /// Creates a point shape from a vertex /// </summary> /// <param name="coord"></param> public Shape(Vertex coord) { _shapeRange = new ShapeRange(FeatureType.Point); _vertices = new double[2]; _vertices[0] = coord.X; _vertices[1] = coord.Y; const int offset = 0; PartRange part = new PartRange(_vertices, 0, offset, FeatureType.Point); part.NumVertices = 1; _shapeRange.Parts.Add(part); _shapeRange.Extent = new Extent(coord.X, coord.Y, coord.X, coord.Y); }
// 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(DataStrings.ArgumentNull_S.Replace("%S", fileName)); } if (File.Exists(fileName) == false) { throw new FileNotFoundException(DataStrings.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 != ShapeType.MultiPoint && header.ShapeType != ShapeType.MultiPointM && header.ShapeType != ShapeType.MultiPointZ) { throw new ArgumentException(DataStrings.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. BufferedBinaryReader bbReader = new 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 == ShapeType.MultiPointZ || header.ShapeType == ShapeType.MultiPointM); bool isZ = (header.ShapeType == ShapeType.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(FeatureType.MultiPoint) { RecordNumber = bbReader.ReadInt32(false), ContentLength = bbReader.ReadInt32(false), ShapeType = (ShapeType)bbReader.ReadInt32(), StartIndex = pointOffset }; //bbReader.Read(bigEndians, shp * 8, 8); if (shape.ShapeType == ShapeType.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 == ShapeType.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) { IExtentM mExt = (IExtentM)MyExtent; mExt.MinM = bbReader.ReadDouble(); mExt.MaxM = bbReader.ReadDouble(); if (allM != null) allM.Read(shape.NumPoints * 8, bbReader); } } if (header.ShapeType == ShapeType.MultiPointZ) { bool hasM = shape.ContentLength * 2 > 60 + 4 * shape.NumParts + 24 * shape.NumPoints; IExtentZ zExt = (IExtentZ)MyExtent; zExt.MinZ = bbReader.ReadDouble(); zExt.MaxZ = 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) { IExtentM mExt = (IExtentM)MyExtent; mExt.MinM = bbReader.ReadDouble(); mExt.MaxM = 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, FeatureType.MultiPoint) { NumVertices = count }; shape.Parts.Add(partR); } bbReader.Dispose(); }
/// <summary> /// Creates a clockwise polygon shape from an extent /// </summary> /// <param name="extent"></param> public Shape(Extent extent) { if (Equals(extent, null)) throw new ArgumentNullException("extent"); _shapeRange = new ShapeRange(FeatureType.Polygon); _vertices = new double[8]; double xMin = extent.MinX; double yMin = extent.MinY; double xMax = extent.MaxX; double yMax = extent.MaxY; _vertices[0] = xMin; _vertices[1] = yMax; _vertices[2] = xMax; _vertices[3] = yMax; _vertices[4] = xMax; _vertices[5] = yMin; _vertices[6] = xMin; _vertices[7] = yMin; const int offset = 0; PartRange part = new PartRange(_vertices, 0, offset, FeatureType.Polygon); part.NumVertices = 4; _shapeRange.Parts.Add(part); }
internal static void FillLines(string fileName, IProgressHandler progressHandler, Shapefile shapefile, FeatureType featureType) { // Check to ensure the fileName is not null if (fileName == null) { throw new NullReferenceException(DataStrings.ArgumentNull_S.Replace("%S", "fileName")); } if (shapefile == null) throw new ArgumentNullException("shapefile"); if (File.Exists(fileName) == false) { throw new FileNotFoundException(DataStrings.FileNotFound_S.Replace("%S", fileName)); } if (featureType != FeatureType.Line && featureType != FeatureType.Polygon) { throw new NotSupportedException(); } var header = shapefile.Header; // Check to ensure that the fileName is the correct shape type switch (featureType) { case FeatureType.Line: if (header.ShapeType != ShapeType.PolyLine && header.ShapeType != ShapeType.PolyLineM && header.ShapeType != ShapeType.PolyLineZ) { throw new ArgumentException(DataStrings.FileNotLines_S.Replace("%S", fileName)); } break; case FeatureType.Polygon: if (header.ShapeType != ShapeType.Polygon && header.ShapeType != ShapeType.PolygonM && header.ShapeType != ShapeType.PolygonZ) { throw new ArgumentException(DataStrings.FileNotLines_S.Replace("%S", fileName)); } break; } if (new FileInfo(fileName).Length == 100) { // the file is empty so we are done reading return; } // Reading the headers gives us an easier way to track the number of shapes and their overall length etc. var shapeHeaders = shapefile.ReadIndexFile(fileName); int numShapes = shapeHeaders.Count; bool isM = false, isZ = false; switch (header.ShapeType) { case ShapeType.PolyLineM: case ShapeType.PolygonM: isM = true; break; case ShapeType.PolyLineZ: case ShapeType.PolygonZ: isZ = true; isM = true; break; } int totalPointsCount = 0; int totalPartsCount = 0; var shapeIndices = new List<ShapeRange>(numShapes); var progressMeter = new ProgressMeter(progressHandler, "Reading from " + Path.GetFileName(fileName)) { StepPercent = 5 }; using (var reader = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read, 65536)) { var boundsBytes = new byte[4 * 8]; var bounds = new double[4]; for (int shp = 0; shp < numShapes; shp++) { progressMeter.CurrentPercent = (int)(shp * 50.0 / numShapes); // Read from the index file because some deleted records // might still exist in the .shp file. long offset = (shapeHeaders[shp].ByteOffset); reader.Seek(offset, SeekOrigin.Begin); var shape = new ShapeRange(featureType, shapefile.CoordinateType) { RecordNumber = reader.ReadInt32(Endian.BigEndian), ContentLength = reader.ReadInt32(Endian.BigEndian), ShapeType = (ShapeType)reader.ReadInt32(), StartIndex = totalPointsCount }; Debug.Assert(shape.RecordNumber == shp + 1); if (shape.ShapeType != ShapeType.NullShape) { // Bounds reader.Read(boundsBytes, 0, boundsBytes.Length); Buffer.BlockCopy(boundsBytes, 0, bounds, 0, boundsBytes.Length); shape.Extent.MinX = bounds[0]; shape.Extent.MinY = bounds[1]; shape.Extent.MaxX = bounds[2]; shape.Extent.MaxY = bounds[3]; // Num Parts shape.NumParts = reader.ReadInt32(); totalPartsCount += shape.NumParts; // Num Points shape.NumPoints = reader.ReadInt32(); totalPointsCount += shape.NumPoints; } shapeIndices.Add(shape); } var vert = new double[totalPointsCount * 2]; var vertInd = 0; var parts = new int[totalPartsCount]; var partsInd = 0; double[] mArray = null, zArray = null; if (isM) { mArray = new double[totalPointsCount]; } int mArrayInd = 0; if (isZ) { zArray = new double[totalPointsCount]; } int zArrayInd = 0; int partsOffset = 0; for (int shp = 0; shp < numShapes; shp++) { progressMeter.CurrentPercent = (int)(50 + shp * 50.0 / numShapes); var shape = shapeIndices[shp]; if (shape.ShapeType == ShapeType.NullShape) continue; reader.Seek(shapeHeaders[shp].ByteOffset, SeekOrigin.Begin); reader.Seek(3 * 4 + 32 + 2 * 4, SeekOrigin.Current); // Skip first bytes // Read parts var partsBytes = reader.ReadBytes(4 * shape.NumParts); Buffer.BlockCopy(partsBytes, 0, parts, partsInd, partsBytes.Length); partsInd += 4 * shape.NumParts; // Read points var pointsBytes = reader.ReadBytes(8 * 2 * shape.NumPoints); Buffer.BlockCopy(pointsBytes, 0, vert, vertInd, pointsBytes.Length); vertInd += 8 * 2 * shape.NumPoints; // Fill parts shape.Parts.Capacity = shape.NumParts; for (int part = 0; part < shape.NumParts; part++) { int endIndex = shape.NumPoints + shape.StartIndex; int startIndex = parts[partsOffset + part] + shape.StartIndex; if (part < shape.NumParts - 1) { endIndex = parts[partsOffset + part + 1] + shape.StartIndex; } int count = endIndex - startIndex; var partR = new PartRange(vert, shape.StartIndex, parts[partsOffset + part], featureType) { NumVertices = count }; shape.Parts.Add(partR); } partsOffset += shape.NumParts; // Fill M and Z arrays switch (header.ShapeType) { case ShapeType.PolyLineM: case ShapeType.PolygonM: if (shape.ContentLength * 2 > 44 + 4 * shape.NumParts + 16 * shape.NumPoints) { var mExt = (IExtentM)shape.Extent; mExt.MinM = reader.ReadDouble(); mExt.MaxM = reader.ReadDouble(); var mBytes = reader.ReadBytes(8 * shape.NumPoints); Buffer.BlockCopy(mBytes, 0, mArray, mArrayInd, mBytes.Length); mArrayInd += 8 * shape.NumPoints; } break; case ShapeType.PolyLineZ: case ShapeType.PolygonZ: var zExt = (IExtentZ)shape.Extent; zExt.MinZ = reader.ReadDouble(); zExt.MaxZ = reader.ReadDouble(); var zBytes = reader.ReadBytes(8 * shape.NumPoints); Buffer.BlockCopy(zBytes, 0, zArray, zArrayInd, zBytes.Length); zArrayInd += 8 * shape.NumPoints; // 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 > 60 + 4 * shape.NumParts + 24 * shape.NumPoints) { goto case ShapeType.PolyLineM; } break; } } if (isM) shapefile.M = mArray; if (isZ) shapefile.Z = zArray; shapefile.ShapeIndices = shapeIndices; shapefile.Vertex = vert; } progressMeter.Reset(); }
/// <summary> /// Creates a clockwise polygon shape from an envelope /// </summary> /// <param name="envelope"></param> public Shape(IEnvelope envelope) { if (Equals(envelope, null)) throw new ArgumentNullException("envelope"); _shapeRange = new ShapeRange(FeatureType.Polygon); _vertices = new double[8]; double xMin = envelope.Minimum.X; double yMin = envelope.Minimum.Y; double xMax = envelope.Maximum.X; double yMax = envelope.Maximum.Y; _vertices[0] = xMin; _vertices[1] = yMax; _vertices[2] = xMax; _vertices[3] = yMax; _vertices[4] = xMax; _vertices[5] = yMin; _vertices[6] = xMin; _vertices[7] = yMin; const int offset = 0; PartRange part = new PartRange(_vertices, 0, offset, FeatureType.Polygon); part.NumVertices = 4; _shapeRange.Parts.Add(part); }
private static void FastBuildLine(GraphicsPath graphPath, double[] vertices, ShapeRange shpx, double minX, double maxY, double dx, double dy) { for (int prt = 0; prt < shpx.Parts.Count; prt++) { PartRange prtx = shpx.Parts[prt]; int start = prtx.StartIndex; int end = prtx.EndIndex; List<Point> partPoints = new List<Point>(); Point previousPoint = new Point(); for (int i = start; i <= end; i++) { if (double.IsNaN(vertices[i * 2]) || double.IsNaN(vertices[i * 2 + 1])) continue; Point pt = new Point(); pt.X = Convert.ToInt32((vertices[i * 2] - minX) * dx); pt.Y = Convert.ToInt32((maxY - vertices[i * 2 + 1]) * dy); if (i == 0 || (pt.X != previousPoint.X || pt.Y != previousPoint.Y)) { // Don't add the same point twice partPoints.Add(pt); previousPoint = pt; } } if (partPoints.Count < 2) continue; // we need two distinct points to make a line graphPath.StartFigure(); graphPath.AddLines(partPoints.ToArray()); } }
/// <summary> /// Create a ShapeRange from a Geometry to use in constructing a Shape /// </summary> /// <param name="geometry"></param> /// <param name="vertices"></param> /// <param name="offset">offset into vertices array where this feature starts</param> /// <returns></returns> public static ShapeRange ShapeRangeFromGeometry(IBasicGeometry geometry, double[] vertices, int offset) { var featureType = geometry.FeatureType; ShapeRange shx = new ShapeRange(featureType) { Extent = new Extent(geometry.Envelope) }; int vIndex = offset / 2; shx.Parts = new List<PartRange>(); int shapeStart = vIndex; for (int part = 0; part < geometry.NumGeometries; part++) { PartRange prtx = new PartRange(vertices, shapeStart, vIndex - shapeStart, featureType); IBasicPolygon bp = geometry.GetBasicGeometryN(part) as IBasicPolygon; if (bp != null) { // Account for the Shell prtx.NumVertices = bp.Shell.NumPoints; vIndex += bp.Shell.NumPoints; // The part range should be adjusted to no longer include the holes foreach (var hole in bp.Holes) { PartRange holex = new PartRange(vertices, shapeStart, vIndex - shapeStart, featureType) { NumVertices = hole.NumPoints }; shx.Parts.Add(holex); vIndex += hole.NumPoints; } } else { int numPoints = geometry.GetBasicGeometryN(part).NumPoints; // This is not a polygon, so just add the number of points. vIndex += numPoints; prtx.NumVertices = numPoints; } shx.Parts.Add(prtx); } return shx; }
/// <inheritdocs/> protected override Shape GetShapeAtIndex(FileStream fs, ShapefileIndexFile shx, ShapefileHeader header, int shp, IEnvelope envelope) { // Read from the index file because some deleted records // might still exist in the .shp file. long offset = (shx.Shapes[shp].ByteOffset); fs.Seek(offset, SeekOrigin.Begin); // Position Value Type Number Byte Order ShapeRange shape = new ShapeRange(FeatureType.Polygon); //-------------------------------------------------------------------- shape.RecordNumber = fs.ReadInt32(Endian.BigEndian); // Byte 0 Record Number Integer 1 Big shape.ContentLength = fs.ReadInt32(Endian.BigEndian); // Byte 4 Content Length Integer 1 Big shape.ShapeType = (ShapeType)fs.ReadInt32(); // Byte 8 Shape Type Integer 1 Little shape.StartIndex = 0; if (shape.ShapeType == ShapeType.NullShape) { return(null); } Shape myShape = new Shape(); myShape.Range = shape; //bbReader.Read(allBounds, shp*32, 32); double xMin = fs.ReadDouble(); // Byte 12 Xmin Double 1 Little double yMin = fs.ReadDouble(); // Byte 20 Ymin Double 1 Little double xMax = fs.ReadDouble(); // Byte 28 Xmax Double 1 Little double yMax = fs.ReadDouble(); // Byte 36 Ymax Double 1 Little shape.Extent = new Extent(xMin, yMin, xMax, yMax); // Don't add this shape to the result if (envelope != null) { if (!myShape.Range.Extent.Intersects(envelope)) { return(null); } } shape.NumParts = fs.ReadInt32(); // Byte 44 NumParts Integer 1 Little //feature.NumPoints = bbReader.ReadInt32(); // Byte 48 NumPoints Integer 1 Little shape.NumPoints = fs.ReadInt32(); // Create an envelope from the extents box in the file. //feature.Envelope = new Envelope(xMin, xMax, yMin, yMax); int[] partIndices = fs.ReadInt32(shape.NumParts); myShape.Vertices = fs.ReadDouble(shape.NumPoints * 2); if (header.ShapeType == ShapeType.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) { myShape.MinM = fs.ReadDouble(); myShape.MaxM = fs.ReadDouble(); myShape.M = fs.ReadDouble(shape.NumPoints); shape.Extent = new ExtentM(shape.Extent, myShape.MinM, myShape.MaxM); } } else if (header.ShapeType == ShapeType.PolygonZ) { bool hasM = shape.ContentLength * 2 > 60 + 4 * shape.NumParts + 24 * shape.NumPoints; myShape.MinZ = fs.ReadDouble(); myShape.MaxZ = fs.ReadDouble(); // For Z shapefiles, the Z part is not optional. myShape.Z = fs.ReadDouble(shape.NumPoints); // 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) { myShape.MinM = fs.ReadDouble(); myShape.MaxM = fs.ReadDouble(); myShape.M = fs.ReadDouble(shape.NumPoints); shape.Extent = new ExtentMZ(shape.Extent.MinX, shape.Extent.MinY, myShape.MinM, myShape.MinZ, shape.Extent.MaxX, shape.Extent.MaxY, myShape.MaxM, myShape.MaxZ); } else { shape.Extent = new ExtentMZ(shape.Extent.MinX, shape.Extent.MinY, double.MaxValue, myShape.MinZ, shape.Extent.MaxX, shape.Extent.MaxY, double.MinValue, myShape.MaxZ); } } myShape.Range = shape; for (int part = 0; part < shape.NumParts; part++) { int partOff = partIndices[part]; int pointCount = shape.NumPoints - partOff; if (part < shape.NumParts - 1) { pointCount = partIndices[part + 1] - partOff; } PartRange partR = new PartRange(myShape.Vertices, 0, partOff, FeatureType.Polygon) { NumVertices = pointCount }; shape.Parts.Add(partR); } return(myShape); }
/// <summary> /// For each coordinate in the other part, if it falls in the extent of this polygon, a /// ray crossing test is used for point in polygon testing. If it is not in the extent, /// it is skipped. /// </summary> /// <param name="polygonShape">The part of the polygon to analyze polygon</param> /// <param name="otherPart">The other part</param> /// <returns>Boolean, true if any coordinate falls inside the polygon</returns> private static bool ContainsVertex(ShapeRange polygonShape, PartRange otherPart) { // Create an extent for faster checking in most cases Extent ext = polygonShape.Extent; foreach (Vertex point in otherPart) { // This extent check shortcut should help speed things up for large polygon parts if (!ext.Intersects(point)) { continue; } // Imagine a ray on the horizontal starting from point.X -> infinity. (In practice this can be ext.XMax) // Count the intersections of segments with that line. If the resulting count is odd, the point is inside. Segment ray = new Segment(point.X, point.Y, ext.MaxX, point.Y); int[] numCrosses = new int[polygonShape.NumParts]; // A cross is a complete cross. Coincident doesn't count because it is either 0 or 2 crosses. int totalCrosses = 0; int iPart = 0; foreach (PartRange ring in polygonShape.Parts) { foreach (Segment segment in ring.Segments) { if (segment.IntersectionCount(ray) != 1) { continue; } numCrosses[iPart]++; totalCrosses++; } iPart++; } // If we didn't actually have any polygons we cant intersect with anything if (polygonShape.NumParts < 1) { return(false); } // For shapes with only one part, we don't need to test part-containment. if (polygonShape.NumParts == 1 && totalCrosses % 2 == 1) { return(true); } // This used to check to see if totalCrosses == 1, but now checks to see if totalCrosses is an odd number. // This change was made to solve the issue described in HD Issue 8593 (http://hydrodesktop.codeplex.com/workitem/8593). if (totalCrosses % 2 == 1) { return(true); } totalCrosses = 0; for (iPart = 0; iPart < numCrosses.Length; iPart++) { int count = numCrosses[iPart]; // If this part does not contain the point, don't bother trying to figure out if the part is a hole or not. if (count % 2 == 0) { continue; } // If this particular part is a hole, subtract the total crosses by 1, otherwise add one. // This takes time, so we want to do this as few times as possible. if (polygonShape.Parts[iPart].IsHole()) { totalCrosses--; } else { totalCrosses++; } } return(totalCrosses > 0); } return(false); }