Exemplo n.º 1
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);
        }