-
Notifications
You must be signed in to change notification settings - Fork 468
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add function for returning the H3 indices of each endpoint of a directed edge #808
Comments
Further experimentation shows that the above doesn't account for the differing order of vertices and edges. To solve this, I believe it's possible to just get the starting vertex of the next edge clockwise around the cell. The below code seems to work fine at multiple resolutions from 0 to 7 in my application; I'll spin up proper tests and a previous-edge function as well. static const Direction nextEdgeClockwiseDirectionsHex[8] = {
// Direction 0, center
CENTER_DIGIT,
// Direction 1, k-axes direction
JK_AXES_DIGIT, // K_AXES_DIGIT -> JK_AXES_DIGIT,
// Direction 2, j-axes direction
IJ_AXES_DIGIT, // J_AXES_DIGIT -> IJ_AXES_DIGIT
// Direction 3, j == k direction
J_AXES_DIGIT, // JK_AXES_DIGIT -> J_AXES_DIGIT
// Direction 4, i-axes direction
IK_AXES_DIGIT, // I_AXES_DIGIT -> IK_AXES_DIGIT
// Direction 5, i == k direction
K_AXES_DIGIT, // IK_AXES_DIGIT -> K_AXES_DIGIT
// Direction 6, i == j direction
I_AXES_DIGIT, // IJ_AXES_DIGIT -> I_AXES_DIGIT
// Invalid digit (7)
INVALID_DIGIT // INVALID_DIGIT -> INVALID_DIGIT
};
static const Direction nextEdgeClockwiseDirectionsPent[8] = {
// Direction 0, center
CENTER_DIGIT,
// Direction 1, k-axes direction
IK_AXES_DIGIT, // K_AXES_DIGIT -> IK_AXES_DIGIT
// Direction 2, j-axes direction
K_AXES_DIGIT, // J_AXES_DIGIT -> K_AXES_DIGIT
// Direction 3, j == k direction
I_AXES_DIGIT, // JK_AXES_DIGIT -> I_AXES_DIGIT
// Direction 4, i-axes direction
J_AXES_DIGIT, // I_AXES_DIGIT -> J_AXES_DIGIT
// Direction 5, i == k direction
JK_AXES_DIGIT, // IK_AXES_DIGIT -> JK_AXES_DIGIT
// Direction 6, i == j direction
INVALID_DIGIT, // IJ_AXES_DIGIT (invalid for pentagons) -> INVALID_DIGIT
// Invalid digit (7)
INVALID_DIGIT // INVALID_DIGIT -> INVALID_DIGIT
};
// ...
/**
* Returns the next edge in the clockwise order around the origin cell
* @param edge The directed edge H3Index
* @param nextClockwiseEdge The next clockwise edge output
*/
H3Error H3_EXPORT(directedEdgeGetNextClockwise)(H3Index edge, H3Index* nextClockwiseEdge) {
Direction direction = H3_GET_RESERVED_BITS(edge);
if (direction == CENTER_DIGIT || direction == INVALID_DIGIT) {
return E_DIR_EDGE_INVALID;
}
H3Index origin;
H3Error originResult = H3_EXPORT(getDirectedEdgeOrigin)(edge, &origin);
if (originResult) {
return originResult;
}
*nextClockwiseEdge = edge;
int isPent = H3_EXPORT(isPentagon)(origin);
if (isPent) {
Direction nextDirection = nextEdgeClockwiseDirectionsPent[direction];
H3_SET_RESERVED_BITS(*nextClockwiseEdge, nextDirection);
} else {
Direction nextDirection = nextEdgeClockwiseDirectionsHex[direction];
H3_SET_RESERVED_BITS(*nextClockwiseEdge, nextDirection);
}
return E_SUCCESS;
}
/**
* Provides the vertices at either end of the directed edge.
* @param edge The directed edge H3Index
* @param vStart The starting vertex H3Index
* @param vEnd The starting vertex H3Index
*/
H3Error H3_EXPORT(directedEdgeToVertices)(H3Index edge, H3Index* vStart, H3Index* vEnd) {
// Get the origin and neighbor direction from the edge
Direction direction = H3_GET_RESERVED_BITS(edge);
H3Index origin;
H3Error originResult = H3_EXPORT(getDirectedEdgeOrigin)(edge, &origin);
if (originResult) {
return originResult;
}
// Get the start vertex for the edge
int startVertex = vertexNumForDirection(origin, direction);
if (startVertex == INVALID_VERTEX_NUM) {
// This is not actually an edge (i.e. no valid direction),
// so return no vertices.
return E_DIR_EDGE_INVALID;
}
H3Error firstVertexErr = H3_EXPORT(cellToVertex)(origin, startVertex, vStart);
if (firstVertexErr) {
return firstVertexErr;
}
// Handle the end vertex for the edge
int isPent = H3_EXPORT(isPentagon)(origin);
int nextDirection;
if (isPent) {
nextDirection = nextEdgeClockwiseDirectionsPent[direction];
} else {
nextDirection = nextEdgeClockwiseDirectionsHex[direction];
}
int endVertex = vertexNumForDirection(origin, nextDirection);
H3Error secondVertexErr = H3_EXPORT(cellToVertex)(origin, endVertex, vEnd);
if (secondVertexErr) {
return secondVertexErr;
}
return E_SUCCESS;
} |
If I'm understanding correctly, this is already implemented as |
Another option here is to use |
Neither of the existing functions you mention really get at what I need, or would require significant additional processing to achieve the result I'm looking for. Both functions mentioned give me, ultimately, the boundary around a group of cells, where the only information on what category adjacent cells belong to is "is this is in the provided set or not?" What it doesn't give me is the set of boundary edges or vertices between cells with different values in the provided set, where I don't care about the edges between cells in the provided set and outside. Consider the following diagram: Here I'm only interested in retrieving the purple edges. This is already doable by iterating over the provided set of cells and retaining only those edges that are both:
Turning this into a rendering solution is where the problem comes in. The size of the dataset means that I have an interest in rendering these borders with as few vertices in the boundary as possible. Furthermore, filtering out coincident points on the purple edges will mean two floating-point comparisons per vertex, instead of a single equality check if I'm able to get vertices associated with each edge directly, impacting performance. The second problem comes with large datasets. I can't fit in memory an array with all the cells in a particular set - think of binning the world by nations at resolution 7+ or so. What I can do is split the entire world up into chunks that I know do fit in memory (say, children of cells at resolution 2) and then compute the interior (purple) borders per chunk. Testing this approach with country data has shown that it's viable - when rendering a slice of the world at resolution 7, I'm able to pull only those cells in resolution 2 within view and then use this chunk-wise method to compute borders. I've actually achieved what I set out to do using the code from my last post, and am still preparing the tests necessary for a proper pull request. I believe there's probably value in users being able to associate vertices with edge data anyway, even beyond my admittedly niche use case. We can associated edges with cells, and cells with vertices, but unless I'm mistaken there's no way to associate edges with vertices directly. |
Ok, I think I understand now. I apologize, I skimmed over this at first and didn't realize you were dealing directly with vertex-mode vertexes instead of lat/lng vertexes. I'm still not 100% sure I understand the use case, which I still suspect could be solved as efficiently without the new method, but I can definitely see that |
After looking through the code, it appears that calculating the IJK coordinates for a face is part of the process for computing a cell boundary, and functions exist internally to convert these coordinates to H3 indices. I believe exposing a function to convert a directed edge to endpoint indices directly is an easy add to the library, and I have one drafted, but want to confirm this is not already implemented in a way I'm not spotting.
The use case that sparks this need is as follows:
polygonToCells
to get a boundary. This works on a small scale. Unfortunately, the dataset I have is too large for this solution.polygonToCells
as mentioned above. This process works fine. This will result in borders along the edge of a chunk, though, even when the adjacent cells share the same color.Creating/exposing the function mentioned will simplify this into a series of integer equality comparisons, and I suspect there are likely other use cases for allowing users to directly get the vertices associated with edges.
Preliminary testing indicates that the below seems to do the trick. I'll put together a proper pull request if this isn't duplicating effort.
The text was updated successfully, but these errors were encountered: