-
Notifications
You must be signed in to change notification settings - Fork 578
Add geoLoopAreaRads2 function
#1101
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
Merged
Merged
Changes from all commits
Commits
Show all changes
63 commits
Select commit
Hold shift + click to select a range
df1b842
area.c
ajfriend b6d9ddb
adding in more stuff
ajfriend 8080e56
merp
ajfriend 5c8950a
kahan accumulator
ajfriend b82cab2
neumaier_add
ajfriend ec26cb8
notes
ajfriend 74e277f
benchmark area
ajfriend c915b51
turn it to 11.
ajfriend a74b9d9
sadly, no improvement from reusing some trig
ajfriend 98ecbee
back to original
ajfriend db21eb0
simplify
ajfriend 04640ef
try adding constants
ajfriend 2349ab4
format, we must
ajfriend 0dd6a5e
Adder docs
ajfriend 7528234
bench
ajfriend 4a4fb07
adder docs
ajfriend 6bd3299
Settled on Kahan implementation
ajfriend 1b21bed
Tighten tolerances in testH3CellAreaExhaustive.c due to compensated sum
ajfriend 64853b3
6
ajfriend 3547c47
notes
ajfriend 0c73921
starting new tests
ajfriend 31fd416
tests work
ajfriend 61702f1
more tests
ajfriend 4b33b02
comments
ajfriend 26eb143
docstring
ajfriend 06b2a6b
better docstrings
ajfriend f55e1b1
Adder note
ajfriend 0c939fc
don't need these imports
ajfriend a292924
clean a few more imports
ajfriend 00f45a2
format
ajfriend 39be84c
needs constants
ajfriend 53e5be9
format
ajfriend 139f483
try fuzzer
ajfriend afa64f1
.mdx
ajfriend f855776
docs in h3api.h.in
ajfriend e48b735
rads2
ajfriend 11b277a
python plan
ajfriend 226da8c
show, don't tell
ajfriend c274bff
benchmark clean up
ajfriend 43f19a9
never say never again
ajfriend fffd723
ugh, would have been cooler if it compiled the first time
ajfriend efcbea0
remove justfile
ajfriend 6e955c9
lighten the h3api.h.in description, as per usual in that file
ajfriend fdcbc17
Merge branch 'master' into cagnoli_area
ajfriend dfeb6d8
simplify adder initialization
ajfriend 6db33a3
drop numVerts < 3
ajfriend e2c20b0
degenerate loop tests
ajfriend eeb3046
try _compareArea(NULL, 0, 0.0);
ajfriend 28105c3
remove docs
ajfriend a3ee94d
drop H3_EXPORT
ajfriend 571d72e
move geoLoopAreaRads2 out of public API for now
ajfriend 7d0f56a
slim down benchmark
ajfriend 77278a6
clear these comments, maybe?
ajfriend fa555e3
try adding area.h to APP_SOURCE_FILES
ajfriend 64e9ad8
can't fail if you don't try!
ajfriend a059397
minor
ajfriend b24442f
remove justfile
ajfriend dd316e0
Adder adder = {};
ajfriend 0c7efce
degenerate loop note
ajfriend ce65b2d
use same public/private underscore convention as iterators.h
ajfriend 2dac047
move other cellArea* functions to area.c
ajfriend 27911ae
Stitch two 1/4 triangles
ajfriend 4931800
add copyright headers
ajfriend File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| /* | ||
| * Copyright 2025 Uber Technologies, Inc. | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
| #include <math.h> | ||
| #include <stdbool.h> | ||
| #include <stdio.h> | ||
|
|
||
| #include "adder.h" | ||
| #include "benchmark.h" | ||
| #include "constants.h" | ||
| #include "h3api.h" | ||
| #include "iterators.h" | ||
|
|
||
| static void doResSum(int res, bool print) { | ||
| Adder adder = {}; | ||
| double cellArea; | ||
| IterCellsResolution iter = iterInitRes(res); | ||
|
|
||
| for (; iter.h; iterStepRes(&iter)) { | ||
| H3_EXPORT(cellAreaRads2)(iter.h, &cellArea); | ||
| kadd(&adder, cellArea); | ||
| } | ||
|
|
||
| double total_area = adder.sum; | ||
| double diff = fabs(total_area - 4 * M_PI); | ||
| if (print) { | ||
| printf("res: %d, diff: %e\n", res, diff); | ||
| } | ||
| } | ||
|
|
||
| BEGIN_BENCHMARKS(); | ||
|
|
||
| static int MAX_RES = 3; | ||
|
|
||
| BENCHMARK(allCellsAtRes_print, 1, { | ||
| for (int i = 0; i <= MAX_RES; i++) { | ||
| doResSum(i, true); | ||
| } | ||
| }); | ||
|
|
||
| BENCHMARK(allCellsAtRes_noprint, 10, { | ||
| for (int i = 0; i <= MAX_RES; i++) { | ||
| doResSum(i, false); | ||
| } | ||
| }); | ||
|
|
||
| END_BENCHMARKS(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,228 @@ | ||
| /* | ||
| * Copyright 2025 Uber Technologies, Inc. | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| /** @file | ||
| * @brief tests H3 GeoLoop area calculation | ||
| * | ||
| * usage: `testGeoLoopArea` | ||
| */ | ||
|
|
||
| #include <math.h> | ||
|
|
||
| #include "area.h" | ||
| #include "h3api.h" | ||
| #include "test.h" | ||
| #include "utility.h" | ||
|
|
||
| static void _compareArea(LatLng *verts, int numVerts, double target_area) { | ||
| double tol = 1e-14; | ||
| GeoLoop loop = {.verts = verts, .numVerts = numVerts}; | ||
|
|
||
| double out; | ||
| t_assertSuccess(geoLoopAreaRads2(loop, &out)); | ||
| t_assert(fabs(out - target_area) < tol, "area should match"); | ||
| } | ||
|
|
||
| SUITE(geoLoopArea) { | ||
| TEST(triangle_basic) { | ||
| /* | ||
| GeoLoop representing a triangle covering 1/8 of the globe, with | ||
| points ordered according to right-hand rule (counter-clockwise). | ||
|
|
||
| The triangle starts at the north pole, moves down 90 degrees to the | ||
| equator, and then sweeps out 90 degrees along the equator | ||
| before returning to the north pole. | ||
|
|
||
| The globe has an area of 4*pi radians, so this 1/8 triangle piece of | ||
| the globe should have area pi/2. | ||
| */ | ||
| LatLng verts[] = { | ||
| {M_PI_2, 0.0}, | ||
| {0.0, 0.0}, | ||
| {0.0, M_PI_2}, | ||
| }; | ||
|
|
||
| _compareArea(verts, ARRAY_SIZE(verts), M_PI / 2); | ||
| } | ||
|
|
||
| TEST(triangle_reversed) { | ||
| /* | ||
| Reverse the order of the points in the triangle from the previous test, | ||
| so that they are in clockwise order. | ||
|
|
||
| Since the points are in clockwise order, GeoLoop represents the whole | ||
| globe minus the triangle above. | ||
| */ | ||
| LatLng verts[] = { | ||
| {0.0, M_PI_2}, | ||
| {0.0, 0.0}, | ||
| {M_PI_2, 0.0}, | ||
| }; | ||
|
|
||
| _compareArea(verts, ARRAY_SIZE(verts), 7 * M_PI / 2); | ||
| } | ||
|
|
||
| TEST(slice) { | ||
| /* | ||
| Stitch two 1/8 triangles together, sharing an edge along the equator | ||
| to create a 1/4 slice of the globe, with vertices at the north and south | ||
| pole. | ||
| */ | ||
| LatLng verts[] = { | ||
| {M_PI_2, 0.0}, | ||
| {0.0, 0.0}, | ||
| {-M_PI_2, 0.0}, | ||
| {0.0, M_PI_2}, | ||
| }; | ||
|
|
||
| _compareArea(verts, ARRAY_SIZE(verts), M_PI); | ||
| } | ||
|
|
||
| TEST(slice_reversed) { | ||
| /* | ||
| 3/4 slice of the globe, from north to south pole, formed by reversing | ||
| order of points from example above. | ||
| */ | ||
| LatLng verts[] = { | ||
| {M_PI_2, 0.0}, | ||
| {0.0, M_PI_2}, | ||
| {-M_PI_2, 0.0}, | ||
| {0.0, 0.0}, | ||
| }; | ||
|
|
||
| _compareArea(verts, ARRAY_SIZE(verts), 3 * M_PI); | ||
| } | ||
|
|
||
| TEST(hemisphere_east) { | ||
| /* | ||
| Stitch two 1/4 triangles together to cover the eastern hemisphere. | ||
| */ | ||
| LatLng verts[] = { | ||
| {M_PI_2, 0.0}, | ||
| {0.0, 0.0}, | ||
| {-M_PI_2, 0.0}, | ||
| {0.0, M_PI}, | ||
| }; | ||
|
|
||
| _compareArea(verts, ARRAY_SIZE(verts), 2 * M_PI); | ||
| } | ||
|
|
||
| TEST(hemisphere_north) { | ||
| /* | ||
| Stitch 4 1/8 triangles together to cover the northern hemisphere. | ||
| */ | ||
| LatLng verts[] = { | ||
| {0.0, -M_PI}, | ||
| {0.0, -M_PI_2}, | ||
| {0.0, 0.0}, | ||
| {0.0, M_PI_2}, | ||
| }; | ||
|
|
||
| _compareArea(verts, ARRAY_SIZE(verts), 2 * M_PI); | ||
| } | ||
|
|
||
| TEST(percentageSlice) { | ||
| /* | ||
| Demonstrate that edge arcs between points in a polygon or geoloop | ||
| should be less than 180 degrees (M_PI radians). | ||
|
|
||
| Create a triangle from north pole to equator and back to the north pole | ||
| that sweeps out an edge arc of t * M_PI radians along the equator, | ||
| so it should have an area of t*M_PI for t \in [0,1]. | ||
|
|
||
| However, there is a discontinuity at t = 1 (i.e., M_PI radians or 180 | ||
| degrees), where expected area goes to (2 + t) * M_PI for 1 < t < 2. | ||
|
|
||
| Recall that the area in stradians of the entire globe is 4*M_PI. | ||
| */ | ||
| for (double t = 0; t <= 1.2; t += 0.01) { | ||
| LatLng verts[] = { | ||
| {M_PI_2, 0.0}, | ||
| {0.0, -M_PI_2}, | ||
| {0.0, t * M_PI - M_PI_2}, | ||
| }; | ||
| GeoLoop loop = {.verts = verts, .numVerts = ARRAY_SIZE(verts)}; | ||
|
|
||
| double out; | ||
| t_assertSuccess(geoLoopAreaRads2(loop, &out)); | ||
|
|
||
| double tol = 1e-13; | ||
| if (t < 0.99) { | ||
| // When t < 1, the largest angle in the triangle is less than | ||
| // 180 degrees | ||
| t_assert(fabs(out - t * M_PI) <= tol, "expected area"); | ||
| } else if (t > 1.01) { | ||
| /* | ||
| Discontinuity at t == 1. For t > 1, the triangle "flips", | ||
| because the shortest geodesic path is on the other side of | ||
| the globe. The triangle is now oriented in clockwise order, | ||
| and the area computed is the area *outside* of the triangle, | ||
| which starts at 3*pi. | ||
| */ | ||
| t_assert(fabs(out - (2 + t) * M_PI) <= tol, "expected area"); | ||
| } | ||
| /* | ||
| Note that we avoid testing t == 1, since the triangle | ||
| isn't well defined because there are many possible geodesic | ||
| shortest paths when consecutive points are antipodal | ||
| (180 degrees apart). | ||
| */ | ||
| } | ||
| } | ||
|
|
||
| TEST(percentageSlice_large) { | ||
| /* | ||
| Continuing from the test above, note that a large polygon with | ||
| t > 1 is *still* representable and we can compute its area accurately; | ||
| we just need to add intermediate vertices so that | ||
| no edge arc is greater than 180 degrees. | ||
| */ | ||
| double t = 1.2; | ||
| LatLng verts[] = { | ||
| {M_PI_2, 0.0}, | ||
| {0.0, -M_PI_2}, | ||
| {0.0, 0.0}, // Extra vertex so every angle is < 180 degrees | ||
| {0.0, t * M_PI - M_PI_2}, | ||
| }; | ||
|
|
||
| _compareArea(verts, ARRAY_SIZE(verts), t * M_PI); | ||
| } | ||
|
|
||
| TEST(degenerateLoop2) { | ||
| // Note that `geoLoopAreaRads2()` works without error on degenerate | ||
| // loops, returning 0 area. | ||
| LatLng verts[] = { | ||
| {M_PI_2, 0.0}, | ||
| {0.0, -M_PI_2}, | ||
| }; | ||
| _compareArea(verts, ARRAY_SIZE(verts), 0.0); | ||
| } | ||
|
|
||
| TEST(degenerateLoop1) { | ||
| // Note that `geoLoopAreaRads2()` works without error on degenerate | ||
| // loops, returning 0 area. | ||
| LatLng verts[] = { | ||
| {0.0, 0.0}, | ||
| }; | ||
| _compareArea(verts, ARRAY_SIZE(verts), 0.0); | ||
| } | ||
|
|
||
| TEST(degenerateLoop0) { | ||
| // Note that `geoLoopAreaRads2()` works without error on degenerate | ||
| // loops, returning 0 area. | ||
| _compareArea(NULL, 0, 0.0); | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.