/src/h3/src/h3lib/lib/directedEdge.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2017-2018, 2020-2021 Uber Technologies, Inc. |
3 | | * |
4 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | | * you may not use this file except in compliance with the License. |
6 | | * You may obtain a copy of the License at |
7 | | * |
8 | | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | | * |
10 | | * Unless required by applicable law or agreed to in writing, software |
11 | | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | | * See the License for the specific language governing permissions and |
14 | | * limitations under the License. |
15 | | */ |
16 | | /** @file directedEdge.c |
17 | | * @brief DirectedEdge functions for manipulating directed edge indexes. |
18 | | */ |
19 | | |
20 | | #include <inttypes.h> |
21 | | #include <stdbool.h> |
22 | | |
23 | | #include "algos.h" |
24 | | #include "constants.h" |
25 | | #include "coordijk.h" |
26 | | #include "h3Assert.h" |
27 | | #include "h3Index.h" |
28 | | #include "latLng.h" |
29 | | #include "vertex.h" |
30 | | |
31 | | /** |
32 | | * Returns whether or not the provided H3Indexes are neighbors. |
33 | | * @param origin The origin H3 index. |
34 | | * @param destination The destination H3 index. |
35 | | * @param out Set to 1 if the indexes are neighbors, 0 otherwise |
36 | | * @return Error code if the origin or destination are invalid or incomparable. |
37 | | */ |
38 | | H3Error H3_EXPORT(areNeighborCells)(H3Index origin, H3Index destination, |
39 | 0 | int *out) { |
40 | | // Make sure they're hexagon indexes |
41 | 0 | if (H3_GET_MODE(origin) != H3_CELL_MODE || |
42 | 0 | H3_GET_MODE(destination) != H3_CELL_MODE) { |
43 | 0 | return E_CELL_INVALID; |
44 | 0 | } |
45 | | |
46 | | // Hexagons cannot be neighbors with themselves |
47 | 0 | if (origin == destination) { |
48 | 0 | *out = 0; |
49 | 0 | return E_SUCCESS; |
50 | 0 | } |
51 | | |
52 | | // Only hexagons in the same resolution can be neighbors |
53 | 0 | if (H3_GET_RESOLUTION(origin) != H3_GET_RESOLUTION(destination)) { |
54 | 0 | return E_RES_MISMATCH; |
55 | 0 | } |
56 | | |
57 | | // H3 Indexes that share the same parent are very likely to be neighbors |
58 | | // Child 0 is neighbor with all of its parent's 'offspring', the other |
59 | | // children are neighbors with 3 of the 7 children. So a simple comparison |
60 | | // of origin and destination parents and then a lookup table of the children |
61 | | // is a super-cheap way to possibly determine they are neighbors. |
62 | 0 | int parentRes = H3_GET_RESOLUTION(origin) - 1; |
63 | 0 | if (parentRes > 0) { |
64 | | // TODO: Return error codes here |
65 | 0 | H3Index originParent; |
66 | 0 | H3_EXPORT(cellToParent)(origin, parentRes, &originParent); |
67 | 0 | H3Index destinationParent; |
68 | 0 | H3_EXPORT(cellToParent)(destination, parentRes, &destinationParent); |
69 | 0 | if (originParent == destinationParent) { |
70 | 0 | Direction originResDigit = |
71 | 0 | H3_GET_INDEX_DIGIT(origin, parentRes + 1); |
72 | 0 | Direction destinationResDigit = |
73 | 0 | H3_GET_INDEX_DIGIT(destination, parentRes + 1); |
74 | 0 | if (originResDigit == CENTER_DIGIT || |
75 | 0 | destinationResDigit == CENTER_DIGIT) { |
76 | 0 | *out = 1; |
77 | 0 | return E_SUCCESS; |
78 | 0 | } |
79 | 0 | if (originResDigit >= INVALID_DIGIT) { |
80 | | // Prevent indexing off the end of the array below |
81 | 0 | return E_CELL_INVALID; |
82 | 0 | } |
83 | 0 | if ((originResDigit == K_AXES_DIGIT || |
84 | 0 | destinationResDigit == K_AXES_DIGIT) && |
85 | 0 | H3_EXPORT(isPentagon)(originParent)) { |
86 | | // If these are invalid cells, fail rather than incorrectly |
87 | | // reporting neighbors. For pentagon cells that are actually |
88 | | // neighbors across the deleted subsequence, they will fail the |
89 | | // optimized check below, but they will be accepted by the |
90 | | // gridDisk check below that. |
91 | 0 | return E_CELL_INVALID; |
92 | 0 | } |
93 | | // These sets are the relevant neighbors in the clockwise |
94 | | // and counter-clockwise |
95 | 0 | const Direction neighborSetClockwise[] = { |
96 | 0 | CENTER_DIGIT, JK_AXES_DIGIT, IJ_AXES_DIGIT, J_AXES_DIGIT, |
97 | 0 | IK_AXES_DIGIT, K_AXES_DIGIT, I_AXES_DIGIT}; |
98 | 0 | const Direction neighborSetCounterclockwise[] = { |
99 | 0 | CENTER_DIGIT, IK_AXES_DIGIT, JK_AXES_DIGIT, K_AXES_DIGIT, |
100 | 0 | IJ_AXES_DIGIT, I_AXES_DIGIT, J_AXES_DIGIT}; |
101 | 0 | if (neighborSetClockwise[originResDigit] == destinationResDigit || |
102 | 0 | neighborSetCounterclockwise[originResDigit] == |
103 | 0 | destinationResDigit) { |
104 | 0 | *out = 1; |
105 | 0 | return E_SUCCESS; |
106 | 0 | } |
107 | 0 | } |
108 | 0 | } |
109 | | |
110 | | // Otherwise, we have to determine the neighbor relationship the "hard" way. |
111 | 0 | H3Index neighborRing[7] = {0}; |
112 | 0 | H3_EXPORT(gridDisk)(origin, 1, neighborRing); |
113 | 0 | for (int i = 0; i < 7; i++) { |
114 | 0 | if (neighborRing[i] == destination) { |
115 | 0 | *out = 1; |
116 | 0 | return E_SUCCESS; |
117 | 0 | } |
118 | 0 | } |
119 | | |
120 | | // Made it here, they definitely aren't neighbors |
121 | 0 | *out = 0; |
122 | 0 | return E_SUCCESS; |
123 | 0 | } |
124 | | |
125 | | /** |
126 | | * Returns a directed edge H3 index based on the provided origin and |
127 | | * destination |
128 | | * @param origin The origin H3 hexagon index |
129 | | * @param destination The destination H3 hexagon index |
130 | | * @return The directed edge H3Index, or H3_NULL on failure. |
131 | | */ |
132 | | H3Error H3_EXPORT(cellsToDirectedEdge)(H3Index origin, H3Index destination, |
133 | 0 | H3Index *out) { |
134 | | // Determine the IJK direction from the origin to the destination |
135 | 0 | Direction direction = directionForNeighbor(origin, destination); |
136 | | |
137 | | // The direction will be invalid if the cells are not neighbors |
138 | 0 | if (direction == INVALID_DIGIT) { |
139 | 0 | return E_NOT_NEIGHBORS; |
140 | 0 | } |
141 | | |
142 | | // Create the edge index for the neighbor direction |
143 | 0 | H3Index output = origin; |
144 | 0 | H3_SET_MODE(output, H3_DIRECTEDEDGE_MODE); |
145 | 0 | H3_SET_RESERVED_BITS(output, direction); |
146 | |
|
147 | 0 | *out = output; |
148 | 0 | return E_SUCCESS; |
149 | 0 | } |
150 | | |
151 | | /** |
152 | | * Returns the origin hexagon from the directed edge H3Index |
153 | | * @param edge The edge H3 index |
154 | | * @return The origin H3 hexagon index, or H3_NULL on failure |
155 | | */ |
156 | 915 | H3Error H3_EXPORT(getDirectedEdgeOrigin)(H3Index edge, H3Index *out) { |
157 | 915 | if (H3_GET_MODE(edge) != H3_DIRECTEDEDGE_MODE) { |
158 | 63 | return E_DIR_EDGE_INVALID; |
159 | 63 | } |
160 | 852 | H3Index origin = edge; |
161 | 852 | H3_SET_MODE(origin, H3_CELL_MODE); |
162 | 852 | H3_SET_RESERVED_BITS(origin, 0); |
163 | 852 | *out = origin; |
164 | 852 | return E_SUCCESS; |
165 | 915 | } |
166 | | |
167 | | /** |
168 | | * Returns the destination hexagon from the directed edge H3Index |
169 | | * @param edge The edge H3 index |
170 | | * @return The destination H3 hexagon index, or H3_NULL on failure |
171 | | */ |
172 | 0 | H3Error H3_EXPORT(getDirectedEdgeDestination)(H3Index edge, H3Index *out) { |
173 | 0 | Direction direction = H3_GET_RESERVED_BITS(edge); |
174 | 0 | int rotations = 0; |
175 | 0 | H3Index origin; |
176 | | // Note: This call is also checking for H3_DIRECTEDEDGE_MODE |
177 | 0 | H3Error originResult = H3_EXPORT(getDirectedEdgeOrigin)(edge, &origin); |
178 | 0 | if (originResult) { |
179 | 0 | return originResult; |
180 | 0 | } |
181 | 0 | return h3NeighborRotations(origin, direction, &rotations, out); |
182 | 0 | } |
183 | | |
184 | | /** |
185 | | * Determines if the provided H3Index is a valid directed edge index |
186 | | * @param edge The directed edge H3Index |
187 | | * @return 1 if it is a directed edge H3Index, otherwise 0. |
188 | | */ |
189 | 0 | int H3_EXPORT(isValidDirectedEdge)(H3Index edge) { |
190 | 0 | Direction neighborDirection = H3_GET_RESERVED_BITS(edge); |
191 | 0 | if (neighborDirection <= CENTER_DIGIT || neighborDirection >= NUM_DIGITS) { |
192 | 0 | return 0; |
193 | 0 | } |
194 | | |
195 | 0 | H3Index origin; |
196 | | // Note: This call is also checking for H3_DIRECTEDEDGE_MODE |
197 | 0 | H3Error originResult = H3_EXPORT(getDirectedEdgeOrigin)(edge, &origin); |
198 | 0 | if (originResult) { |
199 | 0 | return 0; |
200 | 0 | } |
201 | 0 | if (H3_EXPORT(isPentagon)(origin) && neighborDirection == K_AXES_DIGIT) { |
202 | 0 | return 0; |
203 | 0 | } |
204 | | |
205 | 0 | return H3_EXPORT(isValidCell)(origin); |
206 | 0 | } |
207 | | |
208 | | /** |
209 | | * Returns the origin, destination pair of hexagon IDs for the given edge ID |
210 | | * @param edge The directed edge H3Index |
211 | | * @param originDestination Pointer to memory to store origin and destination |
212 | | * IDs |
213 | | */ |
214 | | H3Error H3_EXPORT(directedEdgeToCells)(H3Index edge, |
215 | 0 | H3Index *originDestination) { |
216 | 0 | H3Error originResult = |
217 | 0 | H3_EXPORT(getDirectedEdgeOrigin)(edge, &originDestination[0]); |
218 | 0 | if (originResult) { |
219 | 0 | return originResult; |
220 | 0 | } |
221 | 0 | H3Error destinationResult = |
222 | 0 | H3_EXPORT(getDirectedEdgeDestination)(edge, &originDestination[1]); |
223 | 0 | if (destinationResult) { |
224 | 0 | return destinationResult; |
225 | 0 | } |
226 | 0 | return E_SUCCESS; |
227 | 0 | } |
228 | | |
229 | | /** |
230 | | * Provides all of the directed edges from the current H3Index. |
231 | | * @param origin The origin hexagon H3Index to find edges for. |
232 | | * @param edges The memory to store all of the edges inside. |
233 | | */ |
234 | 0 | H3Error H3_EXPORT(originToDirectedEdges)(H3Index origin, H3Index *edges) { |
235 | | // Determine if the origin is a pentagon and special treatment needed. |
236 | 0 | int isPent = H3_EXPORT(isPentagon)(origin); |
237 | | |
238 | | // This is actually quite simple. Just modify the bits of the origin |
239 | | // slightly for each direction, except the 'k' direction in pentagons, |
240 | | // which is zeroed. |
241 | 0 | for (int i = 0; i < 6; i++) { |
242 | 0 | if (isPent && i == 0) { |
243 | 0 | edges[i] = H3_NULL; |
244 | 0 | } else { |
245 | 0 | edges[i] = origin; |
246 | 0 | H3_SET_MODE(edges[i], H3_DIRECTEDEDGE_MODE); |
247 | 0 | H3_SET_RESERVED_BITS(edges[i], i + 1); |
248 | 0 | } |
249 | 0 | } |
250 | 0 | return E_SUCCESS; |
251 | 0 | } |
252 | | |
253 | | /** |
254 | | * Provides the coordinates defining the directed edge. |
255 | | * @param edge The directed edge H3Index |
256 | | * @param cb The cellboundary object to store the edge coordinates. |
257 | | */ |
258 | 915 | H3Error H3_EXPORT(directedEdgeToBoundary)(H3Index edge, CellBoundary *cb) { |
259 | | // Get the origin and neighbor direction from the edge |
260 | 915 | Direction direction = H3_GET_RESERVED_BITS(edge); |
261 | 915 | H3Index origin; |
262 | 915 | H3Error originResult = H3_EXPORT(getDirectedEdgeOrigin)(edge, &origin); |
263 | 915 | if (originResult) { |
264 | 63 | return originResult; |
265 | 63 | } |
266 | | |
267 | | // Get the start vertex for the edge |
268 | 852 | int startVertex = vertexNumForDirection(origin, direction); |
269 | 852 | if (startVertex == INVALID_VERTEX_NUM) { |
270 | | // This is not actually an edge (i.e. no valid direction), |
271 | | // so return no vertices. |
272 | 21 | cb->numVerts = 0; |
273 | 21 | return E_DIR_EDGE_INVALID; |
274 | 21 | } |
275 | | |
276 | | // Get the geo boundary for the appropriate vertexes of the origin. Note |
277 | | // that while there are always 2 topological vertexes per edge, the |
278 | | // resulting edge boundary may have an additional distortion vertex if it |
279 | | // crosses an edge of the icosahedron. |
280 | 831 | FaceIJK fijk; |
281 | 831 | H3Error fijkResult = _h3ToFaceIjk(origin, &fijk); |
282 | 831 | if (NEVER(fijkResult)) { |
283 | 0 | return fijkResult; |
284 | 0 | } |
285 | 831 | int res = H3_GET_RESOLUTION(origin); |
286 | 831 | int isPent = H3_EXPORT(isPentagon)(origin); |
287 | | |
288 | 831 | if (isPent) { |
289 | 30 | _faceIjkPentToCellBoundary(&fijk, res, startVertex, 2, cb); |
290 | 801 | } else { |
291 | 801 | _faceIjkToCellBoundary(&fijk, res, startVertex, 2, cb); |
292 | 801 | } |
293 | 831 | return E_SUCCESS; |
294 | 831 | } |