/// <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 != FeatureTypes.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 == FeatureTypes.Point || otherShape.FeatureType ==FeatureTypes.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 == FeatureTypes.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(polygonShape.First()); return ContainsVertex(otherShape, onlyFirstPoint); }
/// <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) { _shapeRange = new ShapeRange(feature.FeatureType); _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; } int offset = 0; for(int ig = 0; ig < feature.NumGeometries; ig++) { IBasicGeometry g = feature.GetBasicGeometryN(ig); PartRange prt = new PartRange(_vertices, 0, offset, feature.FeatureType); _shapeRange.Parts.Add(prt); offset += g.NumPoints; } }
/// <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 != FeatureTypes.Line) { throw new ArgumentException("The First parameter should be a point shape, but it was featuretype:" + lineShape.FeatureType); } // Implmented in PolygonShape if(otherShape.FeatureType == FeatureTypes.Polygon) { return otherShape.Intersects(lineShape); } // Test for point on a line if(otherShape.FeatureType == FeatureTypes.Point || otherShape.FeatureType == FeatureTypes.MultiPoint) { return IntersectsVertex(lineShape, otherShape); } // There is no other way for this polygon to intersect the other points if (otherShape.FeatureType == FeatureTypes.Point || otherShape.FeatureType == FeatureTypes.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> /// 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 != FeatureTypes.Point && pointShape.FeatureType != FeatureTypes.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 == FeatureTypes.Polygon || otherShape.FeatureType == FeatureTypes.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> /// 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> /// Creates a new shape type where the shaperange exists and has a type specified /// </summary> /// <param name="type"></param> public Shape(FeatureTypes type) { _shapeRange = new ShapeRange(type); }
private static void ReadMultiPolygon(Stream data, FeatureSetPack results) { int numPolygons = ReadInt32(data); ShapeRange lShp = new ShapeRange(FeatureTypes.Polygon); List<double[]> rings = new List<double[]>(); int partOffset = 0; for (int iPoly = 0; iPoly < numPolygons; iPoly++ ) { data.Seek(5, SeekOrigin.Current); // endian and geometry type int numRings = ReadInt32(data); for (int iRing = 0; iRing < numRings; iRing++) { int numPoints = ReadInt32(data); // ring structures are like a linestring without the final point. double[] coords = ReadDouble(data, 2 * numPoints); if (iRing == 0) { // By shapefile standard, the shell should be clockwise if (IsCounterClockwise(coords)) ReverseCoords(coords); } else { // By shapefile standard, the holes should be counter clockwise. if (!IsCounterClockwise(coords)) ReverseCoords(coords); } PartRange lPrt = new PartRange(FeatureTypes.Polygon); lPrt.PartOffset = partOffset; lPrt.NumVertices = numPoints; lShp.Parts.Add(lPrt); partOffset += coords.Length / 2; rings.Add(coords); } } double[] allVertices = new double[partOffset * 2]; int offset = 0; foreach (double[] ring in rings) { Array.Copy(ring, 0, allVertices, offset, ring.Length); offset += ring.Length; } results.Add(allVertices, lShp); }
/// <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> /// polygonshape and the shape extent is the same as the part extent.</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. double x1 = point.X; double y1 = point.Y; double x2 = ext.XMax; Segment ray = new Segment(point.X, point.Y, ext.XMax, 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; // Regardless of number of parts or holiness, 1 crossing can only be inside. if(totalCrosses == 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> /// returns only the features that have envelopes that /// intersect with the specified envelope. If in indexMode, this uses the ShapeIndices to create /// features on demand, rather than loading all the features. It is much faster to use selectIndices /// when in index mode. /// </summary> /// <param name="region">The specified region to test for intersect with</param> /// <param name="affectedRegion">This returns the geographic extents that contains the modified contents.</param> /// <returns>A List of the IFeature elements that are contained in this region</returns> public virtual List<IFeature> Select(IEnvelope region, out IEnvelope affectedRegion) { List<IFeature> result = new List<IFeature>(); if(IndexMode) { ShapeRange aoi = new ShapeRange(new Extent(region)); IEnvelope env = region.Copy(); Extent affected = new Extent(); List<ShapeRange> shapes = ShapeIndices; if(shapes != null) { for(int shp = 0; shp < shapes.Count; shp++) { if (!shapes[shp].Intersects(aoi)) continue; IFeature f = GetFeature(shp); affected.ExpandToInclude(shapes[shp].Extent); result.Add(f); } } affectedRegion = affected.ToEnvelope(); return result; } affectedRegion = new Envelope(); foreach (IFeature feature in Features) { if (!feature.Envelope.Intersects(region)) continue; result.Add(feature); affectedRegion.ExpandToInclude(feature.Envelope); } return result; }
/// <summary> /// Obtains a typed list of ShapefilePoint structures with double values associated with the various coordinates. /// </summary> /// <param name="filename">A string filename</param> /// <param name="progressHandler">A progress indicator</param> private void FillPoints(string filename, IProgressHandler progressHandler) { // Check to ensure the filename is not null if (filename == null) { throw new NullReferenceException(MessageStrings.ArgumentNull_S.Replace("%S",filename)); } if (File.Exists(filename) == false) { throw new FileNotFoundException(MessageStrings.FileNotFound_S.Replace("%S", filename)); } // Reading the headers gives us an easier way to track the number of shapes and their overall length etc. List<ShapeHeader> shapeHeaders = ReadIndexFile(filename); // Get the basic header information. ShapefileHeader header = new ShapefileHeader(filename); Extent = new Extent(new[] { header.Xmin, header.Ymin, header.Xmax, header.Ymax }); Envelope = Extent.ToEnvelope(); // Check to ensure that the filename is the correct shape type if (header.ShapeType != ShapeTypes.Point && header.ShapeType != ShapeTypes.PointM && header.ShapeType != ShapeTypes.PointZ) { throw new ApplicationException(MessageStrings.FileNotPoints_S.Replace("%S", filename)); } // This will set up a reader so that we can read values in huge chunks, which is much faster than one value at a time. IO.BufferedBinaryReader bbReader = new IO.BufferedBinaryReader(filename, progressHandler); if (bbReader.FileLength == 100) { bbReader.Close(); // the file is empty so we are done reading return; } // Skip the shapefile header by skipping the first 100 bytes in the shapefile bbReader.Seek(100, SeekOrigin.Begin); int numShapes = shapeHeaders.Count; byte[] bigEndian = new byte[numShapes * 8]; byte[] allCoords = new byte[numShapes * 16]; bool isM = false; bool isZ = false; if(header.ShapeType == ShapeTypes.PointM || header.ShapeType == ShapeTypes.PointZ) { isM = true; } if (header.ShapeType == ShapeTypes.PointZ) { isZ = true; } byte[] allM = new byte[8]; if(isM) allM = new byte[numShapes * 8]; byte[] allZ = new byte[8]; if(isZ) allZ = new byte[numShapes * 8]; for (int shp = 0; shp < numShapes; shp++) { // Read from the index file because some deleted records // might still exist in the .shp file. long offset = (shapeHeaders[shp].ByteOffset); bbReader.Seek(offset, SeekOrigin.Begin); bbReader.Read(bigEndian, shp * 8, 8); ShapeTypes type = (ShapeTypes)bbReader.ReadInt32(); //bbReader.Seek(4, SeekOrigin.Current); bbReader.Read(allCoords, shp * 16, 16); if(isZ) { bbReader.Read(allZ, shp*8, 8); } if (isM) { bbReader.Read(allM, shp*8, 8); } ShapeRange shape = new ShapeRange(FeatureTypes.Point); shape.StartIndex = shp; shape.ContentLength = 8; shape.NumPoints = 1; shape.NumParts = 1; ShapeIndices.Add(shape); } double[] vert = new double[2 * numShapes]; Buffer.BlockCopy(allCoords, 0, vert, 0, numShapes*16); Vertex = vert; if(isM) { double[] m = new double[numShapes]; Buffer.BlockCopy(allM, 0, m, 0, numShapes * 8); M = m; } if(isZ) { double[] z = new double[numShapes]; Buffer.BlockCopy(allZ, 0, z, 0, numShapes * 8); Z = z; } for (int shp = 0; shp < numShapes; shp++) { PartRange part = new PartRange(vert, shp, 0, FeatureTypes.Point); part.NumVertices = 1; ShapeRange shape = ShapeIndices[shp]; shape.Parts.Add(part); shape.Extent = new Extent(new[] {vert[shp*2], vert[shp*2 + 1], vert[shp*2], vert[shp*2 + 1]}); } bbReader.Dispose(); }
/// <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 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 != FeatureTypes.Line && lineShape.FeatureType != FeatureTypes.Polygon) throw new ArgumentException("Expected lineShape to be a line or polygon feature type, got " + lineShape.FeatureType); if (otherShape.FeatureType != FeatureTypes.Line && otherShape.FeatureType != FeatureTypes.Polygon) throw new ArgumentException("Expected otherShape to be a line or polygon feature type, got " + otherShape.FeatureType); foreach (PartRange part in lineShape.Parts) { foreach (Segment segment in part.Segments) { foreach (PartRange oPart in otherShape.Parts) { int iSeg = 0; foreach (Segment oSegment in oPart.Segments) { iSeg++; if(iSeg == 279) { bool stop = true; } if (segment.Intersects(oSegment)) { return true; } } } } } // If the other segment is a polygon, we need to check the " return false; }
// X Y MultiPoints: Total Length = 28 Bytes // --------------------------------------------------------- // Position Value Type Number Byte Order // --------------------------------------------------------- // Byte 0 Record Number Integer 1 Big // Byte 4 Content Length Integer 1 Big // Byte 8 Shape Type 8 Integer 1 Little // Byte 12 Xmin Double 1 Little // Byte 20 Ymin Double 1 Little // Byte 28 Xmax Double 1 Little // Byte 36 Ymax Double 1 Little // Byte 48 NumPoints Integer 1 Little // Byte X Points Point NumPoints Little // X Y M MultiPoints: Total Length = 34 Bytes // --------------------------------------------------------- // Position Value Type Number Byte Order // --------------------------------------------------------- // Byte 0 Record Number Integer 1 Big // Byte 4 Content Length Integer 1 Big // Byte 8 Shape Type 28 Integer 1 Little // Byte 12 Box Double 4 Little // Byte 44 NumPoints Integer 1 Little // Byte X Points Point NumPoints Little // Byte Y* Mmin Double 1 Little // Byte Y + 8* Mmax Double 1 Little // Byte Y + 16* Marray Double NumPoints Little // X Y Z M MultiPoints: Total Length = 44 Bytes // --------------------------------------------------------- // Position Value Type Number Byte Order // --------------------------------------------------------- // Byte 0 Record Number Integer 1 Big // Byte 4 Content Length Integer 1 Big // Byte 8 Shape Type 18 Integer 1 Little // Byte 12 Box Double 4 Little // Byte 44 NumPoints Integer 1 Little // Byte X Points Point NumPoints Little // Byte Y Zmin Double 1 Little // Byte Y + 8 Zmax Double 1 Little // Byte Y + 16 Zarray Double NumPoints Little // Byte Z* Mmin Double 1 Little // Byte Z+8* Mmax Double 1 Little // Byte Z+16* Marray Double NumPoints Little private void FillPoints(string filename, IProgressHandler progressHandler) { // Check to ensure the filename is not null if (filename == null) { throw new NullReferenceException(MessageStrings.ArgumentNull_S.Replace("%S", filename)); } if (File.Exists(filename) == false) { throw new FileNotFoundException(MessageStrings.FileNotFound_S.Replace("%S", filename)); } // Get the basic header information. ShapefileHeader header = new ShapefileHeader(filename); Extent = new Extent(new[] { header.Xmin, header.Ymin, header.Xmax, header.Ymax }); // Check to ensure that the filename is the correct shape type if (header.ShapeType != ShapeTypes.MultiPoint && header.ShapeType != ShapeTypes.MultiPointM && header.ShapeType != ShapeTypes.MultiPointZ) { throw new ArgumentException(MessageStrings.FileNotLines_S.Replace("%S", filename)); } // Reading the headers gives us an easier way to track the number of shapes and their overall length etc. List<ShapeHeader> shapeHeaders = ReadIndexFile(filename); // This will set up a reader so that we can read values in huge chunks, which is much faster than one value at a time. IO.BufferedBinaryReader bbReader = new IO.BufferedBinaryReader(filename, progressHandler); if (bbReader.FileLength == 100) { // The shapefile is empty so we can simply return here bbReader.Close(); return; } // Skip the shapefile header by skipping the first 100 bytes in the shapefile bbReader.Seek(100, SeekOrigin.Begin); int numShapes = shapeHeaders.Count; byte[] bigEndians = new byte[numShapes * 8]; byte[] allBounds = new byte[numShapes * 32]; ByteBlock allCoords = new ByteBlock(BLOCKSIZE); bool isM = (header.ShapeType == ShapeTypes.MultiPointZ || header.ShapeType == ShapeTypes.MultiPointM); bool isZ = (header.ShapeType == ShapeTypes.PolyLineZ); ByteBlock allZ = null; ByteBlock allM = null; if (isZ) { allZ = new ByteBlock(BLOCKSIZE); } if (isM) { allM = new ByteBlock(BLOCKSIZE); } int pointOffset = 0; for (int shp = 0; shp < numShapes; shp++) { // Read from the index file because some deleted records // might still exist in the .shp file. long offset = (shapeHeaders[shp].ByteOffset); bbReader.Seek(offset, SeekOrigin.Begin); // time: 200 ms ShapeRange shape = new ShapeRange(FeatureTypes.MultiPoint); shape.RecordNumber = bbReader.ReadInt32(false); // Byte 0 Record Number Integer 1 Big shape.ContentLength = bbReader.ReadInt32(false); // Byte 4 Content Length Integer 1 Big //bbReader.Read(bigEndians, shp * 8, 8); shape.ShapeType = (ShapeTypes)bbReader.ReadInt32(); shape.StartIndex = pointOffset; if (shape.ShapeType == ShapeTypes.NullShape) { continue; } bbReader.Read(allBounds, shp * 32, 32); shape.NumParts = 1; shape.NumPoints = bbReader.ReadInt32(); allCoords.Read(shape.NumPoints * 16, bbReader); pointOffset += shape.NumPoints; if (header.ShapeType == ShapeTypes.MultiPointM) { // These are listed as "optional" but there isn't a good indicator of how to determine if they were added. // To handle the "optional" M values, check the contentLength for the feature. // The content length does not include the 8-byte record header and is listed in 16-bit words. if (shape.ContentLength * 2 > 44 + 4 * shape.NumParts + 16 * shape.NumPoints) { double mMin = bbReader.ReadDouble(); double mMax = bbReader.ReadDouble(); //bbReader.Seek(16, SeekOrigin.Current); if (allM != null) allM.Read(shape.NumPoints * 8, bbReader); } } if (header.ShapeType == ShapeTypes.MultiPointZ) { bool hasM = shape.ContentLength * 2 > 60 + 4 * shape.NumParts + 24 * shape.NumPoints; double zMin = bbReader.ReadDouble(); double zMax = bbReader.ReadDouble(); // For Z shapefiles, the Z part is not optional. if (allZ != null) allZ.Read(shape.NumPoints * 8, bbReader); // These are listed as "optional" but there isn't a good indicator of how to determine if they were added. // To handle the "optional" M values, check the contentLength for the feature. // The content length does not include the 8-byte record header and is listed in 16-bit words. if (hasM) { double mMin = bbReader.ReadDouble(); double mMax = bbReader.ReadDouble(); if (allM != null) allM.Read(shape.NumPoints * 8, bbReader); } } // Now that we have read all the values, create the geometries from the points and parts arrays. ShapeIndices.Add(shape); } double[] vert = allCoords.ToDoubleArray(); Vertex = vert; if (isM) M = allM.ToDoubleArray(); if (isZ) Z = allZ.ToDoubleArray(); Array.Reverse(bigEndians); List<ShapeRange> shapes = ShapeIndices; double[] bounds = new double[numShapes * 4]; Buffer.BlockCopy(allBounds, 0, bounds, 0, allBounds.Length); for (int shp = 0; shp < numShapes; shp++) { ShapeRange shape = shapes[shp]; shape.Extent = new Extent(bounds, shp * 4); int endIndex = shape.NumPoints + shape.StartIndex; int startIndex = shape.StartIndex; int count = endIndex - startIndex; PartRange partR = new PartRange(vert, shape.StartIndex, 0, FeatureTypes.MultiPoint); partR.NumVertices = count; shape.Parts.Add(partR); } GC.Collect(); bbReader.Dispose(); }
/// <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> /// Appends the specified polygon to the graphics path. /// </summary> private static void BuildPolygon(double[] vertices, ShapeRange shpx, GraphicsPath borderPath, MapArgs args, bool clip) { double minX = args.MinX; double maxY = args.MaxY; double dx = args.Dx; double dy = args.Dy; borderPath.FillMode = FillMode.Winding; 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); } if (clip) { points = SoutherlandHodgman.Clip(points); } List<System.Drawing.Point> intPoints = DuplicationPreventer.Clean(points); if (intPoints.Count < 2) { points.Clear(); continue; } borderPath.StartFigure(); System.Drawing.Point[] pointArray = intPoints.ToArray(); borderPath.AddLines(pointArray); points.Clear(); } }
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<System.Drawing.Point> partPoints = new List<System.Drawing.Point>(); System.Drawing.Point previousPoint = new System.Drawing.Point(); for (int i = start; i <= end; i++) { if (double.IsNaN(vertices[i*2]) || double.IsNaN(vertices[i * 2 + 1])) continue; System.Drawing.Point pt = new System.Drawing.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> /// This assumes that the byte order and shapetype have already been read. /// </summary> /// <param name="data"></param> /// <param name="results"></param> private static void ReadPoint(Stream data, FeatureSetPack results) { ShapeRange sr = new ShapeRange(FeatureTypes.MultiPoint); PartRange prt = new PartRange(FeatureTypes.MultiPoint); prt.NumVertices = 1; sr.Parts.Add(prt); double[] coord = ReadDouble(data, 2); results.Add(coord, sr); }
/// <summary> /// This calculations processes the intersections /// </summary> /// <param name="shape">The shape to do intersection calculations with.</param> public bool Intersects(ShapeRange shape) { // Extent check first. If the extents don't intersect, then this doesn't intersect. if (!Extent.Intersects(shape.Extent)) return false; switch (FeatureType) { case FeatureTypes.Polygon: PolygonShape.Epsilon = Epsilon; return PolygonShape.Intersects(this, shape); case FeatureTypes.Line: LineShape.Epsilon = Epsilon; return LineShape.Intersects(this, shape); case FeatureTypes.Point: PointShape.Epsilon = Epsilon; return PointShape.Intersects(this, shape); default: return false; } }
private static void ReadMultiPoint(Stream data, FeatureSetPack results) { int count = ReadInt32(data); ShapeRange sr = new ShapeRange(FeatureTypes.MultiPoint); PartRange prt = new PartRange(FeatureTypes.MultiPoint); prt.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> /// Occurs when the vertices are being re-calculated. /// </summary> protected virtual void OnInitializeVertices() { int count = 0; foreach (IFeature f in _features) { count += f.NumPoints; } _vertices = new double[count*2]; int i = 0; foreach(IFeature f in _features) { IList<Coordinate> coords = f.Coordinates; // this should be all the coordinates, for all parts of the geometry. if (coords == null) continue; foreach (Coordinate c in coords) { _vertices[i*2] = c.X; _vertices[i*2+1] = c.Y; // vertexValues.Add(c.Values); // essentially add a reference pointer to the internal array of values i++; } } //_vertices = vertexValues.ToArray(); // not sure, but I bet arrays a smidge faster at indexed access than lists _shapeIndices = new List<ShapeRange>(); int vIndex = 0; for(int shp = 0; shp < _features.Count; shp++) { IFeature f = _features[shp]; ShapeRange shx = new ShapeRange(FeatureType); shx.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. shx.Parts = new List<PartRange>(); 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); holex.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); } } // _vertices = vertexValues.ToArray(); _verticesAreValid = true; }
private static void ReadLineString(Stream data, FeatureSetPack results) { int count = ReadInt32(data); double[] coords = ReadDouble(data, 2 * count); ShapeRange lShp = new ShapeRange(FeatureTypes.Line); PartRange lPrt = new PartRange(FeatureTypes.Line); lPrt.NumVertices = count; lShp.Parts.Add(lPrt); results.Add(coords, lShp); }
private static void ReadMultiLineString(Stream data, FeatureSetPack results) { int numLineStrings = ReadInt32(data); ShapeRange shp = new ShapeRange(FeatureTypes.Line); List<double[]> strings = new List<double[]>(); int partOffset = 0; for(int iString = 0; iString < numLineStrings; iString++) { // Each of these needs to read a full WKBLineString data.Seek(5, SeekOrigin.Current); //ignore header int numPoints = ReadInt32(data); double[] coords = ReadDouble(data, 2*numPoints); PartRange lPrt = new PartRange(FeatureTypes.Line); lPrt.PartOffset = partOffset; lPrt.NumVertices = numPoints; shp.Parts.Add(lPrt); partOffset += coords.Length/2; strings.Add(coords); } double[] allVertices = new double[partOffset * 2]; int offset = 0; foreach (double[] ring in strings) { Array.Copy(ring, 0, allVertices, offset, ring.Length); offset += ring.Length; } results.Add(allVertices, shp); }
// X Y Poly Lines: Total Length = 28 Bytes // --------------------------------------------------------- // Position Value Type Number Byte Order // --------------------------------------------------------- // Byte 0 Record Number Integer 1 Big // Byte 4 Content Length Integer 1 Big // Byte 8 Shape Type 3 Integer 1 Little // Byte 12 Xmin Double 1 Little // Byte 20 Ymin Double 1 Little // Byte 28 Xmax Double 1 Little // Byte 36 Ymax Double 1 Little // Byte 44 NumParts Integer 1 Little // Byte 48 NumPoints Integer 1 Little // Byte 52 Parts Integer NumParts Little // Byte X Points Point NumPoints Little // X Y M Poly Lines: Total Length = 34 Bytes // --------------------------------------------------------- // Position Value Type Number Byte Order // --------------------------------------------------------- // Byte 0 Record Number Integer 1 Big // Byte 4 Content Length Integer 1 Big // Byte 8 Shape Type 23 Integer 1 Little // Byte 12 Box Double 4 Little // Byte 44 NumParts Integer 1 Little // Byte 48 NumPoints Integer 1 Little // Byte 52 Parts Integer NumParts Little // Byte X Points Point NumPoints Little // Byte Y* Mmin Double 1 Little // Byte Y + 8* Mmax Double 1 Little // Byte Y + 16* Marray Double NumPoints Little // X Y Z M Poly Lines: Total Length = 44 Bytes // --------------------------------------------------------- // Position Value Type Number Byte Order // --------------------------------------------------------- // Byte 0 Record Number Integer 1 Big // Byte 4 Content Length Integer 1 Big // Byte 8 Shape Type 13 Integer 1 Little // Byte 12 Box Double 4 Little // Byte 44 NumParts Integer 1 Little // Byte 48 NumPoints Integer 1 Little // Byte 52 Parts Integer NumParts Little // Byte X Points Point NumPoints Little // Byte Y Zmin Double 1 Little // Byte Y + 8 Zmax Double 1 Little // Byte Y + 16 Zarray Double NumPoints Little // Byte Z* Mmin Double 1 Little // Byte Z+8* Mmax Double 1 Little // Byte Z+16* Marray Double NumPoints Little private void FillPolygons(string filename, IProgressHandler progressHandler) { // Check to ensure the filename is not null if (filename == null) { throw new NullReferenceException(MessageStrings.ArgumentNull_S.Replace("%S", filename)); } if (File.Exists(filename) == false) { throw new FileNotFoundException(MessageStrings.FileNotFound_S.Replace("%S", filename)); } // Get the basic header information. ShapefileHeader header = new ShapefileHeader(filename); Extent = new Extent(new[]{header.Xmin, header.Ymin, header.Xmax, header.Ymax}); // Check to ensure that the filename is the correct shape type if (header.ShapeType != ShapeTypes.Polygon && header.ShapeType != ShapeTypes.PolygonM && header.ShapeType != ShapeTypes.PolygonZ) { throw new ArgumentException(MessageStrings.FileNotLines_S.Replace("%S", filename)); } // Reading the headers gives us an easier way to track the number of shapes and their overall length etc. List<ShapeHeader> shapeHeaders = ReadIndexFile(filename); // This will set up a reader so that we can read values in huge chunks, which is much faster than one value at a time. IO.BufferedBinaryReader bbReader = new IO.BufferedBinaryReader(filename, progressHandler); if (bbReader.FileLength == 100) { // The shapefile is empty so we can simply return here bbReader.Close(); return; } // Skip the shapefile header by skipping the first 100 bytes in the shapefile bbReader.Seek(100, SeekOrigin.Begin); int numShapes = shapeHeaders.Count; int [] partOffsets = new int[numShapes]; byte[] bigEndians = new byte[numShapes * 8]; byte[] allBounds = new byte[numShapes * 32]; ByteBlock allParts = new ByteBlock(BLOCKSIZE); // probably all will be in one block, but use a byteBlock just in case. ByteBlock allCoords = new ByteBlock(BLOCKSIZE); bool isM = (header.ShapeType == ShapeTypes.PolyLineM || header.ShapeType == ShapeTypes.PolyLineZ); bool isZ = (header.ShapeType == ShapeTypes.PolyLineZ); ByteBlock allZ = null; ByteBlock allM = null; if (isZ) { allZ = new ByteBlock(BLOCKSIZE); } if (isM) { allM = new ByteBlock(BLOCKSIZE); } int pointOffset = 0; for (int shp = 0; shp < numShapes; shp++) { // Read from the index file because some deleted records // might still exist in the .shp file. long offset = (shapeHeaders[shp].ByteOffset); bbReader.Seek(offset, SeekOrigin.Begin); // Position Value Type Number Byte Order ShapeRange shape = new ShapeRange(FeatureTypes.Polygon); //-------------------------------------------------------------------- shape.RecordNumber = bbReader.ReadInt32(false); // Byte 0 Record Number Integer 1 Big shape.ContentLength = bbReader.ReadInt32(false); // Byte 4 Content Length Integer 1 Big shape.ShapeType = (ShapeTypes)bbReader.ReadInt32(); // Byte 8 Shape Type Integer 1 Little shape.StartIndex = pointOffset; if (shape.ShapeType == ShapeTypes.NullShape) { continue; } bbReader.Read(allBounds, shp*32, 32); //double xMin = bbReader.ReadDouble(); // Byte 12 Xmin Double 1 Little // double yMin = bbReader.ReadDouble(); // Byte 20 Ymin Double 1 Little //double xMax = bbReader.ReadDouble(); // Byte 28 Xmax Double 1 Little //double yMax = bbReader.ReadDouble(); // Byte 36 Ymax Double 1 Little shape.NumParts = bbReader.ReadInt32(); // Byte 44 NumParts Integer 1 Little //feature.NumPoints = bbReader.ReadInt32(); // Byte 48 NumPoints Integer 1 Little shape.NumPoints = bbReader.ReadInt32(); // Create an envelope from the extents box in the file. //feature.Envelope = new Envelope(xMin, xMax, yMin, yMax); partOffsets[shp] = allParts.IntOffset(); allParts.Read(shape.NumParts * 4, bbReader); allCoords.Read(shape.NumPoints * 16, bbReader); pointOffset += shape.NumPoints; if (header.ShapeType == ShapeTypes.PolygonM) { // These are listed as "optional" but there isn't a good indicator of how to determine if they were added. // To handle the "optional" M values, check the contentLength for the feature. // The content length does not include the 8-byte record header and is listed in 16-bit words. if (shape.ContentLength * 2 > 44 + 4 * shape.NumParts + 16 * shape.NumPoints) { double mMin = bbReader.ReadDouble(); double mMax = bbReader.ReadDouble(); if(allM != null)allM.Read(shape.NumPoints * 8, bbReader); } } if (header.ShapeType == ShapeTypes.PolygonZ) { bool hasM = shape.ContentLength * 2 > 60 + 4 * shape.NumParts + 24 * shape.NumPoints; double zMin = bbReader.ReadDouble(); double zMax = bbReader.ReadDouble(); // For Z shapefiles, the Z part is not optional. if (allZ != null) allZ.Read(shape.NumPoints * 8, bbReader); // These are listed as "optional" but there isn't a good indicator of how to determine if they were added. // To handle the "optional" M values, check the contentLength for the feature. // The content length does not include the 8-byte record header and is listed in 16-bit words. if (hasM) { double mMin = bbReader.ReadDouble(); double mMax = bbReader.ReadDouble(); if (allM != null) allM.Read(shape.NumPoints * 8, bbReader); } } ShapeIndices.Add(shape); } double[] vert = allCoords.ToDoubleArray(); Vertex = vert; if (isM) M = allM.ToDoubleArray(); if (isZ) Z = allZ.ToDoubleArray(); List<ShapeRange> shapes = ShapeIndices; double[] bounds = new double[numShapes * 4]; Buffer.BlockCopy(allBounds, 0, bounds, 0, allBounds.Length); int[] parts = allParts.ToIntArray(); ProgressMeter = new ProgressMeter(ProgressHandler, "Testing Parts and Holes", numShapes); for (int shp = 0; shp < numShapes; shp++) { ShapeRange shape = shapes[shp]; shape.Extent = new Extent(bounds, shp * 4); for (int part = 0; part < shape.NumParts; part++) { int offset = partOffsets[shp]; int endIndex = shape.NumPoints + shape.StartIndex; int startIndex = parts[offset + part] + shape.StartIndex; if (part < shape.NumParts - 1) endIndex = parts[offset + part + 1] + shape.StartIndex; int count = endIndex - startIndex; PartRange partR = new PartRange(vert, shape.StartIndex, parts[offset + part], FeatureTypes.Polygon); partR.NumVertices = count; shape.Parts.Add(partR); } ProgressMeter.CurrentValue = shp; } ProgressMeter.Reset(); GC.Collect(); }