Closed SkybuckFlying closed 2 years ago
Re-opened.
I think you're looking for gridDisk
- you're just trying to get the neighbors of a given cell, right? This shouldn't require using edges - edges are specifically for applications where you need to represent the connection between neighboring cells and store that relationship somewhere. For traversal, you more likely want gridDisk(origin, 1)
to find neighbors.
HAHA, Funny ! :) I actually do want the connection between neighbours. So how to do it ? Maybe gridDisk can help with that too ? I am too tired to check now... if you can save me some energy/effort then I am all ears lol :)
I'm confused. If what you want is what's in the title for this issue, neighbour center geo locations
, then you don't need connections, you just need the neighbors. In JS:
const neighbors = h3.gridDisk(origin, 1);
const centers = neighbors.map(h3.cellToLatLng);
When you use originToDirectedEdges
, you get the directed edges between the origin and the neighbors, and can convert to the neighbor cell using getDirectedEdgeDestination(edge)
:
const edges = h3.originToDirectedEdges(origin);
const neighbors = edges.map(h3.getDirectedEdgeDestination);
const centers = neighbors.map(h3.cellToLatLng);
But this is much less efficient than gridDisk
if you're just trying to get the centers of the neighbors. The reason to use edges is generally because you want to associate data with the connection itself, e.g. a flow rate between cells, or because you want to get geometry for a single edge using directedEdgeToBoundary
. If you can explain your use case, it might be helpful in determining whether you want edges at all.
I am trying or going to try to implement this routine idea:
An_Optimal_Permutation_Routing_Algorithm_on_Full-D.pdf
(Routing like this will become a bit of a problem in and around pentagon cells...)
For now I am working on the visualizations to help me out with it.
Two uses cases:
Thanks very much for you help, I will give your suggestions/advice a try, if you have any further advice I am all ears ! =D
The reason why I did not want to use GridDisk at first is basically "what does distance mean" ? Is there any chance that it might accidently give cells back from the cell further away... but I think now I understand it, if distance means 1 hexagon, then I get it, I will give it a try. There is a little downside to this method, it requires two API calls plus potentially having to allocate something, but that can be done up front, assuming it always returns same ammount, so maybe it does not have to be called:
maxGridDiskSize
The problem with maxGridDiskSize is it returns 7. So the problem with gridDisk could be that it also returns the center cell.
I do not want that. I only want the neighbour cells preferably. Now I could try and filter out the center cell from gridDisk,
However there is a further problem. The API mentions, the returned H3Indexes are in random order... So there is no way to know, what is the center cell, except by comparing the center cell index with all 7 returned, so there is a way to know. Though this costs 7 expensive 64 bit comparisions, not to bad yet, but this is not ideal.
The documentation is a bit shady:
"H3Error gridDiskDistances(H3Index origin, int k, H3Index out, int distances);"
Output is placed in the provided array in no particular order. Elements of the output array may be left as zero, which can happen when crossing a pentagon.
What array, there are only pointers ? But ok, I assume the H3Index is "the array." But what is int distances ? unexplained.
Also is it just 1 parameter or multiple ? Very strange... Almost unusuable, I am going to have to guess or maybe look at other code examples.
Dispite the weirdness, success for now:
var
vNeighbourIndexIterator : integer;
vHexagonalDistance : integer;
vNeighbourIndex : array[0..6] of H3Index;
vNeighbourDistance : array[0..6] of integer;
vCopyIndex : integer;
vIndexCount : int64;
// alternative code:
vHexagonalDistance := 1;
if maxGridDiskSize(vHexagonalDistance, vIndexCount ) = H3Error(E_SUCCESS) then
begin
if gridDiskDistances( H3Index(mCenterH3Index), vHexagonalDistance, @vNeighbourIndex[0], @vNeighbourDistance[0] ) = H3Error(E_SUCCESS) then
begin
vCopyIndex := 1;
for vNeighbourIndexIterator := 0 to 6 do
begin
if vNeighbourIndex[vNeighbourIndexIterator] <> 0 then
begin
// check if it is not the center
if H3Index(mCenterH3Index) <> vNeighbourIndex[vNeighbourIndexIterator] then
begin
mNeighbourH3Index[vCopyIndex].mData := vNeighbourIndex[vNeighbourIndexIterator];
vCopyIndex := vCopyIndex + 1;
end;
end;
end;
end else
begin
result := False;
end;
end else
begin
result := False;
end;
Checking the edge method next.
The problem with maxGridDiskSize is it returns 7. So the problem with gridDisk could be that it also returns the center cell.
I do not want that. I only want the neighbour cells preferably. Now I could try and filter out the center cell from gridDisk,
However there is a further problem. The API mentions, the returned H3Indexes are in random order... So there is no way to know, what is the center cell, except by comparing the center cell index with all 7 returned, so there is a way to know. Though this costs 7 expensive 64 bit comparisions, not to bad yet, but this is not ideal.
So if you want to be exactly sure about the distances, you can either:
I would also personally recommend to not leave disparaging comments about the documentation as you have? Pointing out where you don't find it clear is one thing, but calling it "shady" and "unusable" is somewhat insulting and also not really true when you are the first person in the multiple years of this documentation being available and successfully used by others to call it "unusable."
I didn't notice there were two gridDisk functions:
However what I did notice is both are documented as "un-ordered".
This is a problem for routing. The neighbours should have a fixed order of appearance, as depicted in the documentation:
https://h3geo.org/docs/core-library/h3Indexing
So gridDisk functions are not following the documentation.
(So now you claim gridDiskDistances does garantuee order ? So which one is it ? Is the documentation correct or are you correct ? Perhaps you looked at source code ? I do need some garantuees for future, perhaps gridDiskDistances happens to garantuee order by accident/chance.
I didn't notice there were two gridDisk functions:
1. gridDisk 2. gridDiskDistances <- I happen to use this one.
However what I did notice is both are documented as "un-ordered".
This is a problem for routing. The neighbours should have a fixed order of appearance, as depicted in the documentation:
https://h3geo.org/docs/core-library/h3Indexing
So gridDisk functions are not following the documentation.
(So now you claim gridDiskDistances does garantuee order ? So which one is it ? Is the documentation correct or are you correct ? Perhaps you looked at source code ? I do need some garantuees for future, perhaps gridDiskDistances happens to garantuee order by accident/chance.
It guarantees that each ring is separated from each other and any collection of cells are the same distance away from the origin cell. That is the level of ordering I was referring to.
The order of the cells in each grouping with respect to each other is not guaranteed, which was not what I was referring to. The guarantee on ordering is similar to:
arrayOfCells.sort((a, b) => distanceFromOriginCell(a) - distanceFromOriginCell(b))
to use some Javascript pseudocode. Two cells with the same distance will be considered "equivalent" by the sorting algorithm and the order does not matter.
The issue with the ordering within a particular ring, and why it was left undefined, is that different algorithms do different things to try and be efficient with the guarantee on ordering that has been defined (which is only distance ordering for gridDiskDistances, no guaranteed ordering at all for gridDisk, and actually counter-clockwise ordering on gridRingUnsafe, but only when there's no pentagon within the area enclosed by the ring)
On this point:
The neighbours should have a fixed order of appearance, as depicted in the documentation
This could work for a ring size of 1, but would fail the moment you reach a ring size of 2 or greater. It also would still seem "unordered" in non-trivial use-cases because the orientation of the underlying icosahedron affects which hexagon will be first in the list, such that it is not clearly associated in any way to the poles.
Going to this particular explanation of what you are trying to accomplish, however:
An_Optimal_Permutation_Routing_Algorithm_on_Full-D.pdf
You want a generalized traversal and a known orientation from one cell to the next, which is not what the disk/ring functions are for, and bolting that logic on with the directed edge will be tedious and slow.
There a more relevant API for you: https://h3geo.org/docs/api/traversal#celltolocalij
It is called a "Local IJ" coordinate system because it only works on one icosahedron face of the planet at a time, but it produces a simpler axial coordinate system that you can use.
On this one I admit that the documentation is very poor, because it was considered experimental until this release and we have not yet written the complete documentation for it.
@isaacbrodsky let me know if I am getting any of this wrong:
When you specify an origin hexagon, it is not defining the (0,0)
point, but instead simply selecting the icosahedron face to use, and the (0,0)
coordinate is the center hexagon of that particular icosahedron.
In order to orient the IJ coordinate system with the actual spatial coordinate system you care about, though, you should use three sample points forming a triangle that you convert to hexagon cells first and then acquire the Local IJ coordinates for each, which you can then use to determine the scale, rotation, and skew between your own coordinate system and the IJ coordinate system, then with that known, you can traverse through the Local IJ coordinate system by simply adding or subtracting integer values from the I and J coordinates, and then convert it back into a hexagonal cell, and from there to Lat, Lng or however you wish.
I will end this by noting that you are still being very rude in the face of help we are freely giving, so this will be the last time I message you.
@isaacbrodsky let me know if I am getting any of this wrong:
When you specify an origin hexagon, it is not defining the
(0,0)
point, but instead simply selecting the icosahedron face to use, and the(0,0)
coordinate is the center hexagon of that particular icosahedron.In order to orient the IJ coordinate system with the actual spatial coordinate system you care about, though, you should use three sample points forming a triangle that you convert to hexagon cells first and then acquire the Local IJ coordinates for each, which you can then use to determine the scale, rotation, and skew between your own coordinate system and the IJ coordinate system, then with that known, you can traverse through the Local IJ coordinate system by simply adding or subtracting integer values from the I and J coordinates, and then convert it back into a hexagonal cell, and from there to Lat, Lng or however you wish.
This is right with the correction that it operates on a base cell, not the icosahedron face. So 0,0
refers to the center of the base cell, and the coordinate system is valid only for that base cell and neighboring base cells.
For pentagon base cells it is more limited. The coordinate space encompasses the part of the pentagon base cell on the same icosahedron face as the origin
cell, the neighboring base cell on that icosahedron face, and the parts of the pentagon base cell on neighboring icosahedron faces to the origin
.
I would also personally recommend to not leave disparaging comments about the documentation as you have? Pointing out where you don't find it clear is one thing, but calling it "shady" and "unusable" is somewhat insulting and also not really true when you are the first person in the multiple years of this documentation being available and successfully used by others to call it "unusable."
Perhaps I am the first person in the world to actually use the new version 4 documentation.
I was using the version 3 documentation and I was using the version 3 library. Then recently in the last few days, version 4 appeared. It's a drop box in the top right corner.
Somehow it became difficult to determine if what I was looking at is version 3 or version 4 and that made it unusuable to me, to much time wasted trying to figure out, which version is linked to which version.
Perhaps the documentation does have issues and cross links versions, not sure, just the possibility of it is already confusing.
When looking at a function name from the library API/source code/header I no longer know if it's from version 3 or version 4, so now I have to go find it, in not only a whole bunch of different sections, but also across different versions.
At that point I gave up, and upgraded to version 4.
So far I liked nrabinowitz solution best, but it has a problem, I filled a new issue about it too.
As far as I can tell:
const edges = h3.originToDirectedEdges(origin); const neighbors = edges.map(h3.getDirectedEdgeDestination);
The first api call does not sort the edges in clock-wise order... so it seems to be unsorted... this is a problem when trying to visualize the triangles between the centers.
I want to create a triangle from center of hexagon, to center of neighbour hexagon, and then to next neighbour hexagon.
The next neighbour hexagon should be in clock-wise order to get a nice triangle.
Otherwise 180 degree triangles could occur and this looks bad.
I think the question raised about finding neighbors has been answered
I tried the following;
First of all my H3 application is producing access violations, not yet sure why.
I want to acquire all the neighbours of a H3 Cell Index. Currently at resolution 0, the base cells.
I am investigating API:
originToDirectedEdges
Once it acquires the H3 Edge Indexes I try and convert them with cellToLatLng:
!! but all the coordinates that come out seem to be the same !?!
So this seems useless ?! I also tried to convert the mode to 1, not helping,
(ofcourse then the cell[0] remains zero as it was copied from H3Index cell 0, I saw that in source code of library).
So how to do this successfully ?