gadm cpp exports
This commit is contained in:
parent
3f8073452c
commit
3a53b15274
@ -34,18 +34,22 @@ find_package(OpenMP)
|
||||
add_subdirectory(packages/logger)
|
||||
add_subdirectory(packages/json)
|
||||
|
||||
# ── Executable ───────────────────────────────────────────────────────────────
|
||||
add_executable(boundaries
|
||||
src/main.cpp
|
||||
# ── Library sources (everything except main.cpp) ─────────────────────────────
|
||||
set(GADM_LIB_SOURCES
|
||||
src/gpkg_reader.cpp
|
||||
src/geo_merge.cpp
|
||||
src/ghs_enrich.cpp
|
||||
)
|
||||
|
||||
target_include_directories(boundaries PRIVATE src)
|
||||
set(GADM_PUBLIC_HEADERS
|
||||
src/gpkg_reader.h
|
||||
src/geo_merge.h
|
||||
src/ghs_enrich.h
|
||||
src/pip.h
|
||||
src/types.h
|
||||
)
|
||||
|
||||
target_link_libraries(boundaries PRIVATE
|
||||
CLI11::CLI11
|
||||
set(GADM_LIB_DEPS
|
||||
GDAL::GDAL
|
||||
GEOS::geos_c
|
||||
PROJ::proj
|
||||
@ -54,17 +58,42 @@ target_link_libraries(boundaries PRIVATE
|
||||
json
|
||||
)
|
||||
|
||||
# ── Static library ───────────────────────────────────────────────────────────
|
||||
add_library(gadm_boundaries_static STATIC ${GADM_LIB_SOURCES})
|
||||
target_include_directories(gadm_boundaries_static PUBLIC src)
|
||||
target_link_libraries(gadm_boundaries_static PUBLIC ${GADM_LIB_DEPS})
|
||||
if(OpenMP_CXX_FOUND)
|
||||
target_link_libraries(boundaries PRIVATE OpenMP::OpenMP_CXX)
|
||||
target_compile_definitions(boundaries PRIVATE HAS_OPENMP=1)
|
||||
target_link_libraries(gadm_boundaries_static PUBLIC OpenMP::OpenMP_CXX)
|
||||
target_compile_definitions(gadm_boundaries_static PUBLIC HAS_OPENMP=1)
|
||||
endif()
|
||||
|
||||
# ── Compiler warnings ───────────────────────────────────────────────────────
|
||||
if(MSVC)
|
||||
target_compile_options(boundaries PRIVATE /W4 /permissive-)
|
||||
else()
|
||||
target_compile_options(boundaries PRIVATE -Wall -Wextra -Wpedantic)
|
||||
# ── Shared library ───────────────────────────────────────────────────────────
|
||||
add_library(gadm_boundaries SHARED ${GADM_LIB_SOURCES})
|
||||
target_include_directories(gadm_boundaries PUBLIC src)
|
||||
target_link_libraries(gadm_boundaries PUBLIC ${GADM_LIB_DEPS})
|
||||
if(OpenMP_CXX_FOUND)
|
||||
target_link_libraries(gadm_boundaries PUBLIC OpenMP::OpenMP_CXX)
|
||||
target_compile_definitions(gadm_boundaries PUBLIC HAS_OPENMP=1)
|
||||
endif()
|
||||
# Export all symbols on Windows (no __declspec needed)
|
||||
set_target_properties(gadm_boundaries PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
||||
|
||||
# ── Executable (links against static lib) ────────────────────────────────────
|
||||
add_executable(boundaries src/main.cpp)
|
||||
target_link_libraries(boundaries PRIVATE
|
||||
gadm_boundaries_static
|
||||
CLI11::CLI11
|
||||
)
|
||||
|
||||
# ── Compiler warnings (all targets) ─────────────────────────────────────────
|
||||
set(_GADM_TARGETS boundaries gadm_boundaries_static gadm_boundaries)
|
||||
foreach(_tgt IN LISTS _GADM_TARGETS)
|
||||
if(MSVC)
|
||||
target_compile_options(${_tgt} PRIVATE /W4 /permissive-)
|
||||
else()
|
||||
target_compile_options(${_tgt} PRIVATE -Wall -Wextra -Wpedantic)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# ── Copy data files needed at runtime ────────────────────────────────────────
|
||||
add_custom_command(TARGET boundaries POST_BUILD
|
||||
@ -80,8 +109,10 @@ else()
|
||||
set(DIST_DIR "${CMAKE_SOURCE_DIR}/../dist/linux-x64")
|
||||
endif()
|
||||
|
||||
install(TARGETS boundaries
|
||||
install(TARGETS boundaries gadm_boundaries_static gadm_boundaries
|
||||
RUNTIME DESTINATION "${DIST_DIR}"
|
||||
LIBRARY DESTINATION "${DIST_DIR}/lib"
|
||||
ARCHIVE DESTINATION "${DIST_DIR}/lib"
|
||||
)
|
||||
|
||||
# Post-build: copy binary so `npm run boundaries:cpp` works from dist
|
||||
@ -93,6 +124,58 @@ add_custom_command(TARGET boundaries POST_BUILD
|
||||
COMMENT "Copying boundaries to ${DIST_DIR}/"
|
||||
)
|
||||
|
||||
# Post-build: copy public headers to dist/include/gadm/
|
||||
add_custom_command(TARGET gadm_boundaries POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${DIST_DIR}/include/gadm"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${CMAKE_SOURCE_DIR}/src/gpkg_reader.h" "${DIST_DIR}/include/gadm/"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${CMAKE_SOURCE_DIR}/src/geo_merge.h" "${DIST_DIR}/include/gadm/"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${CMAKE_SOURCE_DIR}/src/ghs_enrich.h" "${DIST_DIR}/include/gadm/"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${CMAKE_SOURCE_DIR}/src/pip.h" "${DIST_DIR}/include/gadm/"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${CMAKE_SOURCE_DIR}/src/types.h" "${DIST_DIR}/include/gadm/"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${CMAKE_SOURCE_DIR}/packages/logger/include/logger/logger.h"
|
||||
"${DIST_DIR}/include/gadm/"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${CMAKE_SOURCE_DIR}/packages/json/include/json/json.h"
|
||||
"${DIST_DIR}/include/gadm/"
|
||||
COMMENT "Copying public headers to ${DIST_DIR}/include/gadm/"
|
||||
)
|
||||
|
||||
# Post-build: copy static library to dist/lib/
|
||||
add_custom_command(TARGET gadm_boundaries_static POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${DIST_DIR}/lib"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
$<TARGET_FILE:gadm_boundaries_static>
|
||||
"${DIST_DIR}/lib/"
|
||||
COMMENT "Copying static library to ${DIST_DIR}/lib/"
|
||||
)
|
||||
|
||||
# Post-build: copy shared library (.dll + import lib) to dist/
|
||||
if(WIN32)
|
||||
add_custom_command(TARGET gadm_boundaries POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${DIST_DIR}/lib"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
$<TARGET_FILE:gadm_boundaries>
|
||||
"${DIST_DIR}/"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
$<TARGET_LINKER_FILE:gadm_boundaries>
|
||||
"${DIST_DIR}/lib/"
|
||||
COMMENT "Copying shared library + import lib to ${DIST_DIR}/"
|
||||
)
|
||||
else()
|
||||
add_custom_command(TARGET gadm_boundaries POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
$<TARGET_FILE:gadm_boundaries>
|
||||
"${DIST_DIR}/"
|
||||
COMMENT "Copying shared library to ${DIST_DIR}/"
|
||||
)
|
||||
endif()
|
||||
|
||||
# Copy dependent DLLs (Windows) or shared libs + proj.db
|
||||
if(WIN32)
|
||||
add_custom_command(TARGET boundaries POST_BUILD
|
||||
|
||||
BIN
dist/win-x64/boundaries.exe
vendored
BIN
dist/win-x64/boundaries.exe
vendored
Binary file not shown.
BIN
dist/win-x64/gadm_boundaries.dll
vendored
Normal file
BIN
dist/win-x64/gadm_boundaries.dll
vendored
Normal file
Binary file not shown.
BIN
dist/win-x64/gadm_boundaries.exp
vendored
Normal file
BIN
dist/win-x64/gadm_boundaries.exp
vendored
Normal file
Binary file not shown.
BIN
dist/win-x64/gadm_boundaries.lib
vendored
Normal file
BIN
dist/win-x64/gadm_boundaries.lib
vendored
Normal file
Binary file not shown.
BIN
dist/win-x64/gadm_boundaries_static.lib
vendored
Normal file
BIN
dist/win-x64/gadm_boundaries_static.lib
vendored
Normal file
Binary file not shown.
14
dist/win-x64/include/gadm/geo_merge.h
vendored
Normal file
14
dist/win-x64/include/gadm/geo_merge.h
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "types.h"
|
||||
|
||||
namespace geo_merge {
|
||||
|
||||
/// Merge multiple features that share the same GID code by unioning their geometries.
|
||||
/// Uses GEOS GEOSUnaryUnion with GEOSMakeValid fallback.
|
||||
std::vector<boundary::BoundaryFeature> merge(
|
||||
const std::vector<boundary::BoundaryFeature>& features
|
||||
);
|
||||
|
||||
} // namespace geo_merge
|
||||
50
dist/win-x64/include/gadm/ghs_enrich.h
vendored
Normal file
50
dist/win-x64/include/gadm/ghs_enrich.h
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
namespace ghs_enrich {
|
||||
|
||||
/// Enrichment results for a single boundary feature.
|
||||
struct EnrichResult {
|
||||
// Population grid (GHS-POP)
|
||||
double ghsPopulation = 0;
|
||||
double ghsPopMaxDensity = 0;
|
||||
double ghsPopCenterLon = 0;
|
||||
double ghsPopCenterLat = 0;
|
||||
std::vector<std::array<double, 3>> ghsPopCenters; // [lon, lat, density]
|
||||
|
||||
// Built-up surface (GHS-BUILT-S)
|
||||
double ghsBuiltWeight = 0;
|
||||
double ghsBuiltMax = 0;
|
||||
double ghsBuiltCenterLon = 0;
|
||||
double ghsBuiltCenterLat = 0;
|
||||
std::vector<std::array<double, 3>> ghsBuiltCenters; // [lon, lat, built]
|
||||
|
||||
bool hasPop = false;
|
||||
bool hasBuilt = false;
|
||||
};
|
||||
|
||||
/// Enrich a GeoJSON feature geometry with GHS raster statistics.
|
||||
///
|
||||
/// The TIFFs are global grids in Mollweide (EPSG:54009) at 100m resolution:
|
||||
/// - GHS_POP_E2030_GLOBE_R2023A_54009_100_V1_0.tif (~3–8 GB)
|
||||
/// - GHS_BUILT_S_E2030_GLOBE_R2023A_54009_100_V1_0.tif
|
||||
///
|
||||
/// GDAL reads these via windowed RasterIO — only the bbox-clipped
|
||||
/// portion is loaded, so memory use stays bounded regardless of file size.
|
||||
EnrichResult enrich_feature(
|
||||
const std::vector<unsigned char> &wkb,
|
||||
double minLon, double minLat, double maxLon, double maxLat,
|
||||
const std::string &pop_tiff_path = "",
|
||||
const std::string &built_tiff_path = "");
|
||||
|
||||
/// Convenience: enrich all features in a batch, returning enrichment data
|
||||
/// indexed by feature position.
|
||||
std::vector<EnrichResult> enrich_batch(
|
||||
const std::vector<std::vector<unsigned char>> &wkbs,
|
||||
const std::string &pop_tiff_path = "",
|
||||
const std::string &built_tiff_path = "");
|
||||
|
||||
} // namespace ghs_enrich
|
||||
28
dist/win-x64/include/gadm/gpkg_reader.h
vendored
Normal file
28
dist/win-x64/include/gadm/gpkg_reader.h
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "types.h"
|
||||
|
||||
namespace gpkg_reader {
|
||||
|
||||
/// Read features from a GeoPackage file, filtered by country (GID_0) and admin level.
|
||||
/// Groups features by GID_{level} and returns one BoundaryFeature per group
|
||||
/// with its geometry as a GEOS handle.
|
||||
/// Retrieve a list of distinct GID_{split_level} values for a given country code
|
||||
std::vector<std::string> get_subregions(
|
||||
const std::string& gpkg_path,
|
||||
const std::string& country_code,
|
||||
int split_level
|
||||
);
|
||||
|
||||
/// Read features from a GeoPackage file, filtered by country (GID_0) and admin level.
|
||||
/// Can also filter directly by sub-region if country_code is a dotted GADM ID (e.g. ESP.6_1).
|
||||
std::vector<boundary::BoundaryFeature> read_features(
|
||||
const std::string& gpkg_path,
|
||||
const std::string& country_code,
|
||||
int level,
|
||||
double tolerance = 0.0
|
||||
);
|
||||
|
||||
} // namespace gpkg_reader
|
||||
23
dist/win-x64/include/gadm/json.h
vendored
Normal file
23
dist/win-x64/include/gadm/json.h
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace json {
|
||||
|
||||
/// Parse a JSON string and return a pretty-printed version.
|
||||
std::string prettify(const std::string &json_str);
|
||||
|
||||
/// Extract a string value by key from a JSON object (top-level only).
|
||||
std::string get_string(const std::string &json_str, const std::string &key);
|
||||
|
||||
/// Extract an int value by key from a JSON object (top-level only).
|
||||
int get_int(const std::string &json_str, const std::string &key);
|
||||
|
||||
/// Check if a JSON string is valid.
|
||||
bool is_valid(const std::string &json_str);
|
||||
|
||||
/// Get all top-level keys from a JSON object.
|
||||
std::vector<std::string> keys(const std::string &json_str);
|
||||
|
||||
} // namespace json
|
||||
16
dist/win-x64/include/gadm/logger.h
vendored
Normal file
16
dist/win-x64/include/gadm/logger.h
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace logger {
|
||||
|
||||
/// Initialize the default logger (call once at startup).
|
||||
void init(const std::string &app_name = "polymech");
|
||||
|
||||
/// Log at various levels.
|
||||
void info(const std::string &msg);
|
||||
void warn(const std::string &msg);
|
||||
void error(const std::string &msg);
|
||||
void debug(const std::string &msg);
|
||||
|
||||
} // namespace logger
|
||||
38
dist/win-x64/include/gadm/pip.h
vendored
Normal file
38
dist/win-x64/include/gadm/pip.h
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
|
||||
namespace pip {
|
||||
|
||||
/// Ray-casting point-in-polygon test.
|
||||
/// polygon is a flat array of [x0, y0, x1, y1, ...] coordinates.
|
||||
/// Returns true if (px, py) is inside the polygon.
|
||||
inline bool point_in_polygon(
|
||||
double px, double py,
|
||||
const double* polygon, size_t num_vertices
|
||||
) {
|
||||
bool inside = false;
|
||||
for (size_t i = 0, j = num_vertices - 1; i < num_vertices; j = i++) {
|
||||
double xi = polygon[i * 2];
|
||||
double yi = polygon[i * 2 + 1];
|
||||
double xj = polygon[j * 2];
|
||||
double yj = polygon[j * 2 + 1];
|
||||
|
||||
if (((yi > py) != (yj > py)) &&
|
||||
(px < (xj - xi) * (py - yi) / (yj - yi) + xi)) {
|
||||
inside = !inside;
|
||||
}
|
||||
}
|
||||
return inside;
|
||||
}
|
||||
|
||||
/// Convenience overload for std::vector<double> ring.
|
||||
inline bool point_in_polygon(
|
||||
double px, double py,
|
||||
const std::vector<double>& ring
|
||||
) {
|
||||
return point_in_polygon(px, py, ring.data(), ring.size() / 2);
|
||||
}
|
||||
|
||||
} // namespace pip
|
||||
30
dist/win-x64/include/gadm/types.h
vendored
Normal file
30
dist/win-x64/include/gadm/types.h
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
namespace boundary {
|
||||
|
||||
/// A single administrative boundary feature (one GID code).
|
||||
struct BoundaryFeature {
|
||||
std::string code; // e.g. "DEU.1_1" (GID_{level})
|
||||
std::string name; // e.g. "Bayern"
|
||||
nlohmann::json geojson; // GeoJSON geometry object
|
||||
std::vector<unsigned char> wkb;
|
||||
|
||||
double minX = 0, minY = 0, maxX = 0, maxY = 0;
|
||||
|
||||
// Raw GEOS geometry handle (opaque, managed by caller)
|
||||
void* geos_geom = nullptr;
|
||||
};
|
||||
|
||||
/// Result of a full boundary batch for one country+level.
|
||||
struct BoundaryResult {
|
||||
std::string country;
|
||||
int level = 0;
|
||||
int featureCount = 0;
|
||||
std::vector<BoundaryFeature> features;
|
||||
};
|
||||
|
||||
} // namespace boundary
|
||||
BIN
dist/win-x64/lib/gadm_boundaries.lib
vendored
Normal file
BIN
dist/win-x64/lib/gadm_boundaries.lib
vendored
Normal file
Binary file not shown.
BIN
dist/win-x64/lib/gadm_boundaries_static.lib
vendored
Normal file
BIN
dist/win-x64/lib/gadm_boundaries_static.lib
vendored
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user