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" 2>&1

Build segment-level ring assembler


[ 50%] Building CXX object CMakeFiles/build-index.dir/src/build_index.cpp.o
[100%] Linking CXX executable build-index
[100%] Built target build-index


ssh node3 "rm -rf /tmp/de-parallel-seg && mkdir -p /tmp/de-parallel-seg && /tmp/traccar-geocoder/build/build-index /tmp/de-parallel-seg ~/pbf/germany.osm.pbf --parallel-admin 2>&1 | grep -E 'polygon|Done|Parallel admin'" 2>&1

Run with segment-level ring assembly


Assembling admin polygons in parallel (40080 relations, 112858 way geometries)...


ssh node3 "ps aux | grep 'build-index /tmp/de-parallel-seg' | grep -v grep | awk '{print \$3\"% CPU\", \$6/1024\"MB RSS\", \$10\" elapsed\"}'" 2>&1

Check if segment build is stuck


(Bash completed with no output)


ssh node3 "ps aux | grep build-index | grep -v grep" 2>&1

Check build process


Exit code 1


Process already finished. Let me check the output.


ssh node3 "ls /tmp/de-parallel-seg/ 2>/dev/null | wc -l; ls -la /tmp/de-parallel-seg/*.bin 2>/dev/null | head -5" 2>&1

Check output files


No output — crashed or DFS took too long. The backtracking is exponential for large relations. Let me fix this with a proper algorithm — Hierholzer's algorithm for finding Euler circuits, which runs in O(E) time.


Edit

    // Also store coordinate values for each key
    std::unordered_map<int64_t, std::pair<double,double>> key_to_coord;
    for (const auto& [way_id, role] : members) {
        if (role != "outer" && !role.empty()) continue;
        auto it = way_geoms.find(way_id);
        if (it == way_geoms.end()) continue;
        for (const auto& [lat, lng] : it->second.coords) {
            int64_t k = coord_key(lat, lng);
            key_to_coord[k] = {lat, lng};
        }
    }

    std::vector<std::vector<std::pair<double,double>>> rings;

    // DFS ring tracer with backtracking
    std::function<bool(int64_t, int64_t, std::vector<int64_t>&)> trace_ring;
    trace_ring = [&](int64_t start_key, int64_t current_key,
                     std::vector<int64_t>& path) -> bool {
        if (path.size() > 1 && current_key == start_key) return true;
        if (path.size() > 50000) return false; // safety limit

        auto git = graph.find(current_key);
        if (git == graph.end()) return false;

        for (const auto& edge : git->second) {
            auto eid = make_edge_id(current_key, edge.target_key);
            auto eit = edge_count.find(eid);
            if (eit == edge_count.end() || eit->second <= 0) continue;

            eit->second--;
            path.push_back(edge.target_key);

            if (trace_ring(start_key, edge.target_key, path)) return true;

            path.pop_back();
            eit->second++;
        }
        return false;
    };

    // Extract rings: start from any node with available edges
    bool found_any = true;
    while (found_any) {
        found_any = false;
        for (auto& [node_key, edges] : graph) {
            // Check for available edges
            bool has_edge = false;
            for (const auto& edge : edges) {
                auto eid = make_edge_id(node_key, edge.target_key);
                if (edge_count[eid] > 0) { has_edge = true; break; }
            }
            if (!has_edge) continue;

            std::vector<int64_t> path = {node_key};
            if (trace_ring(node_key, node_key, path)) {
                // Convert path of coord keys to actual coordinates
                std::vector<std::pair<double,double>> ring;
                for (auto k : path) {
                    auto cit = key_to_coord.find(k);
                    if (cit != key_to_coord.end()) {
                        ring.push_back(cit->second);
                    }
                }
                if (ring.size() >= 4) {
                    rings.push_back(std::move(ring));
                    found_any = true;
                    break; // restart outer loop to find more rings
                }
            }
        }
    }

    return rings;
}

    // Store coordinate values for each key
    std::unordered_map<int64_t, std::pair<double,double>> key_to_coord;
    for (const auto& [way_id, role] : members) {
        if (role != "outer" && !role.empty()) continue;
        auto it = way_geoms.find(way_id);
        if (it == way_geoms.end()) continue;
        for (const auto& [lat, lng] : it->second.coords) {
            key_to_coord[coord_key(lat, lng)] = {lat, lng};
        }
    }

    // Track edge iterator position per node for O(E) traversal
    std::unordered_map<int64_t, size_t> edge_pos;
    for (auto& [k, _] : graph) edge_pos[k] = 0;

    std::vector<std::vector<std::pair<double,double>>> rings;

    // Find all closed rings using modified Hierholzer's algorithm
    // Process each connected component
    for (auto& [start_key, _] : graph) {
        // Check if this node has any unused edges
        bool has_edge = false;
        for (const auto& edge : graph[start_key]) {
            if (edge_count[make_edge_id(start_key, edge.target_key)] > 0) {
                has_edge = true; break;
            }
        }
        if (!has_edge) continue;

        // Hierholzer's: find an Euler circuit starting from start_key
        std::vector<int64_t> stack = {start_key};
        std::vector<int64_t> circuit;

        while (!stack.empty()) {
            int64_t v = stack.back();
            auto& edges = graph[v];
            bool found = false;

            // Find next unused edge from v
            while (edge_pos[v] < edges.size()) {
                auto& edge = edges[edge_pos[v]];
                auto eid = make_edge_id(v, edge.target_key);
                auto eit = edge_count.find(eid);
                if (eit != edge_count.end() && eit->second > 0) {
                    eit->second--;
                    stack.push_back(edge.target_key);
                    edge_pos[v]++;
                    found = true;
                    break;
                }
                edge_pos[v]++;
            }

            if (!found) {
                circuit.push_back(v);
                stack.pop_back();
            }
        }

        // The circuit may contain multiple loops joined at shared nodes.
        // Split into individual closed rings at repeated nodes.
        if (circuit.size() < 4) continue;

        // Find closed sub-circuits: scan for repeated coordinate keys
        std::unordered_map<int64_t, size_t> last_seen;
        for (size_t i = 0; i < circuit.size(); i++) {
            auto it2 = last_seen.find(circuit[i]);
            if (it2 != last_seen.end()) {
                // Found a closed loop from last_seen to i
                size_t ring_start = it2->second;
                std::vector<std::pair<double,double>> ring;
                for (size_t j = ring_start; j <= i; j++) {
                    auto cit = key_to_coord.find(circuit[j]);
                    if (cit != key_to_coord.end()) {
                        ring.push_back(cit->second);
                    }
                }
                if (ring.size() >= 4) {
                    rings.push_back(std::move(ring));
                }
                // Mark these positions as consumed by setting them to -1
                for (size_t j = ring_start; j < i; j++) {
                    last_seen.erase(circuit[j]);
                    circuit[j] = -1;
                }
                // Keep circuit[i] in last_seen for potential larger rings
            }
            last_seen[circuit[i]] = i;
        }
    }

    return rings;
}