/// <summary> /// Returns a unidirectional edge H3 index based on the provided origin and /// destination /// </summary> /// <param name="origin">The origin H3 hexagon index</param> /// <param name="destination">The destination H3 hexagon index</param> /// <returns>The unidirectional edge H3Index, or 0 on failure.</returns> /// <!-- Based off 3.1.1 --> public static H3Index getH3UnidirectionalEdge(H3Index origin, H3Index destination) { // Short-circuit and return an invalid index value if they are not neighbors if (h3IndexesAreNeighbors(origin, destination) == 0) { return(H3Index.H3_INVALID_INDEX); } // Otherwise, determine the IJK direction from the origin to the destination H3Index output = origin; H3Index.H3_SET_MODE(ref output, Constants.H3_UNIEDGE_MODE); // Checks each neighbor, in order, to determine which direction the // destination neighbor is located. Skips CENTER_DIGIT since that // would be this index. for (var direction = Direction.K_AXES_DIGIT; direction < Direction.NUM_DIGITS; direction++) { int rotations = 0; H3Index neighbor = Algos.h3NeighborRotations(origin, direction, ref rotations); if (neighbor == destination) { H3Index.H3_SET_RESERVED_BITS(ref output, (ulong)direction); return(output); } } // This should be impossible, return an invalid H3Index in this case; return(H3Index.H3_INVALID_INDEX); // LCOV_EXCL_LINE }
/// <summary> /// k-rings produces indices within k distance of the origin index. /// /// k-ring 0 is defined as the origin index, k-ring 1 is defined as k-ring 0 and /// all neighboring indices, and so on. /// /// Output is placed in the provided array in no particular order. Elements of /// the output array may be left zero, as can happen when crossing a pentagon. /// </summary> /// <param name="origin">Origin location</param> /// <param name="k">k >= 0</param> /// <param name="out_hex">Zero-filled array which must be of size <see cref="maxKringSize"/>(k)</param> /// <!-- Based off 3.1.1 --> public static void kRing(H3Index origin, int k, ref List <H3Index> out_hex) { int maxIdx = maxKringSize(k); var distances = new List <int>(maxIdx); Algos.kRingDistances(origin, k, ref out_hex, ref distances); }
/// <summary> /// Returns the destination hexagon from the unidirectional edge H3Index /// </summary> /// <param name="edge">The edge H3 index</param> /// <returns>The destination H3 hexagon index</returns> /// <!-- Based off 3.1.1 --> public static H3Index getDestinationH3IndexFromUnidirectionalEdge(H3Index edge) { if (H3Index.H3_GET_MODE(ref edge) != Constants.H3_UNIEDGE_MODE) { return(H3Index.H3_INVALID_INDEX); } Direction direction = (Direction)H3Index.H3_GET_RESERVED_BITS(edge); int rotations = 0; H3Index destination = Algos .h3NeighborRotations ( getOriginH3IndexFromUnidirectionalEdge(edge), direction, ref rotations ); return(destination); }
/// <summary> /// Returns whether or not the provided H3Indexes are neighbors. /// </summary> /// <param name="origin">The origin H3 index</param> /// <param name="destination">The destination H3 index</param> /// <returns>1 if the indexes are neighbors, 0 otherwise</returns> /// <!-- Based off 3.1.1 --> public static int h3IndexesAreNeighbors(H3Index origin, H3Index destination) { // Make sure they're hexagon indexes if (H3Index.H3_GET_MODE(ref origin) != Constants.H3_HEXAGON_MODE || H3Index.H3_GET_MODE(ref destination) != Constants.H3_HEXAGON_MODE) { return(0); } // Hexagons cannot be neighbors with themselves if (origin == destination) { return(0); } // Only hexagons in the same resolution can be neighbors if (H3Index.H3_GET_RESOLUTION(origin) != H3Index.H3_GET_RESOLUTION(destination)) { return(0); } // H3 Indexes that share the same parent are very likely to be neighbors // Child 0 is neighbor with all of its parent's 'offspring', the other // children are neighbors with 3 of the 7 children. So a simple comparison // of origin and destination parents and then a lookup table of the children // is a super-cheap way to possibly determine they are neighbors. int parentRes = H3Index.H3_GET_RESOLUTION(origin) - 1; if (parentRes > 0 && (H3Index.h3ToParent(origin, parentRes) == H3Index.h3ToParent(destination, parentRes))) { Direction originResDigit = H3Index.H3_GET_INDEX_DIGIT(origin, parentRes + 1); Direction destinationResDigit = H3Index.H3_GET_INDEX_DIGIT(destination, parentRes + 1); if (originResDigit == Direction.CENTER_DIGIT || destinationResDigit == Direction.CENTER_DIGIT) { return(1); } // These sets are the relevant neighbors in the clockwise // and counter-clockwise Direction[] neighborSetClockwise = { Direction.CENTER_DIGIT, Direction.JK_AXES_DIGIT, Direction.IJ_AXES_DIGIT, Direction.J_AXES_DIGIT, Direction.IK_AXES_DIGIT, Direction.K_AXES_DIGIT, Direction.I_AXES_DIGIT }; Direction[] neighborSetCounterclockwise = { Direction.CENTER_DIGIT, Direction.IK_AXES_DIGIT, Direction.JK_AXES_DIGIT, Direction.K_AXES_DIGIT, Direction.IJ_AXES_DIGIT, Direction.I_AXES_DIGIT, Direction.J_AXES_DIGIT }; if ( neighborSetClockwise[(int)originResDigit] == destinationResDigit || neighborSetCounterclockwise[(int)originResDigit] == destinationResDigit) { return(1); } } // Otherwise, we have to determine the neighbor relationship the "hard" way. var neighborRing = new ulong[7].Select(cell => new H3Index(cell)).ToList(); Algos.kRing(origin, 1, ref neighborRing); for (int i = 0; i < 7; i++) { if (neighborRing[i] == destination) { return(1); } } // Made it here, they definitely aren't neighbors return(0); }