/// <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> /// 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; } } }