The greedy trace won't work for complex topologies. Let me rewrite with proper DFS backtracking.


Edit

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

    // Trace closed rings by following the graph
    // For each unused edge, start tracing and try to return to the starting node
    auto trace_ring = [&](int64_t start_key, double start_lat, double start_lng)
        -> std::vector<std::pair<double,double>>
    {
        std::vector<std::pair<double,double>> ring;
        ring.push_back({start_lat, start_lng});
        int64_t current = start_key;

        while (true) {
            auto git = graph.find(current);
            if (git == graph.end()) break;

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

                // Use this edge
                eit->second--;
                ring.push_back({edge.target_lat, edge.target_lng});
                current = edge.target_key;
                found_next = true;

                // Check if we closed the ring
                if (current == start_key && ring.size() >= 4) {
                    return ring;
                }
                break;
            }
            if (!found_next) break;
        }

        // Failed to close — restore edges
        for (size_t i = 0; i + 1 < ring.size(); i++) {
            int64_t k1 = coord_key(ring[i].first, ring[i].second);
            int64_t k2 = coord_key(ring[i+1].first, ring[i+1].second);
            auto eid = make_edge_id(k1, k2);
            edge_count[eid]++;
        }
        return {};
    };

    // Find nodes with odd degree — these are endpoints of open chains
    // In a proper multipolygon, all nodes should have even degree
    // Nodes with degree 1 are ring starting points (or endpoints of ways)

    // Use Hierholzer-like approach: start from any node with available edges
    for (auto& [node_key, edges] : graph) {
        while (true) {
            // Check if this node has any available edge
            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) break;

            // Get coordinates for this node from any available edge's reverse
            double start_lat = 0, start_lng = 0;
            for (const auto& [other_key, other_edges] : graph) {
                for (const auto& e : other_edges) {
                    if (e.target_key == node_key) {
                        start_lat = e.target_lat;
                        start_lng = e.target_lng;
                        goto found_coords;
                    }
                }
            }
            found_coords:

            auto ring = trace_ring(node_key, start_lat, start_lng);
            if (!ring.empty()) {
                rings.push_back(std::move(ring));
            } else {
                break; // couldn't form a ring from this node
            }
        }
    }

    return rings;
}

    // 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;
}