예제 #1
0
        /// <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);
        }
예제 #2
0
        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());
        }
예제 #3
0
        /// <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));
        }
예제 #4
0
        /// <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;
            }
        }
예제 #5
0
        /// <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;
            }
        }
예제 #6
0
 public static int MaxPolyFillSize(Code.GeoPolygon geoPolygon, int res)
 {
     return(Code.Algos.maxPolyfillSize(ref geoPolygon, res));
 }
예제 #7
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;
                }
            }
        }