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

Build with exact-bytes dedup


[ 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)...
  Deduplicated 3509 cross-relation duplicate rings.
  Parallel admin assembly complete: 39676 polygon rings from 40080 relations.
Done reading:
  39713 admin polygon rings
Waiting for admin polygon S2 covering to complete...
Admin polygon S2 covering complete (6103 cells).
Deduplicating...
admin index: 6103 cells, 39713 polygons
Done.


Still 3509. These are genuine duplicate rings — same exact coordinates produced by different relations. This means 3509 rings are created by multiple relations from shared ways. The problem is my "keep last" heuristic removes the wrong copy for most of them.

The correct approach: only deduplicate when both copies have the same name AND level. Rings from DIFFERENT relations with different names should NOT be deduped — they're legitimate overlapping admin areas.


Edit

                        // Mark duplicates for removal — keep the one from the relation
                        // with the larger total ring area (osmium assigns tiny shared rings
                        // to the relation they make smaller)
                        std::set<size_t> to_remove;
                        for (auto& [key, indices] : key_to_indices) {
                            if (indices.size() <= 1) continue;
                            // Keep only one copy — the last one (higher relation ID = later processing)
                            // This approximates osmium's assignment order
                            for (size_t k = 0; k + 1 < indices.size(); k++) {
                                to_remove.insert(indices[k]);
                            }
                        }

                        // Mark duplicates for removal — only dedup rings that have
                        // different name|level (from shared ways between relations).
                        // Keep the copy from the relation with MORE member ways
                        // (the "larger" relation, matching osmium's preference).
                        std::set<size_t> to_remove;
                        for (auto& [key, indices] : key_to_indices) {
                            if (indices.size() <= 1) continue;
                            // Check if these are from different relations (different name|level)
                            bool all_same = true;
                            for (size_t k = 1; k < indices.size(); k++) {
                                if (all_admin_results[indices[k]].name != all_admin_results[indices[0]].name ||
                                    all_admin_results[indices[k]].admin_level != all_admin_results[indices[0]].admin_level) {
                                    all_same = false;
                                    break;
                                }
                            }
                            if (all_same) continue; // Same relation produced duplicate rings — keep both

                            // Different relations — keep the last copy (higher relation ID)
                            for (size_t k = 0; k + 1 < indices.size(); k++) {
                                to_remove.insert(indices[k]);
                            }
                        }