Skip to content

Commit

Permalink
Add support for full containment and overlapping modes in polygonToCe…
Browse files Browse the repository at this point in the history
…llsExperimental (#796)

Adds support for full containment mode and overlapping modes in polygonToCellsExperimental.

* Defines an enum for the containment modes. My assumption here is that the lowest 4 bits of the 32-bit flag are reserved for containment modes (not sure that we'd need more than 7, but wanted to be on the safe side), and that we'd use them as an integer, not as bitwise flags, since the modes are mutually exclusive.
* Implements the check for containment in iterStepPolygonCompact. This was very straightforward as we already have cellBoundaryInsidePolygon polygon available.
* Implements the check for overlapping cells in iterStepPolygonCompact. This checks center point inclusion first, then shares code with the full containment check, substituting a new cellBoundaryCrossesPolygon check for cellBoundaryInsidePolygon.
  • Loading branch information
nrabinowitz authored Nov 27, 2023
1 parent 6c1b003 commit 9af7218
Show file tree
Hide file tree
Showing 12 changed files with 537 additions and 123 deletions.
17 changes: 15 additions & 2 deletions scripts/make_countries.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ async function makeCountries(sourceUrl, targetPath) {
#include "h3api.h"
#include "mathExtensions.h"
#include "polyfill.h"
#include "polygon.h"
const GeoPolygon COUNTRIES[${polygons.length}] = {${
polygons.map((poly, i) => formatGeoPolygon(poly, names[i])).join(',')
Expand All @@ -149,13 +150,25 @@ for (int res = 0; res < MAX_RES + 1; res++) {
BENCHMARK(polygonToCells_AllCountries1, 5, {
for (int index = 0; index < ${polygons.length}; index++) {
H3_EXPORT(polygonToCells)(&COUNTRIES[index], res, 0, hexagons);
H3_EXPORT(polygonToCells)(&COUNTRIES[index], res, CONTAINMENT_CENTER, hexagons);
}
});
BENCHMARK(polygonToCells_AllCountries2, 5, {
for (int index = 0; index < ${polygons.length}; index++) {
H3_EXPORT(polygonToCellsExperimental)(&COUNTRIES[index], res, 0, hexagons);
H3_EXPORT(polygonToCellsExperimental)(&COUNTRIES[index], res, CONTAINMENT_CENTER, hexagons);
}
});
BENCHMARK(polygonToCells_AllCountries3, 5, {
for (int index = 0; index < ${polygons.length}; index++) {
H3_EXPORT(polygonToCellsExperimental)(&COUNTRIES[index], res, CONTAINMENT_FULL, hexagons);
}
});
BENCHMARK(polygonToCells_AllCountries4, 5, {
for (int index = 0; index < ${polygons.length}; index++) {
H3_EXPORT(polygonToCellsExperimental)(&COUNTRIES[index], res, CONTAINMENT_OVERLAPPING, hexagons);
}
});
Expand Down
79 changes: 70 additions & 9 deletions src/apps/benchmarks/benchmarkPolygonToCellsExperimental.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "benchmark.h"
#include "h3api.h"
#include "polyfill.h"
#include "polygon.h"

// Fixtures
LatLng sfVerts[] = {
Expand Down Expand Up @@ -122,24 +123,84 @@ southernGeoPolygon.geoloop = southernGeoLoop;
int64_t numHexagons;
H3Index *hexagons;

BENCHMARK(polygonToCellsSF, 500, {
H3_EXPORT(maxPolygonToCellsSize)(&sfGeoPolygon, 9, 0, &numHexagons);
BENCHMARK(polygonToCellsSF_Center, 500, {
H3_EXPORT(maxPolygonToCellsSize)
(&sfGeoPolygon, 9, CONTAINMENT_CENTER, &numHexagons);
hexagons = calloc(numHexagons, sizeof(H3Index));
H3_EXPORT(polygonToCellsExperimental)(&sfGeoPolygon, 9, 0, hexagons);
H3_EXPORT(polygonToCellsExperimental)
(&sfGeoPolygon, 9, CONTAINMENT_CENTER, hexagons);
free(hexagons);
});

BENCHMARK(polygonToCellsAlameda, 500, {
H3_EXPORT(maxPolygonToCellsSize)(&alamedaGeoPolygon, 9, 0, &numHexagons);
BENCHMARK(polygonToCellsSF_Full, 500, {
H3_EXPORT(maxPolygonToCellsSize)
(&sfGeoPolygon, 9, CONTAINMENT_FULL, &numHexagons);
hexagons = calloc(numHexagons, sizeof(H3Index));
H3_EXPORT(polygonToCellsExperimental)(&alamedaGeoPolygon, 9, 0, hexagons);
H3_EXPORT(polygonToCellsExperimental)
(&sfGeoPolygon, 9, CONTAINMENT_FULL, hexagons);
free(hexagons);
});

BENCHMARK(polygonToCellsSouthernExpansion, 10, {
H3_EXPORT(maxPolygonToCellsSize)(&southernGeoPolygon, 9, 0, &numHexagons);
BENCHMARK(polygonToCellsSF_Overlapping, 500, {
H3_EXPORT(maxPolygonToCellsSize)
(&sfGeoPolygon, 9, CONTAINMENT_OVERLAPPING, &numHexagons);
hexagons = calloc(numHexagons, sizeof(H3Index));
H3_EXPORT(polygonToCellsExperimental)(&southernGeoPolygon, 9, 0, hexagons);
H3_EXPORT(polygonToCellsExperimental)
(&sfGeoPolygon, 9, CONTAINMENT_OVERLAPPING, hexagons);
free(hexagons);
});

BENCHMARK(polygonToCellsAlameda_Center, 500, {
H3_EXPORT(maxPolygonToCellsSize)
(&alamedaGeoPolygon, 9, CONTAINMENT_CENTER, &numHexagons);
hexagons = calloc(numHexagons, sizeof(H3Index));
H3_EXPORT(polygonToCellsExperimental)
(&alamedaGeoPolygon, 9, CONTAINMENT_CENTER, hexagons);
free(hexagons);
});

BENCHMARK(polygonToCellsAlameda_Full, 500, {
H3_EXPORT(maxPolygonToCellsSize)
(&alamedaGeoPolygon, 9, CONTAINMENT_FULL, &numHexagons);
hexagons = calloc(numHexagons, sizeof(H3Index));
H3_EXPORT(polygonToCellsExperimental)
(&alamedaGeoPolygon, 9, CONTAINMENT_FULL, hexagons);
free(hexagons);
});

BENCHMARK(polygonToCellsAlameda_Overlapping, 500, {
H3_EXPORT(maxPolygonToCellsSize)
(&alamedaGeoPolygon, 9, CONTAINMENT_OVERLAPPING, &numHexagons);
hexagons = calloc(numHexagons, sizeof(H3Index));
H3_EXPORT(polygonToCellsExperimental)
(&alamedaGeoPolygon, 9, CONTAINMENT_OVERLAPPING, hexagons);
free(hexagons);
});

BENCHMARK(polygonToCellsSouthernExpansion_Center, 10, {
H3_EXPORT(maxPolygonToCellsSize)
(&southernGeoPolygon, 9, CONTAINMENT_CENTER, &numHexagons);
hexagons = calloc(numHexagons, sizeof(H3Index));
H3_EXPORT(polygonToCellsExperimental)
(&southernGeoPolygon, 9, CONTAINMENT_CENTER, hexagons);
free(hexagons);
});

BENCHMARK(polygonToCellsSouthernExpansion_Full, 10, {
H3_EXPORT(maxPolygonToCellsSize)
(&southernGeoPolygon, 9, CONTAINMENT_FULL, &numHexagons);
hexagons = calloc(numHexagons, sizeof(H3Index));
H3_EXPORT(polygonToCellsExperimental)
(&southernGeoPolygon, 9, CONTAINMENT_FULL, hexagons);
free(hexagons);
});

BENCHMARK(polygonToCellsSouthernExpansion_Overlapping, 10, {
H3_EXPORT(maxPolygonToCellsSize)
(&southernGeoPolygon, 9, CONTAINMENT_OVERLAPPING, &numHexagons);
hexagons = calloc(numHexagons, sizeof(H3Index));
H3_EXPORT(polygonToCellsExperimental)
(&southernGeoPolygon, 9, CONTAINMENT_OVERLAPPING, hexagons);
free(hexagons);
});

Expand Down
13 changes: 7 additions & 6 deletions src/apps/testapps/testH3Memory.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "h3api.h"
#include "latLng.h"
#include "polyfill.h"
#include "polygon.h"
#include "test.h"
#include "utility.h"

Expand Down Expand Up @@ -230,22 +231,22 @@ SUITE(h3Memory) {
sfGeoPolygon.numHoles = 0;

int64_t numHexagons;
t_assertSuccess(H3_EXPORT(maxPolygonToCellsSize)(&sfGeoPolygon, 9, 0,
&numHexagons));
t_assertSuccess(H3_EXPORT(maxPolygonToCellsSize)(
&sfGeoPolygon, 9, CONTAINMENT_CENTER, &numHexagons));
H3Index *hexagons = calloc(numHexagons, sizeof(H3Index));

resetMemoryCounters(0);
failAlloc = true;
H3Error err = H3_EXPORT(polygonToCellsExperimental)(&sfGeoPolygon, 9, 0,
hexagons);
H3Error err = H3_EXPORT(polygonToCellsExperimental)(
&sfGeoPolygon, 9, CONTAINMENT_CENTER, hexagons);
t_assert(err == E_MEMORY_ALLOC,
"polygonToCellsExperimental failed (1)");
t_assert(actualAllocCalls == 1, "alloc called once");
t_assert(actualFreeCalls == 0, "free not called");

resetMemoryCounters(1);
err = H3_EXPORT(polygonToCellsExperimental)(&sfGeoPolygon, 9, 0,
hexagons);
err = H3_EXPORT(polygonToCellsExperimental)(
&sfGeoPolygon, 9, CONTAINMENT_CENTER, hexagons);
t_assert(err == E_SUCCESS, "polygonToCellsExperimental succeeded (1)");
t_assert(actualAllocCalls == 1, "alloc called one time");
t_assert(actualFreeCalls == 1, "free called one time");
Expand Down
77 changes: 70 additions & 7 deletions src/apps/testapps/testPolyfillInternal.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,14 @@
* limitations under the License.
*/

#include <math.h>

#include "bbox.h"
#include "h3Index.h"
#include "h3api.h"
#include "latLng.h"
#include "polyfill.h"
#include "polygon.h"
#include "test.h"
#include "utility.h"

Expand All @@ -33,16 +36,24 @@ static GeoPolygon sfGeoPolygon = {
{0.6599990002976, -2.1376771158464}}},
.numHoles = 0};

static GeoPolygon invalidGeoPolygon = {
.geoloop = {.numVerts = 4,
.verts = (LatLng[]){{NAN, -2.1364398519396},
{0.6595011102219, NAN},
{NAN, -2.1354884206045},
{0.6581220034068, NAN}}},
.numHoles = 0};

SUITE(polyfillInternal) {
TEST(iterInitPolygonCompact_errors) {
IterCellsPolygonCompact iter;

iter = iterInitPolygonCompact(&sfGeoPolygon, -1, 0);
iter = iterInitPolygonCompact(&sfGeoPolygon, -1, CONTAINMENT_CENTER);
t_assert(iter.error == E_RES_DOMAIN,
"Got expected error for invalid res");
t_assert(iter.cell == H3_NULL, "Got null output for invalid res");

iter = iterInitPolygonCompact(&sfGeoPolygon, 16, 0);
iter = iterInitPolygonCompact(&sfGeoPolygon, 16, CONTAINMENT_CENTER);
t_assert(iter.error == E_RES_DOMAIN,
"Got expected error for invalid res");
t_assert(iter.cell == H3_NULL, "Got null output for invalid res");
Expand All @@ -53,11 +64,12 @@ SUITE(polyfillInternal) {
t_assert(iter.cell == H3_NULL, "Got null output for invalid flags");
}

TEST(iterStepPolygonCompact_errors) {
TEST(iterStepPolygonCompact_invalidCellErrors) {
IterCellsPolygonCompact iter;
H3Index cell;

iter = iterInitPolygonCompact(&sfGeoPolygon, 9, 0);
iter = iterInitPolygonCompact(&sfGeoPolygon, 9, CONTAINMENT_CENTER);
t_assertSuccess(iter.error);

// Give the iterator a cell with a bad base cell
cell = 0x85283473fffffff;
Expand All @@ -69,7 +81,37 @@ SUITE(polyfillInternal) {
"Got expected error for invalid cell");
t_assert(iter.cell == H3_NULL, "Got null output for invalid cell");

iter = iterInitPolygonCompact(&sfGeoPolygon, 9, 0);
iter = iterInitPolygonCompact(&sfGeoPolygon, 9, CONTAINMENT_CENTER);
t_assertSuccess(iter.error);

// Give the iterator a cell with a bad base cell, at the target res
cell = 0x89283470003ffff;
H3_SET_BASE_CELL(cell, 123);
iter.cell = cell;

iterStepPolygonCompact(&iter);
t_assert(iter.error == E_CELL_INVALID,
"Got expected error for invalid cell");
t_assert(iter.cell == H3_NULL,
"Got null output for invalid cell at res");

iter = iterInitPolygonCompact(&sfGeoPolygon, 9, CONTAINMENT_FULL);
t_assertSuccess(iter.error);

// Give the iterator a cell with a bad base cell, at the target res
// (full containment)
cell = 0x89283470003ffff;
H3_SET_BASE_CELL(cell, 123);
iter.cell = cell;

iterStepPolygonCompact(&iter);
t_assert(iter.error == E_CELL_INVALID,
"Got expected error for invalid cell");
t_assert(iter.cell == H3_NULL,
"Got null output for invalid cell at res");

iter = iterInitPolygonCompact(&sfGeoPolygon, 9, CONTAINMENT_CENTER);
t_assertSuccess(iter.error);

// Give the iterator a cell that's too fine for a child check,
// and a target resolution that allows this to run. This cell has
Expand All @@ -84,9 +126,28 @@ SUITE(polyfillInternal) {
t_assert(iter.cell == H3_NULL, "Got null output for invalid cell");
}

TEST(iterStepPolygonCompact_invalidPolygonErrors) {
IterCellsPolygonCompact iter;

// Start with a good polygon, otherwise we error out early
iter =
iterInitPolygonCompact(&sfGeoPolygon, 5, CONTAINMENT_OVERLAPPING);
t_assertSuccess(iter.error);

// Give the iterator a bad polygon and a cell at target res
iter._polygon = &invalidGeoPolygon;
iter.cell = 0x85283473fffffff;

iterStepPolygonCompact(&iter);
t_assert(iter.error == E_LATLNG_DOMAIN,
"Got expected error for invalid polygon");
t_assert(iter.cell == H3_NULL, "Got null output for invalid cell");
}

TEST(iterDestroyPolygonCompact) {
IterCellsPolygonCompact iter =
iterInitPolygonCompact(&sfGeoPolygon, 9, 0);
iterInitPolygonCompact(&sfGeoPolygon, 9, CONTAINMENT_CENTER);
t_assertSuccess(iter.error);

iterDestroyPolygonCompact(&iter);
t_assert(iter.error == E_SUCCESS, "Got success for destroyed iterator");
Expand All @@ -101,7 +162,9 @@ SUITE(polyfillInternal) {
}

TEST(iterDestroyPolygon) {
IterCellsPolygon iter = iterInitPolygon(&sfGeoPolygon, 9, 0);
IterCellsPolygon iter =
iterInitPolygon(&sfGeoPolygon, 9, CONTAINMENT_CENTER);
t_assertSuccess(iter.error);

iterDestroyPolygon(&iter);
t_assert(iter.error == E_SUCCESS, "Got success for destroyed iterator");
Expand Down
24 changes: 12 additions & 12 deletions src/apps/testapps/testPolygonInternal.c
Original file line number Diff line number Diff line change
Expand Up @@ -626,55 +626,55 @@ SUITE(polygonInternal) {
H3_EXPORT(destroyLinkedMultiPolygon)(&polygon);
}

TEST(lineIntersectsLine) {
TEST(lineCrossesLine) {
LatLng lines1[4] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}};
t_assert(
lineIntersectsLine(&lines1[0], &lines1[1], &lines1[2], &lines1[3]),
lineCrossesLine(&lines1[0], &lines1[1], &lines1[2], &lines1[3]),
"diagonal intersection");

LatLng lines2[4] = {{1, 1}, {0, 0}, {1, 0}, {0, 1}};
t_assert(
lineIntersectsLine(&lines2[0], &lines2[1], &lines2[2], &lines2[3]),
lineCrossesLine(&lines2[0], &lines2[1], &lines2[2], &lines2[3]),
"diagonal intersection, reverse vertexes");

LatLng lines3[4] = {{0.5, 0}, {0.5, 1}, {0, 0.5}, {1, 0.5}};
t_assert(
lineIntersectsLine(&lines3[0], &lines3[1], &lines3[2], &lines3[3]),
lineCrossesLine(&lines3[0], &lines3[1], &lines3[2], &lines3[3]),
"horizontal/vertical intersection");

LatLng lines4[4] = {{0.5, 1}, {0.5, 0}, {1, 0.5}, {0, 0.5}};
t_assert(
lineIntersectsLine(&lines4[0], &lines4[1], &lines4[2], &lines4[3]),
lineCrossesLine(&lines4[0], &lines4[1], &lines4[2], &lines4[3]),
"horizontal/vertical intersection, reverse vertexes");

LatLng lines5[4] = {{0, 0}, {0.4, 0.4}, {0, 1}, {1, 0}};
t_assert(
!lineIntersectsLine(&lines5[0], &lines5[1], &lines5[2], &lines5[3]),
!lineCrossesLine(&lines5[0], &lines5[1], &lines5[2], &lines5[3]),
"diagonal non-intersection, below");

LatLng lines6[4] = {{0.6, 0.6}, {1, 1}, {0, 1}, {1, 0}};
t_assert(
!lineIntersectsLine(&lines6[0], &lines6[1], &lines6[2], &lines6[3]),
!lineCrossesLine(&lines6[0], &lines6[1], &lines6[2], &lines6[3]),
"diagonal non-intersection, above");

LatLng lines7[4] = {{0.5, 0}, {0.5, 1}, {0, 0.5}, {0.4, 0.5}};
t_assert(
!lineIntersectsLine(&lines7[0], &lines7[1], &lines7[2], &lines7[3]),
!lineCrossesLine(&lines7[0], &lines7[1], &lines7[2], &lines7[3]),
"horizontal/vertical non-intersection, below");

LatLng lines8[4] = {{0.5, 0}, {0.5, 1}, {0.6, 0.5}, {1, 0.5}};
t_assert(
!lineIntersectsLine(&lines8[0], &lines8[1], &lines8[2], &lines8[3]),
!lineCrossesLine(&lines8[0], &lines8[1], &lines8[2], &lines8[3]),
"horizontal/vertical non-intersection, above");

LatLng lines9[4] = {{0.5, 0}, {0.5, 0.4}, {0, 0.5}, {1, 0.5}};
t_assert(
!lineIntersectsLine(&lines9[0], &lines9[1], &lines9[2], &lines9[3]),
!lineCrossesLine(&lines9[0], &lines9[1], &lines9[2], &lines9[3]),
"horizontal/vertical non-intersection, left");

LatLng lines10[4] = {{0.5, 0.6}, {0.5, 1}, {0, 0.5}, {1, 0.5}};
t_assert(!lineIntersectsLine(&lines10[0], &lines10[1], &lines10[2],
&lines10[3]),
t_assert(!lineCrossesLine(&lines10[0], &lines10[1], &lines10[2],
&lines10[3]),
"horizontal/vertical non-intersection, right");
}

Expand Down
Loading

0 comments on commit 9af7218

Please sign in to comment.