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-rolefix3 && mkdir -p /tmp/de-par-rolefix3 && /tmp/traccar-geocoder/build/build-index /tmp/de-par-rolefix3 ~/pbf/germany.osm.pbf --parallel-admin 2>&1 | grep -E 'polygon|Done'" 2>&1

Build with refined role fallback


[ 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: 43186 polygon rings from 40080 relations.
Done reading:
  43223 admin polygon rings
Waiting for admin polygon S2 covering to complete...
Admin polygon S2 covering complete (6103 cells).
admin index: 6103 cells, 43223 polygons
Done.


ssh node3 "/tmp/freq_compare /tmp/de-par-rolefix3 /tmp/de-sequential-final" 2>&1

Check Germany freq differences


  'Krefeld-Ost|9': A=2 B=1 diff=+1
  'Krefeld-Uerdingen|9': A=1 B=2 diff=-1
  'Neuhaus|10': A=5 B=4 diff=+1
  'Reislingen|10': A=1 B=0 diff=+1
Total frequency differences: 4


Still Reislingen. The has_other_roles check finds inner ways in Reislingen's relation, so it retries. The issue is that Reislingen's relation shares `w1448056003` (role='outer' in Reislingen, but the same way exists with different role in Neuhaus-Reislingen). Let me check.

Actually, Reislingen only has 6 members (5 outer). The retry happens because there's a way with non-outer role. Let me check what roles Reislingen's members have.


ssh node3 "cat > /tmp/check_reislingen.cpp << 'EOFCPP'
#include <set>
#include <cstdint>
#include <cstring>
#include <iostream>
#include <osmium/io/pbf_input.hpp>
#include <osmium/osm/relation.hpp>
int main(int argc, char* argv[]) {
    osmium::io::Reader reader{argv[1], osmium::osm_entity_bits::relation};
    while (auto buffer = reader.read()) {
        for (const auto& item : buffer) {
            if (item.type() != osmium::item_type::relation) continue;
            const auto& rel = static_cast<const osmium::Relation&>(item);
            const char* name = rel.tags()[\"name\"];
            if (!name || std::string(name) != \"Reislingen\") continue;
            const char* level = rel.tags()[\"admin_level\"];
            printf(\"Relation %ld: '%s' level=%s\\n\", (long)rel.id(), name, level ? level : \"?\");
            for (const auto& m : rel.members()) {
                printf(\"  %s %ld role='%s'\\n\",
                       m.type() == osmium::item_type::way ? \"way\" : \"node\",
                       (long)m.ref(), m.role());
            }
        }
    }
    return 0;
}
EOFCPP
g++ -O2 -std=c++17 -o /tmp/check_reislingen /tmp/check_reislingen.cpp -ls2 /usr/lib/x86_64-linux-gnu/libz.so /usr/lib/x86_64-linux-gnu/libbz2.so /usr/lib/x86_64-linux-gnu/libexpat.so -lpthread && /tmp/check_reislingen ~/pbf/germany.osm.pbf" 2>&1

Check Reislingen relation members


Relation 3514569: 'Reislingen' level=?
  node 514510 role=''
Relation 3514570: 'Reislingen' level=?
  node 514510 role=''
Relation 19800448: 'Reislingen' level=10
  way 1448056005 role='inner'
  way 1448056002 role='outer'
  way 1448056006 role='outer'
  way 1448056001 role='outer'
  way 1448056003 role='outer'
  way 1465686504 role='outer'
  node 76006487 role='label'


Reislingen (level 10) has 5 outer ways AND 1 inner way. The outer-only pass found 5 ways but couldn't close. The all-ways retry includes the inner way which bridges the gap. But this creates a polygon osmium doesn't create.

The inner way `w1448056005` might be a hole inside Reislingen. Including it in the ring assembly creates a ring that merges the hole boundary with the outer boundary — producing a figure-8 or similar invalid shape.

The correct behavior is: outer-only fails → the relation has genuinely disconnected outer boundary → don't create it. The inner way is a separate feature (a hole) that shouldn't bridge the outer ring.


Edit

                                if (rings.empty()) {
                                    // Check if there are non-outer ways we haven't tried
                                    bool has_other_roles = false;
                                    for (const auto& [wid, role] : rel.members) {
                                        if (role != "outer" && !role.empty()) {
                                            if (data.way_geometries.find(wid) != data.way_geometries.end()) {
                                                has_other_roles = true;
                                                break;
                                            }
                                        }
                                    }
                                    if (has_other_roles) {
                                        rings = assemble_outer_rings(rel.members, data.way_geometries, true);
                                    }
                                }

                                if (rings.empty()) {
                                    // Retry with all ways only if the relation has
                                    // NO outer/empty-role ways at all (the boundary IS
                                    // the inner ways). Don't retry when outer ways exist
                                    // but fail to close — that's genuinely broken geometry.
                                    bool has_outer = false;
                                    for (const auto& [wid, role] : rel.members) {
                                        if (role == "outer" || role.empty()) {
                                            if (data.way_geometries.find(wid) != data.way_geometries.end()) {
                                                has_outer = true;
                                                break;
                                            }
                                        }
                                    }
                                    if (!has_outer) {
                                        rings = assemble_outer_rings(rel.members, data.way_geometries, true);
                                    }
                                }