diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c90917a1..e7893ef36 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -180,8 +180,9 @@ set(EXAMPLE_SOURCE_FILES examples/neighbors.c examples/compactCells.c examples/edge.c) +set(H3_BIN_SOURCE_FILES + src/apps/filters/h3.c) set(OTHER_SOURCE_FILES - src/apps/filters/h3.c src/apps/filters/cellToLatLng.c src/apps/filters/cellToLocalIj.c src/apps/filters/localIjToCell.c @@ -289,7 +290,7 @@ set(OTHER_SOURCE_FILES src/apps/benchmarks/benchmarkH3Api.c) set(ALL_SOURCE_FILES - ${LIB_SOURCE_FILES} ${APP_SOURCE_FILES} ${TEST_APP_SOURCE_FILES} ${OTHER_SOURCE_FILES}) + ${LIB_SOURCE_FILES} ${APP_SOURCE_FILES} ${TEST_APP_SOURCE_FILES} ${H3_BIN_SOURCE_FILES} ${OTHER_SOURCE_FILES}) # This is done for quality control purposes (to detect if any source files are missing from # our list), but is not done as the authoritative list as per CMake developer recommendations. diff --git a/CMakeTests.cmake b/CMakeTests.cmake index a5d57e279..a84f1b0d4 100644 --- a/CMakeTests.cmake +++ b/CMakeTests.cmake @@ -1,4 +1,4 @@ -# Copyright 2017-2022 Uber Technologies, Inc. +# Copyright 2017-2022, 2024 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. @@ -228,8 +228,9 @@ add_h3_test(testGridDistanceExhaustive src/apps/testapps/testGridDistanceExhaust add_h3_test(testH3CellAreaExhaustive src/apps/testapps/testH3CellAreaExhaustive.c) add_h3_test(testCellToBBoxExhaustive src/apps/testapps/testCellToBBoxExhaustive.c) -add_h3_cli_test(testCliCellToLatLng "cellToLatLng -c 8928342e20fffff" "37.5012466151, -122.5003039349") +add_h3_cli_test(testCliCellToLatLng "cellToLatLng -c 8928342e20fffff" "POINT(-122.5003039349 37.5012466151)") add_h3_cli_test(testCliLatLngToCell "latLngToCell --lat 20 --lng 123 -r 2" "824b9ffffffffff") +add_h3_cli_test(testCliCellToBoundary "cellToBoundary -c 8928342e20fffff" "POLYGON((-122.4990471431 37.4997389893, -122.4979805011 37.5014245698, -122.4992373065 37.5029321860, -122.5015607527 37.5027541980, -122.5026273256 37.5010686174, -122.5013705214 37.4995610248, -122.4990471431 37.4997389893))") if(BUILD_ALLOC_TESTS) add_h3_library(h3WithTestAllocators test_prefix_) diff --git a/src/apps/filters/h3.c b/src/apps/filters/h3.c index 5625bc069..9734b5d36 100644 --- a/src/apps/filters/h3.c +++ b/src/apps/filters/h3.c @@ -1,5 +1,5 @@ /* - * Copyright 2021 Uber Technologies, Inc. + * Copyright 2021, 2024 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. @@ -43,19 +43,24 @@ bool cellToLatLngCmd(int argc, char *argv[]) { Arg cellToLatLngArg = { .names = {"cellToLatLng"}, .required = true, - .helpText = "Convert an H3 cell to a latitude/longitude coordinate", + .helpText = "Convert an H3 cell to a WKT POINT coordinate", }; Arg helpArg = ARG_HELP; DEFINE_CELL_ARG(cell, cellArg); Arg *args[] = {&cellToLatLngArg, &helpArg, &cellArg}; - if (parseArgs(argc, argv, 3, args, &helpArg, - "Convert an H3 cell to a latitude/longitude coordinate")) { + if (parseArgs(argc, argv, sizeof(args) / sizeof(Arg *), args, &helpArg, + "Convert an H3 cell to a WKT POINT coordinate")) { return helpArg.found; } LatLng ll; - H3_EXPORT(cellToLatLng)(cell, &ll); - printf("%.10lf, %.10lf\n", H3_EXPORT(radsToDegs)(ll.lat), - H3_EXPORT(radsToDegs)(ll.lng)); + H3Error err = H3_EXPORT(cellToLatLng)(cell, &ll); + if (err) { + return false; + } + // Using WKT formatting for the output. TODO: Add support for JSON + // formatting + printf("POINT(%.10lf %.10lf)\n", H3_EXPORT(radsToDegs)(ll.lng), + H3_EXPORT(radsToDegs)(ll.lat)); return true; } @@ -92,7 +97,7 @@ bool latLngToCellCmd(int argc, char *argv[]) { Arg *args[] = {&latLngToCellArg, &helpArg, &resArg, &latArg, &lngArg}; if (parseArgs( - argc, argv, 5, args, &helpArg, + argc, argv, sizeof(args) / sizeof(Arg *), args, &helpArg, "Convert degrees latitude/longitude coordinate to an H3 cell.")) { return helpArg.found; } @@ -102,6 +107,7 @@ bool latLngToCellCmd(int argc, char *argv[]) { H3Index c; H3Error e = H3_EXPORT(latLngToCell)(&ll, res, &c); + // TODO: Add support for JSON formatting if (e == E_SUCCESS) { h3Println(c); } else { @@ -110,23 +116,63 @@ bool latLngToCellCmd(int argc, char *argv[]) { return true; } +bool cellToBoundaryCmd(int argc, char *argv[]) { + Arg cellToBoundaryArg = { + .names = {"cellToBoundary"}, + .required = true, + .helpText = "Convert an H3 cell to a WKT POLYGON defining its boundary", + }; + Arg helpArg = ARG_HELP; + DEFINE_CELL_ARG(cell, cellArg); + Arg *args[] = {&cellToBoundaryArg, &helpArg, &cellArg}; + if (parseArgs( + argc, argv, sizeof(args) / sizeof(Arg *), args, &helpArg, + "Convert an H3 cell to a WKT POLYGON defining its boundary")) { + return helpArg.found; + } + CellBoundary cb; + H3Error err = H3_EXPORT(cellToBoundary)(cell, &cb); + if (err) { + return false; + } + // Using WKT formatting for the output. TODO: Add support for JSON + // formatting + printf("POLYGON(("); + for (int i = 0; i < cb.numVerts; i++) { + LatLng *ll = &cb.verts[i]; + printf("%.10lf %.10lf, ", H3_EXPORT(radsToDegs)(ll->lng), + H3_EXPORT(radsToDegs)(ll->lat)); + } + // WKT has the first and last points match, so re-print the first one + printf("%.10lf %.10lf))\n", H3_EXPORT(radsToDegs)(cb.verts[0].lng), + H3_EXPORT(radsToDegs)(cb.verts[0].lat)); + return true; +} + bool generalHelp(int argc, char *argv[]) { Arg helpArg = ARG_HELP; Arg cellToLatLngArg = { .names = {"cellToLatLng"}, - .helpText = "Convert an H3 cell to a latitude/longitude coordinate", + .helpText = "Convert an H3 cell to a WKT POINT coordinate", }; Arg latLngToCellArg = { .names = {"latLngToCell"}, .helpText = "Convert degrees latitude/longitude coordinate to an H3 cell.", }; - Arg *args[] = {&helpArg, &cellToLatLngArg, &latLngToCellArg}; + Arg cellToBoundaryArg = { + .names = {"cellToBoundary"}, + .helpText = "Convert an H3 cell to a WKT POLYGON defining its boundary", + }; + Arg *args[] = {&helpArg, &cellToLatLngArg, &latLngToCellArg, + &cellToBoundaryArg}; + const char *helpText = "Please use one of the subcommands listed to perform an H3 " "calculation. Use h3 --help for details on the usage of " "any subcommand."; - return parseArgs(argc, argv, 3, args, &helpArg, helpText); + return parseArgs(argc, argv, sizeof(args) / sizeof(Arg *), args, &helpArg, + helpText); } int main(int argc, char *argv[]) { @@ -140,6 +186,9 @@ int main(int argc, char *argv[]) { if (has("latLngToCell", 1, argv) && latLngToCellCmd(argc, argv)) { return 0; } + if (has("cellToBoundary", 1, argv) && cellToBoundaryCmd(argc, argv)) { + return 0; + } if (generalHelp(argc, argv)) { return 0; }