Beispiel #1
0
        /// <summary>
        /// Returns whether or not the provided H3Indexes are neighbors.
        /// </summary>
        /// <param name="origin">The origin H3 index</param>
        /// <param name="destination">The destination H3 index</param>
        /// <returns>1 if the indexes are neighbors, 0 otherwise</returns>
        /// <!-- Based off 3.1.1 -->
        public static int h3IndexesAreNeighbors(H3Index origin, H3Index destination)
        {
            // Make sure they're hexagon indexes
            if (H3Index.H3_GET_MODE(ref origin) != Constants.H3_HEXAGON_MODE ||
                H3Index.H3_GET_MODE(ref destination) != Constants.H3_HEXAGON_MODE)
            {
                return(0);
            }

            // Hexagons cannot be neighbors with themselves
            if (origin == destination)
            {
                return(0);
            }

            // Only hexagons in the same resolution can be neighbors
            if (H3Index.H3_GET_RESOLUTION(origin) != H3Index.H3_GET_RESOLUTION(destination))
            {
                return(0);
            }

            // H3 Indexes that share the same parent are very likely to be neighbors
            // Child 0 is neighbor with all of its parent's 'offspring', the other
            // children are neighbors with 3 of the 7 children. So a simple comparison
            // of origin and destination parents and then a lookup table of the children
            // is a super-cheap way to possibly determine they are neighbors.
            int parentRes = H3Index.H3_GET_RESOLUTION(origin) - 1;

            if (parentRes > 0 && (H3Index.h3ToParent(origin, parentRes) ==
                                  H3Index.h3ToParent(destination, parentRes)))
            {
                Direction originResDigit      = H3Index.H3_GET_INDEX_DIGIT(origin, parentRes + 1);
                Direction destinationResDigit =
                    H3Index.H3_GET_INDEX_DIGIT(destination, parentRes + 1);
                if (originResDigit == Direction.CENTER_DIGIT ||
                    destinationResDigit == Direction.CENTER_DIGIT)
                {
                    return(1);
                }

                // These sets are the relevant neighbors in the clockwise
                // and counter-clockwise
                Direction[] neighborSetClockwise =
                {
                    Direction.CENTER_DIGIT, Direction.JK_AXES_DIGIT, Direction.IJ_AXES_DIGIT,
                    Direction.J_AXES_DIGIT, Direction.IK_AXES_DIGIT, Direction.K_AXES_DIGIT,
                    Direction.I_AXES_DIGIT
                };
                Direction[] neighborSetCounterclockwise =
                {
                    Direction.CENTER_DIGIT, Direction.IK_AXES_DIGIT, Direction.JK_AXES_DIGIT,
                    Direction.K_AXES_DIGIT, Direction.IJ_AXES_DIGIT, Direction.I_AXES_DIGIT,
                    Direction.J_AXES_DIGIT
                };
                if (
                    neighborSetClockwise[(int)originResDigit] == destinationResDigit ||
                    neighborSetCounterclockwise[(int)originResDigit] == destinationResDigit)
                {
                    return(1);
                }
            }

            // Otherwise, we have to determine the neighbor relationship the "hard" way.
            var neighborRing = new ulong[7].Select(cell => new H3Index(cell)).ToList();

            Algos.kRing(origin, 1, ref neighborRing);
            for (int i = 0; i < 7; i++)
            {
                if (neighborRing[i] == destination)
                {
                    return(1);
                }
            }

            // Made it here, they definitely aren't neighbors
            return(0);
        }
Beispiel #2
0
        /// <summary>
        /// Returns the hexagon index neighboring the origin, in the direction dir.
        ///
        /// Implementation note: The only reachable case where this returns 0 is if the
        /// origin is a pentagon and the translation is in the k direction. Thus,
        /// 0 can only be returned if origin is a pentagon.
        /// </summary>
        /// <param name="origin">Origin index</param>
        /// <param name="dir">Direction to move in</param>
        /// <param name="rotations">
        /// Number of ccw rotations to perform to reorient the translation vector.
        /// Will be modified to the new number of rotations to perform (such as
        /// when crossing a face edge.)
        /// </param>
        /// <returns>H3Index of the specified neighbor or 0 if deleted k-subsequence distortion is encountered.</returns>
        /// <!-- Based off 3.2.0 -->
        internal static ulong h3NeighborRotations(H3Index origin, Direction dir, ref int rotations)
        {
            H3Index out_hex = origin;

            for (int i = 0; i < rotations; i++)
            {
                dir = CoordIJK._rotate60ccw(dir);
            }

            int       newRotations    = 0;
            int       oldBaseCell     = H3Index.H3_GET_BASE_CELL(out_hex);
            Direction oldLeadingDigit = H3Index._h3LeadingNonZeroDigit(out_hex);

            // Adjust the indexing digits and, if needed, the base cell.
            int r = H3Index.H3_GET_RESOLUTION(out_hex) - 1;

            while (true)
            {
                if (r == -1)
                {
                    H3Index.H3_SET_BASE_CELL(ref out_hex, BaseCells.baseCellNeighbors[oldBaseCell, (int)dir]);
                    newRotations = BaseCells.baseCellNeighbor60CCWRots[oldBaseCell, (int)dir];

                    if (H3Index.H3_GET_BASE_CELL(out_hex) == BaseCells.INVALID_BASE_CELL)
                    {
                        // Adjust for the deleted k vertex at the base cell level.
                        // This edge actually borders a different neighbor.
                        H3Index.H3_SET_BASE_CELL(ref out_hex,
                                                 BaseCells.baseCellNeighbors[oldBaseCell, (int)Direction.IK_AXES_DIGIT]);
                        newRotations =
                            BaseCells.baseCellNeighbor60CCWRots[oldBaseCell, (int)Direction.IK_AXES_DIGIT];

                        // perform the adjustment for the k-subsequence we're skipping
                        // over.
                        out_hex = H3Index._h3Rotate60ccw(ref out_hex);
                        rotations++;
                    }

                    break;
                }

                Direction oldDigit = H3Index.H3_GET_INDEX_DIGIT(out_hex, r + 1);
                Direction nextDir;
                if (H3Index.isResClassIII(r + 1))
                {
                    H3Index.H3_SET_INDEX_DIGIT(ref out_hex, r + 1, (ulong)NEW_DIGIT_II[(int)oldDigit, (int)dir]);
                    nextDir = NEW_ADJUSTMENT_II[(int)oldDigit, (int)dir];
                }
                else
                {
                    H3Index.H3_SET_INDEX_DIGIT(ref out_hex, r + 1,
                                               (ulong)NEW_DIGIT_III[(int)oldDigit, (int)dir]);
                    nextDir = NEW_ADJUSTMENT_III[(int)oldDigit, (int)dir];
                }

                if (nextDir != Direction.CENTER_DIGIT)
                {
                    dir = nextDir;
                    r--;
                }
                else
                {
                    // No more adjustment to perform
                    break;
                }
            }

            int newBaseCell = H3Index.H3_GET_BASE_CELL(out_hex);

            if (BaseCells._isBaseCellPentagon(newBaseCell))
            {
                int alreadyAdjustedKSubsequence = 0;

                // force rotation out of missing k-axes sub-sequence
                if (H3Index._h3LeadingNonZeroDigit(out_hex) == Direction.K_AXES_DIGIT)
                {
                    if (oldBaseCell != newBaseCell)
                    {
                        // in this case, we traversed into the deleted
                        // k subsequence of a pentagon base cell.
                        // We need to rotate out of that case depending
                        // on how we got here.
                        // check for a cw/ccw offset face; default is ccw
                        if (BaseCells._baseCellIsCwOffset(
                                newBaseCell, BaseCells.baseCellData[oldBaseCell].homeFijk.face))
                        {
                            out_hex = H3Index._h3Rotate60cw(ref out_hex);
                        }
                        else
                        {
                            out_hex = H3Index._h3Rotate60ccw(ref out_hex); // LCOV_EXCL_LINE
                        }

                        // See cwOffsetPent in testKRing.c for why this is
                        // unreachable.

                        alreadyAdjustedKSubsequence = 1;
                    }
                    else
                    {
                        // In this case, we traversed into the deleted
                        // k subsequence from within the same pentagon
                        // base cell.
                        if (oldLeadingDigit == Direction.CENTER_DIGIT)
                        {
                            // Undefined: the k direction is deleted from here
                            return(H3Index.H3_INVALID_INDEX);
                        }

                        switch (oldLeadingDigit)
                        {
                        case Direction.JK_AXES_DIGIT:
                            // Rotate out of the deleted k subsequence
                            // We also need an additional change to the direction we're
                            // moving in
                            out_hex = H3Index._h3Rotate60ccw(ref out_hex);
                            rotations++;
                            break;

                        case Direction.IK_AXES_DIGIT:
                            // Rotate out of the deleted k subsequence
                            // We also need an additional change to the direction we're
                            // moving in
                            out_hex    = H3Index._h3Rotate60cw(ref out_hex);
                            rotations += 5;
                            break;

                        default:
                            // Should never occur
                            return(H3Index.H3_INVALID_INDEX);    // LCOV_EXCL_LINE
                        }
                    }
                }

                for (int i = 0; i < newRotations; i++)
                {
                    out_hex = H3Index._h3RotatePent60ccw(ref out_hex);
                }

                // Account for differing orientation of the base cells (this edge
                // might not follow properties of some other edges.)
                if (oldBaseCell != newBaseCell)
                {
                    if (BaseCells._isBaseCellPolarPentagon(newBaseCell))
                    {
                        // 'polar' base cells behave differently because they have all
                        // i neighbors.
                        if (oldBaseCell != 118 && oldBaseCell != 8 &&
                            H3Index._h3LeadingNonZeroDigit(out_hex) != Direction.JK_AXES_DIGIT)
                        {
                            rotations++;
                        }
                    }
                    else if (H3Index._h3LeadingNonZeroDigit(out_hex) == Direction.IK_AXES_DIGIT &&
                             alreadyAdjustedKSubsequence == 0)
                    {
                        // account for distortion introduced to the 5 neighbor by the
                        // deleted k subsequence.
                        rotations++;
                    }
                }
            }
            else
            {
                for (int i = 0; i < newRotations; i++)
                {
                    out_hex = H3Index._h3Rotate60ccw(ref out_hex);
                }
            }

            rotations = (rotations + newRotations) % 6;
            return(out_hex);
        }