public void Clear() { if (Next == null) { return; } Next.Clear(); Next = null; }
public LinkedGeoCoord(Code.LinkedGeo.LinkedGeoCoord codeLinkedGeoCoord) { Vertex = new GeoCoord { Latitude = codeLinkedGeoCoord.vertex.lat, Longitude = codeLinkedGeoCoord.vertex.lon }; if (codeLinkedGeoCoord.next != null) { Next = new LinkedGeoCoord(codeLinkedGeoCoord.next); } }
/// <summary> /// Count the number of coordinates in a loop /// </summary> /// <param name="loop"> Loop to count coordinates for</param> /// <returns>Count</returns> /// <!-- Based off 3.1.1 --> public static int countLinkedCoords(ref LinkedGeoLoop loop) { LinkedGeoCoord coord = loop.first; int count = 0; while (coord != null) { count++; coord = coord.next; } return(count); }
/// <summary> /// Free all allocated memory for a linked geo loop. The caller is /// responsible for freeing memory allocated to input loop struct. /// </summary> /// <param name="loop">Loop to free</param> /// <!-- Based off 3.1.1 --> public static void destroyLinkedGeoLoop(ref LinkedGeoLoop loop) { LinkedGeoCoord nextCoord; for (LinkedGeoCoord currentCoord = loop.first; currentCoord != null; currentCoord = nextCoord) { nextCoord = currentCoord.next; // ReSharper disable once RedundantAssignment currentCoord = null; } }
public LinkedGeoLoop(Code.LinkedGeo.LinkedGeoLoop codeLinkedGeoLoop) { if (codeLinkedGeoLoop.first != null) { First = new LinkedGeoCoord(codeLinkedGeoLoop.first); } if (codeLinkedGeoLoop.last != null) { Last = new LinkedGeoCoord(codeLinkedGeoLoop.last); } if (codeLinkedGeoLoop.next != null) { Next = new LinkedGeoLoop(codeLinkedGeoLoop.next); } }
public void Clear() { if (First != null) { First.Clear(); First = null; } if (Last != null) { Last.Clear(); Last = null; } if (Next != null) { Next.Clear(); Next = null; } }
/// <summary> /// Whether the winding order of a given LinkedGeoLoop is clockwise /// </summary> /// <param name="loop">The loop to check</param> /// <returns>Whether the loop is clockwise</returns> /// <!-- Based off 3.1.1 --> static bool isClockwiseNormalizedLinkedGeoLoop(LinkedGeoLoop loop, bool isTransmeridian) { double sum = 0; GeoCoord a; GeoCoord b; LinkedGeoCoord currentCoord = null; LinkedGeoCoord nextCoord = null; while (true) { currentCoord = currentCoord == null ? loop.first : currentCoord.next; if (currentCoord == null) { break; } a = currentCoord.vertex; nextCoord = currentCoord.next == null ? loop.first : currentCoord.next; b = nextCoord.vertex; // If we identify a transmeridian arc (> 180 degrees longitude), // start over with the transmeridian flag set if (!isTransmeridian && Math.Abs(a.lon - b.lon) > Constants.M_PI) { return(isClockwiseNormalizedLinkedGeoLoop(loop, true)); } sum += ((NORMALIZE_LON(b.lon, isTransmeridian) - NORMALIZE_LON(a.lon, isTransmeridian)) * (b.lat + a.lat)); } return(sum > 0); }
/// <summary> /// Add a new linked coordinate to the current loop /// </summary> /// <param name="loop">Loop to add coordinate to</param> /// <param name="vertex">Coordinate to add</param> /// <returns>Pointer to the coordinate</returns> /// <!-- Based off 3.1.1 --> public static LinkedGeoCoord addLinkedCoord(ref LinkedGeoLoop loop, ref GeoCoord vertex) { LinkedGeoCoord coord = new LinkedGeoCoord(); coord.vertex = new GeoCoord(vertex.lat, vertex.lon); coord.next = null; LinkedGeoCoord last = loop.last; if (last == null) { if (loop.first != null) { throw new Exception("assert(loop->first == NULL);"); } loop.first = coord; } else { last.next = coord; } loop.last = coord; return(coord); }
/// <summary> /// Take a given LinkedGeoLoop data structure and check if it /// contains a given geo coordinate. /// </summary> /// <param name="loop">The linked loop</param> /// <param name="bbox">The bbox for the loop</param> /// <param name="coord">The coordinate to check</param> /// <returns>Whether the point is contained</returns> /// <!-- Based off 3.1.1 --> public static bool pointInsideLinkedGeoLoop(ref LinkedGeoLoop loop, ref BBox bbox, ref GeoCoord coord) { // fail fast if we're outside the bounding box if (!BBox.bboxContains(bbox, coord)) { return(false); } bool isTransmeridian = BBox.bboxIsTransmeridian(bbox); bool contains = false; double lat = coord.lat; double lng = NORMALIZE_LON(coord.lon, isTransmeridian); GeoCoord a; GeoCoord b; LinkedGeoCoord currentCoord = null; LinkedGeoCoord nextCoord = null; while (true) { currentCoord = currentCoord == null ? loop.first : currentCoord.next; if (currentCoord == null) { break; } a = currentCoord.vertex; nextCoord = currentCoord.next == null ? loop.first : currentCoord.next; b = nextCoord.vertex; // Ray casting algo requires the second point to always be higher // than the first, so swap if needed if (a.lat > b.lat) { GeoCoord tmp = new GeoCoord(a.lat, a.lon); a = new GeoCoord(b.lat, b.lon); b = new GeoCoord(tmp.lat, tmp.lon); } // If we're totally above or below the latitude ranges, the test // ray cannot intersect the line segment, so let's move on if (lat < a.lat || lat > b.lat) { continue; } double aLng = NORMALIZE_LON(a.lon, isTransmeridian); double bLng = NORMALIZE_LON(b.lon, isTransmeridian); // Rays are cast in the longitudinal direction, in case a point // exactly matches, to decide tiebreakers, bias westerly if (Math.Abs(aLng - lng) < Constants.EPSILON || Math.Abs(bLng - lng) < Constants.EPSILON) { lng -= Constants.DBL_EPSILON; } // For the latitude of the point, compute the longitude of the // point that lies on the line segment defined by a and b // This is done by computing the percent above a the lat is, // and traversing the same percent in the longitudinal direction // of a to b double ratio = (lat - a.lat) / (b.lat - a.lat); double testLng = NORMALIZE_LON(aLng + (bLng - aLng) * ratio, isTransmeridian); // Intersection of the ray if (testLng > lng) { contains = !contains; } } return(contains); }
/// <summary> /// Create a bounding box from a simple polygon loop. /// Known limitations: /// - Does not support polygons with two adjacent points > 180 degrees of /// longitude apart. These will be interpreted as crossing the antimeridian. /// - Does not currently support polygons containing a pole. /// </summary> /// <param name="loop">Loop of coordinates</param> /// <param name="bbox">bbox</param> /// <!-- Based off 3.1.1 --> public static void bboxFromLinkedGeoLoop(ref LinkedGeoLoop loop, ref BBox bbox) { // Early exit if there are no vertices if (loop.first == null) { bbox = new BBox(); return; } bbox.south = Double.MaxValue; bbox.west = Double.MaxValue; bbox.north = -Double.MaxValue; bbox.east = -Double.MaxValue; double minPosLon = Double.MaxValue; double maxNegLon = -Double.MaxValue; bool isTransmeridian = false; double lat; double lon; GeoCoord coord; GeoCoord next; LinkedGeoCoord currentCoord = null; LinkedGeoCoord nextCoord = null; while (true) { currentCoord = currentCoord == null ? loop.first : currentCoord.next; if (currentCoord == null) { break; } coord = currentCoord.vertex; nextCoord = currentCoord.next == null ? loop.first : currentCoord.next; next = nextCoord.vertex; lat = coord.lat; lon = coord.lon; if (lat < bbox.south) { bbox.south = lat; } if (lon < bbox.west) { bbox.west = lon; } if (lat > bbox.north) { bbox.north = lat; } if (lon > bbox.east) { bbox.east = lon; } // Save the min positive and max negative longitude for // use in the transmeridian case if (lon > 0 && lon < minPosLon) { minPosLon = lon; } if (lon < 0 && lon > maxNegLon) { maxNegLon = lon; } // check for arcs > 180 degrees longitude, flagging as transmeridian if (Math.Abs(lon - next.lon) > Constants.M_PI) { isTransmeridian = true; } } // Swap east and west if transmeridian if (isTransmeridian) { bbox.east = maxNegLon; bbox.west = minPosLon; } }