Ejemplo n.º 1
0
        /// <summary>
        /// Rotate an H3Index 60 degrees clockwise about a pentagonal center.
        /// </summary>
        /// <param name="h"> The H3Index.</param>
        /// <!-- Based off 3.1.1 -->
        public static H3Index _h3RotatePent60cw(H3Index h)
        {
            // rotate in place; skips any leading 1 digits (k-axis)
            int foundFirstNonZeroDigit = 0;

            for (int r = 1, res = H3_GET_RESOLUTION(h); r <= res; r++)
            {
                // rotate this digit
                H3_SET_INDEX_DIGIT(ref h, r, (ulong)CoordIJK._rotate60cw(H3_GET_INDEX_DIGIT(h, r)));

                // look for the first non-zero digit so we
                // can adjust for deleted k-axes sequence
                // if necessary
                if ((foundFirstNonZeroDigit == 0) && H3_GET_INDEX_DIGIT(h, r) != 0)
                {
                    foundFirstNonZeroDigit = 1;

                    // adjust for deleted k-axes sequence
                    if (_h3LeadingNonZeroDigit(h) == Direction.K_AXES_DIGIT)
                    {
                        h = _h3Rotate60cw(ref h);
                    }
                }
            }
            return(h);
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Determines the center point in spherical coordinates of a cell given by
        /// a FaceIJK address at a specified resolution.
        /// </summary>
        /// <param name="h">The FaceIJK address of the cell.</param>
        /// <param name="res">The H3 resolution of the cell.</param>
        /// <param name="g">The spherical coordinates of the cell center point.</param>
        /// <!-- Based off 3.1.1 -->
        public static void _faceIjkToGeo(FaceIJK h, int res, ref GeoCoord g)
        {
            Vec2d v = new Vec2d();

            CoordIJK._ijkToHex2d(h.coord, ref v);
            _hex2dToGeo(ref v, h.face, res, 0, ref g);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Transforms coordinates from the IJ coordinate system to the IJK+ coordinate system
        /// </summary>
        /// <param name="ij">The input IJ coordinates</param>
        /// <param name="ijk">The output IJK+ coordinates</param>
        /// <!-- Based off 3.2.0 -->
        public static void ijToIjk(LocalIJ.CoordIJ ij, ref CoordIJK ijk)
        {
            ijk.i = ij.i;
            ijk.j = ij.j;
            ijk.k = 0;

            _ijkNormalize(ref ijk);
        }
Ejemplo n.º 4
0
 /// <summary>
 /// Find the normalized ijk coordinates of the hex in the specified digit
 /// direction from the specified ijk coordinates. Works in place.
 /// </summary>
 /// <param name="ijk">The ijk coordinates.</param>
 /// <param name="digit">The digit direction from the original ijk coordinates.</param>
 /// <!-- Based off 3.1.1 -->
 public static void _neighbor(ref CoordIJK ijk, Direction digit)
 {
     if (digit > Direction.CENTER_DIGIT && digit < Direction.NUM_DIGITS)
     {
         _ijkAdd(ijk, UNIT_VECS[(int)digit], ref ijk);
         _ijkNormalize(ref ijk);
     }
 }
Ejemplo n.º 5
0
        /// <summary>
        /// Find the center point in 2D cartesian coordinates of a hex.
        /// </summary>
        /// <param name="h">The ijk coordinates of the hex.</param>
        /// <param name="v">The 2D cartesian coordinates of the hex center point.</param>
        /// <!-- Based off 3.1.1 -->
        public static void _ijkToHex2d(CoordIJK h, ref Vec2d v)
        {
            int i = h.i - h.k;
            int j = h.j - h.k;

            v.x = i - 0.5 * j;
            v.y = j * Constants.M_SQRT3_2;
        }
Ejemplo n.º 6
0
 /// <summary>
 /// Rotate an H3Index 60 degrees clockwise.
 /// </summary>
 /// <param name="h">The H3Index.</param>
 /// <!-- Based off 3.1.1 -->
 public static H3Index _h3Rotate60cw(ref H3Index h)
 {
     for (int r = 1, res = H3_GET_RESOLUTION(h); r <= res; r++)
     {
         Direction oldDigit = H3_GET_INDEX_DIGIT(h, r);
         H3_SET_INDEX_DIGIT(ref h, r, (ulong)CoordIJK._rotate60cw(oldDigit));
     }
     return(h);
 }
Ejemplo n.º 7
0
        /// <summary>
        /// Finds the distance between the two coordinates. Returns result.
        /// </summary>
        /// <param name="c1">The first set of ijk coordinates.</param>
        /// <param name="c2">The second set of ijk coordinates.</param>
        /// <!-- Based off 3.1.1 -->
        public static int ijkDistance(CoordIJK c1, CoordIJK c2)
        {
            CoordIJK diff = new CoordIJK();

            _ijkSub(ref c1, ref c2, ref diff);
            _ijkNormalize(ref diff);
            CoordIJK absDiff = new CoordIJK(Math.Abs(diff.i), Math.Abs(diff.j), Math.Abs(diff.k));

            return(Math.Max(absDiff.i, Math.Max(absDiff.j, absDiff.k)));
        }
Ejemplo n.º 8
0
        /// <summary>
        /// Encodes a coordinate on the sphere to the FaceIJK address of the containing
        /// cell at the specified resolution.
        /// </summary>
        /// <param name="g">The spherical coordinates to encode.</param>
        /// <param name="res">The desired H3 resolution for the encoding.</param>
        /// <param name="h">The FaceIJK address of the containing cell at resolution res.</param>
        /// <!-- Based off 3.1.1 -->
        public static void _geoToFaceIjk(GeoCoord g, int res, ref FaceIJK h)
        {
            // first convert to hex2d
            Vec2d v = new Vec2d();

            _geoToHex2d(g, res, ref h.face, ref v);

            // then convert to ijk+
            CoordIJK._hex2dToCoordIJK(ref v, ref h.coord);
        }
Ejemplo n.º 9
0
        /// <summary>
        /// Find the normalized ijk coordinates of the indexing parent of a cell in a
        /// clockwise aperture 7 grid. Works in place.
        /// </summary>
        /// <param name="ijk">The ijk coordinates</param>
        /// <!-- Based off 3.1.1 -->
        public static void _upAp7r(ref CoordIJK ijk)
        {
            // convert to CoordIJ
            int i = ijk.i - ijk.k;
            int j = ijk.j - ijk.k;

            ijk.i = (int)Math.Round(((2 * i + j) / 7.0d), MidpointRounding.AwayFromZero);
            ijk.j = (int)Math.Round(((3 * j - i) / 7.0d), MidpointRounding.AwayFromZero);
            ijk.k = 0;
            _ijkNormalize(ref ijk);
        }
Ejemplo n.º 10
0
        /// <summary>
        /// Produces an index for ij coordinates anchored by an origin.
        ///
        /// The coordinate space used by this function may have deleted
        /// regions or warping due to pentagonal distortion.
        ///
        /// Failure may occur if the index is too far away from the origin
        /// or if the index is on the other side of a pentagon.
        ///
        /// This function is experimental, and its output is not guaranteed
        /// to be compatible across different versions of H3.
        /// </summary>
        /// <param name="origin">An anchoring index for the ij coordinate system.</param>
        /// <param name="ij">ij coordinates to index.</param>
        /// <param name="out_h3">Index will be placed here on success.</param>
        /// <returns>0 on succedd, or another value on failure</returns>
        /// <!-- Based off 3.2.0 -->
        public static int experimentalLocalIjToH3(H3Index origin, CoordIJ ij,
                                                  ref H3Index out_h3)
        {
            // This function is currently experimental. Once ready to be part of the
            // non-experimental API, this function (with the experimental prefix) will
            // be marked as deprecated and to be removed in the next major version. It
            // will be replaced with a non-prefixed function name.
            CoordIJK ijk = new CoordIJK();

            CoordIJK.ijToIjk(ij, ref ijk);

            return(localIjkToH3(origin, ijk, ref out_h3));
        }
Ejemplo n.º 11
0
        /// <summary>
        /// Rotates ijk coordinates 60 degrees clockwise. Works in place.
        /// </summary>
        /// <param name="ijk">The ijk coordinates.</param>
        /// <!-- Based off 3.1.1 -->
        public static void _ijkRotate60cw(ref CoordIJK ijk)
        {
            // unit vector rotations
            CoordIJK iVec = new CoordIJK(1, 0, 1);
            CoordIJK jVec = new CoordIJK(1, 1, 0);
            CoordIJK kVec = new CoordIJK(0, 1, 1);

            _ijkScale(ref iVec, ijk.i);
            _ijkScale(ref jVec, ijk.j);
            _ijkScale(ref kVec, ijk.k);

            _ijkAdd(iVec, jVec, ref ijk);
            _ijkAdd(ijk, kVec, ref ijk);

            _ijkNormalize(ref ijk);
        }
Ejemplo n.º 12
0
        /// <summary>
        /// Find the normalized ijk coordinates of the hex centered on the indicated
        /// hex at the next finer aperture 3 clockwise resolution. Works in place.
        /// </summary>
        /// <param name="ijk">The ijk coordinates.</param>
        /// <!-- Based off 3.1.1 -->
        public static void _downAp3r(ref CoordIJK ijk)
        {
            // res r unit vectors in res r+1
            CoordIJK iVec = new CoordIJK(2, 1, 0);
            CoordIJK jVec = new CoordIJK(0, 2, 1);
            CoordIJK kVec = new CoordIJK(1, 0, 2);

            _ijkScale(ref iVec, ijk.i);
            _ijkScale(ref jVec, ijk.j);
            _ijkScale(ref kVec, ijk.k);

            _ijkAdd(iVec, jVec, ref ijk);
            _ijkAdd(ijk, kVec, ref ijk);

            _ijkNormalize(ref ijk);
        }
Ejemplo n.º 13
0
        /// <summary>
        /// Determines the H3 digit corresponding to a unit vector in ijk coordinates.
        /// </summary>
        /// <param name="ijk">The ijk coordinates; must be a unit vector.</param>
        /// <returns>The H3 digit (0-6) corresponding to the ijk unit vector, or <see cref="Direction.INVALID_DIGIT"/> INVALID_DIGIT on failure</returns>
        /// <!-- Based off 3.1.1 -->
        public static Direction _unitIjkToDigit(ref CoordIJK ijk)
        {
            CoordIJK c = new CoordIJK(ijk.i, ijk.j, ijk.k);

            _ijkNormalize(ref c);

            Direction digit = Direction.INVALID_DIGIT;

            for (Direction i = Direction.CENTER_DIGIT; i < Direction.NUM_DIGITS; i++)
            {
                if (_ijkMatches(c, UNIT_VECS[(int)i]) == 1)
                {
                    digit = i;
                    break;
                }
            }
            return(digit);
        }
Ejemplo n.º 14
0
        /// <summary>
        /// Produces the grid distance between the two indexes.
        ///
        /// This function may fail to find the distance between two indexes, for
        /// example if they are very far apart. It may also fail when finding
        /// distances for indexes on opposite sides of a pentagon.
        /// </summary>
        /// <param name="origin">Index to find the distance from</param>
        /// <param name="h3">Index to find the distance to</param>
        /// <returns>
        /// The distance, or a negative number if the library could not compute the distance
        /// </returns>
        /// <!-- Based off 3.2.0 -->
        public static int h3Distance(H3Index origin, H3Index h3)
        {
            CoordIJK originIjk = new CoordIJK();
            CoordIJK h3Ijk     = new CoordIJK();

            if (h3ToLocalIjk(origin, origin, ref originIjk) != 0)
            {
                // Currently there are no tests that would cause getting the coordinates
                // for an index the same as the origin to fail.
                return(-1);  // LCOV_EXCL_LINE
            }
            if (h3ToLocalIjk(origin, h3, ref h3Ijk) != 0)
            {
                return(-1);
            }

            return(CoordIJK.ijkDistance(originIjk, h3Ijk));
        }
Ejemplo n.º 15
0
        /// <summary>
        /// Produces ij coordinates for an index anchored by an origin.
        ///
        /// The coordinate space used by this function may have deleted
        /// regions or warping due to pentagonal distortion.
        ///
        /// Coordinates are only comparable if they come from the same
        /// origin index.
        ///
        /// Failure may occur if the index is too far away from the origin
        /// or if the index is on the other side of a pentagon.
        ///
        /// This function is experimental, and its output is not guaranteed
        /// to be compatible across different versions of H3.
        /// </summary>
        /// <param name="origin">An anchoring index for the ij coordinate system.</param>
        /// <param name="h3">Index to find the coordinates of</param>
        /// <param name="out_coord">ij coordinates of the index will be placed here on success</param>
        /// <returns>0 on success, or another value on failure.</returns>
        /// <!-- Based off 3.2.0 -->
        public static int experimentalH3ToLocalIj(H3Index origin, H3Index h3, CoordIJ out_coord)
        {
            // This function is currently experimental. Once ready to be part of the
            // non-experimental API, this function (with the experimental prefix) will
            // be marked as deprecated and to be removed in the next major version. It
            // will be replaced with a non-prefixed function name.
            CoordIJK ijk    = new CoordIJK();
            int      failed = h3ToLocalIjk(origin, h3, ref ijk);

            if (failed != 0)
            {
                return(failed);
            }

            CoordIJK.ijkToIj(ijk, ref out_coord);

            return(0);
        }
Ejemplo n.º 16
0
        /// <summary>
        /// Normalizes ijk coordinates by setting the components to the smallest possible
        /// values. Works in place.
        /// </summary>
        /// <param name="c">The ijk coordinates to normalize.</param>
        /// <!-- Based off 3.1.1 -->
        public static void _ijkNormalize(ref CoordIJK c)
        {
            // remove any negative values
            if (c.i < 0)
            {
                c.j -= c.i;
                c.k -= c.i;
                c.i  = 0;
            }

            if (c.j < 0)
            {
                c.i -= c.j;
                c.k -= c.j;
                c.j  = 0;
            }

            if (c.k < 0)
            {
                c.i -= c.k;
                c.j -= c.k;
                c.k  = 0;
            }

            // remove the min value if needed
            int min = c.i;

            if (c.j < min)
            {
                min = c.j;
            }
            if (c.k < min)
            {
                min = c.k;
            }
            if (min > 0)
            {
                c.i -= min;
                c.j -= min;
                c.k -= min;
            }
        }
Ejemplo n.º 17
0
        /// <summary>
        /// Find the normalized ijk coordinates of the hex centered on the indicated
        /// hex at the next finer aperture 7 clockwise resolution. Works in place.
        /// </summary>
        /// <param name="ijk">The ijk coordinates.</param>
        /// <!-- Based off 3.1.1 -->
        public static void _downAp7r(ref CoordIJK ijk)
        {
            // res r unit vectors in res r+1
            CoordIJK iVec = new CoordIJK {
                i = 3, j = 1, k = 0
            };
            CoordIJK jVec = new CoordIJK {
                i = 0, j = 3, k = 1
            };
            CoordIJK kVec = new CoordIJK {
                i = 1, j = 0, k = 3
            };

            _ijkScale(ref iVec, ijk.i);
            _ijkScale(ref jVec, ijk.j);
            _ijkScale(ref kVec, ijk.k);

            _ijkAdd(iVec, jVec, ref ijk);
            _ijkAdd(ijk, kVec, ref ijk);

            _ijkNormalize(ref ijk);
        }
Ejemplo n.º 18
0
        /// <summary>
        /// Convert an H3Index to the FaceIJK address on a specified icosahedral face.
        /// </summary>
        /// <param name="h"> The H3Index.</param>
        /// <param name="fijk">
        /// The FaceIJK address, initialized with the desired face
        /// and normalized base cell coordinates.
        /// </param>
        /// <returns>Returns 1 if the possibility of overage exists, otherwise 0.</returns>
        /// <!-- Based off 3.1.1 -->
        internal static int _h3ToFaceIjkWithInitializedFijk(H3Index h, ref FaceIJK fijk)
        {
            CoordIJK ijk = new CoordIJK(fijk.coord.i, fijk.coord.j, fijk.coord.k);
            int      res = H3_GET_RESOLUTION(h);

            // center base cell hierarchy is entirely on this face
            int possibleOverage = 1;

            if (!BaseCells._isBaseCellPentagon(H3_GET_BASE_CELL(h)) &&
                (res == 0 ||
                 fijk.coord.i == 0 && fijk.coord.j == 0 && fijk.coord.k == 0))
            {
                possibleOverage = 0;
            }

            for (int r = 1; r <= res; r++)
            {
                if (isResClassIII(r))
                {
                    // Class III == rotate ccw
                    CoordIJK._downAp7(ref ijk);
                }
                else
                {
                    // Class II == rotate cw
                    CoordIJK._downAp7r(ref ijk);
                }

                CoordIJK._neighbor(ref ijk, H3_GET_INDEX_DIGIT(h, r));
            }

            fijk.coord.i = ijk.i;
            fijk.coord.j = ijk.j;
            fijk.coord.k = ijk.k;
            return(possibleOverage);
        }
Ejemplo n.º 19
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);
        }
Ejemplo n.º 20
0
        /// <summary>
        /// Produces an index for ijk+ coordinates anchored by an origin.
        ///
        /// The coordinate space used by this function may have deleted
        /// regions or warping due to pentagonal distortion.
        ///
        /// Failure may occur if the coordinates are too far away from the origin
        /// or if the index is on the other side of a pentagon.
        /// </summary>
        /// <param name="origin">An anchoring index for the ijk+ coordinate system.</param>
        /// <param name="ijk">IJK+ Coordinates to find the index of</param>
        /// <param name="out_h3">The index will be placed here on success</param>
        /// <returns>0 on success, or another value on failure</returns>
        /// <!-- Based off 3.2.0 -->
        internal static int localIjkToH3(H3Index origin, CoordIJK ijk, ref H3Index out_h3)
        {
            int res            = H3Index.H3_GET_RESOLUTION(origin);
            int originBaseCell = H3Index.H3_GET_BASE_CELL(origin);
            int originOnPent   = BaseCells._isBaseCellPentagon(originBaseCell)
                                   ? 1
                                   : 0;

            // This logic is very similar to faceIjkToH3
            // initialize the index
            out_h3 = H3Index.H3_INIT;
            H3Index.H3_SET_MODE(ref out_h3, Constants.H3_HEXAGON_MODE);
            H3Index.H3_SET_RESOLUTION(ref out_h3, res);
            Direction dir;

            // check for res 0/base cell
            if (res == 0)
            {
                if (ijk.i > 1 || ijk.j > 1 || ijk.k > 1)
                {
                    // out of range input
                    return(1);
                }

                dir = CoordIJK._unitIjkToDigit(ref ijk);
                int newBaseCell = BaseCells._getBaseCellNeighbor(originBaseCell, dir);
                if (newBaseCell == BaseCells.INVALID_BASE_CELL)
                {
                    // Moving in an invalid direction off a pentagon.
                    return(1);
                }

                H3Index.H3_SET_BASE_CELL(ref out_h3, newBaseCell);
                return(0);
            }

            // we need to find the correct base cell offset (if any) for this H3 index;
            // start with the passed in base cell and resolution res ijk coordinates
            // in that base cell's coordinate system
            CoordIJK ijkCopy = new CoordIJK(ijk.i, ijk.j, ijk.k);

            // build the H3Index from finest res up
            // adjust r for the fact that the res 0 base cell offsets the indexing
            // digits
            for (int r = res - 1; r >= 0; r--)
            {
                CoordIJK lastIJK = ijkCopy;
                CoordIJK lastCenter;
                if (H3Index.isResClassIII(r + 1))
                {
                    // rotate ccw
                    CoordIJK._upAp7(ref ijkCopy);
                    lastCenter = ijkCopy;
                    CoordIJK._downAp7(ref lastCenter);
                }
                else
                {
                    // rotate cw
                    CoordIJK._upAp7r(ref ijkCopy);
                    lastCenter = ijkCopy;
                    CoordIJK._downAp7r(ref lastCenter);
                }

                CoordIJK diff = new CoordIJK();
                CoordIJK._ijkSub(ref lastIJK, ref lastCenter, ref diff);
                CoordIJK._ijkNormalize(ref diff);

                H3Index.H3_SET_INDEX_DIGIT(ref out_h3, r + 1, (ulong)CoordIJK._unitIjkToDigit(ref diff));
            }

            // ijkCopy should now hold the IJK of the base cell in the
            // coordinate system of the current base cell

            if (ijkCopy.i > 1 || ijkCopy.j > 1 || ijkCopy.k > 1)
            {
                // out of range input
                return(2);
            }

            // lookup the correct base cell
            dir = CoordIJK._unitIjkToDigit(ref ijkCopy);
            int baseCell = BaseCells._getBaseCellNeighbor(originBaseCell, dir);
            // If baseCell is invalid, it must be because the origin base cell is a
            // pentagon, and because pentagon base cells do not border each other,
            // baseCell must not be a pentagon.
            int indexOnPent =
                (baseCell == BaseCells.INVALID_BASE_CELL
                     ? 0
                     : BaseCells._isBaseCellPentagon(baseCell)
                         ? 1
                         : 0);

            if (dir != (int)Direction.CENTER_DIGIT)
            {
                // If the index is in a warped direction, we need to unwarp the base
                // cell direction. There may be further need to rotate the index digits.
                int pentagonRotations = 0;
                if (originOnPent != 0)
                {
                    Direction originLeadingDigit = H3Index._h3LeadingNonZeroDigit(origin);
                    pentagonRotations =
                        PENTAGON_ROTATIONS_REVERSE[(int)originLeadingDigit, (int)dir];
                    for (int i = 0; i < pentagonRotations; i++)
                    {
                        dir = CoordIJK._rotate60ccw(dir);
                    }

                    // The pentagon rotations are being chosen so that dir is not the
                    // deleted direction. If it still happens, it means we're moving
                    // into a deleted subsequence, so there is no index here.
                    if (dir == Direction.K_AXES_DIGIT)
                    {
                        return(3);
                    }

                    baseCell = BaseCells._getBaseCellNeighbor(originBaseCell, dir);

                    // indexOnPent does not need to be checked again since no pentagon
                    // base cells border each other.
                    if (baseCell == BaseCells.INVALID_BASE_CELL)
                    {
                        throw new Exception("assert(baseCell != BaseCells.INVALID_BASE_CELL);");
                    }

                    if (BaseCells._isBaseCellPolarPentagon(baseCell))
                    {
                        throw new Exception("assert(!BaseCells._isBaseCellPentagon(baseCell));");
                    }
                }

                // Now we can determine the relation between the origin and target base
                // cell.
                int baseCellRotations =
                    BaseCells.baseCellNeighbor60CCWRots[originBaseCell, (int)dir];
                if (baseCellRotations < 0)
                {
                    throw new Exception("assert(baseCellRotations >= 0);");
                }

                // Adjust for pentagon warping within the base cell. The base cell
                // should be in the right location, so now we need to rotate the index
                // back. We might not need to check for errors since we would just be
                // double mapping.
                if (indexOnPent != 0)
                {
                    Direction revDir =
                        BaseCells._getBaseCellDirection(baseCell, originBaseCell);

                    if (revDir == Direction.INVALID_DIGIT)
                    {
                        throw new Exception("assert(revDir != Direction.INVALID_DIGIT);");
                    }


                    // Adjust for the different coordinate space in the two base cells.
                    // This is done first because we need to do the pentagon rotations
                    // based on the leading digit in the pentagon's coordinate system.
                    for (int i = 0; i < baseCellRotations; i++)
                    {
                        out_h3 = H3Index._h3Rotate60ccw(ref out_h3);
                    }

                    Direction indexLeadingDigit = H3Index._h3LeadingNonZeroDigit(out_h3);
                    if (BaseCells._isBaseCellPolarPentagon(baseCell))
                    {
                        pentagonRotations =
                            PENTAGON_ROTATIONS_REVERSE_POLAR[(int)revDir, (int)indexLeadingDigit];
                    }
                    else
                    {
                        pentagonRotations =
                            PENTAGON_ROTATIONS_REVERSE_NONPOLAR[(int)revDir, (int)indexLeadingDigit];
                    }

                    if (pentagonRotations < 0)
                    {
                        throw new Exception("assert(pentagonRotations >= 0);");
                    }


                    for (int i = 0; i < pentagonRotations; i++)
                    {
                        out_h3 = H3Index._h3RotatePent60ccw(ref out_h3);
                    }
                }
                else
                {
                    if (pentagonRotations < 0)
                    {
                        throw new Exception("assert(pentagonRotations >= 0);");
                    }


                    for (int i = 0; i < pentagonRotations; i++)
                    {
                        out_h3 = H3Index._h3Rotate60ccw(ref out_h3);
                    }

                    // Adjust for the different coordinate space in the two base cells.
                    for (int i = 0; i < baseCellRotations; i++)
                    {
                        out_h3 = H3Index._h3Rotate60ccw(ref out_h3);
                    }
                }
            }
            else if (originOnPent != 0 && indexOnPent != 0)
            {
                int originLeadingDigit = (int)H3Index._h3LeadingNonZeroDigit(origin);
                int indexLeadingDigit  = (int)H3Index._h3LeadingNonZeroDigit(out_h3);

                int withinPentagonRotations =
                    PENTAGON_ROTATIONS_REVERSE[originLeadingDigit, indexLeadingDigit];
                if (withinPentagonRotations < 0)
                {
                    throw new Exception("assert(withinPentagonRotations >= 0);");
                }

                for (int i = 0; i < withinPentagonRotations; i++)
                {
                    out_h3 = H3Index._h3Rotate60ccw(ref out_h3);
                }
            }

            if (indexOnPent != 0)
            {
                // TODO: There are cases in h3ToLocalIjk which are failed but not
                // accounted for here - instead just fail if the recovered index is
                // invalid.
                if (H3Index._h3LeadingNonZeroDigit(out_h3) == Direction.K_AXES_DIGIT)
                {
                    return(4);
                }
            }

            H3Index.H3_SET_BASE_CELL(ref out_h3, baseCell);

            return(0);
        }
Ejemplo n.º 21
0
        /// <summary>
        /// Produces ijk+ coordinates for an index anchored by an origin.
        ///
        /// The coordinate space used by this function may have deleted
        /// regions or warping due to pentagonal distortion.
        ///
        /// Coordinates are only comparable if they come from the same
        /// origin index.
        ///
        /// Failure may occur if the index is too far away from the origin
        /// or if the index is on the other side of a pentagon.
        /// </summary>
        /// <param name="origin">An anchoring index for the ijk+ coordinate system</param>
        /// <param name="h3">Index to find the coordinates of</param>
        /// <param name="out_coord">ijk+ coordinates of the index will be placed here on success</param>
        /// <returns>0 on success, or another value on failure.</returns>
        /// <!-- Based off 3.2.0 -->
        static int h3ToLocalIjk(H3Index origin, H3Index h3, ref CoordIJK out_coord)
        {
            int res = H3Index.H3_GET_RESOLUTION(origin);

            if (res != H3Index.H3_GET_RESOLUTION(h3))
            {
                return(1);
            }

            int originBaseCell = H3Index.H3_GET_BASE_CELL(origin);
            int baseCell       = H3Index.H3_GET_BASE_CELL(h3);

            // Direction from origin base cell to index base cell
            Direction dir    = 0;
            Direction revDir = 0;

            if (originBaseCell != baseCell)
            {
                dir = BaseCells._getBaseCellDirection(originBaseCell, baseCell);
                if (dir == Direction.INVALID_DIGIT)
                {
                    // Base cells are not neighbors, can't unfold.
                    return(2);
                }

                revDir = BaseCells._getBaseCellDirection(baseCell, originBaseCell);
                if (revDir == Direction.INVALID_DIGIT)
                {
                    throw new Exception("assert(revDir != INVALID_DIGIT)");
                }
            }

            int originOnPent = (BaseCells._isBaseCellPentagon(originBaseCell)
                                    ? 1
                                    : 0);
            int indexOnPent = (BaseCells._isBaseCellPentagon(baseCell)
                                   ? 1
                                   : 0);

            FaceIJK indexFijk = new FaceIJK();

            if (dir != Direction.CENTER_DIGIT)
            {
                // Rotate index into the orientation of the origin base cell.
                // cw because we are undoing the rotation into that base cell.
                int baseCellRotations = BaseCells.baseCellNeighbor60CCWRots[originBaseCell, (int)dir];
                if (indexOnPent != 0)
                {
                    for (int i = 0; i < baseCellRotations; i++)
                    {
                        h3 = H3Index._h3RotatePent60cw(h3);

                        revDir = CoordIJK._rotate60cw(revDir);
                        if (revDir == Direction.K_AXES_DIGIT)
                        {
                            revDir = CoordIJK._rotate60cw(revDir);
                        }
                    }
                }
                else
                {
                    for (int i = 0; i < baseCellRotations; i++)
                    {
                        h3 = H3Index._h3Rotate60cw(ref h3);

                        revDir = CoordIJK._rotate60cw(revDir);
                    }
                }
            }

            // Face is unused. This produces coordinates in base cell coordinate space.
            H3Index._h3ToFaceIjkWithInitializedFijk(h3, ref indexFijk);

            if (dir != Direction.CENTER_DIGIT)
            {
                if (baseCell == originBaseCell)
                {
                    throw new Exception("assert(baseCell != originBaseCell);");
                }

                if ((originOnPent != 0) && (indexOnPent != 0))
                {
                    throw new Exception("assert(!(originOnPent && indexOnPent));");
                }

                int pentagonRotations  = 0;
                int directionRotations = 0;

                if (originOnPent != 0)
                {
                    int originLeadingDigit = (int)H3Index._h3LeadingNonZeroDigit(origin);

                    if ((H3Index.isResClassIII(res) &&
                         FAILED_DIRECTIONS_III[originLeadingDigit, (int)dir]) ||
                        (!H3Index.isResClassIII(res) &&
                         FAILED_DIRECTIONS_II[originLeadingDigit, (int)dir]))
                    {
                        // TODO this part of the pentagon might not be unfolded
                        // correctly.
                        return(3);
                    }

                    directionRotations = PENTAGON_ROTATIONS[originLeadingDigit, (int)dir];
                    pentagonRotations  = directionRotations;
                }
                else if (indexOnPent != 0)
                {
                    int indexLeadingDigit = (int)H3Index._h3LeadingNonZeroDigit(h3);

                    if ((H3Index.isResClassIII(res) &&
                         FAILED_DIRECTIONS_III[indexLeadingDigit, (int)revDir]) ||
                        (!H3Index.isResClassIII(res) &&
                         FAILED_DIRECTIONS_II[indexLeadingDigit, (int)revDir]))
                    {
                        // TODO this part of the pentagon might not be unfolded
                        // correctly.
                        return(4);
                    }

                    pentagonRotations = PENTAGON_ROTATIONS[(int)revDir, indexLeadingDigit];
                }

                if (pentagonRotations < 0)
                {
                    throw new Exception("assert(pentagonRotations >= 0);");
                }

                if (directionRotations < 0)
                {
                    throw new Exception("assert(directionRotations >= 0);");
                }



                for (int i = 0; i < pentagonRotations; i++)
                {
                    CoordIJK._ijkRotate60cw(ref indexFijk.coord);
                }

                CoordIJK offset = new CoordIJK();
                CoordIJK._neighbor(ref offset, dir);
                // Scale offset based on resolution
                for (int r = res - 1; r >= 0; r--)
                {
                    if (H3Index.isResClassIII(r + 1))
                    {
                        // rotate ccw
                        CoordIJK._downAp7(ref offset);
                    }
                    else
                    {
                        // rotate cw
                        CoordIJK._downAp7r(ref offset);
                    }
                }

                for (int i = 0; i < directionRotations; i++)
                {
                    CoordIJK._ijkRotate60cw(ref offset);
                }

                // Perform necessary translation
                CoordIJK._ijkAdd(indexFijk.coord, offset, ref indexFijk.coord);
                CoordIJK._ijkNormalize(ref indexFijk.coord);
            }
            else if (originOnPent != 0 && indexOnPent != 0)
            {
                // If the origin and index are on pentagon, and we checked that the base
                // cells are the same or neighboring, then they must be the same base
                // cell.
                if (baseCell != originBaseCell)
                {
                    throw new Exception("assert(baseCell == originBaseCell);");
                }


                int originLeadingDigit = (int)H3Index._h3LeadingNonZeroDigit(origin);
                int indexLeadingDigit  = (int)H3Index._h3LeadingNonZeroDigit(h3);

                if (FAILED_DIRECTIONS_III[originLeadingDigit, indexLeadingDigit] ||
                    FAILED_DIRECTIONS_II[originLeadingDigit, indexLeadingDigit])
                {
                    // TODO this part of the pentagon might not be unfolded
                    // correctly.
                    return(5);
                }

                int withinPentagonRotations =
                    PENTAGON_ROTATIONS[originLeadingDigit, indexLeadingDigit];

                for (int i = 0; i < withinPentagonRotations; i++)
                {
                    CoordIJK._ijkRotate60cw(ref indexFijk.coord);
                }
            }

            out_coord = indexFijk.coord;
            return(0);
        }
Ejemplo n.º 22
0
        /// <summary>
        /// Generates the cell boundary in spherical coordinates for a cell given by a
        /// FaceIJK address at a specified resolution.
        /// </summary>
        /// <param name="h">The FaceIJK address of the cell.</param>
        /// <param name="res">The H3 resolution of the cell.</param>
        /// <param name="isPentagon">Whether or not the cell is a pentagon.</param>
        /// <param name="g">The spherical coordinates of the cell boundary.</param>
        /// <!-- Based off 3.1.1 -->
        public static void _faceIjkToGeoBoundary(ref FaceIJK h, int res, int isPentagon, ref GeoBoundary g)
        {
            if (isPentagon > 0)
            {
                _faceIjkPentToGeoBoundary(ref h, res, ref g);
                return;
            }

            // the vertexes of an origin-centered cell in a Class II resolution on a
            // substrate grid with aperture sequence 33r. The aperture 3 gets us the
            // vertices, and the 3r gets us back to Class II.
            // vertices listed ccw from the i-axes
            CoordIJK[] vertsCII =
            {
                new CoordIJK {
                    i = 2, j = 1, k = 0
                },                                  // 0
                new CoordIJK {
                    i = 1, j = 2, k = 0
                },                                  // 1
                new CoordIJK {
                    i = 0, j = 2, k = 1
                },                                  // 2
                new CoordIJK {
                    i = 0, j = 1, k = 2
                },                                  // 3
                new CoordIJK {
                    i = 1, j = 0, k = 2
                },                                  // 4
                new CoordIJK {
                    i = 2, j = 0, k = 1
                }                                  // 5
            };

            // the vertexes of an origin-centered cell in a Class III resolution on a
            // substrate grid with aperture sequence 33r7r. The aperture 3 gets us the
            // vertices, and the 3r7r gets us to Class II.
            // vertices listed ccw from the i-axes
            CoordIJK[] vertsCIII =
            {
                new CoordIJK {
                    i = 5, j = 4, k = 0
                },                                  // 0
                new CoordIJK {
                    i = 1, j = 5, k = 0
                },                                  // 1
                new CoordIJK {
                    i = 0, j = 5, k = 4
                },                                  // 2
                new CoordIJK {
                    i = 0, j = 1, k = 5
                },                                  // 3
                new CoordIJK {
                    i = 4, j = 0, k = 5
                },                                  // 4
                new CoordIJK {
                    i = 5, j = 0, k = 1
                }                                  // 5
            };

            // get the correct set of substrate vertices for this resolution
            CoordIJK[] verts;
            if (H3Index.isResClassIII(res))
            {
                verts = vertsCIII;
            }
            else
            {
                verts = vertsCII;
            }

            // adjust the center point to be in an aperture 33r substrate grid
            // these should be composed for speed
            FaceIJK centerIJK = new FaceIJK(h.face, new CoordIJK(h.coord.i, h.coord.j, h.coord.k));

            CoordIJK._downAp3(ref centerIJK.coord);
            CoordIJK._downAp3r(ref centerIJK.coord);

            // if res is Class III we need to add a cw aperture 7 to get to
            // icosahedral Class II
            int adjRes = res;

            if (H3Index.isResClassIII(res))
            {
                CoordIJK._downAp7r(ref centerIJK.coord);
                adjRes++;
            }

            // The center point is now in the same substrate grid as the origin
            // cell vertices. Add the center point substate coordinates
            // to each vertex to translate the vertices to that cell.
            FaceIJK[] fijkVerts = new FaceIJK[Constants.NUM_HEX_VERTS];
            for (int v = 0; v < Constants.NUM_HEX_VERTS; v++)
            {
                fijkVerts[v]      = new FaceIJK();
                fijkVerts[v].face = centerIJK.face;
                CoordIJK._ijkAdd(centerIJK.coord, verts[v], ref fijkVerts[v].coord);
                CoordIJK._ijkNormalize(ref fijkVerts[v].coord);
            }

            // convert each vertex to lat/lon
            // adjust the face of each vertex as appropriate and introduce
            // edge-crossing vertices as needed
            g.numVerts = 0;
            int lastFace    = -1;
            int lastOverage = 0; // 0: none; 1: edge; 2: overage

            for (int vert = 0; vert < Constants.NUM_HEX_VERTS + 1; vert++)
            {
                int v = vert % Constants.NUM_HEX_VERTS;

                FaceIJK fijk = new FaceIJK
                               (
                    fijkVerts[v].face,
                    new CoordIJK(fijkVerts[v].coord.i, fijkVerts[v].coord.j, fijkVerts[v].coord.k)
                               );

                int pentLeading4 = 0;
                int overage      = _adjustOverageClassII(ref fijk, adjRes, pentLeading4, 1);

                /*
                 * Check for edge-crossing. Each face of the underlying icosahedron is a
                 * different projection plane. So if an edge of the hexagon crosses an
                 * icosahedron edge, an additional vertex must be introduced at that
                 * intersection point. Then each half of the cell edge can be projected
                 * to geographic coordinates using the appropriate icosahedron face
                 * projection. Note that Class II cell edges have vertices on the face
                 * edge, with no edge line intersections.
                 */
                if (H3Index.isResClassIII(res) && vert > 0 && fijk.face != lastFace &&
                    lastOverage != 1)
                {
                    // find hex2d of the two vertexes on original face
                    int   lastV   = (v + 5) % Constants.NUM_HEX_VERTS;
                    Vec2d orig2d0 = new Vec2d();
                    CoordIJK._ijkToHex2d(fijkVerts[lastV].coord, ref orig2d0);

                    Vec2d orig2d1 = new Vec2d();
                    CoordIJK._ijkToHex2d(fijkVerts[v].coord, ref orig2d1);

                    // find the appropriate icosa face edge vertexes
                    int   maxDim = maxDimByCIIres[adjRes];
                    Vec2d v0     = new Vec2d(3.0 * maxDim, 0.0);
                    Vec2d v1     = new Vec2d(-1.5 * maxDim, 3.0 * Constants.M_SQRT3_2 * maxDim);
                    Vec2d v2     = new Vec2d(-1.5 * maxDim, -3.0 * Constants.M_SQRT3_2 * maxDim);

                    int   face2 = lastFace == centerIJK.face ? fijk.face : lastFace;
                    Vec2d edge0 = new Vec2d();
                    Vec2d edge1 = new Vec2d();
                    switch (adjacentFaceDir[centerIJK.face, face2])
                    {
                    case IJ:
                        edge0 = v0;
                        edge1 = v1;
                        break;

                    case JK:
                        edge0 = v1;
                        edge1 = v2;
                        break;

                    case KI:
                    default:
                        if (adjacentFaceDir[centerIJK.face, face2] != KI)
                        {
                            throw new Exception("Default failure in _faceIjkToGeoBoundary");
                        }

                        edge0 = v2;
                        edge1 = v0;
                        break;
                    }

                    // find the intersection and add the lat/lon point to the result
                    Vec2d inter = new Vec2d();
                    Vec2d._v2dIntersect(orig2d0, orig2d1, edge0, edge1, ref inter);

                    /*
                     * If a point of intersection occurs at a hexagon vertex, then each
                     * adjacent hexagon edge will lie completely on a single icosahedron
                     * face, and no additional vertex is required.
                     */
                    bool isIntersectionAtVertex =
                        Vec2d._v2dEquals(orig2d0, inter) || Vec2d._v2dEquals(orig2d1, inter);
                    if (!isIntersectionAtVertex)
                    {
                        var temp_verts = g.verts[g.numVerts];
                        _hex2dToGeo(ref inter, centerIJK.face, adjRes, 1, ref temp_verts);
                        g.verts[g.numVerts] = temp_verts;
                        g.numVerts++;
                        Debug.WriteLine(string.Format("!IsIntersection {0}", g.numVerts));
                    }
                }

                // convert vertex to lat/lon and add to the result
                // vert == NUM_HEX_VERTS is only used to test for possible intersection
                // on last edge
                if (vert < Constants.NUM_HEX_VERTS)
                {
                    Vec2d vec = new Vec2d();
                    CoordIJK._ijkToHex2d(fijk.coord, ref vec);
                    var temp_verts = g.verts[g.numVerts];
                    _hex2dToGeo(ref vec, fijk.face, adjRes, 1, ref temp_verts);
                    g.verts[g.numVerts] = temp_verts;
                    g.numVerts++;
                }

                lastFace    = fijk.face;
                lastOverage = overage;
            }
        }
Ejemplo n.º 23
0
 public FaceIJK(int f, CoordIJK cijk)
 {
     face  = f;
     coord = cijk;
 }
Ejemplo n.º 24
0
 public FaceIJK()
 {
     face  = 0;
     coord = new CoordIJK(0, 0, 0);
 }
Ejemplo n.º 25
0
        /// <summary>
        /// Generates the cell boundary in spherical coordinates for a pentagonal cell
        /// given by a FaceIJK address at a specified resolution.
        /// </summary>
        /// <param name="h">The FaceIJK address of the pentagonal cell.</param>
        /// <param name="res">The H3 resolution of the cell.</param>
        /// <param name="g">The spherical coordinates of the cell boundary.</param>
        /// <!-- Based off 3.1.1 -->
        public static void _faceIjkPentToGeoBoundary(ref FaceIJK h, int res, ref GeoBoundary g)
        {
            // the vertexes of an origin-centered pentagon in a Class II resolution on a
            // substrate grid with aperture sequence 33r. The aperture 3 gets us the
            // vertices, and the 3r gets us back to Class II.
            // vertices listed ccw from the i-axes
            CoordIJK[] vertsCII =
            {
                new CoordIJK(2, 1, 0), // 0
                new CoordIJK(1, 2, 0), // 1
                new CoordIJK(0, 2, 1), // 2
                new CoordIJK(0, 1, 2), // 3
                new CoordIJK(1, 0, 2)  // 4
            };

            // the vertexes of an origin-centered pentagon in a Class III resolution on
            // a substrate grid with aperture sequence 33r7r. The aperture 3 gets us the
            // vertices, and the 3r7r gets us to Class II. vertices listed ccw from the
            // i-axes
            CoordIJK[] vertsCIII =
            {
                new CoordIJK(5, 4, 0), // 0
                new CoordIJK(1, 5, 0), // 1
                new CoordIJK(0, 5, 4), // 2
                new CoordIJK(0, 1, 5), // 3
                new CoordIJK(4, 0, 5)  // 4
            };

            // get the correct set of substrate vertices for this resolution
            List <CoordIJK> verts = new List <CoordIJK>();

            verts = H3Index.isResClassIII(res)
                ? vertsCIII.ToList()
                : vertsCII.ToList();

            // adjust the center point to be in an aperture 33r substrate grid
            // these should be composed for speed
            FaceIJK centerIJK = new FaceIJK();

            centerIJK.face  = h.face;
            centerIJK.coord = new CoordIJK(h.coord.i, h.coord.j, h.coord.k);
            CoordIJK._downAp3(ref centerIJK.coord);
            CoordIJK._downAp3r(ref centerIJK.coord);

            // if res is Class III we need to add a cw aperture 7 to get to
            // icosahedral Class II
            int adjRes = res;

            if (H3Index.isResClassIII(res))
            {
                CoordIJK._downAp7r(ref centerIJK.coord);
                adjRes++;
            }

            // The center point is now in the same substrate grid as the origin
            // cell vertices. Add the center point substate coordinates
            // to each vertex to translate the vertices to that cell.
            FaceIJK[] fijkVerts = new FaceIJK[Constants.NUM_PENT_VERTS];
            for (int v = 0; v < Constants.NUM_PENT_VERTS; v++)
            {
                fijkVerts[v]      = new FaceIJK();
                fijkVerts[v].face = centerIJK.face;
                CoordIJK._ijkAdd(centerIJK.coord, verts[v], ref fijkVerts[v].coord);
                CoordIJK._ijkNormalize(ref fijkVerts[v].coord);
            }

            // convert each vertex to lat/lon
            // adjust the face of each vertex as appropriate and introduce
            // edge-crossing vertices as needed
            g.numVerts = 0;
            for (int i = 0; i < g.verts.Count; i++)
            {
                g.verts[i] = new GeoCoord();
            }
            FaceIJK lastFijk = new FaceIJK();

            for (int vert = 0; vert < Constants.NUM_PENT_VERTS + 1; vert++)
            {
                int v = vert % Constants.NUM_PENT_VERTS;

                FaceIJK fijk = fijkVerts[v];

                int pentLeading4 = 0;
                int overage      = _adjustOverageClassII(ref fijk, adjRes, pentLeading4, 1);
                if (overage == 2) // in a different triangle
                {
                    while (true)
                    {
                        overage = _adjustOverageClassII(ref fijk, adjRes, pentLeading4, 1);
                        if (overage != 2) // not in a different triangle
                        {
                            break;
                        }
                    }
                }

                // all Class III pentagon edges cross icosa edges
                // note that Class II pentagons have vertices on the edge,
                // not edge intersections
                if (H3Index.isResClassIII(res) && vert > 0)
                {
                    // find hex2d of the two vertexes on the last face

                    FaceIJK tmpFijk = new FaceIJK(fijk.face, new CoordIJK(fijk.coord.i, fijk.coord.j, fijk.coord.k));

                    Vec2d orig2d0 = new Vec2d();
                    CoordIJK._ijkToHex2d(lastFijk.coord, ref orig2d0);

                    int currentToLastDir = adjacentFaceDir[tmpFijk.face, lastFijk.face];

                    FaceOrientIJK fijkOrient =
                        new FaceOrientIJK(
                            faceNeighbors[tmpFijk.face, currentToLastDir].face,
                            faceNeighbors[tmpFijk.face, currentToLastDir].translate.i,
                            faceNeighbors[tmpFijk.face, currentToLastDir].translate.j,
                            faceNeighbors[tmpFijk.face, currentToLastDir].translate.k,
                            faceNeighbors[tmpFijk.face, currentToLastDir].ccwRot60
                            );

//                        faceNeighbors[tmpFijk.face,currentToLastDir];

                    tmpFijk.face = fijkOrient.face;
                    //CoordIJK ijk = tmpFijk.coord;
                    CoordIJK ijk = new CoordIJK(tmpFijk.coord.i, tmpFijk.coord.j, tmpFijk.coord.k);

                    // rotate and translate for adjacent face
                    for (int i = 0; i < fijkOrient.ccwRot60; i++)
                    {
                        CoordIJK._ijkRotate60ccw(ref ijk);
                    }

                    CoordIJK transVec = fijkOrient.translate;
                    CoordIJK._ijkScale(ref transVec, unitScaleByCIIres[adjRes] * 3);
                    CoordIJK._ijkAdd(ijk, transVec, ref ijk);
                    CoordIJK._ijkNormalize(ref ijk);

                    Vec2d orig2d1 = new Vec2d();
                    CoordIJK._ijkToHex2d(ijk, ref orig2d1);

                    // find the appropriate icosa face edge vertexes
                    int   maxDim = maxDimByCIIres[adjRes];
                    Vec2d v0     = new Vec2d(3.0 * maxDim, 0.0);
                    Vec2d v1     = new Vec2d(-1.5 * maxDim, 3.0 * Constants.M_SQRT3_2 * maxDim);
                    Vec2d v2     = new Vec2d(-1.5 * maxDim, -3.0 * Constants.M_SQRT3_2 * maxDim);

                    Vec2d edge0 = new Vec2d();
                    Vec2d edge1 = new Vec2d();
                    switch (adjacentFaceDir[tmpFijk.face, fijk.face])
                    {
                    case IJ:
                        edge0 = v0;
                        edge1 = v1;
                        break;

                    case JK:
                        edge0 = v1;
                        edge1 = v2;
                        break;

                    case KI:
                    default:
                        if (adjacentFaceDir[tmpFijk.face, fijk.face] != KI)
                        {
                            throw new Exception("assert(adjacentFaceDir[tmpFijk.face][fijk.face] == KI);");
                        }
                        edge0 = v2;
                        edge1 = v0;
                        break;
                    }

                    // find the intersection and add the lat/lon point to the result
                    Vec2d inter = new Vec2d();
                    Vec2d._v2dIntersect(orig2d0, orig2d1, edge0, edge1, ref inter);
                    var gnv = g.verts[g.numVerts];
                    _hex2dToGeo(ref inter, tmpFijk.face, adjRes, 1, ref gnv);
                    g.verts[g.numVerts] = gnv;
                    g.numVerts++;
                }

                // convert vertex to lat/lon and add to the result
                // vert == NUM_PENT_VERTS is only used to test for possible intersection
                // on last edge
                if (vert < Constants.NUM_PENT_VERTS)
                {
                    Vec2d vec = new Vec2d();
                    CoordIJK._ijkToHex2d(fijk.coord, ref vec);
                    var gnv = g.verts[g.numVerts];
                    _hex2dToGeo(ref vec, fijk.face, adjRes, 1, ref gnv);
                    g.verts[g.numVerts] = gnv;
                    g.numVerts++;
                }
                lastFijk = fijk;
            }
        }
Ejemplo n.º 26
0
 public FaceOrientIJK(int f, int i, int j, int k, int c)
 {
     face      = f;
     translate = new CoordIJK(i, j, k);
     ccwRot60  = c;
 }
Ejemplo n.º 27
0
        /// <summary>
        /// Convert an H3Index to a FaceIJK address.
        /// </summary>
        /// <param name="h"> The H3Index.</param>
        /// <param name="fijk"> The corresponding FaceIJK address.</param>
        /// <!-- Based off 3.1.1 -->
        public static void _h3ToFaceIjk(H3Index h, ref FaceIJK fijk)
        {
            int baseCell = H3_GET_BASE_CELL(h);

            // adjust for the pentagonal missing sequence; all of sub-sequence 5 needs
            // to be adjusted (and some of sub-sequence 4 below)
            if (BaseCells._isBaseCellPentagon(baseCell) && _h3LeadingNonZeroDigit(h) == Direction.IK_AXES_DIGIT)
            {
                h = _h3Rotate60cw(ref h);
            }

            // start with the "home" face and ijk+ coordinates for the base cell of c
            fijk = new FaceIJK(
                BaseCells.baseCellData[baseCell].homeFijk.face,
                new CoordIJK(
                    BaseCells.baseCellData[baseCell].homeFijk.coord.i,
                    BaseCells.baseCellData[baseCell].homeFijk.coord.j,
                    BaseCells.baseCellData[baseCell].homeFijk.coord.k
                    )
                );
            //fijk = BaseCells.baseCellData[baseCell].homeFijk;
            if (_h3ToFaceIjkWithInitializedFijk(h, ref fijk) == 0)
            {
                return; // no overage is possible; h lies on this face
            }

            // if we're here we have the potential for an "overage"; i.e., it is
            // possible that c lies on an adjacent face
            CoordIJK origIJK = new CoordIJK(fijk.coord.i, fijk.coord.j, fijk.coord.k);

            // if we're in Class III, drop into the next finer Class II grid
            int res = H3_GET_RESOLUTION(h);

            if (isResClassIII(res))
            {
                // Class III
                CoordIJK._downAp7r(ref fijk.coord);
                res++;
            }

            // adjust for overage if needed
            // a pentagon base cell with a leading 4 digit requires special handling
            bool pentLeading4 =
                (BaseCells._isBaseCellPentagon(baseCell) && _h3LeadingNonZeroDigit(h) == Direction.I_AXES_DIGIT);

            if (FaceIJK._adjustOverageClassII(ref fijk, res, pentLeading4 ? 1 : 0, 0) > 0)
            {
                // if the base cell is a pentagon we have the potential for secondary
                // overages
                if (BaseCells._isBaseCellPentagon(baseCell))
                {
                    while (true)
                    {
                        if (FaceIJK._adjustOverageClassII(ref fijk, res, 0, 0) == 0)
                        {
                            break;
                        }
                    }
                }

                if (res != H3_GET_RESOLUTION(h))
                {
                    CoordIJK._upAp7r(ref fijk.coord);
                }
            }
            else if (res != H3_GET_RESOLUTION(h))
            {
                fijk.coord = new CoordIJK(origIJK.i, origIJK.j, origIJK.k);
            }
        }
Ejemplo n.º 28
0
        /// <summary>
        /// Adjusts a FaceIJK address in place so that the resulting cell address is
        /// relative to the correct icosahedral face.
        /// </summary>
        /// <param name="fijk">The FaceIJK address of the cell.</param>
        /// <param name="res">The H3 resolution of the cell.</param>
        /// <param name="pentLeading4">Whether or not the cell is a pentagon with a leading digit 4</param>
        /// <param name="substrate">Whether or not the cell is in a substrate grid.</param>
        /// <returns>
        /// 0 if on original face (no overage); 1 if on face edge (only occurs
        /// on substrate grids); 2 if overage on new face interior
        /// </returns>
        /// <!-- Based off 3.1.1 -->
        public static int _adjustOverageClassII(ref FaceIJK fijk, int res, int pentLeading4, int substrate)
        {
            int overage = 0;

            CoordIJK ijk = new CoordIJK(fijk.coord.i, fijk.coord.j, fijk.coord.k);

            // get the maximum dimension value; scale if a substrate grid
            int maxDim = maxDimByCIIres[res];

            if (substrate != 0)
            {
                maxDim *= 3;
            }

            // check for overage
            if ((substrate != 0) && ijk.i + ijk.j + ijk.k == maxDim) // on edge
            {
                overage = 1;
            }
            else if (ijk.i + ijk.j + ijk.k > maxDim)  // overage
            {
                overage = 2;

                FaceOrientIJK fijkOrient = new FaceOrientIJK();
                if (ijk.k > 0)
                {
                    if (ijk.j > 0) // jk "quadrant"
                    {
                        //fijkOrient = faceNeighbors[fijk.face,JK];
                        fijkOrient = new FaceOrientIJK(
                            faceNeighbors[fijk.face, JK].face,
                            faceNeighbors[fijk.face, JK].translate.i,
                            faceNeighbors[fijk.face, JK].translate.j,
                            faceNeighbors[fijk.face, JK].translate.k,
                            faceNeighbors[fijk.face, JK].ccwRot60);
                    }
                    else  // ik "quadrant"
                    {
                        //fijkOrient = faceNeighbors[fijk.face,KI];
                        fijkOrient = new FaceOrientIJK(
                            faceNeighbors[fijk.face, KI].face,
                            faceNeighbors[fijk.face, KI].translate.i,
                            faceNeighbors[fijk.face, KI].translate.j,
                            faceNeighbors[fijk.face, KI].translate.k,
                            faceNeighbors[fijk.face, KI].ccwRot60);

                        // adjust for the pentagonal missing sequence
                        if (pentLeading4 != 0)
                        {
                            // translate origin to center of pentagon
                            CoordIJK origin = new CoordIJK();
                            CoordIJK._setIJK(ref origin, maxDim, 0, 0);
                            CoordIJK tmp = new CoordIJK();
                            CoordIJK._ijkSub(ref ijk, ref origin, ref tmp);
                            // rotate to adjust for the missing sequence
                            CoordIJK._ijkRotate60cw(ref tmp);
                            // translate the origin back to the center of the triangle
                            CoordIJK._ijkAdd(tmp, origin, ref ijk);
                        }
                    }
                }
                else // ij "quadrant"
                {
//                    fijkOrient = faceNeighbors[fijk.face,IJ];
                    fijkOrient = new FaceOrientIJK(
                        faceNeighbors[fijk.face, IJ].face,
                        faceNeighbors[fijk.face, IJ].translate.i,
                        faceNeighbors[fijk.face, IJ].translate.j,
                        faceNeighbors[fijk.face, IJ].translate.k,
                        faceNeighbors[fijk.face, IJ].ccwRot60);
                }

                fijk.face = fijkOrient.face;

                // rotate and translate for adjacent face
                for (int i = 0; i < fijkOrient.ccwRot60; i++)
                {
                    CoordIJK._ijkRotate60ccw(ref ijk);
                }

//                CoordIJK transVec = fijkOrient.translate;
                CoordIJK transVec = new CoordIJK
                                    (
                    fijkOrient.translate.i,
                    fijkOrient.translate.j,
                    fijkOrient.translate.k
                                    );
                int unitScale = unitScaleByCIIres[res];
                if (substrate != 0)
                {
                    unitScale *= 3;
                }
                CoordIJK._ijkScale(ref transVec, unitScale);
                CoordIJK._ijkAdd(ijk, transVec, ref ijk);
                CoordIJK._ijkNormalize(ref ijk);

                // overage points on pentagon boundaries can end up on edges
                if ((substrate != 0) && ijk.i + ijk.j + ijk.k == maxDim) // on edge
                {
                    overage = 1;
                }
            }

            fijk.coord = ijk;
            return(overage);
        }
Ejemplo n.º 29
0
        /// <summary>
        /// Convert an FaceIJK address to the corresponding H3Index.
        /// </summary>
        /// <param name="fijk"> The FaceIJK address.</param>
        /// <param name="res">The cell resolution.</param>
        /// <returns>The encoded H3Index (or 0 on failure).</returns>
        /// <!-- Based off 3.1.1 -->
        public static H3Index _faceIjkToH3(ref FaceIJK fijk, int res)
        {
            // initialize the index
            H3Index h = H3_INIT;

            H3_SET_MODE(ref h, Constants.H3_HEXAGON_MODE);
            H3_SET_RESOLUTION(ref h, res);

            // check for res 0/base cell
            if (res == 0)
            {
                if (fijk.coord.i > BaseCells.MAX_FACE_COORD ||
                    fijk.coord.j > BaseCells.MAX_FACE_COORD ||
                    fijk.coord.k > BaseCells.MAX_FACE_COORD)
                {
                    // out of range input
                    return(H3_INVALID_INDEX);
                }

                H3_SET_BASE_CELL(ref h, BaseCells._faceIjkToBaseCell(fijk));
                return(h);
            }

            // we need to find the correct base cell FaceIJK for this H3 index;
            // start with the passed in face and resolution res ijk coordinates
            // in that face's coordinate system
            //FaceIJK fijkBC = new FaceIJK(fijk.face, fijk.coord);
            FaceIJK fijkBC = new FaceIJK(fijk.face, new CoordIJK(fijk.coord.i, fijk.coord.j, fijk.coord.k));

            // build the H3Index from finest res up
            // adjust r for the fact that the res 0 base cell offsets the indexing digits
            CoordIJK ijk = fijkBC.coord;

            for (int r = res - 1; r >= 0; r--)
            {
                //CoordIJK lastIJK = ijk;
                CoordIJK lastIJK    = new CoordIJK(ijk.i, ijk.j, ijk.k);
                CoordIJK lastCenter = new CoordIJK();
                if (isResClassIII(r + 1))
                {
                    // rotate ccw
                    CoordIJK._upAp7(ref ijk);
                    lastCenter.i = ijk.i;
                    lastCenter.j = ijk.j;
                    lastCenter.k = ijk.k;
                    CoordIJK._downAp7(ref lastCenter);
                }
                else
                {
                    // rotate cw
                    CoordIJK._upAp7r(ref ijk);
                    lastCenter.i = ijk.i;
                    lastCenter.j = ijk.j;
                    lastCenter.k = ijk.k;
                    CoordIJK._downAp7r(ref lastCenter);
                }

                CoordIJK diff = new CoordIJK();
                CoordIJK._ijkSub(ref lastIJK, ref lastCenter, ref diff);
                CoordIJK._ijkNormalize(ref diff);

                H3_SET_INDEX_DIGIT(ref h, r + 1,
                                   (ulong)CoordIJK._unitIjkToDigit(ref diff));
            }

            // fijkBC should now hold the IJK of the base cell in the
            // coordinate system of the current face
            if (fijkBC.coord.i > BaseCells.MAX_FACE_COORD ||
                fijkBC.coord.j > BaseCells.MAX_FACE_COORD ||
                fijkBC.coord.k > BaseCells.MAX_FACE_COORD)
            {
                // out of range input
                return(H3_INVALID_INDEX);
            }

            // lookup the correct base cell
            int baseCell = BaseCells._faceIjkToBaseCell(fijkBC);

            H3_SET_BASE_CELL(ref h, baseCell);

            // rotate if necessary to get canonical base cell orientation
            // for this base cell
            int numRots = BaseCells._faceIjkToBaseCellCCWrot60(fijkBC);

            if (BaseCells._isBaseCellPentagon(baseCell))
            {
                // force rotation out of missing k-axes sub-sequence
                if (_h3LeadingNonZeroDigit(h) == Direction.K_AXES_DIGIT)
                {
                    // check for a cw/ccw offset face; default is ccw
                    if (BaseCells._baseCellIsCwOffset(baseCell, fijkBC.face))
                    {
                        h = _h3Rotate60cw(ref h);
                    }
                    else
                    {
                        h = _h3Rotate60ccw(ref h);
                    }
                }

                for (int i = 0; i < numRots; i++)
                {
                    h = _h3RotatePent60ccw(ref h);
                }
            }
            else
            {
                for (int i = 0; i < numRots; i++)
                {
                    h = _h3Rotate60ccw(ref h);
                }
            }

            return(h);
        }
Ejemplo n.º 30
0
 /// <summary>
 /// Sets an IJK coordinate to the specified component values.
 /// </summary>
 /// <param name="ijk">The IJK coordinate to set.</param>
 /// <param name="i">The desired i component value.</param>
 /// <param name="j">The desired j component value.</param>
 /// <param name="k">The desired k component value.</param>
 /// <!-- Based off 3.1.1 -->
 public static void _setIJK(ref CoordIJK ijk, int i, int j, int k)
 {
     ijk.i = i;
     ijk.j = j;
     ijk.k = k;
 }