Now let me also remove the diagnostic logging that won't fire anymore (since we skip missing-way relations) and build.


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

Build and run with all fixes


[ 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 40 admin polygons from closed ways.
  Assembling admin polygons in parallel (40080 relations, 112858 way geometries)...
  Parallel admin assembly complete: 43187 polygon rings from 40080 relations.
Done reading:
  43227 admin polygon rings
Waiting for admin polygon S2 covering to complete...
Admin polygon S2 covering complete (6103 cells).
admin index: 6103 cells, 43227 polygons
Done.


43,227 — down from 43,280 (removed the cross-border extras and unnamed postal codes). Sequential has 43,221. Very close. Let me check.


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

Check remaining frequency differences


  'Deutschland - Österreich|2': A=1 B=0 diff=+1
  'Deutschland — Schweiz / Suisse / Svizerra|2': A=1 B=0 diff=+1
  'Kleines Dorf|11': A=1 B=0 diff=+1
  'Krefeld-Ost|9': A=2 B=1 diff=+1
  'Krefeld-Uerdingen|9': A=1 B=2 diff=-1
  'Moorkolonie Neudorf|11': A=1 B=0 diff=+1
  'Moorkolonie Platendorf|11': A=1 B=0 diff=+1
  'Neuhaus|10': A=5 B=4 diff=+1
Total frequency differences: 8


Down to 8 differences from 61! The remaining:

1. **Deutschland-Österreich, Deutschland-Schweiz** (level 2): Cross-border relations with 0 missing ways (all ways in the extract), but these are international boundary relations that osmium's assembler rejects. These must have ways that are present but form broken rings.

2. **Kleines Dorf, Moorkolonie Neudorf, Moorkolonie Platendorf** (level 11): Closed-way postal codes that osmium doesn't create. These are ways that exist as both a closed way AND part of a multipolygon relation.

3. **Krefeld-Ost/Krefeld-Uerdingen** (level 9): Ring assignment mismatch.

4. **Neuhaus** (level 10): One extra polygon.

Let me check if the closed-way admin polygons duplicate relation-based ones. The issue might be that a way is both a standalone closed polygon AND a member of a multipolygon relation for the same admin boundary.


ssh node3 "cat > /tmp/check_closedway_overlap.cpp << 'EOFCPP'
#include <cstdint>
#include <cstring>
#include <iostream>
#include <set>
#include <map>
#include <osmium/io/pbf_input.hpp>
#include <osmium/osm/way.hpp>
#include <osmium/osm/relation.hpp>
int main(int argc, char* argv[]) {
    // Pass 1: collect way IDs that are members of admin/postal relations
    std::set<int64_t> relation_member_ways;
    {
        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* boundary = rel.tags()[\"boundary\"];
                if (!boundary) continue;
                if (std::strcmp(boundary, \"administrative\") != 0 && std::strcmp(boundary, \"postal_code\") != 0) continue;
                for (const auto& m : rel.members()) {
                    if (m.type() == osmium::item_type::way) {
                        relation_member_ways.insert(m.ref());
                    }
                }
            }
        }
    }
    printf(\"Relation member ways: %zu\\n\", relation_member_ways.size());

    // Pass 2: find closed ways with admin tags that are ALSO relation members
    int overlap = 0, standalone = 0;
    {
        osmium::io::Reader reader{argv[1], osmium::osm_entity_bits::way};
        while (auto buffer = reader.read()) {
            for (const auto& item : buffer) {
                if (item.type() != osmium::item_type::way) continue;
                const auto& way = static_cast<const osmium::Way&>(item);
                const char* boundary = way.tags()[\"boundary\"];
                if (!boundary) continue;
                if (std::strcmp(boundary, \"administrative\") != 0 && std::strcmp(boundary, \"postal_code\") != 0) continue;
                auto& nodes = way.nodes();
                if (nodes.size() < 4 || nodes.front().ref() != nodes.back().ref()) continue;

                const char* name = way.tags()[\"name\"];
                const char* level = way.tags()[\"admin_level\"];
                bool in_relation = relation_member_ways.count(way.id());
                if (in_relation) {
                    overlap++;
                    if (overlap <= 10) printf(\"  OVERLAP: way %ld '%s' level=%s (also in relation)\\n\",
                           (long)way.id(), name ? name : \"(none)\", level ? level : \"?\");
                } else {
                    standalone++;
                }
            }
        }
    }
    printf(\"Closed admin ways in relations: %d, standalone: %d\\n\", overlap, standalone);
    return 0;
}
EOFCPP
g++ -O2 -std=c++17 -o /tmp/check_closedway_overlap /tmp/check_closedway_overlap.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_closedway_overlap ~/pbf/germany.osm.pbf" 2>&1

Check if closed admin ways overlap with relations


Relation member ways: 129119
  OVERLAP: way 24718735 'Deutschland - België / Belgique / Belgien' level=2 (also in relation)
  OVERLAP: way 26563455 '(none)' level=10 (also in relation)
  OVERLAP: way 29247029 '(none)' level=6 (also in relation)
  OVERLAP: way 29247030 '(none)' level=6 (also in relation)
  OVERLAP: way 29247031 '(none)' level=6 (also in relation)
  OVERLAP: way 29412338 '(none)' level=6 (also in relation)
  OVERLAP: way 29413138 '(none)' level=6 (also in relation)
  OVERLAP: way 29413247 '(none)' level=6 (also in relation)
  OVERLAP: way 53378681 '(none)' level=8 (also in relation)
  OVERLAP: way 54883087 '(none)' level=8 (also in relation)
Closed admin ways in relations: 741, standalone: 35


741 closed admin ways that are ALSO members of relations. Osmium's `MultipolygonManager` consumes these ways for relation assembly and does NOT create standalone polygons from them. Our code creates them as BOTH relation rings AND closed-way polygons — double counting.

The fix: skip closed ways that are members of any collected admin/postal relation.


Edit

                                    // Handle closed ways that are admin boundaries themselves
                                    // (not part of a multipolygon relation, but standalone polygons)
                                    if (parallel_admin) {

                                    // Handle closed ways that are admin boundaries themselves
                                    // (not part of a multipolygon relation, but standalone polygons)
                                    // Skip ways that are members of admin relations — those are
                                    // handled by the multipolygon assembler, not as standalone polygons.
                                    if (parallel_admin && !admin_way_ids.count(way.id())) {