diff --git a/src/apps/testapps/testCellsToLinkedMultiPolygon.c b/src/apps/testapps/testCellsToLinkedMultiPolygon.c index effb7b225..9f1a739a3 100644 --- a/src/apps/testapps/testCellsToLinkedMultiPolygon.c +++ b/src/apps/testapps/testCellsToLinkedMultiPolygon.c @@ -16,7 +16,8 @@ #include -#include "algos.h" +#include "algos.h" // not needed? prolly just need linkedGeo.h +#include "h3api.h" // todo: don't be lazy, this file has some lax imports #include "test.h" #include "utility.h" @@ -47,6 +48,61 @@ SUITE(cellsToLinkedMultiPolygon) { H3_EXPORT(destroyLinkedMultiPolygon)(&polygon); } + TEST(singleHex_GMP) { + GeoMultiPolygon mpoly; + H3Index cells[] = {0x890dab6220bffff}; + int numCells = ARRAY_SIZE(cells); + + t_assertSuccess( + H3_EXPORT(cellsToGeoMultiPolygon)(cells, numCells, &mpoly)); + + t_assert(mpoly.numPolygons == 1, "only one polygon"); + t_assert(mpoly.polygons[0].numHoles == 0, "1 outer loop and no holes"); + t_assert(mpoly.polygons[0].geoloop.numVerts == 6, "6 vertices in hex"); + + H3_EXPORT(freeGeoMultiPolygon)(mpoly); + } + + TEST(nestedDonut_GMP) { + GeoMultiPolygon mpoly; + GeoPolygon poly; + // hollow 1-ring + hollow 3-ring around the same hex + H3Index cells[] = { + 0x89283082813ffff, 0x8928308281bffff, 0x8928308280bffff, + 0x8928308280fffff, 0x89283082807ffff, 0x89283082817ffff, + 0x8928308289bffff, 0x892830828d7ffff, 0x892830828c3ffff, + 0x892830828cbffff, 0x89283082853ffff, 0x89283082843ffff, + 0x8928308284fffff, 0x8928308287bffff, 0x89283082863ffff, + 0x89283082867ffff, 0x8928308282bffff, 0x89283082823ffff, + 0x89283082837ffff, 0x892830828afffff, 0x892830828a3ffff, + 0x892830828b3ffff, 0x89283082887ffff, 0x89283082883ffff}; + int numCells = ARRAY_SIZE(cells); + + t_assertSuccess( + H3_EXPORT(cellsToGeoMultiPolygon)(cells, numCells, &mpoly)); + + t_assert(mpoly.numPolygons == 2, "Polygon count correct"); + t_assert(mpoly.polygons[0].numHoles == 1, "1 outer loop and 1 hole"); + t_assert(mpoly.polygons[1].numHoles == 1, "1 outer loop and 1 hole"); + + if (mpoly.polygons[0].geoloop.numVerts != 42) { + // Polygon order is arbitrary; Swap so that the larger ring is first + poly = mpoly.polygons[0]; + mpoly.polygons[0] = mpoly.polygons[1]; + mpoly.polygons[1] = poly; + } + + poly = mpoly.polygons[0]; + t_assert(poly.geoloop.numVerts == 42, "Got expected outer loop"); + t_assert(poly.holes[0].numVerts == 30, "Got expected inner loop"); + + poly = mpoly.polygons[1]; + t_assert(poly.geoloop.numVerts == 18, "Got expected outer loop"); + t_assert(poly.holes[0].numVerts == 6, "Got expected inner loop"); + + H3_EXPORT(freeGeoMultiPolygon)(mpoly); + } + TEST(invalid) { LinkedGeoPolygon polygon; H3Index set[] = {0xfffffffffffffff}; diff --git a/src/h3lib/include/h3api.h.in b/src/h3lib/include/h3api.h.in index 3f533d5de..725cf340c 100644 --- a/src/h3lib/include/h3api.h.in +++ b/src/h3lib/include/h3api.h.in @@ -298,6 +298,12 @@ DECLSPEC H3Error H3_EXPORT(cellsToLinkedMultiPolygon)(const H3Index *h3Set, DECLSPEC void H3_EXPORT(destroyLinkedMultiPolygon)(LinkedGeoPolygon *polygon); /** @} */ +DECLSPEC H3Error H3_EXPORT(cellsToGeoMultiPolygon)(const H3Index *cells, + const int numCells, + GeoMultiPolygon *out); + +DECLSPEC void H3_EXPORT(freeGeoMultiPolygon)(GeoMultiPolygon mpoly); + /** @defgroup degsToRads degsToRads * Functions for degsToRads * @{ diff --git a/src/h3lib/lib/algos.c b/src/h3lib/lib/algos.c index 7d304f62b..c6d70ec7f 100644 --- a/src/h3lib/lib/algos.c +++ b/src/h3lib/lib/algos.c @@ -1173,3 +1173,182 @@ H3Error H3_EXPORT(cellsToLinkedMultiPolygon)(const H3Index *h3Set, } return normalizeResult; } + +int num_polys(LinkedGeoPolygon *L) { + int n = 0; + while (L) { + n++; + L = L->next; + } + return n; +} + +int num_loops(LinkedGeoLoop *L) { + int n = 0; + while (L) { + n++; + L = L->next; + } + return n; +} + +int num_latlngs(LinkedLatLng *L) { + int n = 0; + while (L) { + n++; + L = L->next; + } + return n; +} + +/* +LinkedGeoLoop: + LinkedLatLng *first + LinkedLatLng *last + LinkedGeoLoop *next + +LinkedLatLng: + LatLng vertex + LinkedLatLng *next + +GeoLoop: + int numVerts + LatLng *verts + */ +GeoLoop _LinkedGeoLoop_to_GeoLoop(LinkedGeoLoop link_loop) { + int n = num_latlngs(link_loop.first); // double? + + GeoLoop geo_loop = { + .numVerts = n, + .verts = H3_MEMORY(calloc)(n, sizeof(LatLng)) // hello there + }; + + LinkedLatLng *L = link_loop.first; // double? + int i = 0; + while (L) { + geo_loop.verts[i] = L->vertex; + L = L->next; + i++; + } + + return geo_loop; +} + +/* +LinkedGeoPolygon: + LinkedGeoLoop *first + LinkedGeoLoop *last + LinkedGeoPolygon *next + +LinkedGeoLoop: + LinkedLatLng *first + LinkedLatLng *last + LinkedGeoLoop *next + +GeoPolygon: + GeoLoop geoloop + int numHoles + GeoLoop *holes + */ +GeoPolygon _LinkedGeoPolygon_to_Polygon(LinkedGeoPolygon link_poly) { + LinkedGeoLoop *L = link_poly.first; + + int n = num_loops(L) - 1; + GeoPolygon geo_poly = { + .geoloop = _LinkedGeoLoop_to_GeoLoop(*L), + .numHoles = n, + .holes = H3_MEMORY(calloc)(n, sizeof(GeoLoop)), + }; + + L = L->next; + int i = 0; + while (L) { + geo_poly.holes[i] = _LinkedGeoLoop_to_GeoLoop(*L); + L = L->next; + i++; + } + + return geo_poly; +} + +/* +LinkedGeoPolygon: + LinkedGeoLoop *first + LinkedGeoLoop *last + LinkedGeoPolygon *next + +GeoMultiPolygon: + int numPolygons + GeoPolygon *polygons + + */ +GeoMultiPolygon _LinkedGeoPoly_to_GeoMultiPolygon(LinkedGeoPolygon *link_poly) { + int n = num_polys(link_poly); + + GeoMultiPolygon geo_mpoly = { + .numPolygons = n, + .polygons = H3_MEMORY(calloc)(n, sizeof(GeoPolygon)) // combine this + }; + + LinkedGeoPolygon *L = link_poly; + int i = 0; + while (L) { + geo_mpoly.polygons[i] = _LinkedGeoPolygon_to_Polygon(*L); + L = L->next; + i++; + } + + return geo_mpoly; +} + +H3Error H3_EXPORT(cellsToGeoMultiPolygon)(const H3Index *cells, + const int numCells, + GeoMultiPolygon *out) { + H3Error err; + LinkedGeoPolygon lgp; + + err = H3_EXPORT(cellsToLinkedMultiPolygon)(cells, numCells, &lgp); + if (err) { + return err; + } + + *out = _LinkedGeoPoly_to_GeoMultiPolygon(&lgp); + // todo: capture errors from conversion function + + H3_EXPORT(destroyLinkedMultiPolygon)(&lgp); + + return E_SUCCESS; +} + +/* +GeoLoop: + int numVerts + LatLng *verts + +GeoPolygon: + GeoLoop geoloop + int numHoles + GeoLoop *holes + +GeoMultiPolygon: + int numPolygons + GeoPolygon *polygons + */ + +void freeGeoLoop(GeoLoop loop) { H3_MEMORY(free)(loop.verts); } + +void freeGeoPolygon(GeoPolygon poly) { + freeGeoLoop(poly.geoloop); + + for (int i = 0; i < poly.numHoles; i++) { + freeGeoLoop(poly.holes[i]); + } + H3_MEMORY(free)(poly.holes); +} + +void H3_EXPORT(freeGeoMultiPolygon)(GeoMultiPolygon mpoly) { + for (int i = 0; i < mpoly.numPolygons; i++) { + freeGeoPolygon(mpoly.polygons[i]); + } + H3_MEMORY(free)(mpoly.polygons); +}