Coverage Report

Created: 2023-09-25 06:53

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