-
Notifications
You must be signed in to change notification settings - Fork 472
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 h3ToIj #102
Add h3ToIj #102
Conversation
h3ToIj wraps the internal function h3ToIjk and produces a two-dimensional IJ coordinate system. It has the same limitations of h3ToIjk regarding cells too far away from the origin or across pentagonal distortion.
src/apps/testapps/testH3ToIj.c
Outdated
@@ -111,6 +120,48 @@ void h3Distance_kRing_assertions(H3Index h3) { | |||
|
|||
BEGIN_TESTS(h3ToIjk); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: h3ToIj
now? Not actually sure this is shown anywhere.
src/apps/testapps/testH3ToIj.c
Outdated
@@ -111,6 +120,48 @@ void h3Distance_kRing_assertions(H3Index h3) { | |||
|
|||
BEGIN_TESTS(h3ToIjk); | |||
|
|||
H3Index bc1 = H3_INIT; | |||
setH3Index(&bc1, 0, 15, 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Can you use clearer var names or add comments explaining what these are?
src/h3lib/lib/h3Index.c
Outdated
* @param origin An anchoring index for the ij coordinate system. | ||
* @param index Index to find the coordinates of | ||
* @param out ij coordinates of the index will be placed here on success | ||
* @return 0 on success, or another value on failure. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain in the doc comment why you might get failure values?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All comments are nits. Would prefer they're fixed but not blocking.
src/apps/testapps/testH3ToIj.c
Outdated
@@ -38,8 +39,11 @@ void h3Distance_identity_assertions(H3Index h3) { | |||
|
|||
t_assert(H3_EXPORT(h3Distance)(h3, h3) == 0, "distance to self is 0"); | |||
|
|||
// Test that coordinates are as expected | |||
CoordIJ ij; | |||
t_assert(H3_EXPORT(h3ToIj)(h3, h3, &ij) == 0, "failed to get ij"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment on this test doesn't make sense?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clarified that comment.
src/apps/testapps/testH3ToIj.c
Outdated
CoordIJK origin = {0}; | ||
t_assert(h3ToIjk(h3, h3, &origin) == 0, "got ijk for origin"); | ||
CoordIJ origin = {0}; | ||
t_assert(H3_EXPORT(h3ToIj)(h3, h3, &origin) == 0, "got ijk for origin"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Should say got ij for origin
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
src/apps/testapps/testH3ToIj.c
Outdated
t_assert(h3ToIjk(h3, offset, &ijk) == 0, "got ijk for destination"); | ||
CoordIJ ij = {0}; | ||
t_assert(H3_EXPORT(h3ToIj)(h3, offset, &ij) == 0, | ||
"got ijk for destination"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same nit here on ijk -> ij
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
* Each axis is spaced 120 degrees apart. | ||
*/ | ||
typedef struct { | ||
int i; ///< i component |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Do we really need these inline comments?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dfellis they parallel the inline comments on components in CoordIJK
, so consistency would argue for keeping them here (or changing them there). Putting them in CoordIJK
was a coin-flip in favor of completeness, but I agree they're overkill.
@isaacbrodsky asked awhile ago "Do we want to make any changes to support evolving the CoordIJ system in the future?" You might want to specifically mention in the documentation that the result of Otherwise LGTM. |
Sorry I haven't had time to return to this PR. Re: @sahrk
While I like the flexibility that gives me as a library developer, I'm concerned it might be confusing for library users. This also came up in #83 (comment), but most of the discussion there was about whether to expose |
I've been on vacation until today so I didn't notice. ;)
I'm with @sahrk about noting that these values could change between versions of the code. As long as they're internally consistent and operations performed on them will work the same each time (eg |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only just realized that the reverse function, ijToH3
is missing.
|
Yes, but without something like that, this library would need to implement every kind of planar operation users desire (such as line tracing that's desired for more efficient polyfill operations). Having an "out" into a coordinate system will reduce the bloating of the API. |
Agreed @dfellis, I think the utility case is very convincing. I was just encouraging more of a warning label. |
To throw in my two cents here:
Is that true, or will people use the IJ values directly in their own code to implement their own algorithms in planar space? If so, I'd want a guarantee that output is stable or a very clear delineation of the unstable interfaces. I don't think a comment is sufficient unless we're willing to stick with current (successful) output until the next major version change. |
I definitely want to encourage that. Would changing the name of the function help, perhaps I like @nrabinowitz' suggestion of adding an "experimental" prefix, which could then become a deprecated alias when the function is promoted to not have the prefix. ( |
@isaacbrodsky encoding a warning in the name sounds like a great idea to me. I think that adding I realized it might be relevant that I now have the sequence number mapping mentioned in #83 implemented and will be offering it up asap. Assuming you want to use it, it seems like that should have the same kind of caveats as the |
The function is renamed to indicate it's experimental, and the suite of functions is refactored to its own `localij.c` module.
Passing INVALID_BASE_CELL caused a lookup beyond the table, and thus undefined behavior. The behavior should always be to treat the base cell as not a pentagon in that case.
…ection is not K_AXES_DIGIT.
Now that we have a working build, I think this is ready for a new review. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Several comments asking for clarification, but very close to approving this.
- Benchmarks for the kRing method for k's of size 10, 20, 30, and 40. | ||
### Changed | ||
- Internal `h3ToIjk` function renamed to `h3ToLocalIjk`. (#102) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this supposed to be h3ToLocalIj
like elsewhere or no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, as below the IJK functions need to be kept for internal implementation.
* For reversing the rotation introduced in PENTAGON_ROTATIONS when | ||
* the origin is on a pentagon. | ||
*/ | ||
const int PENTAGON_ROTATIONS_REVERSE[7][7] = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I get PENTAGON_ROTATIONS
, PENTAGON_ROTATIONS_REVERSE_NONPOLAR
and PENTAGON_ROTATIONS_REVERSE_POLAR
, but I don't get this one. Is this one applied in both cases, but then why have the other two? Or why not simply push the compound rotation results into both of the ones below if this is applied and then one of the following two are applied?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PENTAGON_ROTATIONS_REVERSE
is not used in the same cases. The comments try to differentiate that this table is used when the origin index is on a pentagon base cell, the two "reverse" tables are used when the origin index is not on a pentagon base cell.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I share David's confusion - maybe expand the comments below to say ...when the index is on a pentagon and the origin is not.
* @param out The index will be placed here on success | ||
* @return 0 on success, or another value on failure. | ||
*/ | ||
int localIjkToH3(H3Index origin, const CoordIJK* ijk, H3Index* out) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought IJK was going away from this diff? Or are we going H3 -> IJK -> IJ?
CoordIJK ijk; | ||
ijToIjk(ij, &ijk); | ||
|
||
return localIjkToH3(origin, &ijk, out); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, you are doing exactly that. Alright, then.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a few nits, but overall this is looking really good! Prestamping, assuming the nits get addressed.
src/apps/filters/h3ToLocalIj.c
Outdated
@@ -14,18 +14,20 @@ | |||
* limitations under the License. | |||
*/ | |||
/** @file | |||
* @brief stdin/stdout filter that converts from H3 indexes to relative IJK | |||
* coordinates. This is experimental. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still true, yes?
src/apps/testapps/testH3ToLocalIj.c
Outdated
t_assert(_ijkMatches(&ijk, &UNIT_VECS[0]) == 1, "not at 0,0,0 (res 0)"); | ||
} else if (r == 1) { | ||
t_assert(_ijkMatches(&ijk, &UNIT_VECS[H3_GET_INDEX_DIGIT(h3, 1)]) == 1, | ||
"not at expected coordinates (res 1)"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: It looks like these are using negative assertion messages ("test failed for condition") where most of our tests (and some in this file) use positive messages ("test succeeds for condition") or spec messages ("test must succeed when conditition").
"test coordinates result in valid index"); | ||
if (_isBaseCellPentagon(H3_EXPORT(h3GetBaseCell)(testH3))) { | ||
// h3IsValid doesn't test for indexes representing part of the | ||
// deleted subsequence. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we do this? Should we add an issue for it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd say such an index is invalid, similar to if the index had digits set to 7
(invalid child). I think opening an issue to make those indexes invalid would be good.
src/apps/testapps/testH3ToLocalIj.c
Outdated
// Don't iterate all of res 3, to save time | ||
iterateAllIndexesAtResPartial(3, localIjToH3_kRing_assertions, 27); | ||
// These would take too long, even at partial execution | ||
// iterateAllIndexesAtResPartial(4, localIjToH3_kRing_assertions, 20); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we include them, or just delete the commented code in this case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replaced in all cases with the comment that further resolutions aren't tested for time reasons.
We could also consider adding some tests to sample indexes at higher resolutions in the future.
@@ -369,7 +369,7 @@ H3Index h3NeighborRotations(H3Index origin, Direction dir, int* rotations) { | |||
// Account for differing orientation of the base cells (this edge | |||
// might not follow properties of some other edges.) | |||
if (oldBaseCell != newBaseCell) { | |||
if (newBaseCell == 4 || newBaseCell == 117) { | |||
if (_isBaseCellPolarPentagon(newBaseCell)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
src/h3lib/lib/localij.c
Outdated
{0, -1, 0, 0, 0, 0, 0}, // 6 | ||
}; | ||
/** | ||
* Reverse base cell direction -> leading index digit -> roations 60 ccw. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo, here and below: roations
* For reversing the rotation introduced in PENTAGON_ROTATIONS when | ||
* the origin is on a pentagon. | ||
*/ | ||
const int PENTAGON_ROTATIONS_REVERSE[7][7] = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I share David's confusion - maybe expand the comments below to say ...when the index is on a pentagon and the origin is not.
src/h3lib/lib/localij.c
Outdated
*/ | ||
int h3ToLocalIjk(H3Index origin, H3Index h3, CoordIJK* out) { | ||
if (H3_GET_MODE(origin) != H3_HEXAGON_MODE || | ||
H3_GET_MODE(h3) != H3_HEXAGON_MODE) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure about this check - I feel like, similar to the discussion we had around h3IsValid
, it would apply to many of our functions, and it's not clear to me why we'd use it here and not in other places.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we have the ability to validate some user inputs, is there a reason we would not want to? I think some functions aren't able to cleanly report failures right now, which certainly limits where we can add checks (until 4.0.0 and #104)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, actually. If the function could be conceivably used in a looping hotspot I could see users want to have a faster iteration time if they're confident in the data already being valid.
This feels like one of those functions. H3 indexes could already be validated and stored in DB, then they want fast conversion to IJK for some planar operations and then back again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I should have replied directly to the performance concern rather than asking a question. I think in this specific case I am OK with removing the mode check because it doesn't appear this will cause issues, for example there are no array accesses based on the mode of the indexes.
Aside: This code was not introduced in this PR, it was introduced in #83 where we had a similar discussion but didn't come to a conclusion as @nrabinowitz suggested.
I believe there are at least some inputs we should validate, a good example being where there are array accesses based on the inputs, since that could potentially result in segfault, etc.
Normalized test assertion messages to assert a positive outcome.
Tests are updated to assume that unidirectional edges are treated as their origin by this function. There does not appear to be a case where finding the IJK of the origin itself would fail, so the line checking for that in h3Distance is excluded from coverage.
@dfellis and others, please review again. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One nit, otherwise LGTM.
} else { | ||
t_assert(0, "wrong resolution"); | ||
t_assert(0, "resolution supported by test function (coordinates)"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: This (essentially assert.fail
) is the one place where negative assertion messages actually make more sense IMO.
int H3_EXPORT(h3Distance)(H3Index origin, H3Index h3) { | ||
CoordIJK originIjk, h3Ijk; | ||
if (h3ToLocalIjk(origin, origin, &originIjk)) { | ||
// Currently there are no tests that would cause getting the coordinates |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does this mean practically? Is there no way to create a new test case that would trigger this check to fail?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I can see there is no input to h3ToLocalIjk
where the origin is the same as the index that would fail. The only cases that return failure involve either resolution mismatch, being on different base cells, or deleted subsequence on the pentagon (which should be unreachable when origin=index.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see what you mean.
h3ToIj wraps the internal function h3ToIjk and produces a two-dimensional IJ coordinate system. It has the same limitations of h3ToIjk regarding cells too far away from the origin or across pentagonal distortion.
Do we want to make any changes to support evolving the CoordIJ system in the future?
Builds on #83 and relates to #99.