From cc31655c8bfb14c9e22a74d76e4b32554f5843f3 Mon Sep 17 00:00:00 2001 From: Go Kudo Date: Fri, 22 May 2026 15:28:18 +0900 Subject: [PATCH 1/6] chore: refactor --- Dockerfile | 2 +- src/geohex.c | 159 ++++++++++++++++++++++++-------------------- tests/test_geohex.c | 22 +++--- 3 files changed, 103 insertions(+), 80 deletions(-) diff --git a/Dockerfile b/Dockerfile index 0776c2b..2237bac 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ ARG PLATFORM=${BUILDPLATFORM:-linux/amd64} -FROM --platform=${PLATFORM} debian:bookworm +FROM --platform=${PLATFORM} debian:trixie ARG PLATFORM diff --git a/src/geohex.c b/src/geohex.c index c2b452b..355ae53 100644 --- a/src/geohex.c +++ b/src/geohex.c @@ -72,40 +72,42 @@ void xy2loc(double dx, double dy, double *lon, double *lat) { *lat = (2.0 * atan(exp(lat_rad)) - M_PI / 2.0) * 180.0 / M_PI; } - bool adjust_xy(int32_t x, int32_t y, uint32_t level, xy_t *out) { + int32_t tmp, max_hsteps, hsteps, dif, dif_x, dif_y, edge_x, edge_y; + bool rev; + if (!out) { return false; } - int32_t max_hsteps = pow3_table[level + 2]; - int32_t hsteps = abs(x - y); - bool rev = false; + max_hsteps = pow3_table[level + 2]; + hsteps = abs(x - y); + rev = false; if (hsteps == max_hsteps && x > y) { - int32_t tmp = x; + tmp = x; x = y; y = tmp; rev = true; } else if (hsteps > max_hsteps) { - int32_t dif = hsteps - max_hsteps; - int32_t dif_x = dif / 2; - int32_t dif_y = dif - dif_x; + dif = hsteps - max_hsteps; + dif_x = dif / 2; + dif_y = dif - dif_x; if (x > y) { - int32_t edge_x = x - dif_x; - int32_t edge_y = y + dif_y; - int32_t temp = edge_x; + edge_x = x - dif_x; + edge_y = y + dif_y; + tmp = edge_x; edge_x = edge_y; - edge_y = temp; + edge_y = tmp; x = edge_x + dif_x; y = edge_y - dif_y; } else if (y > x) { - int32_t edge_x = x + dif_x; - int32_t edge_y = y - dif_y; - int32_t temp = edge_x; + edge_x = x + dif_x; + edge_y = y - dif_y; + tmp = edge_x; edge_x = edge_y; - edge_y = temp; + edge_y = tmp; x = edge_x - dif_x; y = edge_y + dif_y; } @@ -114,29 +116,33 @@ bool adjust_xy(int32_t x, int32_t y, uint32_t level, xy_t *out) { out->x = x; out->y = y; out->rev = rev; + return true; } bool get_xy_by_location(const loc_t *location, uint32_t level, xy_t *out) { + int32_t h_x, h_y; + double h_size, lon_grid, lat_grid, unit_x, unit_y, h_pos_x, h_pos_y, + h_x_q, h_y_q; + if (!location || !out) { return false; } - double h_size = calc_hex_size(level); - double lon_grid, lat_grid; + h_size = calc_hex_size(level); loc2xy(location->lon, location->lat, &lon_grid, &lat_grid); - double unit_x = 6.0 * h_size; - double unit_y = 6.0 * h_size * H_K; + unit_x = 6.0 * h_size; + unit_y = 6.0 * h_size * H_K; - double h_pos_x = (lon_grid + lat_grid / H_K) / unit_x; - double h_pos_y = (lat_grid - H_K * lon_grid) / unit_y; + h_pos_x = (lon_grid + lat_grid / H_K) / unit_x; + h_pos_y = (lat_grid - H_K * lon_grid) / unit_y; - int32_t h_x = (int32_t) round(h_pos_x); - int32_t h_y = (int32_t) round(h_pos_y); + h_x = (int32_t) round(h_pos_x); + h_y = (int32_t) round(h_pos_y); - double h_x_q = h_pos_x - floor(h_pos_x); - double h_y_q = h_pos_y - floor(h_pos_y); + h_x_q = h_pos_x - floor(h_pos_x); + h_y_q = h_pos_y - floor(h_pos_y); if (h_y_q > -h_x_q + 1) { if (h_y_q < 2 * h_x_q && h_y_q > 0.5 * h_x_q) { @@ -154,24 +160,30 @@ bool get_xy_by_location(const loc_t *location, uint32_t level, xy_t *out) { } bool get_xy_by_code(const geohex_code_t code, xy_t *out) { + uint32_t code_len, level; + int32_t i, h_x, h_y, c1_idx, c2_idx, code3, d9xlen, target_len, + h_decx[MAX_H_DEC3_LEN] = {0}, h_decy[MAX_H_DEC3_LEN] = {0}, + digit, h_pow; + char code3_str[5], h_dec9[MAX_H_DEC9_LEN] = {0}; + if (!out) { return false; } - uint32_t code_len = strlen(code); - uint32_t level = code_len - 2; - int32_t h_x = 0, h_y = 0; + code_len = strlen(code); + level = code_len - 2; + h_x = 0; + h_y = 0; - int32_t c1_idx = char_to_index(code[0]); - int32_t c2_idx = char_to_index(code[1]); + c1_idx = char_to_index(code[0]); + c2_idx = char_to_index(code[1]); if (c1_idx == -1 || c2_idx == -1) { return false; } - int32_t code3 = c1_idx * 30 + c2_idx; + code3 = c1_idx * 30 + c2_idx; - char code3_str[5]; snprintf(code3_str, sizeof(code3_str), "%d", code3); if ((code3_str[0] == '1' || code3_str[0] == '5') && @@ -180,27 +192,24 @@ bool get_xy_by_code(const geohex_code_t code, xy_t *out) { code3_str[0] = (code3_str[0] == '5') ? '7' : '3'; } - char h_dec9[MAX_H_DEC9_LEN] = {0}; snprintf(h_dec9, sizeof(h_dec9), "%s%s", code3_str, code + 2); - int32_t d9xlen = strlen(h_dec9); - int32_t target_len = level + 3; + d9xlen = strlen(h_dec9); + target_len = level + 3; if (d9xlen < target_len) { memmove(h_dec9 + (target_len - d9xlen), h_dec9, d9xlen + 1); memset(h_dec9, '0', target_len - d9xlen); d9xlen = target_len; } - int32_t h_decx[MAX_H_DEC3_LEN] = {0}; - int32_t h_decy[MAX_H_DEC3_LEN] = {0}; - for (int32_t i = 0; i < d9xlen; i++) { - int32_t digit = h_dec9[i] - '0'; + for (i = 0; i < d9xlen; i++) { + digit = h_dec9[i] - '0'; h_decx[i] = digit / 3; h_decy[i] = digit % 3; } - for (int32_t i = 0; i <= (int32_t)(level + 2); i++) { - int32_t h_pow = pow3_table[level + 2 - i]; + for (i = 0; i <= (int32_t)(level + 2); i++) { + h_pow = pow3_table[level + 2 - i]; if (h_decx[i] == 0) { h_x -= h_pow; @@ -219,12 +228,12 @@ bool get_xy_by_code(const geohex_code_t code, xy_t *out) { } bool get_zone_by_location(const loc_t *location, uint32_t level, zone_t *out) { + xy_t xy; + if (!out) { return false; } - xy_t xy; - if (!get_xy_by_location(location, level, &xy)) { return false; } @@ -233,52 +242,59 @@ bool get_zone_by_location(const loc_t *location, uint32_t level, zone_t *out) { } bool get_zone_by_code(const geohex_code_t code, zone_t *out) { + xy_t xy; + if (!out) { return false; } - xy_t xy; - if (!get_xy_by_code(code, &xy)) { return false; } - uint32_t level = strlen(code) - 2; - - return get_zone_by_xy(&xy, level, out); + return get_zone_by_xy(&xy, (strlen(code) - 2), out); } bool get_zone_by_xy(const xy_t *xy, uint32_t level, zone_t *out) { + int32_t tmp, i, h_x, h_y, max_hsteps, + code3_x[MAX_CODE_LEN + 2], code3_y[MAX_CODE_LEN + 2], + h_code_digits[MAX_CODE_LEN + 2], + mod_x, mod_y, h_1_int, h_a1, h_a2, + h_pow, half_h_pow; + double h_size, unit_x, unit_y, h_lat, h_lon, z_loc_x, z_loc_y; + if (!xy || !out) { return false; } - double h_size = calc_hex_size(level); - int32_t h_x = xy->x, h_y = xy->y; + h_size = calc_hex_size(level); + h_x = xy->x; + h_y = xy->y; - double unit_x = 6.0 * h_size; - double unit_y = 6.0 * h_size * H_K; + unit_x = 6.0 * h_size; + unit_y = 6.0 * h_size * H_K; - double h_lat = (H_K * h_x * unit_x + h_y * unit_y) / 2.0; - double h_lon = (h_lat - h_y * unit_y) / H_K; + h_lat = (H_K * h_x * unit_x + h_y * unit_y) / 2.0; + h_lon = (h_lat - h_y * unit_y) / H_K; - double z_loc_x, z_loc_y; + z_loc_x = 0.0; + z_loc_y = 0.0; xy2loc(h_lon, h_lat, &z_loc_x, &z_loc_y); - int32_t max_hsteps = pow3_table[level + 2]; + max_hsteps = pow3_table[level + 2]; if (abs(h_x - h_y) == max_hsteps && h_x > h_y) { - int32_t tmp = h_x; + tmp = h_x; h_x = h_y; h_y = tmp; z_loc_x = -180.0; } - int32_t code3_x[MAX_CODE_LEN + 2], code3_y[MAX_CODE_LEN + 2]; - int32_t mod_x = h_x, mod_y = h_y; + mod_x = h_x; + mod_y = h_y; - for (int32_t i = 0; i <= level + 2; i++) { - int32_t h_pow = pow3_table[level + 2 - i]; - int32_t half_h_pow = (h_pow + 1) / 2; + for (i = 0; i <= level + 2; i++) { + h_pow = pow3_table[level + 2 - i]; + half_h_pow = (h_pow + 1) / 2; if (mod_x >= half_h_pow) { code3_x[i] = 2; @@ -302,30 +318,31 @@ bool get_zone_by_xy(const xy_t *xy, uint32_t level, zone_t *out) { if (i == 2 && (z_loc_x == -180.0 || z_loc_x >= 0.0)) { if (code3_x[0] == 2 && code3_y[0] == 1 && - code3_x[1] == code3_y[1] && code3_x[2] == code3_y[2]) { + code3_x[1] == code3_y[1] && code3_x[2] == code3_y[2] + ) { code3_x[0] = 1; code3_y[0] = 2; } else if (code3_x[0] == 1 && code3_y[0] == 0 && - code3_x[1] == code3_y[1] && code3_x[2] == code3_y[2]) { + code3_x[1] == code3_y[1] && code3_x[2] == code3_y[2] + ) { code3_x[0] = 0; code3_y[0] = 1; } } } - int32_t h_code_digits[MAX_CODE_LEN + 2]; - for (int32_t i = 0; i <= level + 2; i++) { + for (i = 0; i <= level + 2; i++) { h_code_digits[i] = code3_x[i] * 3 + code3_y[i]; } - int32_t h_1_int = h_code_digits[0] * 100 + h_code_digits[1] * 10 + h_code_digits[2]; - int32_t h_a1 = h_1_int / 30; - int32_t h_a2 = h_1_int % 30; + h_1_int = h_code_digits[0] * 100 + h_code_digits[1] * 10 + h_code_digits[2]; + h_a1 = h_1_int / 30; + h_a2 = h_1_int % 30; out->code[0] = GEOHEX_KEY[h_a1]; out->code[1] = GEOHEX_KEY[h_a2]; - for (int32_t i = 3; i <= level + 2; i++) { + for (i = 3; i <= level + 2; i++) { out->code[i - 1] = '0' + h_code_digits[i]; } out->code[level + 2] = '\0'; diff --git a/tests/test_geohex.c b/tests/test_geohex.c index e438caf..28e5563 100644 --- a/tests/test_geohex.c +++ b/tests/test_geohex.c @@ -75,9 +75,10 @@ void test_adjust_xy(void) void test_get_xy_by_location(void) { xy_t out; + loc_t loc; for (uint32_t i = 0; i < (sizeof(coord2xy_data) / sizeof(coord2xy_data[0])); i++) { - loc_t loc = { + loc = (loc_t) { .lat = coord2xy_data[i].lat, .lon = coord2xy_data[i].lon, }; @@ -91,9 +92,10 @@ void test_get_xy_by_location(void) void test_get_zone_by_xy(void) { zone_t out; + xy_t xy; for (uint32_t i = 0; i < (sizeof(xy2hex_data) / sizeof(xy2hex_data[0])); i++) { - xy_t xy = { + xy = (xy_t) { .x = xy2hex_data[i].x, .y = xy2hex_data[i].y, .rev = false, @@ -107,8 +109,9 @@ void test_get_zone_by_xy(void) void test_get_xy_by_code(void) { xy_t out; + uint32_t i; - for (uint32_t i = 0; i < (sizeof(code2xy_data) / sizeof(code2xy_data[0])); i++) { + for (i = 0; i < (sizeof(code2xy_data) / sizeof(code2xy_data[0])); i++) { TEST_ASSERT_TRUE(get_xy_by_code(code2xy_data[i].code, &out)); TEST_ASSERT_EQUAL_INT32(code2xy_data[i].x, out.x); TEST_ASSERT_EQUAL_INT32(code2xy_data[i].y, out.y); @@ -118,9 +121,11 @@ void test_get_xy_by_code(void) void test_get_zone_by_location(void) { zone_t out; + loc_t loc; + uint32_t i; - for (uint32_t i = 0; i < (sizeof(code2hex_data) / sizeof(code2hex_data[0])); i++) { - loc_t loc = { + for (i = 0; i < (sizeof(code2hex_data) / sizeof(code2hex_data[0])); i++) { + loc = (loc_t) { .lat = code2hex_data[i].lat, .lon = code2hex_data[i].lon, }; @@ -129,8 +134,8 @@ void test_get_zone_by_location(void) TEST_ASSERT_EQUAL_STRING(code2hex_data[i].code, out.code); } - for (uint32_t i = 0; i < sizeof(coord2hex_data) / sizeof(coord2hex_data[0]); i++) { - loc_t loc = { + for (i = 0; i < sizeof(coord2hex_data) / sizeof(coord2hex_data[0]); i++) { + loc = (loc_t) { .lat = coord2hex_data[i].lat, .lon = coord2hex_data[i].lon, }; @@ -143,8 +148,9 @@ void test_get_zone_by_location(void) void test_get_zone_by_code(void) { zone_t out; + uint32_t i; - for (uint32_t i = 0; i < (sizeof(code2hex_data) / sizeof(code2hex_data[0])); i++) { + for (i = 0; i < (sizeof(code2hex_data) / sizeof(code2hex_data[0])); i++) { TEST_ASSERT_TRUE(get_zone_by_code(code2hex_data[i].code, &out)); TEST_ASSERT_DOUBLE_WITHIN(15, code2hex_data[i].lat, out.latlon.lat); TEST_ASSERT_DOUBLE_WITHIN(15, code2hex_data[i].lon, out.latlon.lon); From e3464a83f40a58e8e2bbbc4fa73ada2101e43dbd Mon Sep 17 00:00:00 2001 From: Go Kudo Date: Fri, 22 May 2026 15:30:37 +0900 Subject: [PATCH 2/6] update licence --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 85cbf77..a3750db 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2024 Go Kudo +Copyright (c) 2026 Go Kudo Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From c566588651cdc8b0cfe8ac7f929b3b9227cff7fa Mon Sep 17 00:00:00 2001 From: Go Kudo Date: Fri, 22 May 2026 15:31:18 +0900 Subject: [PATCH 3/6] update licence --- LICENSE | 2 +- include/geohex/geohex.h | 2 +- src/geohex.c | 2 +- tests/geohex_private.h | 2 +- tests/json_data.h | 2 +- tests/test_geohex.c | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/LICENSE b/LICENSE index a3750db..c786b5b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2026 Go Kudo +Copyright (c) 2024-2026 Go Kudo Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/include/geohex/geohex.h b/include/geohex/geohex.h index d89aa5b..1929e97 100644 --- a/include/geohex/geohex.h +++ b/include/geohex/geohex.h @@ -2,7 +2,7 @@ /* * libgeohex * - * Copyright (c) 2024 Go Kudo (https://github.com/zeriyoshi) + * Copyright (c) 2024-2026 Go Kudo Kudo (https://github.com/zeriyoshi) * * GeoHex original implementation by @sa2da (http://twitter.com/sa2da) * https://www.geohex.org/ diff --git a/src/geohex.c b/src/geohex.c index 355ae53..8b5e9fa 100644 --- a/src/geohex.c +++ b/src/geohex.c @@ -2,7 +2,7 @@ /* * libgeohex * - * Copyright (c) 2024 Go Kudo (https://github.com/zeriyoshi) + * Copyright (c) 2024-2026 Go Kudo Kudo (https://github.com/zeriyoshi) * * GeoHex original implementation by @sa2da (http://twitter.com/sa2da) * https://www.geohex.org/ diff --git a/tests/geohex_private.h b/tests/geohex_private.h index aa44f75..e5b0592 100644 --- a/tests/geohex_private.h +++ b/tests/geohex_private.h @@ -2,7 +2,7 @@ /* * libgeohex * - * Copyright (c) 2024 Go Kudo (https://github.com/zeriyoshi) + * Copyright (c) 2024-2026 Go Kudo Kudo (https://github.com/zeriyoshi) * * GeoHex original implementation by @sa2da (http://twitter.com/sa2da) * https://www.geohex.org/ diff --git a/tests/json_data.h b/tests/json_data.h index e046c05..f0ae2da 100644 --- a/tests/json_data.h +++ b/tests/json_data.h @@ -2,7 +2,7 @@ /* * libgeohex * - * Copyright (c) 2024 Go Kudo (https://github.com/zeriyoshi) + * Copyright (c) 2024-2026 Go Kudo Kudo (https://github.com/zeriyoshi) * * GeoHex original implementation by @sa2da (http://twitter.com/sa2da) * https://www.geohex.org/ diff --git a/tests/test_geohex.c b/tests/test_geohex.c index 28e5563..e78aaa3 100644 --- a/tests/test_geohex.c +++ b/tests/test_geohex.c @@ -2,7 +2,7 @@ /* * libgeohex * - * Copyright (c) 2024 Go Kudo (https://github.com/zeriyoshi) + * Copyright (c) 2024-2026 Go Kudo Kudo (https://github.com/zeriyoshi) * * GeoHex original implementation by @sa2da (http://twitter.com/sa2da) * https://www.geohex.org/ From dd46023286ee50db59e5bd3792e881cecf6538e2 Mon Sep 17 00:00:00 2001 From: Go Kudo Date: Fri, 22 May 2026 15:48:47 +0900 Subject: [PATCH 4/6] support windows and macos --- .devcontainer/devcontainer.json | 5 ++-- .devcontainer/local/devcontainer.json | 21 ----------------- .github/workflows/ci.yaml | 32 +++++++++++++++++++++++--- CMakeLists.txt | 22 ++++++++++++++++-- include/geohex/compat.h | 32 ++++++++++++++++++++++++++ include/geohex/geohex.h | 16 +++++++------ src/geohex.c | 33 +++++++++++++++------------ tests/geohex_private.h | 10 ++++---- tests/json_data.h | 2 +- tests/test_geohex.c | 28 ++++++++--------------- 10 files changed, 128 insertions(+), 73 deletions(-) delete mode 100644 .devcontainer/local/devcontainer.json create mode 100644 include/geohex/compat.h diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index f482aed..2c72797 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,5 +1,5 @@ { - "name": "GitHub Codespaces", + "name": "libgeohex", "customizations": { "vscode": { "extensions": [ @@ -13,5 +13,6 @@ } }, "dockerComposeFile": "./../compose.yaml", - "service": "shell" + "service": "shell", + "workspaceFolder": "/workspaces/libgeohex" } diff --git a/.devcontainer/local/devcontainer.json b/.devcontainer/local/devcontainer.json deleted file mode 100644 index c0d5dfd..0000000 --- a/.devcontainer/local/devcontainer.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "Local VSCode", - "customizations": { - "vscode": { - "extensions": [ - "ms-vscode.cpptools", - "ms-vscode.cpptools-extension-pack", - "ms-vscode.cmake-tools", - "ms-azuretools.vscode-docker", - "editorconfig.editorconfig", - "markis.code-coverage" - ] - } - }, - "dockerComposeFile": "./../../compose.yaml", - "service": "shell", - "mounts": [ - "source=${localWorkspaceFolder},target=/workspaces/libgeohex,type=bind,consistency=cached" - ], - "workspaceFolder": "/workspaces/libgeohex" -} diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index eea5784..58b6331 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -12,15 +12,15 @@ jobs: platform: ['linux/amd64', 'linux/arm64/v8', 'linux/s390x', 'linux/arm/v7'] steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: true - name: Setup QEMU - uses: docker/setup-qemu-action@v3 + uses: docker/setup-qemu-action@vce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0 with: platforms: "arm64,s390x,arm" - name: Setup buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 - name: Build container run: | docker build --pull --no-cache --build-arg PLATFORM="${{ matrix.platform }}" --tag "test" . @@ -35,3 +35,29 @@ jobs: -w "/workspaces/libgeohex" \ -i "test" \ ./ci.sh + macOS: + runs-on: macos-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + fetch-depth: 1 + - name: Configure + run: cmake -S . -B build -DBUILD_TESTS=ON + - name: Build + run: cmake --build build --parallel + - name: Test + run: ctest --test-dir build --output-on-failure + Windows: + runs-on: windows-latest + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + fetch-depth: 1 + - name: Configure + run: cmake -S . -B build -DBUILD_TESTS=ON + - name: Build + run: cmake --build build --config Debug --parallel + - name: Test + run: ctest --test-dir build --build-config Debug --output-on-failure diff --git a/CMakeLists.txt b/CMakeLists.txt index 7e77915..eaaae4c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,15 @@ option(BUILD_STATIC_LIBS "Build static libraries" ON) option(BUILD_SHARED_LIBS "Build shared libraries" ON) set(GEOHEX_TARGETS) +set(GEOHEX_MATH_LIBRARIES) + +if(NOT WIN32) + list(APPEND GEOHEX_MATH_LIBRARIES m) +endif() + +if(WIN32) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") +endif() if (BUILD_STATIC_LIBS) add_library(geohex_static STATIC @@ -28,7 +37,10 @@ if (BUILD_STATIC_LIBS) $ ) - target_link_libraries(geohex_static PUBLIC m) + target_compile_definitions(geohex_static PUBLIC GEOHEX_STATIC_DEFINE) + if(GEOHEX_MATH_LIBRARIES) + target_link_libraries(geohex_static PUBLIC ${GEOHEX_MATH_LIBRARIES}) + endif() list(APPEND GEOHEX_TARGETS geohex_static) endif() @@ -39,13 +51,19 @@ if(BUILD_SHARED_LIBS) ) set_target_properties(geohex_shared PROPERTIES OUTPUT_NAME geohex) + if(WIN32) + set_target_properties(geohex_shared PROPERTIES ARCHIVE_OUTPUT_NAME geohex_shared) + endif() target_include_directories(geohex_shared PUBLIC $ $ ) - target_link_libraries(geohex_shared PUBLIC m) + target_compile_definitions(geohex_shared PRIVATE GEOHEX_BUILDING_LIBRARY) + if(GEOHEX_MATH_LIBRARIES) + target_link_libraries(geohex_shared PUBLIC ${GEOHEX_MATH_LIBRARIES}) + endif() list(APPEND GEOHEX_TARGETS geohex_shared) endif() diff --git a/include/geohex/compat.h b/include/geohex/compat.h new file mode 100644 index 0000000..3654a28 --- /dev/null +++ b/include/geohex/compat.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: MIT */ +/* + * libgeohex + * + * Copyright (c) 2024-2026 Go Kudo Kudo (https://github.com/zeriyoshi) + * + * GeoHex original implementation by @sa2da (http://twitter.com/sa2da) + * https://www.geohex.org/ + * + * Released under the MIT license. + * see https://opensource.org/licenses/MIT + */ +#ifndef GEOHEX_COMPAT_H +#define GEOHEX_COMPAT_H + +#if defined(_WIN32) || defined(__CYGWIN__) +# if defined(GEOHEX_STATIC_DEFINE) +# define GEOHEX_API +# elif defined(GEOHEX_BUILDING_LIBRARY) +# define GEOHEX_API __declspec(dllexport) +# else +# define GEOHEX_API __declspec(dllimport) +# endif +#elif defined(__GNUC__) && __GNUC__ >= 4 +# define GEOHEX_API __attribute__((visibility("default"))) +#else +# define GEOHEX_API +#endif + +#define GEOHEX_PI 3.14159265358979323846 + +#endif /* GEOHEX_COMPAT_H */ diff --git a/include/geohex/geohex.h b/include/geohex/geohex.h index 1929e97..93aef91 100644 --- a/include/geohex/geohex.h +++ b/include/geohex/geohex.h @@ -2,7 +2,7 @@ /* * libgeohex * - * Copyright (c) 2024-2026 Go Kudo Kudo (https://github.com/zeriyoshi) + * Copyright (c) 2024-2026 Go Kudo Kudo (https://github.com/zeriyoshi) * * GeoHex original implementation by @sa2da (http://twitter.com/sa2da) * https://www.geohex.org/ @@ -16,6 +16,8 @@ #include #include +#include "geohex/compat.h" + #ifdef __cplusplus extern "C" { #endif @@ -47,12 +49,12 @@ typedef struct { geohex_code_t code; } zone_t; -bool adjust_xy(int32_t x, int32_t y, uint32_t level, xy_t *out); -bool get_xy_by_location(const loc_t *location, uint32_t level, xy_t *out); -bool get_xy_by_code(const geohex_code_t code, xy_t *out); -bool get_zone_by_location(const loc_t *location, uint32_t level, zone_t *out); -bool get_zone_by_code(const geohex_code_t code, zone_t *out); -bool get_zone_by_xy(const xy_t *xy, uint32_t level, zone_t *out); +GEOHEX_API bool adjust_xy(int32_t x, int32_t y, uint32_t level, xy_t *out); +GEOHEX_API bool get_xy_by_location(const loc_t *location, uint32_t level, xy_t *out); +GEOHEX_API bool get_xy_by_code(const geohex_code_t code, xy_t *out); +GEOHEX_API bool get_zone_by_location(const loc_t *location, uint32_t level, zone_t *out); +GEOHEX_API bool get_zone_by_code(const geohex_code_t code, zone_t *out); +GEOHEX_API bool get_zone_by_xy(const xy_t *xy, uint32_t level, zone_t *out); #ifdef __cplusplus } diff --git a/src/geohex.c b/src/geohex.c index 8b5e9fa..2323827 100644 --- a/src/geohex.c +++ b/src/geohex.c @@ -2,7 +2,7 @@ /* * libgeohex * - * Copyright (c) 2024-2026 Go Kudo Kudo (https://github.com/zeriyoshi) + * Copyright (c) 2024-2026 Go Kudo Kudo (https://github.com/zeriyoshi) * * GeoHex original implementation by @sa2da (http://twitter.com/sa2da) * https://www.geohex.org/ @@ -21,7 +21,7 @@ #define GEOHEX_KEY "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" #define H_BASE 20037508.34 -#define H_K 0.5773502691896257 /* tan(M_PI / 6.0) */ +#define H_K 0.5773502691896257 /* tan(GEOHEX_PI / 6.0) */ const uint32_t pow3_table[] = { 1, /* pow(3, 0) */ @@ -57,22 +57,24 @@ static inline int char_to_index(char c) { } } -double calc_hex_size(uint32_t level) { +GEOHEX_API double calc_hex_size(uint32_t level) { return H_BASE / pow3_table[level + 3]; } -void loc2xy(double lon, double lat, double *dx, double *dy) { +GEOHEX_API void loc2xy(double lon, double lat, double *dx, double *dy) { *dx = lon * H_BASE / 180.0; - *dy = log(tan((90.0 + lat) * M_PI / 360.0)) * (H_BASE / M_PI); + *dy = log(tan((90.0 + lat) * GEOHEX_PI / 360.0)) * (H_BASE / GEOHEX_PI); } -void xy2loc(double dx, double dy, double *lon, double *lat) { +GEOHEX_API void xy2loc(double dx, double dy, double *lon, double *lat) { + double lat_rad; + *lon = (dx / H_BASE) * 180.0; - double lat_rad = (dy / H_BASE) * M_PI; - *lat = (2.0 * atan(exp(lat_rad)) - M_PI / 2.0) * 180.0 / M_PI; + lat_rad = (dy / H_BASE) * GEOHEX_PI; + *lat = (2.0 * atan(exp(lat_rad)) - GEOHEX_PI / 2.0) * 180.0 / GEOHEX_PI; } -bool adjust_xy(int32_t x, int32_t y, uint32_t level, xy_t *out) { +GEOHEX_API bool adjust_xy(int32_t x, int32_t y, uint32_t level, xy_t *out) { int32_t tmp, max_hsteps, hsteps, dif, dif_x, dif_y, edge_x, edge_y; bool rev; @@ -120,7 +122,7 @@ bool adjust_xy(int32_t x, int32_t y, uint32_t level, xy_t *out) { return true; } -bool get_xy_by_location(const loc_t *location, uint32_t level, xy_t *out) { +GEOHEX_API bool get_xy_by_location(const loc_t *location, uint32_t level, xy_t *out) { int32_t h_x, h_y; double h_size, lon_grid, lat_grid, unit_x, unit_y, h_pos_x, h_pos_y, h_x_q, h_y_q; @@ -159,7 +161,7 @@ bool get_xy_by_location(const loc_t *location, uint32_t level, xy_t *out) { return adjust_xy(h_x, h_y, level, out); } -bool get_xy_by_code(const geohex_code_t code, xy_t *out) { +GEOHEX_API bool get_xy_by_code(const geohex_code_t code, xy_t *out) { uint32_t code_len, level; int32_t i, h_x, h_y, c1_idx, c2_idx, code3, d9xlen, target_len, h_decx[MAX_H_DEC3_LEN] = {0}, h_decy[MAX_H_DEC3_LEN] = {0}, @@ -227,7 +229,7 @@ bool get_xy_by_code(const geohex_code_t code, xy_t *out) { return adjust_xy(h_x, h_y, level, out); } -bool get_zone_by_location(const loc_t *location, uint32_t level, zone_t *out) { +GEOHEX_API bool get_zone_by_location(const loc_t *location, uint32_t level, zone_t *out) { xy_t xy; if (!out) { @@ -241,7 +243,7 @@ bool get_zone_by_location(const loc_t *location, uint32_t level, zone_t *out) { return get_zone_by_xy(&xy, level, out); } -bool get_zone_by_code(const geohex_code_t code, zone_t *out) { +GEOHEX_API bool get_zone_by_code(const geohex_code_t code, zone_t *out) { xy_t xy; if (!out) { @@ -255,8 +257,9 @@ bool get_zone_by_code(const geohex_code_t code, zone_t *out) { return get_zone_by_xy(&xy, (strlen(code) - 2), out); } -bool get_zone_by_xy(const xy_t *xy, uint32_t level, zone_t *out) { - int32_t tmp, i, h_x, h_y, max_hsteps, +GEOHEX_API bool get_zone_by_xy(const xy_t *xy, uint32_t level, zone_t *out) { + uint32_t i; + int32_t tmp, h_x, h_y, max_hsteps, code3_x[MAX_CODE_LEN + 2], code3_y[MAX_CODE_LEN + 2], h_code_digits[MAX_CODE_LEN + 2], mod_x, mod_y, h_1_int, h_a1, h_a2, diff --git a/tests/geohex_private.h b/tests/geohex_private.h index e5b0592..1e6d49d 100644 --- a/tests/geohex_private.h +++ b/tests/geohex_private.h @@ -2,7 +2,7 @@ /* * libgeohex * - * Copyright (c) 2024-2026 Go Kudo Kudo (https://github.com/zeriyoshi) + * Copyright (c) 2024-2026 Go Kudo Kudo (https://github.com/zeriyoshi) * * GeoHex original implementation by @sa2da (http://twitter.com/sa2da) * https://www.geohex.org/ @@ -15,8 +15,10 @@ #include -double calc_hex_size(uint32_t level); -void loc2xy(double lon, double lat, double *dx, double *dy); -void xy2loc(double dx, double dy, double *lon, double *lat); +#include "geohex/compat.h" + +GEOHEX_API double calc_hex_size(uint32_t level); +GEOHEX_API void loc2xy(double lon, double lat, double *dx, double *dy); +GEOHEX_API void xy2loc(double dx, double dy, double *lon, double *lat); #endif /* GEOHEX_PRIVATE_H */ diff --git a/tests/json_data.h b/tests/json_data.h index f0ae2da..969daad 100644 --- a/tests/json_data.h +++ b/tests/json_data.h @@ -2,7 +2,7 @@ /* * libgeohex * - * Copyright (c) 2024-2026 Go Kudo Kudo (https://github.com/zeriyoshi) + * Copyright (c) 2024-2026 Go Kudo Kudo (https://github.com/zeriyoshi) * * GeoHex original implementation by @sa2da (http://twitter.com/sa2da) * https://www.geohex.org/ diff --git a/tests/test_geohex.c b/tests/test_geohex.c index e78aaa3..64e32e9 100644 --- a/tests/test_geohex.c +++ b/tests/test_geohex.c @@ -2,7 +2,7 @@ /* * libgeohex * - * Copyright (c) 2024-2026 Go Kudo Kudo (https://github.com/zeriyoshi) + * Copyright (c) 2024-2026 Go Kudo Kudo (https://github.com/zeriyoshi) * * GeoHex original implementation by @sa2da (http://twitter.com/sa2da) * https://www.geohex.org/ @@ -78,10 +78,8 @@ void test_get_xy_by_location(void) loc_t loc; for (uint32_t i = 0; i < (sizeof(coord2xy_data) / sizeof(coord2xy_data[0])); i++) { - loc = (loc_t) { - .lat = coord2xy_data[i].lat, - .lon = coord2xy_data[i].lon, - }; + loc.lat = coord2xy_data[i].lat; + loc.lon = coord2xy_data[i].lon; TEST_ASSERT_TRUE(get_xy_by_location(&loc, coord2xy_data[i].level, &out)); TEST_ASSERT_DOUBLE_WITHIN(15, coord2xy_data[i].x, out.x); @@ -95,11 +93,9 @@ void test_get_zone_by_xy(void) xy_t xy; for (uint32_t i = 0; i < (sizeof(xy2hex_data) / sizeof(xy2hex_data[0])); i++) { - xy = (xy_t) { - .x = xy2hex_data[i].x, - .y = xy2hex_data[i].y, - .rev = false, - }; + xy.x = xy2hex_data[i].x; + xy.y = xy2hex_data[i].y; + xy.rev = false; TEST_ASSERT_TRUE(get_zone_by_xy(&xy, xy2hex_data[i].level, &out)); TEST_ASSERT_EQUAL_STRING(xy2hex_data[i].code, out.code); @@ -125,20 +121,16 @@ void test_get_zone_by_location(void) uint32_t i; for (i = 0; i < (sizeof(code2hex_data) / sizeof(code2hex_data[0])); i++) { - loc = (loc_t) { - .lat = code2hex_data[i].lat, - .lon = code2hex_data[i].lon, - }; + loc.lat = code2hex_data[i].lat; + loc.lon = code2hex_data[i].lon; TEST_ASSERT_TRUE(get_zone_by_location(&loc, strlen(code2hex_data[i].code) - 2, &out)); TEST_ASSERT_EQUAL_STRING(code2hex_data[i].code, out.code); } for (i = 0; i < sizeof(coord2hex_data) / sizeof(coord2hex_data[0]); i++) { - loc = (loc_t) { - .lat = coord2hex_data[i].lat, - .lon = coord2hex_data[i].lon, - }; + loc.lat = coord2hex_data[i].lat; + loc.lon = coord2hex_data[i].lon; TEST_ASSERT_TRUE(get_zone_by_location(&loc, coord2hex_data[i].level, &out)); TEST_ASSERT_EQUAL_STRING(coord2hex_data[i].code, out.code); From 22d8a022a8f52b8d2282b6e0e35cd4ea5cb67c02 Mon Sep 17 00:00:00 2001 From: Go Kudo Date: Fri, 22 May 2026 15:50:16 +0900 Subject: [PATCH 5/6] fix pinning --- .github/workflows/ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 58b6331..a92a7de 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -16,7 +16,7 @@ jobs: with: submodules: true - name: Setup QEMU - uses: docker/setup-qemu-action@vce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0 + uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0 with: platforms: "arm64,s390x,arm" - name: Setup buildx From 07bc31b6f09739c31ed40102cca08dad439a3d11 Mon Sep 17 00:00:00 2001 From: Go Kudo Date: Fri, 22 May 2026 16:00:05 +0900 Subject: [PATCH 6/6] wip --- .github/workflows/ci.yaml | 52 +++++++++++++++++++++++++++++++++++---- CMakeLists.txt | 2 +- ci.sh | 20 +++++++++++++-- include/geohex/geohex.h | 2 +- 4 files changed, 67 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index a92a7de..5ada1d6 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -6,31 +6,73 @@ on: pull_request: jobs: Linux: - runs-on: ubuntu-latest + name: Linux (${{ matrix.display_platform }}) + runs-on: ${{ matrix.runner }} strategy: + fail-fast: false matrix: - platform: ['linux/amd64', 'linux/arm64/v8', 'linux/s390x', 'linux/arm/v7'] + include: + - display_platform: linux/amd64 + platform: linux/amd64 + runner: ubuntu-24.04 + native_platform: linux/amd64 + qemu_platforms: "" + run_sanitizers: "true" + - display_platform: linux/arm64 + platform: linux/arm64 + runner: ubuntu-24.04-arm + native_platform: linux/arm64 + qemu_platforms: "" + run_sanitizers: "true" + - display_platform: linux/s390x + platform: linux/s390x + runner: ubuntu-24.04 + native_platform: linux/amd64 + qemu_platforms: s390x + run_sanitizers: "false" + - display_platform: linux/arm/v7 + platform: linux/arm/v7 + runner: ubuntu-24.04 + native_platform: linux/amd64 + qemu_platforms: arm + run_sanitizers: "false" + - display_platform: linux/i386 + platform: linux/386 + runner: ubuntu-24.04 + native_platform: linux/amd64 + qemu_platforms: "386" + run_sanitizers: "false" steps: - name: Checkout uses: actions/checkout@v6 with: submodules: true - name: Setup QEMU + if: matrix.qemu_platforms != '' uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4.0.0 with: - platforms: "arm64,s390x,arm" + platforms: ${{ matrix.qemu_platforms }} - name: Setup buildx uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 - name: Build container run: | - docker build --pull --no-cache --build-arg PLATFORM="${{ matrix.platform }}" --tag "test" . + docker buildx build \ + --pull \ + --no-cache \ + --load \ + --platform "${{ matrix.platform }}" \ + --build-arg PLATFORM="${{ matrix.platform }}" \ + --tag "test" \ + . - name: Test run: | docker run \ + --platform "${{ matrix.platform }}" \ --privileged \ --cap-add SYS_ADMIN \ --security-opt seccomp:unconfined \ - -e NATIVE_PLATFORM="linux/amd64" \ + -e NATIVE_PLATFORM="${{ matrix.native_platform }}" \ + -e RUN_SANITIZERS="${{ matrix.run_sanitizers }}" \ -v "$(pwd):/workspaces/libgeohex" \ -w "/workspaces/libgeohex" \ -i "test" \ diff --git a/CMakeLists.txt b/CMakeLists.txt index eaaae4c..af14ec9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.10) -project(libgeohex VERSION 1.1.1 LANGUAGES C) +project(libgeohex VERSION 2.0.0 LANGUAGES C) set(CMAKE_C_STANDARD 99) set(CMAKE_C_STANDARD_REQUIRED ON) diff --git a/ci.sh b/ci.sh index 5e1c657..66dc8ed 100755 --- a/ci.sh +++ b/ci.sh @@ -18,15 +18,31 @@ build_and_test() { rm -rf "build" } +should_run_sanitizers() { + if test "x${RUN_SANITIZERS}" = "xtrue"; then + return 0 + fi + + if test "x${RUN_SANITIZERS}" = "xfalse"; then + return 1 + fi + + if test "x${NATIVE_PLATFORM}" = "x"; then + return 0 + fi + + test "x${PLATFORM}" != "xlinux/s390x" && test "x${PLATFORM}" = "x${NATIVE_PLATFORM}" +} + build_and_test "clang" false false false -if test "x${NATIVE_PLATFORM}" = "x" || (test "x${PLATFORM}" != "xlinux/s390x" && test "x${PLATFORM}" = "x${NATIVE_PLATFORM}"); then +if should_run_sanitizers; then build_and_test "clang" true false false build_and_test "clang" false true false build_and_test "clang" false false true fi build_and_test "gcc" false false false -if test "x${NATIVE_PLATFORM}" = "x" || (test "x${PLATFORM}" != "xlinux/s390x" && test "x${PLATFORM}" = "x${NATIVE_PLATFORM}"); then +if should_run_sanitizers; then build_and_test "gcc" true false false build_and_test "gcc" false true false fi diff --git a/include/geohex/geohex.h b/include/geohex/geohex.h index 93aef91..9004dab 100644 --- a/include/geohex/geohex.h +++ b/include/geohex/geohex.h @@ -22,7 +22,7 @@ extern "C" { #endif -#define LIBGEOHEX_VERSION "1.1.1" +#define LIBGEOHEX_VERSION "2.0.0" #define GEOHEX_COMPLIANT_VERSION "3.2" #define MAX_LEVEL 15