private static List <PointInt> ClipRingShape(PointInt[] inputPoints, RectangleInt tileScreenBoundingBox) { List <PointInt> outputList = new List <PointInt>(); List <PointInt> inputList = new List <PointInt>(inputPoints.Length); bool previousInside; for (int n = 0; n < inputPoints.Length; ++n) { inputList.Add(inputPoints[n]); } bool inputPolygonIsHole = IsPolygonHole(inputPoints, inputPoints.Length); //test left previousInside = inputList[inputList.Count - 1].X >= tileScreenBoundingBox.XMin; for (int n = 0; n < inputList.Count; ++n) { PointInt currentPoint = inputList[n]; bool currentInside = currentPoint.X >= tileScreenBoundingBox.XMin; if (currentInside != previousInside) { //add intersection PointInt prevPoint = n == 0 ? inputList[inputList.Count - 1] : inputList[n - 1]; int x = tileScreenBoundingBox.XMin; int y = prevPoint.Y + (currentPoint.Y - prevPoint.Y) * (x - prevPoint.X) / (currentPoint.X - prevPoint.X); outputList.Add(new PointInt(x, y)); } if (currentInside) { outputList.Add(currentPoint); } previousInside = currentInside; } if (outputList.Count == 0) { return(outputList); } //test top inputList = outputList.ToList(); previousInside = inputList[inputList.Count - 1].Y <= tileScreenBoundingBox.YMax;; outputList.Clear(); for (int n = 0; n < inputList.Count; ++n) { PointInt currentPoint = inputList[n]; bool currentInside = currentPoint.Y <= tileScreenBoundingBox.YMax; if (currentInside != previousInside) { //add intersection PointInt prevPoint = n == 0 ? inputList[inputList.Count - 1] : inputList[n - 1]; int y = tileScreenBoundingBox.YMax; int x = prevPoint.X + (currentPoint.X - prevPoint.X) * (y - prevPoint.Y) / (currentPoint.Y - prevPoint.Y); outputList.Add(new PointInt(x, y)); } if (currentInside) { outputList.Add(currentPoint); } previousInside = currentInside; } if (outputList.Count == 0) { return(outputList); } //test right inputList = outputList.ToList(); previousInside = inputList[inputList.Count - 1].X <= tileScreenBoundingBox.XMax; outputList.Clear(); for (int n = 0; n < inputList.Count; ++n) { PointInt currentPoint = inputList[n]; bool currentInside = currentPoint.X <= tileScreenBoundingBox.XMax; if (currentInside != previousInside) { //add intersection PointInt prevPoint = n == 0 ? inputList[inputList.Count - 1] : inputList[n - 1]; int x = tileScreenBoundingBox.XMax; int y = prevPoint.Y + (currentPoint.Y - prevPoint.Y) * (x - prevPoint.X) / (currentPoint.X - prevPoint.X); outputList.Add(new PointInt(x, y)); } if (currentInside) { outputList.Add(currentPoint); } previousInside = currentInside; } if (outputList.Count == 0) { return(outputList); } //test bottom inputList = outputList.ToList(); previousInside = inputList[inputList.Count - 1].Y >= tileScreenBoundingBox.YMin; outputList.Clear(); for (int n = 0; n < inputList.Count; ++n) { PointInt currentPoint = inputList[n]; bool currentInside = currentPoint.Y >= tileScreenBoundingBox.YMin; if (currentInside != previousInside) { //add intersection PointInt prevPoint = n == 0 ? inputList[inputList.Count - 1] : inputList[n - 1]; int y = tileScreenBoundingBox.YMin; int x = prevPoint.X + (currentPoint.X - prevPoint.X) * (y - prevPoint.Y) / (currentPoint.Y - prevPoint.Y); outputList.Add(new PointInt(x, y)); } if (currentInside) { outputList.Add(currentPoint); } previousInside = currentInside; } if (outputList.Count == 0) { return(outputList); } bool clippedPolygonIsHole = IsPolygonHole(outputList, outputList.Count); if (clippedPolygonIsHole == inputPolygonIsHole) { outputList.Reverse(); } if (outputList.Count > 3 && outputList[0] != outputList[outputList.Count - 1]) { outputList.Insert(0, outputList[0]); } return(outputList); }
private static TileFeature GetVectorTileFeature(Feature feature, int zoom, int tileSize, RectangleInt tileScreenBoundingBox, int simplificationFactor, RectangleShape tileBoundingBox) { TileFeature tileFeature = new TileFeature(); switch (feature.GetWellKnownType()) { case WellKnownType.Line: case WellKnownType.Multiline: tileFeature.Type = GeometryType.LineString; MultilineShape multiLineShape = new MultilineShape(feature.GetWellKnownBinary()); ProcessLineShape(zoom, tileSize, tileScreenBoundingBox, tileFeature, multiLineShape, simplificationFactor, tileBoundingBox); break; case WellKnownType.Polygon: case WellKnownType.Multipolygon: tileFeature.Type = GeometryType.Polygon; MultipolygonShape multiPolygonShape = new MultipolygonShape(feature.GetWellKnownBinary()); foreach (PolygonShape polygonShape in multiPolygonShape.Polygons) { ProcessRingShape(zoom, tileSize, tileScreenBoundingBox, tileFeature, polygonShape.OuterRing, simplificationFactor, tileBoundingBox); foreach (RingShape ringShape in polygonShape.InnerRings) { ProcessRingShape(zoom, tileSize, tileScreenBoundingBox, tileFeature, ringShape, simplificationFactor, tileBoundingBox); } } break; case WellKnownType.Point: case WellKnownType.Multipoint: tileFeature.Type = GeometryType.Point; List <PointInt> coordinates = new List <PointInt>(); MultipointShape multiPointShape = new MultipointShape(); if (feature.GetWellKnownType() == WellKnownType.Point) { PointShape pointShape = new PointShape(feature.GetWellKnownBinary()); multiPointShape.Points.Add(pointShape); } else if (feature.GetWellKnownType() == WellKnownType.Multipoint) { multiPointShape = new MultipointShape(feature.GetWellKnownBinary()); } foreach (PointShape point in multiPointShape.Points) { PointInt pointI = WorldPointToTilePoint(point.X, point.Y, zoom, tileSize, tileBoundingBox); coordinates.Add(new PointInt(pointI.X, pointI.Y)); } if (coordinates.Count > 0) { tileFeature.Geometry.Add(coordinates); } break; default: tileFeature.Type = GeometryType.Unknown; break; } //add the record attributes foreach (var attributes in feature.ColumnValues) { tileFeature.Attributes.Add(new TileAttribute(attributes.Key, attributes.Value)); } return(tileFeature); }
/// <summary> /// Cohen–Sutherland clipping algorithm clips a line from P0 = (x0, y0) to P1 = (x1, y1) against a clipBounds rectangle /// </summary> /// <param name="x0"></param> /// <param name="y0"></param> /// <param name="x1"></param> /// <param name="y1"></param> /// <param name="tileScreenBoundingBox"></param> /// <param name="clipState"> state of clipped line. ClipState.None if line is not clipped. If either end of the line /// is clipped then the clipState flag are set (ClipState.Start or ClipState.End) </param> /// <returns>true if clipped line is inside given clip bounds</returns> private static bool CohenSutherlandLineClip(ref double x0, ref double y0, ref double x1, ref double y1, ref RectangleInt tileScreenBoundingBox, out ClipState clipState) { // compute outcodes for P0, P1, and whatever point lies outside the clip rectangle OutCode outcode0 = ComputeOutCode(x0, y0, ref tileScreenBoundingBox); OutCode outcode1 = ComputeOutCode(x1, y1, ref tileScreenBoundingBox); bool accept = false; clipState = ClipState.None; while (true) { if ((outcode0 | outcode1) == OutCode.Inside) { // bitwise OR is 0: both points inside window; trivially accept and exit loop accept = true; break; } else if ((outcode0 & outcode1) != OutCode.Inside) { // bitwise AND is not 0: both points share an outside zone (LEFT, RIGHT, TOP, // or BOTTOM), so both must be outside window; exit loop (accept is false) break; } else { // failed both tests, so calculate the line segment to clip // from an outside point to an intersection with clip edge double x = 0, y = 0; // At least one endpoint is outside the clip rectangle; pick it. OutCode outcodeOut = outcode0 != OutCode.Inside ? outcode0 : outcode1; // Now find the intersection point; // use formulas: // slope = (y1 - y0) / (x1 - x0) // x = x0 + (1 / slope) * (ym - y0), where ym is ymin or ymax // y = y0 + slope * (xm - x0), where xm is xmin or xmax // No need to worry about divide-by-zero because, in each case, the // outcode bit being tested guarantees the denominator is non-zero if ((outcodeOut & OutCode.Top) == OutCode.Top) { // point is above the clip window x = x0 + (x1 - x0) * (tileScreenBoundingBox.YMax - y0) / (y1 - y0); y = tileScreenBoundingBox.YMax; } else if ((outcodeOut & OutCode.Bottom) == OutCode.Bottom) { // point is below the clip window x = x0 + (x1 - x0) * (tileScreenBoundingBox.YMin - y0) / (y1 - y0); y = tileScreenBoundingBox.YMin; // ymin; } else if ((outcodeOut & OutCode.Right) == OutCode.Right) { // point is to the right of clip window y = y0 + (y1 - y0) * (tileScreenBoundingBox.XMax - x0) / (x1 - x0); x = tileScreenBoundingBox.XMax; // xmax; } else if ((outcodeOut & OutCode.Left) == OutCode.Left) { // point is to the left of clip window y = y0 + (y1 - y0) * (tileScreenBoundingBox.XMin - x0) / (x1 - x0); x = tileScreenBoundingBox.XMin; // xmin; } // Now we move outside point to intersection point to clip // and get ready for next pass. if (outcodeOut == outcode0) { clipState |= ClipState.Start; x0 = x; y0 = y; outcode0 = ComputeOutCode(x0, y0, ref tileScreenBoundingBox); } else { clipState |= ClipState.End; x1 = x; y1 = y; outcode1 = ComputeOutCode(x1, y1, ref tileScreenBoundingBox); } } } return(accept); }