diff --git a/.github/workflows/build_win.yml b/.github/workflows/build_win.yml index 2d0f1c0..4a87cef 100644 --- a/.github/workflows/build_win.yml +++ b/.github/workflows/build_win.yml @@ -58,6 +58,19 @@ jobs: name: proj-windows path: proj-windows.zip + - name: Build sample app + run: build_sample.bat + + - name: Package sample app artifact + run: | + tar -a -c -f sample-app-windows.zip build_sample\proj_sample.exe install_proj\bin\*.dll + + - name: Upload sample app artifact + uses: actions/upload-artifact@v4 + with: + name: sample-app-windows + path: sample-app-windows.zip + - name: Create GitHub Release if: startsWith(github.ref, 'refs/tags/') uses: softprops/action-gh-release@v2 @@ -70,4 +83,4 @@ jobs: proj-windows.zip.sha256 sample-app-windows.zip.sha256 env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/build_proj.bat b/build_proj.bat index 83b3f89..054a97e 100644 --- a/build_proj.bat +++ b/build_proj.bat @@ -35,6 +35,9 @@ cmake --fresh -S zlib -B build_zlib -G Ninja ^ cmake --build build_zlib cmake --install build_zlib +set ZLIB_ROOT=%CD%\install_zlib +set ZLIB_DIR=%CD%\install_zlib + echo === Building libtiff (minimal static, no extra codecs) === cmake --fresh -S libtiff -B build_tiff -G Ninja ^ -DCMAKE_BUILD_TYPE=Release ^ @@ -44,6 +47,7 @@ cmake --fresh -S libtiff -B build_tiff -G Ninja ^ -Dtiff-tools=OFF ^ -Dtiff-tests=OFF ^ -Dtiff_static=ON ^ +-Dzlib=ON ^ -Djpeg=OFF ^ -Dwebp=OFF ^ -Dzstd=OFF ^ @@ -71,9 +75,17 @@ if %errorlevel% neq 0 exit /b 1 lib sqlite3.obj /OUT:sqlite3.lib popd +echo ==== Patch PROJ ============================== +cd proj +git apply ..\proj.patch +if %errorlevel% neq 0 ( +echo WARNING: Patch may already be applied or failed, continuing... +) +cd .. + echo === Configure PROJ (Ninja + CMake 4.x fix) === cmake --fresh -S PROJ -B build_proj -G Ninja ^ - -DCMAKE_PREFIX_PATH=%CD%\install_tiff;%CD%\install_zlib\lib\cmake\zlib ^ + -DCMAKE_PREFIX_PATH="%CD%\install_zlib;%CD%\install_tiff;" ^ -DCMAKE_INSTALL_PREFIX=%CD%\install_proj ^ -DCMAKE_BUILD_TYPE=Release ^ -DCMAKE_POLICY_VERSION_MINIMUM=3.5 ^ @@ -96,10 +108,14 @@ cmake --build build_proj if %errorlevel% neq 0 exit /b 1 + echo ==== Instal PROJ =============== cmake --install build_proj if %errorlevel% neq 0 exit /b 1 +echo ==== COPY z.dll to PROJ install folder =============== +copy /Y install_zlib\bin\z.dll install_proj\bin\ + @REM echo === Verifying dependencies of proj.dll === @REM if exist install_proj\bin\proj_9_3.dll ( @REM dumpbin /dependents install_proj\bin\proj_9_3.dll diff --git a/build_sample.bat b/build_sample.bat new file mode 100644 index 0000000..97a5adc --- /dev/null +++ b/build_sample.bat @@ -0,0 +1,56 @@ +@echo off +setlocal ENABLEDELAYEDEXPANSION + +echo === Building sample app === + +echo === Configure sample app === +cmake --fresh -S sample_app -B build_sample -G Ninja ^ + -DCMAKE_PREFIX_PATH=%CD%\install_proj ^ + -DCMAKE_BUILD_TYPE=Release ^ + -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDLL + +if %errorlevel% neq 0 ( + echo ERROR: Sample app configure failed + exit /b 1 +) + +echo === Build sample app === +cmake --build build_sample + +if %errorlevel% neq 0 ( + echo ERROR: Sample app build failed + exit /b 1 +) + +echo === Install sample app === +cmake --install build_sample --prefix install_sample + +if %errorlevel% neq 0 ( + echo ERROR: Sample app install failed + exit /b 1 +) + +echo === Test sample app === +REM Copy PROJ DLL to sample app directory for testing +copy install_proj\bin\*.dll build_sample\ >nul 2>&1 + +REM Run the sample app +cd build_sample + +proj_sample.exe + +if %errorlevel% neq 0 ( + echo ERROR: Sample app test failed + exit /b 1 +) + +REM Run the sample app + +grid_transform.exe + +if %errorlevel% neq 0 ( + echo ERROR: grid_transform app test failed + exit /b 1 +) + +echo === Sample app build complete === diff --git a/proj.patch b/proj.patch new file mode 100644 index 0000000..0ea9019 --- /dev/null +++ b/proj.patch @@ -0,0 +1,13 @@ +diff --git a/CMakeLists.txt b/CMakeLists.txt +index df42815b..0c9d8dc0 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -18,7 +18,7 @@ project(PROJ + + # Only interpret if() arguments as variables or keywords when unquoted + cmake_policy(SET CMP0054 NEW) +- ++find_package(ZLIB) + # Set C++ version + # Make CMAKE_CXX_STANDARD available as cache option overridable by user + set(CMAKE_CXX_STANDARD 11 diff --git a/sample_app/CMakeLists.txt b/sample_app/CMakeLists.txt new file mode 100644 index 0000000..1230580 --- /dev/null +++ b/sample_app/CMakeLists.txt @@ -0,0 +1,53 @@ +cmake_minimum_required(VERSION 3.10) +project(proj_sample_app) + +# Find PROJ library +add_library(proj SHARED IMPORTED) + +set_target_properties(proj PROPERTIES + IMPORTED_LOCATION "${CMAKE_SOURCE_DIR}/../install_proj/bin/proj_9_3.dll" + IMPORTED_IMPLIB "${CMAKE_SOURCE_DIR}/../install_proj/lib/proj.lib" + INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}/../install_proj/include" +) + +# Create executable +add_executable(proj_sample simple_transform.cpp) +target_link_libraries(proj_sample PRIVATE proj) +target_compile_features(proj_sample PRIVATE cxx_std_11) + + +add_executable(grid_transform grid_transform.cpp) +target_link_libraries(grid_transform PRIVATE proj) +target_compile_features(grid_transform PRIVATE cxx_std_11) + + + +add_custom_command(TARGET proj_sample POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + $ + $ +) + +add_custom_command(TARGET proj_sample POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${CMAKE_SOURCE_DIR}/../install_proj/share/proj/proj.db" + $ +) + +add_custom_command(TARGET grid_transform POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + $ + $ +) + +add_custom_command(TARGET grid_transform POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${CMAKE_SOURCE_DIR}/../install_proj/bin/z.dll" + $ +) + +add_custom_command(TARGET grid_transform POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "${CMAKE_SOURCE_DIR}/us_nga_egm08_25.tif" + $ +) diff --git a/sample_app/grid_transform.cpp b/sample_app/grid_transform.cpp new file mode 100644 index 0000000..511b0da --- /dev/null +++ b/sample_app/grid_transform.cpp @@ -0,0 +1,80 @@ +#include +#include +#include +#include + +int main() +{ + std::cout << "=== WGS84 ellipsoidal → Geoid height (EGM2008) ===\n"; + + PJ_CONTEXT* ctx = proj_context_create(); + if (!ctx) { + std::cerr << "Failed to create PROJ context\n"; + return 1; + } + + // WGS84 3D → EGM2008 geoid height + PJ* P = proj_create_crs_to_crs( + ctx, + "EPSG:4979", // WGS84 lat, lon, ellipsoidal height + "EPSG:3855", // EGM2008 height + nullptr + ); + + if (!P) { + std::cerr << "Failed to create transformation (missing geoid grid?)\n"; + proj_context_destroy(ctx); + return 1; + } + + PJ* P_norm = proj_normalize_for_visualization(ctx, P); + proj_destroy(P); + P = P_norm; + + if (!P) { + std::cerr << "Failed to normalize transformation\n"; + proj_context_destroy(ctx); + return 1; + } + + // Example: Berlin + double lat = 52.5200; + double lon = 13.4050; + double h_ellipsoid = 100.0; // 100 m ellipsoidal height + + std::cout << std::fixed << std::setprecision(4); + std::cout << "Input:\n"; + std::cout << " Lat: " << lat << "\n"; + std::cout << " Lon: " << lon << "\n"; + std::cout << " Ellipsoidal height: " << h_ellipsoid << " m\n\n"; + + PJ_COORD in = proj_coord(lon, lat, h_ellipsoid, 0); + PJ_COORD out = proj_trans(P, PJ_FWD, in); + + if (out.xyz.z == HUGE_VAL) { + std::cerr << "ERROR: Transformation failed (geoid grid missing?)\n"; + proj_destroy(P); + proj_context_destroy(ctx); + return 1; + } + + std::cout << "Output:\n"; + std::cout << " Orthometric height (EGM2008): " + << out.xyz.z << " m\n"; + + double geoid_undulation = h_ellipsoid - out.xyz.z; + + std::cout << "\nComputed geoid undulation: " + << geoid_undulation << " m\n"; + + if (std::fabs(geoid_undulation) > 0.01) { + std::cout << "\nSUCCESS: Geoid correction applied (GeoTIFF working)\n"; + } else { + std::cout << "\nWARNING: No geoid correction detected\n"; + } + + proj_destroy(P); + proj_context_destroy(ctx); + + return 0; +} \ No newline at end of file diff --git a/sample_app/simple_transform.cpp b/sample_app/simple_transform.cpp new file mode 100644 index 0000000..1920706 --- /dev/null +++ b/sample_app/simple_transform.cpp @@ -0,0 +1,83 @@ +// Sample application to test PROJ library installation +// Converts WGS84 coordinates to UTM projection + +#include +#include +#include + +int main() { + std::cout << "hello" << std::endl; + // Create PROJ context + PJ_CONTEXT *ctx = proj_context_create(); + + // Define transformation: WGS84 (EPSG:4326) to UTM Zone 33N (EPSG:32633) + // Example: Berlin, Germany coordinates + PJ *P = proj_create_crs_to_crs(ctx, + "EPSG:4326", // WGS84 + "EPSG:32633", // UTM Zone 33N + NULL); + + if (P == NULL) { + std::cerr << "Failed to create transformation" << std::endl; + proj_context_destroy(ctx); + return 1; + } + + // Normalize for input/output in radians for geographic, meters for projected + PJ* P_norm = proj_normalize_for_visualization(ctx, P); + if (P_norm == NULL) { + std::cerr << "Failed to normalize transformation" << std::endl; + proj_destroy(P); + proj_context_destroy(ctx); + return 1; + } + proj_destroy(P); + P = P_norm; + + // Test coordinates: Berlin, Germany (latitude, longitude in degrees) + double lat = 52.5200; // degrees N + double lon = 13.4050; // degrees E + + std::cout << "Testing PROJ library installation" << std::endl; + std::cout << "===================================" << std::endl; + std::cout << std::fixed << std::setprecision(6); + std::cout << "Input (WGS84):" << std::endl; + std::cout << " Latitude: " << lat << " degrees" << std::endl; + std::cout << " Longitude: " << lon << " degrees" << std::endl; + std::cout << std::endl; + + // Create coordinate structure + PJ_COORD c_in, c_out; + c_in = proj_coord(lon, lat, 0, 0); + + // Transform + c_out = proj_trans(P, PJ_FWD, c_in); + + std::cout << "Output (UTM Zone 33N):" << std::endl; + std::cout << " Easting: " << c_out.xy.x << " meters" << std::endl; + std::cout << " Northing: " << c_out.xy.y << " meters" << std::endl; + std::cout << std::endl; + + // Verify transformation was successful + if (c_out.xy.x == HUGE_VAL || c_out.xy.y == HUGE_VAL) { + std::cerr << "ERROR: Transformation failed!" << std::endl; + proj_destroy(P); + proj_context_destroy(ctx); + return 1; + } + + // Expected values for Berlin (approximate) + // UTM Zone 33N: E~391000, N~5820000 + if (c_out.xy.x > 390000 && c_out.xy.x < 392000 && + c_out.xy.y > 5819000 && c_out.xy.y < 5821000) { + std::cout << "SUCCESS: Transformation result is within expected range!" << std::endl; + } else { + std::cout << "WARNING: Result outside expected range" << std::endl; + } + + // Cleanup + proj_destroy(P); + proj_context_destroy(ctx); + + return 0; +} diff --git a/sample_app/us_nga_egm08_25.tif b/sample_app/us_nga_egm08_25.tif new file mode 100644 index 0000000..c075cd8 Binary files /dev/null and b/sample_app/us_nga_egm08_25.tif differ