Exemple #1
0
        /// <summary>
        /// Provides all of the unidirectional edges from the current H3Index.
        /// </summary>
        /// <param name="origin">The origin hexagon H3Index to find edges for.</param>
        /// <param name="edges">The memory to store all of the edges inside.</param>
        /// <!-- Based off 3.1.1 -->
        public static void getH3UnidirectionalEdgesFromHexagon(H3Index origin,
                                                               List <H3Index> edges)
        {
            // Determine if the origin is a pentagon and special treatment needed.
            int isPentagon = H3Index.h3IsPentagon(origin);

            // This is actually quite simple. Just modify the bits of the origin
            // slightly for each direction, except the 'k' direction in pentagons,
            // which is zeroed.
            for (int i = 0; i < 6; i++)
            {
                if (isPentagon != 0 && i == 0)
                {
                    edges[i] = H3Index.H3_INVALID_INDEX;
                }
                else
                {
                    edges[i] = origin;
                    var ei = edges[i];
                    H3Index.H3_SET_MODE(ref ei, Constants.H3_UNIEDGE_MODE);
                    H3Index.H3_SET_RESERVED_BITS(ref ei, (ulong)i + 1);
                    edges[i] = ei;
                }
            }
        }
Exemple #2
0
        /// <summary>
        /// Determines if the provided H3Index is a valid unidirectional edge index
        /// </summary>
        /// <param name="edge">The unidirectional edge H3Index</param>
        /// <returns>1 if it is a unidirectional edge H3Index, otherwise 0.</returns>
        /// <!-- Based off 3.1.1 -->
        public static int h3UnidirectionalEdgeIsValid(H3Index edge)
        {
            if (H3Index.H3_GET_MODE(ref edge) != Constants.H3_UNIEDGE_MODE)
            {
                return(0);
            }

            Direction neighborDirection = (Direction)H3Index.H3_GET_RESERVED_BITS(edge);

            if (neighborDirection <= Direction.CENTER_DIGIT ||
                neighborDirection >= Direction.NUM_DIGITS)
            {
                return(0);
            }

            H3Index origin = getOriginH3IndexFromUnidirectionalEdge(edge);

            if (H3Index.h3IsPentagon(origin) != 0 && neighborDirection == Direction.K_AXES_DIGIT)
            {
                return(0);
            }

            return(H3Index.h3IsValid(origin));
        }
Exemple #3
0
        /// <summary>
        /// Returns the hollow hexagonal ring centered at origin with sides of length k.
        /// </summary>
        /// <param name="origin">Origin location</param>
        /// <param name="k">k &gt;= 0</param>
        /// <param name="out_hex">Array which must be of size 6 * k (or 1 if k == 0)</param>
        /// <returns>0 if no pentagonal distortion was encountered</returns>
        /// <!-- Based off 3.1.1 -->
        public static int hexRing(H3Index origin, int k, ref List <H3Index> out_hex)
        {
            // Short-circuit on 'identity' ring
            if (k == 0)
            {
                out_hex[0] = origin;
                return(0);
            }

            int idx = 0;
            // Number of 60 degree ccw rotations to perform on the direction (based on
            // which faces have been crossed.)
            int rotations = 0;

            // Scratch structure for checking for pentagons
            if (H3Index.h3IsPentagon(origin) > 0)
            {
                // Pentagon was encountered; bail out as user doesn't want this.
                return(Constants.HEX_RANGE_PENTAGON);
            }

            for (int ring = 0; ring < k; ring++)
            {
                origin = h3NeighborRotations(origin, Constants.NEXT_RING_DIRECTION, ref rotations);
                if (origin == 0)
                {
                    // Should not be possible because `origin` would have to be a
                    // pentagon
                    return(Constants.HEX_RANGE_K_SUBSEQUENCE); // LCOV_EXCL_LINE
                }

                if (H3Index.h3IsPentagon(origin) > 0)
                {
                    return(Constants.HEX_RANGE_PENTAGON);
                }
            }

            H3Index lastIndex = origin;

            out_hex[idx] = origin;
            idx++;

            for (int direction = 0; direction < 6; direction++)
            {
                for (int pos = 0; pos < k; pos++)
                {
                    origin =
                        h3NeighborRotations(origin, DIRECTIONS[direction], ref rotations);
                    if (origin == 0)
                    {
                        // Should not be possible because `origin` would have to be a
                        // pentagon
                        return(Constants.HEX_RANGE_K_SUBSEQUENCE); // LCOV_EXCL_LINE
                    }

                    // Skip the very last index, it was already added. We do
                    // however need to traverse to it because of the pentagonal
                    // distortion check, below.
                    if (pos != k - 1 || direction != 5)
                    {
                        out_hex[idx] = origin;
                        idx++;

                        if (H3Index.h3IsPentagon(origin) > 0)
                        {
                            return(Constants.HEX_RANGE_PENTAGON);
                        }
                    }
                }
            }

            // Check that this matches the expected lastIndex, if it doesn't,
            // it indicates pentagonal distortion occurred and we should report
            // failure.
            if (lastIndex != origin)
            {
                return(Constants.HEX_RANGE_PENTAGON);
            }
            else
            {
                return(Constants.HEX_RANGE_SUCCESS);
            }
        }
Exemple #4
0
        /// <summary>
        /// compact takes a set of hexagons all at the same resolution and compresses
        /// them by pruning full child branches to the parent level. This is also done
        /// for all parents recursively to get the minimum number of hex addresses that
        /// perfectly cover the defined space.
        /// </summary>
        /// <param name="h3Set"> Set of hexagons</param>
        /// <param name="compactedSet"> The output array of compressed hexagons (pre-allocated)</param>
        /// <param name="numHexes"> The size of the input and output arrays (possible that no
        /// contiguous regions exist in the set at all and no compression possible)</param>
        /// <returns>an error code on bad input data</returns>
        /// <remarks>
        /// We're going to modify this a little bit using some LINQ.
        /// </remarks>
        /// <!-- Based off 3.1.1 -->
        public static int compact(ref List <H3Index> h3Set, ref List <H3Index> compactedSet, int numHexes)
        {
            //  Maximum resolution.  We're out.
            int res = H3_GET_RESOLUTION(h3Set[0]);

            if (res == 0)
            {
                // No compaction possible, just copy the set to output
                compactedSet = new List <H3Index>(h3Set);
                return(0);
            }

            var realCompacted = new List <ulong>();

            //  Create a scratch list
            List <H3Index> scratchList = new List <H3Index>(numHexes);
            Dictionary <ulong, List <ulong> > generation = new Dictionary <ulong, List <ulong> >();
            //  These are ones we haven't processed.
            List <H3Index> remainingHexes = new List <H3Index>(h3Set.Take(numHexes));

            //  Loop through until we've removed the stragglers at each resolution
            //  and eventually have gotten the biggest sized hexes possible stored away
            while (remainingHexes.Count > 0)
            {
                //  What resolution are we looking at, and what resolution is our parent?
                res = H3_GET_RESOLUTION(remainingHexes[0]);
                int parentRes = res - 1;
                //  Start processing our unprocessed and storing the parent and children in generation
                foreach (var hex in remainingHexes)
                {
                    H3Index parent = h3ToParent(hex, parentRes);
                    if (!generation.ContainsKey(parent.value))
                    {
                        generation[parent.value] = new List <ulong>();
                    }

                    if (generation[parent].Contains(hex.value))
                    {
                        return(-1);  //  We have duplicate hexes that we're trying to compact
                    }
                    generation[parent].Add(hex.value);
                }
                // Only possible on duplicate input
                var errorTest = generation.Where(hex => hex.Value.Count > 7);
                if (errorTest.Any())
                {
                    return(-2);
                }

                remainingHexes.Clear();
                var pentagons    = generation.Where(parent => H3Index.h3IsPentagon(parent.Key) == 1);
                var pentaParents = pentagons.Select(keyValuePair => keyValuePair.Key)
                                   .Select(dummy => (H3Index)dummy).ToList();
                foreach (var pentaParent in pentaParents)
                {
                    if (generation[pentaParent].Count == 6)
                    {
                        remainingHexes.Add(pentaParent);
                    }
                    else
                    {
                        realCompacted.AddRange(generation[pentaParent]);
                    }
                    generation.Remove(pentaParent);
                }

                var orphans = generation.Where(hex => hex.Key != 0 && hex.Value.Count > 0 && hex.Value.Count < 7);
                realCompacted.AddRange(orphans.SelectMany(valuePair => valuePair.Value));

                var nextgen = generation.Where(hex => hex.Value.Count == 7);
                remainingHexes.AddRange(nextgen.Select(valuePair => new H3Index(valuePair.Key)).ToList());
                generation.Clear();
            }

            compactedSet.Capacity = numHexes;
            compactedSet          = realCompacted.Select(number => new H3Index(number)).ToList();
            return(0);
        }
Exemple #5
0
        /// <summary>
        /// hexRange produces indexes within k distance of the origin index.
        /// Output behavior is undefined when one of the indexes returned by this
        /// function is a pentagon or is in the pentagon distortion area.
        ///
        /// k-ring 0 is defined as the origin index, k-ring 1 is defined as k-ring 0 and
        /// all neighboring indexes, and so on.
        ///
        /// Output is placed in the provided array in order of increasing distance from
        /// the origin. The distances in hexagons is placed in the distances array at
        /// the same offset.
        /// </summary>
        /// <param name="origin">Origin location</param>
        /// <param name="k">k >= 0</param>
        /// <param name="out_size">Array which must be of size <see cref="maxKringSize"/>(k)</param>
        /// <param name="distances">Null or array which must be of size <see cref="maxKringSize"/>(k)</param>
        /// <returns>0 if no pentagon or pentagonal distortion area was encountered.</returns>
        /// <!-- Based off 3.1.1 -->
        internal static int hexRangeDistances(H3Index origin, int k, ref List <H3Index> out_size, ref List <int> distances)
        {
            // Return codes:
            // 1 Pentagon was encountered
            // 2 Pentagon distortion (deleted k subsequence) was encountered
            // Pentagon being encountered is not itself a problem; really the deleted
            // k-subsequence is the problem, but for compatibility reasons we fail on
            // the pentagon.
            for (int m = 0; m < out_size.Count; m++)
            {
                //out_size.Add(0);
                distances.Add(0);
            }
            // k must be >= 0, so origin is always needed
            int idx = 0;

            out_size[idx] = origin;
            distances[0]  = 0;
            idx++;

            if (H3Index.h3IsPentagon(origin) > 0)
            {
                // Pentagon was encountered; bail out as user doesn't want this.
                return(Constants.HEX_RANGE_PENTAGON);
            }

            // 0 < ring <= k, current ring
            int ring = 1;
            // 0 <= direction < 6, current side of the ring
            int direction = 0;
            // 0 <= i < ring, current position on the side of the ring
            int i = 0;
            // Number of 60 degree ccw rotations to perform on the direction (based on
            // which faces have been crossed.)
            int rotations = 0;

            while (ring <= k)
            {
                if (direction == 0 && i == 0)
                {
                    // Not putting in the output set as it will be done later, at
                    // the end of this ring.
                    origin = h3NeighborRotations(origin, Constants.NEXT_RING_DIRECTION, ref rotations);
                    if (origin == 0)
                    {
                        // Should not be possible because 'origin' would have to be a
                        // pentagon
                        return(Constants.HEX_RANGE_K_SUBSEQUENCE);
                    }

                    if (H3Index.h3IsPentagon(origin) > 0)
                    {
                        // Pentagon was encountered; bail out as user doesn't want this.
                        return(Constants.HEX_RANGE_PENTAGON);
                    }
                }

                origin = h3NeighborRotations(origin, DIRECTIONS[direction], ref rotations);
                if (origin == 0)
                {
                    // Should not be possible because `origin` would have to be a
                    // pentagon
                    return(Constants.HEX_RANGE_K_SUBSEQUENCE);
                }

                out_size[idx] = origin;
                if (distances.Count > 0)
                {
                    distances[idx] = ring;
                }

                idx++;

                i++;
                // Check if end of this side of the k-ring
                if (i == ring)
                {
                    i = 0;
                    direction++;
                    // Check if end of this ring.
                    if (direction == 6)
                    {
                        direction = 0;
                        ring++;
                    }
                }

                if (H3Index.h3IsPentagon(origin) > 0)
                {
                    // Pentagon was encountered; bail out as user doesn't want this.
                    return(Constants.HEX_RANGE_PENTAGON);
                }
            }

            return(Constants.HEX_RANGE_SUCCESS);
        }