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