Coverage Report

Created: 2025-07-01 06:06

/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
756
                                    int *out) {
40
    // Make sure they're hexagon indexes
41
756
    if (H3_GET_MODE(origin) != H3_CELL_MODE ||
42
756
        H3_GET_MODE(destination) != H3_CELL_MODE) {
43
491
        return E_CELL_INVALID;
44
491
    }
45
46
    // Hexagons cannot be neighbors with themselves
47
265
    if (origin == destination) {
48
2
        *out = 0;
49
2
        return E_SUCCESS;
50
2
    }
51
52
    // Only hexagons in the same resolution can be neighbors
53
263
    if (H3_GET_RESOLUTION(origin) != H3_GET_RESOLUTION(destination)) {
54
15
        return E_RES_MISMATCH;
55
15
    }
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
248
    int parentRes = H3_GET_RESOLUTION(origin) - 1;
63
248
    if (parentRes > 0) {
64
        // TODO: Return error codes here
65
202
        H3Index originParent;
66
202
        H3_EXPORT(cellToParent)(origin, parentRes, &originParent);
67
202
        H3Index destinationParent;
68
202
        H3_EXPORT(cellToParent)(destination, parentRes, &destinationParent);
69
202
        if (originParent == destinationParent) {
70
20
            Direction originResDigit =
71
20
                H3_GET_INDEX_DIGIT(origin, parentRes + 1);
72
20
            Direction destinationResDigit =
73
20
                H3_GET_INDEX_DIGIT(destination, parentRes + 1);
74
20
            if (originResDigit == CENTER_DIGIT ||
75
20
                destinationResDigit == CENTER_DIGIT) {
76
3
                *out = 1;
77
3
                return E_SUCCESS;
78
3
            }
79
17
            if (originResDigit >= INVALID_DIGIT) {
80
                // Prevent indexing off the end of the array below
81
1
                return E_CELL_INVALID;
82
1
            }
83
16
            if ((originResDigit == K_AXES_DIGIT ||
84
16
                 destinationResDigit == K_AXES_DIGIT) &&
85
16
                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
1
                return E_CELL_INVALID;
92
1
            }
93
            // These sets are the relevant neighbors in the clockwise
94
            // and counter-clockwise
95
15
            const Direction neighborSetClockwise[] = {
96
15
                CENTER_DIGIT,  JK_AXES_DIGIT, IJ_AXES_DIGIT, J_AXES_DIGIT,
97
15
                IK_AXES_DIGIT, K_AXES_DIGIT,  I_AXES_DIGIT};
98
15
            const Direction neighborSetCounterclockwise[] = {
99
15
                CENTER_DIGIT,  IK_AXES_DIGIT, JK_AXES_DIGIT, K_AXES_DIGIT,
100
15
                IJ_AXES_DIGIT, I_AXES_DIGIT,  J_AXES_DIGIT};
101
15
            if (neighborSetClockwise[originResDigit] == destinationResDigit ||
102
15
                neighborSetCounterclockwise[originResDigit] ==
103
11
                    destinationResDigit) {
104
6
                *out = 1;
105
6
                return E_SUCCESS;
106
6
            }
107
15
        }
108
202
    }
109
110
    // Otherwise, we have to determine the neighbor relationship the "hard" way.
111
237
    H3Index neighborRing[7] = {0};
112
237
    H3_EXPORT(gridDisk)(origin, 1, neighborRing);
113
1.85k
    for (int i = 0; i < 7; i++) {
114
1.63k
        if (neighborRing[i] == destination) {
115
11
            *out = 1;
116
11
            return E_SUCCESS;
117
11
        }
118
1.63k
    }
119
120
    // Made it here, they definitely aren't neighbors
121
226
    *out = 0;
122
226
    return E_SUCCESS;
123
237
}
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
 * @param out Output: The directed edge H3Index.
131
 */
132
H3Error H3_EXPORT(cellsToDirectedEdge)(H3Index origin, H3Index destination,
133
756
                                       H3Index *out) {
134
    // Determine the IJK direction from the origin to the destination
135
756
    Direction direction = directionForNeighbor(origin, destination);
136
137
    // The direction will be invalid if the cells are not neighbors
138
756
    if (direction == INVALID_DIGIT) {
139
728
        return E_NOT_NEIGHBORS;
140
728
    }
141
142
    // Create the edge index for the neighbor direction
143
28
    H3Index output = origin;
144
28
    H3_SET_MODE(output, H3_DIRECTEDEDGE_MODE);
145
28
    H3_SET_RESERVED_BITS(output, direction);
146
147
28
    *out = output;
148
28
    return E_SUCCESS;
149
756
}
150
151
/**
152
 * Returns the origin hexagon from the directed edge H3Index
153
 * @param edge The edge H3 index
154
 * @param out Output: The origin H3 hexagon index
155
 */
156
5.22k
H3Error H3_EXPORT(getDirectedEdgeOrigin)(H3Index edge, H3Index *out) {
157
5.22k
    if (H3_GET_MODE(edge) != H3_DIRECTEDEDGE_MODE) {
158
1.66k
        return E_DIR_EDGE_INVALID;
159
1.66k
    }
160
3.56k
    H3Index origin = edge;
161
3.56k
    H3_SET_MODE(origin, H3_CELL_MODE);
162
3.56k
    H3_SET_RESERVED_BITS(origin, 0);
163
3.56k
    *out = origin;
164
3.56k
    return E_SUCCESS;
165
5.22k
}
166
167
/**
168
 * Returns the destination hexagon from the directed edge H3Index
169
 * @param edge The edge H3 index
170
 * @param out Output: The destination H3 hexagon index
171
 */
172
1.16k
H3Error H3_EXPORT(getDirectedEdgeDestination)(H3Index edge, H3Index *out) {
173
1.16k
    Direction direction = H3_GET_RESERVED_BITS(edge);
174
1.16k
    int rotations = 0;
175
1.16k
    H3Index origin;
176
    // Note: This call is also checking for H3_DIRECTEDEDGE_MODE
177
1.16k
    H3Error originResult = H3_EXPORT(getDirectedEdgeOrigin)(edge, &origin);
178
1.16k
    if (originResult) {
179
343
        return originResult;
180
343
    }
181
826
    return h3NeighborRotations(origin, direction, &rotations, out);
182
1.16k
}
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
756
int H3_EXPORT(isValidDirectedEdge)(H3Index edge) {
190
756
    Direction neighborDirection = H3_GET_RESERVED_BITS(edge);
191
756
    if (neighborDirection <= CENTER_DIGIT || neighborDirection >= NUM_DIGITS) {
192
118
        return 0;
193
118
    }
194
195
638
    H3Index origin;
196
    // Note: This call is also checking for H3_DIRECTEDEDGE_MODE
197
638
    H3Error originResult = H3_EXPORT(getDirectedEdgeOrigin)(edge, &origin);
198
638
    if (originResult) {
199
232
        return 0;
200
232
    }
201
406
    if (H3_EXPORT(isPentagon)(origin) && neighborDirection == K_AXES_DIGIT) {
202
1
        return 0;
203
1
    }
204
205
405
    return H3_EXPORT(isValidCell)(origin);
206
406
}
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
756
                                       H3Index *originDestination) {
216
756
    H3Error originResult =
217
756
        H3_EXPORT(getDirectedEdgeOrigin)(edge, &originDestination[0]);
218
756
    if (originResult) {
219
343
        return originResult;
220
343
    }
221
413
    H3Error destinationResult =
222
413
        H3_EXPORT(getDirectedEdgeDestination)(edge, &originDestination[1]);
223
413
    if (destinationResult) {
224
76
        return destinationResult;
225
76
    }
226
337
    return E_SUCCESS;
227
413
}
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
756
H3Error H3_EXPORT(originToDirectedEdges)(H3Index origin, H3Index *edges) {
235
    // Determine if the origin is a pentagon and special treatment needed.
236
756
    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
5.29k
    for (int i = 0; i < 6; i++) {
242
4.53k
        if (isPent && i == 0) {
243
23
            edges[i] = H3_NULL;
244
4.51k
        } else {
245
4.51k
            edges[i] = origin;
246
4.51k
            H3_SET_MODE(edges[i], H3_DIRECTEDEDGE_MODE);
247
4.51k
            H3_SET_RESERVED_BITS(edges[i], i + 1);
248
4.51k
        }
249
4.53k
    }
250
756
    return E_SUCCESS;
251
756
}
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
1.90k
H3Error H3_EXPORT(directedEdgeToBoundary)(H3Index edge, CellBoundary *cb) {
259
    // Get the origin and neighbor direction from the edge
260
1.90k
    Direction direction = H3_GET_RESERVED_BITS(edge);
261
1.90k
    H3Index origin;
262
1.90k
    H3Error originResult = H3_EXPORT(getDirectedEdgeOrigin)(edge, &origin);
263
1.90k
    if (originResult) {
264
403
        return originResult;
265
403
    }
266
267
    // Get the start vertex for the edge
268
1.50k
    int startVertex = vertexNumForDirection(origin, direction);
269
1.50k
    if (startVertex == INVALID_VERTEX_NUM) {
270
        // This is not actually an edge (i.e. no valid direction),
271
        // so return no vertices.
272
30
        cb->numVerts = 0;
273
30
        return E_DIR_EDGE_INVALID;
274
30
    }
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
1.47k
    FaceIJK fijk;
281
1.47k
    H3Error fijkResult = _h3ToFaceIjk(origin, &fijk);
282
1.47k
    if (NEVER(fijkResult)) {
283
0
        return fijkResult;
284
0
    }
285
1.47k
    int res = H3_GET_RESOLUTION(origin);
286
1.47k
    int isPent = H3_EXPORT(isPentagon)(origin);
287
288
1.47k
    if (isPent) {
289
38
        _faceIjkPentToCellBoundary(&fijk, res, startVertex, 2, cb);
290
1.43k
    } else {
291
1.43k
        _faceIjkToCellBoundary(&fijk, res, startVertex, 2, cb);
292
1.43k
    }
293
1.47k
    return E_SUCCESS;
294
1.47k
}