/// <summary> /// takes a given GeoPolygon data structure and /// checks if it contains a given geo coordinate. /// </summary> /// <param name="geoPolygon">The Geofence and holes defining the relevant area</param> /// <param name="bboxes">The bboxes for the main Geofence and each of its holes</param> /// <param name="coord">The coordinate to check</param> /// <returns>Whether the point is contained</returns> /// <!-- Based off 3.1.1 --> public static bool pointInsidePolygon(GeoPolygon geoPolygon, List <BBox> bboxes, GeoCoord coord) { // Start with contains state of primary Geofence var tempBox = bboxes[0]; bool contains = pointInsideGeofence( ref geoPolygon.Geofence, ref tempBox, ref coord); bboxes[0] = tempBox; // If the point is contained in the primary Geofence, but there are holes in // the Geofence iterate through all holes and return false if the point is // contained in any hole if (contains && geoPolygon.numHoles > 0) { for (int i = 0; i < geoPolygon.numHoles; i++) { var hole = geoPolygon.holes[i]; var box = bboxes[i + 1]; var isInside = pointInsideGeofence(ref hole, ref box, ref coord); geoPolygon.holes[i] = hole; bboxes[i + 1] = box; if (isInside) { return(false); } } } return(contains); }
public static H3Index[] PolyFill(Code.GeoPolygon geoPolygon, int res) { var maxSize = MaxPolyFillSize(geoPolygon, res); var outHexes = MakeEmpty(maxSize); Code.Algos.polyfill(geoPolygon, res, outHexes); return(outHexes.Select(v => new H3Index { Value = v.value }).ToArray()); }
/// <summary> /// maxPolyfillSize returns the number of hexagons to allocate space for when /// performing a polyfill on the given GeoJSON-like data structure. /// /// Currently a laughably padded response, being a k-ring that wholly contains /// a bounding box of the GeoJSON, but still less wasted memory than initializing /// a Python application? ;) /// </summary> /// <param name="geoPolygon">A GeoJSON-like data structure indicating the poly to fill</param> /// <param name="res">Hexagon resolution (0-15)</param> /// <returns>number of hexagons to allocate for</returns> /// <!-- Based off 3.1.1 --> internal static int maxPolyfillSize(ref GeoPolygon geoPolygon, int res) { // Get the bounding box for the GeoJSON-like struct BBox bbox = new BBox(); Polygon.bboxFromGeofence(ref geoPolygon.Geofence, ref bbox); int minK = BBox.bboxHexRadius(bbox, res); // The total number of hexagons to allocate can now be determined by // the k-ring hex allocation helper function. return(maxKringSize(minK)); }
/// <summary> /// Create a bounding box from a GeoPolygon /// </summary> /// <param name="polygon">Input <see cref="GeoPolygon"/></param> /// <param name="bboxes">Output bboxes, one for the outer loop and one for each hole</param> /// <!-- Based off 3.1.1 --> void bboxesFromGeoPolygon(GeoPolygon polygon, ref List <BBox> bboxes) { var bb = bboxes[0]; bboxFromGeofence(polygon.Geofence, ref bb); bboxes[0] = bb; for (int i = 0; i < polygon.numHoles; i++) { bb = bboxes[i + 1]; bboxFromGeofence(polygon.holes[i], ref bb); bboxes[i + 1] = bb; } }
/// <summary> /// Create a bounding box from a GeoPolygon /// </summary> /// <param name="polygon">Input <see cref="GeoPolygon"/></param> /// <param name="bboxes">Output bboxes, one for the outer loop and one for each hole</param> /// <!-- Based off 3.1.1 --> public static void bboxesFromGeoPolygon(GeoPolygon polygon, ref List <BBox> bboxes) { var bbox0 = bboxes[0]; bboxFromGeofence(ref polygon.Geofence, ref bbox0); bboxes[0] = bbox0; for (int i = 0; i < polygon.numHoles; i++) { var tempBox = bboxes[i + 1]; var hole = polygon.holes[i]; bboxFromGeofence(ref hole, ref tempBox); bboxes[i + 1] = tempBox; polygon.holes[i] = hole; } }
public static int MaxPolyFillSize(Code.GeoPolygon geoPolygon, int res) { return(Code.Algos.maxPolyfillSize(ref geoPolygon, res)); }
///<summary> /// polyfill takes a given GeoJSON-like data structure and preallocated, /// zeroed memory, and fills it with the hexagons that are contained by /// the GeoJSON-like data structure. /// /// The current implementation is very primitive and slow, but correct, /// performing a point-in-poly operation on every hexagon in a k-ring defined /// around the given Geofence. /// </summary> /// <param name="geoPolygon">The Geofence and holes defining the relevant area</param> /// <param name="res"> The Hexagon resolution (0-15)</param> /// <param name="out_hex">The slab of zeroed memory to write to. Assumed to be big enough.</param> /// <!-- Based off 3.1.1 --> internal static void polyfill(GeoPolygon geoPolygon, int res, List <H3Index> out_hex) { // One of the goals of the polyfill algorithm is that two adjacent polygons // with zero overlap have zero overlapping hexagons. That the hexagons are // uniquely assigned. There are a few approaches to take here, such as // deciding based on which polygon has the greatest overlapping area of the // hexagon, or the most number of contained points on the hexagon (using the // center point as a tiebreaker). // // But if the polygons are convex, both of these more complex algorithms can // be reduced down to checking whether or not the center of the hexagon is // contained in the polygon, and so this is the approach that this polyfill // algorithm will follow, as it's simpler, faster, and the error for concave // polygons is still minimal (only affecting concave shapes on the order of // magnitude of the hexagon size or smaller, not impacting larger concave // shapes) // // This first part is identical to the maxPolyfillSize above. // Get the bounding boxes for the polygon and any holes int cnt = geoPolygon.numHoles + 1; List <BBox> bboxes = new List <BBox>(); for (int i = 0; i < cnt; i++) { bboxes.Add(new BBox()); } Polygon.bboxesFromGeoPolygon(geoPolygon, ref bboxes); int minK = BBox.bboxHexRadius(bboxes[0], res); int numHexagons = maxKringSize(minK); // Get the center hex GeoCoord center = new GeoCoord(); BBox.bboxCenter(bboxes[0], ref center); H3Index centerH3 = H3Index.geoToH3(ref center, res); // From here on it works differently, first we get all potential // hexagons inserted into the available memory kRing(centerH3, minK, ref out_hex); // Next we iterate through each hexagon, and test its center point to see if // it's contained in the GeoJSON-like struct for (int i = 0; i < numHexagons; i++) { // Skip records that are already zeroed if (out_hex[i] == 0) { continue; } // Check if hexagon is inside of polygon GeoCoord hexCenter = new GeoCoord(); H3Index.h3ToGeo(out_hex[i], ref hexCenter); hexCenter.lat = GeoCoord.constrainLat(hexCenter.lat); hexCenter.lon = GeoCoord.constrainLng(hexCenter.lon); // And remove from list if not if (!Polygon.pointInsidePolygon(geoPolygon, bboxes, hexCenter)) { out_hex[i] = H3Index.H3_INVALID_INDEX; } } }