Now build and test on Germany first (fast), then Europe.


Bash

cat /root/traccar-geocoder/builder/src/build_index.cpp | ssh node3 "cat > /tmp/traccar-geocoder/builder/src/build_index.cpp" && ssh node3 "cd /tmp/traccar-geocoder/build && make -j\$(nproc) 2>&1 | tail -3 && rm -rf /tmp/de-par-rolefix && mkdir -p /tmp/de-par-rolefix && /tmp/traccar-geocoder/build/build-index /tmp/de-par-rolefix ~/pbf/germany.osm.pbf --parallel-admin 2>&1 | grep -E 'polygon|Done|closed way'" 2>&1

Build and test on Germany with role fix


[ 50%] Building CXX object CMakeFiles/build-index.dir/src/build_index.cpp.o
[100%] Linking CXX executable build-index
[100%] Built target build-index
  Added 37 admin polygons from closed ways.
  Assembling admin polygons in parallel (40080 relations, 112858 way geometries)...
  Parallel admin assembly complete: 45093 polygon rings from 40080 relations.
Done reading:
  45130 admin polygon rings
Waiting for admin polygon S2 covering to complete...
Admin polygon S2 covering complete (6103 cells).
admin index: 6103 cells, 45130 polygons
Done.


45,130 is way too many — inner ways are forming extra rings. The fix: try outer-only first, then fall back to all-ways only when outer-only produces 0 rings.


Edit

    // Collect coordinates from ALL member ways regardless of role.
    // Osmium ignores roles during assembly (check_roles=false by default) —
    // inner ways often bridge gaps between outer ways in complex relations.
    std::unordered_map<int64_t, int> coord_count;
    std::vector<const std::vector<std::pair<double,double>>*> way_coords;
    for (const auto& [way_id, role] : members) {

    // Collect coordinates from member ways.
    // First pass uses outer/empty-role ways only. If that produces 0 rings,
    // retry with ALL ways (matching osmium's check_roles=false fallback) —
    // inner ways sometimes bridge gaps between outer ways.
    std::unordered_map<int64_t, int> coord_count;
    std::vector<const std::vector<std::pair<double,double>>*> way_coords;
    for (const auto& [way_id, role] : members) {
        if (role != "outer" && !role.empty()) continue;