Exemple #1
0
        /// <summary>
        /// Radius of bounding box in hexagons.  i.e., the radius of a k-ring centered
        /// on the bbox center and covering the entire bbox.
        /// </summary>
        /// <param name="bbox">Bounding box to measure</param>
        /// <param name="res">Resolution of hexagons to use in measurement</param>
        /// <returns>Radius in hexagons</returns>
        /// <!-- Based off 3.1.1 -->
        public static int bboxHexRadius(BBox bbox, int res)
        {
            // Determine the center of the bounding box
            GeoCoord center = new GeoCoord();

            bboxCenter(bbox, ref center);

            // Use a vertex on the side closest to the equator, to ensure the longest
            // radius in cases with significant distortion. East/west is arbitrary.
            double lat =
                Math.Abs(bbox.north) > Math.Abs(bbox.south)
                    ? bbox.south
                    : bbox.north;

            GeoCoord vertex = new GeoCoord {
                lat = lat, lon = bbox.east
            };

            // Determine the length of the bounding box "radius" to then use
            // as a circle on the earth that the k-rings must be greater than
            double bboxRadiusKm = GeoCoord._geoDistKm(center, new [] { vertex });

            // Determine the radius of the center hexagon
            double centerHexRadiusKm = _hexRadiusKm(H3Index.geoToH3(ref center, res));

            // The closest point along a hexagon drawn through the center points
            // of a k-ring aggregation is exactly 1.5 radii of the hexagon. For
            // any orientation of the GeoJSON encased in a circle defined by the
            // bounding box radius and center, it is guaranteed to fit in this k-ring
            // Rounded *up* to guarantee containment
            return((int)Math.Ceiling(bboxRadiusKm / (1.5 * centerHexRadiusKm)));
        }
Exemple #2
0
        ///<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;
                }
            }
        }