diff --git a/CMakeLists.txt b/CMakeLists.txt index 7c64d8cdf..b8e5195d0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -156,6 +156,7 @@ set(EXAMPLE_SOURCE_FILES examples/compactCells.c examples/edge.c) set(OTHER_SOURCE_FILES + src/apps/filters/h3.c src/apps/filters/cellToLatLng.c src/apps/filters/h3ToLocalIj.c src/apps/filters/localIjToH3.c @@ -380,6 +381,8 @@ if(BUILD_FILTERS) list(APPEND INSTALL_TARGETS ${name}) endmacro() + add_h3_filter(h3_bin src/apps/filters/h3.c ${APP_SOURCE_FILES}) + set_target_properties(h3_bin PROPERTIES OUTPUT_NAME h3) # Special logic for the `h3` executable add_h3_filter(latLngToCell src/apps/filters/latLngToCell.c ${APP_SOURCE_FILES}) add_h3_filter(h3ToComponents src/apps/filters/h3ToComponents.c ${APP_SOURCE_FILES}) add_h3_filter(cellToLatLng src/apps/filters/cellToLatLng.c ${APP_SOURCE_FILES}) diff --git a/src/apps/applib/include/args.h b/src/apps/applib/include/args.h index ae264d4a0..511913836 100644 --- a/src/apps/applib/include/args.h +++ b/src/apps/applib/include/args.h @@ -100,6 +100,14 @@ int _parseArgsList(int argc, char *argv[], int numArgs, Arg *args[], .value = &varName, \ .helpText = \ "Index, or not specified to read indexes from standard input."} +#define DEFINE_CELL_ARG(varName, argName) \ + H3Index varName = 0; \ + Arg argName = {.names = {"-c", "--cell"}, \ + .required = true, \ + .scanFormat = "%" PRIx64, \ + .valueName = "index", \ + .value = &varName, \ + .helpText = "H3 Cell"} #define ARG_KML \ { .names = {"-k", "--kml"}, .helpText = "Print output in KML format." } #define DEFINE_KML_NAME_ARG(varName, argName) \ diff --git a/src/apps/applib/lib/args.c b/src/apps/applib/lib/args.c index 67ed3d889..3141a74bc 100644 --- a/src/apps/applib/lib/args.c +++ b/src/apps/applib/lib/args.c @@ -24,6 +24,16 @@ #include #include +#ifdef _WIN32 + +#define strcasecmp _stricmp + +#else + +#include + +#endif + #include "h3api.h" /* @@ -113,7 +123,7 @@ int _parseArgsList(int argc, char *argv[], int numArgs, Arg *args[], for (int k = 0; k < NUM_ARG_NAMES; k++) { if (args[j]->names[k] == NULL) continue; - if (strcmp(argv[i], args[j]->names[k]) == 0) { + if (strcasecmp(argv[i], args[j]->names[k]) == 0) { argName = args[j]->names[k]; break; } diff --git a/src/apps/filters/h3.c b/src/apps/filters/h3.c new file mode 100644 index 000000000..5625bc069 --- /dev/null +++ b/src/apps/filters/h3.c @@ -0,0 +1,148 @@ +/* + * Copyright 2021 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 cli app that exposes most of the H3 C library for scripting + * + * See `h3 --help` for usage. + */ + +#include + +#ifdef _WIN32 + +#define strcasecmp _stricmp + +#else + +#include + +#endif + +#include "args.h" +#include "h3Index.h" +#include "utility.h" + +bool has(char *subcommand, int level, char *argv[]) { + return strcasecmp(subcommand, argv[level]) == 0; +} + +bool cellToLatLngCmd(int argc, char *argv[]) { + Arg cellToLatLngArg = { + .names = {"cellToLatLng"}, + .required = true, + .helpText = "Convert an H3 cell to a latitude/longitude 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")) { + return helpArg.found; + } + LatLng ll; + H3_EXPORT(cellToLatLng)(cell, &ll); + printf("%.10lf, %.10lf\n", H3_EXPORT(radsToDegs)(ll.lat), + H3_EXPORT(radsToDegs)(ll.lng)); + return true; +} + +bool latLngToCellCmd(int argc, char *argv[]) { + int res = 0; + double lat = 0; + double lng = 0; + + Arg latLngToCellArg = { + .names = {"latLngToCell"}, + .required = true, + .helpText = + "Convert degrees latitude/longitude coordinate to an H3 cell.", + }; + Arg helpArg = ARG_HELP; + Arg resArg = {.names = {"-r", "--resolution"}, + .required = true, + .scanFormat = "%d", + .valueName = "res", + .value = &res, + .helpText = "Resolution, 0-15 inclusive."}; + Arg latArg = {.names = {"--lat", "--latitude"}, + .required = true, + .scanFormat = "%lf", + .valueName = "lat", + .value = &lat, + .helpText = "Latitude in degrees."}; + Arg lngArg = {.names = {"--lng", "--longitude"}, + .required = true, + .scanFormat = "%lf", + .valueName = "lng", + .value = &lng, + .helpText = "Longitude in degrees."}; + + Arg *args[] = {&latLngToCellArg, &helpArg, &resArg, &latArg, &lngArg}; + if (parseArgs( + argc, argv, 5, args, &helpArg, + "Convert degrees latitude/longitude coordinate to an H3 cell.")) { + return helpArg.found; + } + LatLng ll = {.lat = H3_EXPORT(degsToRads)(lat), + .lng = H3_EXPORT(degsToRads)(lng)}; + + H3Index c; + H3Error e = H3_EXPORT(latLngToCell)(&ll, res, &c); + + if (e == E_SUCCESS) { + h3Println(c); + } else { + h3Println(H3_NULL); + } + 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", + }; + Arg latLngToCellArg = { + .names = {"latLngToCell"}, + .helpText = + "Convert degrees latitude/longitude coordinate to an H3 cell.", + }; + Arg *args[] = {&helpArg, &cellToLatLngArg, &latLngToCellArg}; + 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); +} + +int main(int argc, char *argv[]) { + if (argc <= 1) { + printf("Please use h3 --help to see how to use this command.\n"); + return 1; + } + if (has("cellToLatLng", 1, argv) && cellToLatLngCmd(argc, argv)) { + return 0; + } + if (has("latLngToCell", 1, argv) && latLngToCellCmd(argc, argv)) { + return 0; + } + if (generalHelp(argc, argv)) { + return 0; + } + printf("Please use h3 --help to see how to use this command.\n"); + return 1; +}