/// <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); }