github.com/liucxer/courier@v1.7.1/h3/h3_uni_edge.go (about) 1 package h3 2 3 /** 4 * Returns whether or not the provided H3Indexes are neighbors. 5 * @param origin The origin H3 index. 6 * @param destination The destination H3 index. 7 * @return 1 if the indexes are neighbors, 0 otherwise; 8 */ 9 func h3IndexesAreNeighbors(origin H3Index, destination H3Index) int { 10 // Make sure they're hexagon indexes 11 if H3_GET_MODE(origin) != H3_HEXAGON_MODE || 12 H3_GET_MODE(destination) != H3_HEXAGON_MODE { 13 return 0 14 } 15 16 // Hexagons cannot be neighbors with themselves 17 if origin == destination { 18 return 0 19 } 20 21 // Only hexagons in the same resolution can be neighbors 22 if H3_GET_RESOLUTION(origin) != H3_GET_RESOLUTION(destination) { 23 return 0 24 } 25 26 // H3 Indexes that share the same parent are very likely to be neighbors 27 // Child 0 is neighbor with all of its parent's 'offspring', the other 28 // children are neighbors with 3 of the 7 children. So a simple comparison 29 // of origin and destination parents and then a lookup table of the children 30 // is a super-cheap way to possibly determine they are neighbors. 31 parentRes := H3_GET_RESOLUTION(origin) - 1 32 if parentRes > 0 && (h3ToParent(origin, parentRes) == 33 h3ToParent(destination, parentRes)) { 34 originResDigit := H3_GET_INDEX_DIGIT(origin, parentRes+1) 35 destinationResDigit := H3_GET_INDEX_DIGIT(destination, parentRes+1) 36 if originResDigit == CENTER_DIGIT || 37 destinationResDigit == CENTER_DIGIT { 38 return 1 39 } 40 // These sets are the relevant neighbors in the clockwise 41 // and counter-clockwise 42 neighborSetClockwise := []Direction{ 43 CENTER_DIGIT, 44 JK_AXES_DIGIT, 45 IJ_AXES_DIGIT, 46 J_AXES_DIGIT, 47 IK_AXES_DIGIT, 48 K_AXES_DIGIT, 49 I_AXES_DIGIT, 50 } 51 52 neighborSetCounterclockwise := []Direction{ 53 CENTER_DIGIT, IK_AXES_DIGIT, JK_AXES_DIGIT, K_AXES_DIGIT, 54 IJ_AXES_DIGIT, I_AXES_DIGIT, J_AXES_DIGIT} 55 56 if neighborSetClockwise[originResDigit] == destinationResDigit || neighborSetCounterclockwise[originResDigit] == 57 destinationResDigit { 58 return 1 59 } 60 } 61 62 // Otherwise, we have to determine the neighbor relationship the "hard" way. 63 neighborRing := make([]H3Index, 7) 64 kRing(origin, 1, neighborRing) 65 for i := 0; i < 7; i++ { 66 if neighborRing[i] == destination { 67 return 1 68 } 69 } 70 71 // Made it here, they definitely aren't neighbors 72 return 0 73 } 74 75 /** 76 * Returns a unidirectional edge H3 index based on the provided origin and 77 * destination 78 * @param origin The origin H3 hexagon index 79 * @param destination The destination H3 hexagon index 80 * @return The unidirectional edge H3Index, or 0 on failure. 81 */ 82 func getH3UnidirectionalEdge(origin H3Index, destination H3Index) H3Index { 83 // Short-circuit and return an invalid index value if they are not neighbors 84 if h3IndexesAreNeighbors(origin, destination) == 0 { 85 return H3_INVALID_INDEX 86 } 87 88 // Otherwise, determine the IJK direction from the origin to the destination 89 output := origin 90 H3_SET_MODE(&output, H3_UNIEDGE_MODE) 91 92 // Checks each neighbor, in order, to determine which direction the 93 // destination neighbor is located. Skips CENTER_DIGIT since that 94 // would be this index. 95 var neighbor H3Index 96 for direction := K_AXES_DIGIT; direction < NUM_DIGITS; direction++ { 97 rotations := 0 98 neighbor = h3NeighborRotations(origin, direction, &rotations) 99 if neighbor == destination { 100 H3_SET_RESERVED_BITS(&output, int(direction)) 101 return output 102 } 103 } 104 105 // This should be impossible, return an invalid H3Index in this case; 106 return H3_INVALID_INDEX // LCOV_EXCL_LINE 107 } 108 109 /** 110 * Returns the origin hexagon from the unidirectional edge H3Index 111 * @param edge The edge H3 index 112 * @return The origin H3 hexagon index 113 */ 114 func getOriginH3IndexFromUnidirectionalEdge(edge H3Index) H3Index { 115 if H3_GET_MODE(edge) != H3_UNIEDGE_MODE { 116 return H3_INVALID_INDEX 117 } 118 origin := edge 119 H3_SET_MODE(&origin, H3_HEXAGON_MODE) 120 H3_SET_RESERVED_BITS(&origin, 0) 121 return origin 122 } 123 124 /** 125 * Returns the destination hexagon from the unidirectional edge H3Index 126 * @param edge The edge H3 index 127 * @return The destination H3 hexagon index 128 */ 129 func getDestinationH3IndexFromUnidirectionalEdge(edge H3Index) H3Index { 130 if H3_GET_MODE(edge) != H3_UNIEDGE_MODE { 131 return H3_INVALID_INDEX 132 } 133 direction := H3_GET_RESERVED_BITS(edge) 134 rotations := 0 135 destination := h3NeighborRotations(getOriginH3IndexFromUnidirectionalEdge(edge), Direction(direction), &rotations) 136 return destination 137 } 138 139 /** 140 * Determines if the provided H3Index is a valid unidirectional edge index 141 * @param edge The unidirectional edge H3Index 142 * @return 1 if it is a unidirectional edge H3Index, otherwise 0. 143 */ 144 func h3UnidirectionalEdgeIsValid(edge H3Index) bool { 145 if H3_GET_MODE(edge) != H3_UNIEDGE_MODE { 146 return false 147 } 148 149 neighborDirection := H3_GET_RESERVED_BITS(edge) 150 if Direction(neighborDirection) <= CENTER_DIGIT || Direction(neighborDirection) >= NUM_DIGITS { 151 return false 152 } 153 154 origin := getOriginH3IndexFromUnidirectionalEdge(edge) 155 if h3IsPentagon(origin) && Direction(neighborDirection) == K_AXES_DIGIT { 156 return false 157 } 158 159 return h3IsValid(origin) 160 } 161 162 /** 163 * Returns the origin, destination pair of hexagon IDs for the given edge ID 164 * @param edge The unidirectional edge H3Index 165 * @param originDestination Pointer to memory to store origin and destination 166 * IDs 167 */ 168 func getH3IndexesFromUnidirectionalEdge(edge H3Index, originDestination []H3Index) { 169 originDestination[0] = getOriginH3IndexFromUnidirectionalEdge(edge) 170 originDestination[1] = getDestinationH3IndexFromUnidirectionalEdge(edge) 171 } 172 173 /** 174 * Provides all of the unidirectional edges from the current H3Index. 175 * @param origin The origin hexagon H3Index to find edges for. 176 * @param edges The memory to store all of the edges inside. 177 */ 178 func getH3UnidirectionalEdgesFromHexagon(origin H3Index, edges []H3Index) { 179 // Determine if the origin is a pentagon and special treatment needed. 180 isPentagon := h3IsPentagon(origin) 181 182 // This is actually quite simple. Just modify the bits of the origin 183 // slightly for each direction, except the 'k' direction in pentagons, 184 // which is zeroed. 185 for i := 0; i < 6; i++ { 186 if isPentagon && i == 0 { 187 edges[i] = H3_INVALID_INDEX 188 } else { 189 edges[i] = origin 190 H3_SET_MODE(&edges[i], H3_UNIEDGE_MODE) 191 H3_SET_RESERVED_BITS(&edges[i], i+1) 192 } 193 } 194 } 195 196 /** 197 * Whether the given coordinate has a matching vertex in the given geo boundary. 198 * @param vertex Coordinate to check 199 * @param boundary Geo boundary to look in 200 * @return Whether a match was found 201 */ 202 func _hasMatchingVertex(vertex *GeoCoord, boundary *GeoBoundary) bool { 203 for i := 0; i < boundary.numVerts; i++ { 204 if geoAlmostEqualThreshold(vertex, &boundary.Verts[i], 0.000001) { 205 return true 206 } 207 } 208 return false 209 } 210 211 /** 212 * Provides the coordinates defining the unidirectional edge. 213 * @param edge The unidirectional edge H3Index 214 * @param gb The geoboundary object to store the edge coordinates. 215 */ 216 func getH3UnidirectionalEdgeBoundary(edge H3Index, gb *GeoBoundary) { 217 // TODO: More efficient solution :) 218 origin := GeoBoundary{} 219 destination := GeoBoundary{} 220 postponedVertex := GeoCoord{} 221 hasPostponedVertex := false 222 223 h3ToGeoBoundary(getOriginH3IndexFromUnidirectionalEdge(edge), &origin) 224 h3ToGeoBoundary(getDestinationH3IndexFromUnidirectionalEdge(edge), &destination) 225 226 k := 0 227 for i := 0; i < origin.numVerts; i++ { 228 if _hasMatchingVertex(&origin.Verts[i], &destination) { 229 // If we are on vertex 0, we need to handle the case where it's the 230 // end of the edge, not the beginning. 231 if i == 0 && !_hasMatchingVertex(&origin.Verts[i+1], &destination) { 232 postponedVertex = origin.Verts[i] 233 hasPostponedVertex = true 234 } else { 235 gb.Verts[k] = origin.Verts[i] 236 k++ 237 } 238 } 239 } 240 // If we postponed adding the last vertex, add it now 241 if hasPostponedVertex { 242 gb.Verts[k] = postponedVertex 243 k++ 244 } 245 gb.numVerts = k 246 }