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

Build with all closed ways included


[ 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.


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

Check 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


Back to 8 differences. Let me tackle each one:

1. **Deutschland-Österreich, Deutschland-Schweiz** (+1 each): Our stitcher creates rings from these level 2 boundary relations. All ways present but osmium doesn't create them. These must be relations where our coordinate matching connects partial rings that osmium leaves open.

2. **Kleines Dorf, Moorkolonie Neudorf, Moorkolonie Platendorf** (+1 each): Closed-way postal codes (level 11). These exist as closed ways but osmium's sequential path doesn't create them. Let me check why.

3. **Krefeld-Ost** (+1), **Krefeld-Uerdingen** (-1): Ring assignment swap.

4. **Neuhaus** (+1): Extra polygon.


ssh node3 "cat > /tmp/check_postal.cpp << 'EOFCPP'
#include <cstdint>
#include <cstring>
#include <iostream>
#include <osmium/io/pbf_input.hpp>
#include <osmium/osm/way.hpp>
int main(int argc, char* argv[]) {
    std::set<std::string> targets = {\"Kleines Dorf\", \"Moorkolonie Neudorf\", \"Moorkolonie Platendorf\"};
    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* name = way.tags()[\"name\"];
            if (!name || !targets.count(name)) continue;
            printf(\"Way %ld: name='%s' boundary='%s' postal_code='%s' admin_level='%s' nodes=%d closed=%s\\n\",
                   (long)way.id(), name,
                   way.tags()[\"boundary\"] ? way.tags()[\"boundary\"] : \"(none)\",
                   way.tags()[\"postal_code\"] ? way.tags()[\"postal_code\"] : \"(none)\",
                   way.tags()[\"admin_level\"] ? way.tags()[\"admin_level\"] : \"(none)\",
                   (int)way.nodes().size(),
                   (way.nodes().size() >= 4 && way.nodes().front().ref() == way.nodes().back().ref()) ? \"yes\" : \"no\");
            for (const char* key : {\"boundary\", \"admin_level\", \"name\", \"postal_code\", \"type\"}) {
                const char* val = way.tags()[key];
                if (val) printf(\"  %s=%s\\n\", key, val);
            }
        }
    }
    return 0;
}
EOFCPP
g++ -O2 -std=c++17 -o /tmp/check_postal /tmp/check_postal.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_postal ~/pbf/germany.osm.pbf" 2>&1

Check postal code ways that are extras


Error: Exit code 1
/tmp/check_postal.cpp: In function ‘int main(int, char**)’:
/tmp/check_postal.cpp:7:10: error: ‘set’ is not a member of ‘std’
    7 |     std::set<std::string> targets = {"Kleines Dorf", "Moorkolonie Neudorf", "Moorkolonie Platendorf"};
      |          ^~~
/tmp/check_postal.cpp:5:1: note: ‘std::set’ is defined in header ‘<set>’; this is probably fixable by adding ‘#include <set>’
    4 | #include <osmium/io/pbf_input.hpp>
  +++ |+#include <set>
    5 | #include <osmium/osm/way.hpp>
/tmp/check_postal.cpp:7:25: error: expected primary-expression before ‘>’ token
    7 |     std::set<std::string> targets = {"Kleines Dorf", "Moorkolonie Neudorf", "Moorkolonie Platendorf"};
      |                         ^
/tmp/check_postal.cpp:7:27: error: ‘targets’ was not declared in this scope
    7 |     std::set<std::string> targets = {"Kleines Dorf", "Moorkolonie Neudorf", "Moorkolonie Platendorf"};
      |                           ^~~~~~~


ssh node3 "sed -i '1i#include <set>' /tmp/check_postal.cpp && g++ -O2 -std=c++17 -o /tmp/check_postal /tmp/check_postal.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_postal ~/pbf/germany.osm.pbf" 2>&1

Fix and run postal check


Way 25420259: name='Kleines Dorf' boundary='(none)' postal_code='(none)' admin_level='(none)' nodes=8 closed=no
  name=Kleines Dorf
Way 25555888: name='Kleines Dorf' boundary='(none)' postal_code='(none)' admin_level='(none)' nodes=19 closed=no
  name=Kleines Dorf
Way 32459353: name='Kleines Dorf' boundary='(none)' postal_code='(none)' admin_level='(none)' nodes=4 closed=no
  name=Kleines Dorf
Way 32459387: name='Kleines Dorf' boundary='(none)' postal_code='(none)' admin_level='(none)' nodes=17 closed=no
  name=Kleines Dorf
Way 62069633: name='Kleines Dorf' boundary='(none)' postal_code='(none)' admin_level='(none)' nodes=4 closed=no
  name=Kleines Dorf
Way 62069634: name='Kleines Dorf' boundary='(none)' postal_code='(none)' admin_level='(none)' nodes=2 closed=no
  name=Kleines Dorf
Way 62069646: name='Kleines Dorf' boundary='(none)' postal_code='(none)' admin_level='(none)' nodes=9 closed=no
  name=Kleines Dorf
Way 196386932: name='Kleines Dorf' boundary='(none)' postal_code='(none)' admin_level='(none)' nodes=4 closed=no
  name=Kleines Dorf
Way 380533770: name='Kleines Dorf' boundary='administrative' postal_code='(none)' admin_level='11' nodes=7 closed=yes
  boundary=administrative
  admin_level=11
  name=Kleines Dorf
Way 467193284: name='Moorkolonie Platendorf' boundary='administrative' postal_code='(none)' admin_level='11' nodes=5 closed=yes
  boundary=administrative
  admin_level=11
  name=Moorkolonie Platendorf
Way 467193286: name='Moorkolonie Neudorf' boundary='administrative' postal_code='(none)' admin_level='11' nodes=5 closed=yes
  boundary=administrative
  admin_level=11
  name=Moorkolonie Neudorf
Way 680426941: name='Kleines Dorf' boundary='(none)' postal_code='(none)' admin_level='(none)' nodes=6 closed=no
  name=Kleines Dorf
Way 680426943: name='Kleines Dorf' boundary='(none)' postal_code='(none)' admin_level='(none)' nodes=2 closed=no
  name=Kleines Dorf
Way 680601041: name='Kleines Dorf' boundary='(none)' postal_code='(none)' admin_level='(none)' nodes=2 closed=no
  name=Kleines Dorf
Way 680601042: name='Kleines Dorf' boundary='(none)' postal_code='(none)' admin_level='(none)' nodes=2 closed=no
  name=Kleines Dorf
Way 817502961: name='Kleines Dorf' boundary='(none)' postal_code='(none)' admin_level='(none)' nodes=6 closed=no
  name=Kleines Dorf
Way 956484545: name='Kleines Dorf' boundary='(none)' postal_code='(none)' admin_level='(none)' nodes=3 closed=no
  name=Kleines Dorf
Way 1195274944: name='Kleines Dorf' boundary='(none)' postal_code='(none)' admin_level='(none)' nodes=6 closed=no
  name=Kleines Dorf
Way 1434481105: name='Kleines Dorf' boundary='(none)' postal_code='(none)' admin_level='(none)' nodes=5 closed=no
  name=Kleines Dorf
Way 1434481107: name='Kleines Dorf' boundary='(none)' postal_code='(none)' admin_level='(none)' nodes=4 closed=no
  name=Kleines Dorf


All three are `boundary=administrative, admin_level=11` closed ways. But they're level **11** (our code uses 11 for postal codes), while they're actually `boundary=administrative` (not `postal_code`).

The issue is: osmium's assembler checks `admin_level` values 2-10 for admin boundaries. Level 11 is not a standard admin level — we use it internally for postal codes. These ways have `admin_level=11` which our RelationCollector filters out (`if (admin_level < 2 || admin_level > 10) return;`). But osmium processes them as areas and our BuildHandler::area() also filters level 2-10 for admin.

So osmium shouldn't create these either! The sequential build shouldn't have them. Let me double check.


ssh node3 "/tmp/check_extra /tmp/de-sequential-final 2>/dev/null" 2>&1 | grep -i "kleines\|moorkolonie"

Check if sequential has these level 11 polygons


(Bash completed with no output)


The sequential build does NOT have them. They appear in our parallel build because our closed-way scanner accepts `admin_level=11` as a valid admin boundary. The fix: in the closed-way scanner, only accept admin levels 2-10 (matching the BuildHandler filter).


  2239→                                                }
  2240→                                                if (al >= 2 && al <= 11 && (kMaxAdminLevel == 0 || al <= kMaxAdminLevel)) {
  2241→                                                    const char* aname = way.tags()["name"];

                                                }
                                                if (al >= 2 && al <= 11 && (kMaxAdminLevel == 0 || al <= kMaxAdminLevel)) {
                                                    const char* aname = way.tags()["name"];


Edit

if (al >= 2 && al <= 11 && (kMaxAdminLevel == 0 || al <= kMaxAdminLevel)) {

                                                int max_al = is_postal ? 11 : 10;
                                                if (al >= 2 && al <= max_al && (kMaxAdminLevel == 0 || al <= kMaxAdminLevel)) {