Now I need to update uses of `ring_has_duplicate_coords` since the old version took a function parameter, but the new one uses the built-in `coord_key`:


341:// segments_intersect, ring_has_self_intersection, ring_has_duplicate_coords are in geometry.h
2413:                                        if (!ring_has_duplicate_coords(ring, ck)) {


  2400→                                // If outer-only assembly failed, retry with all ways.
  2401→                                // Osmium ignores roles during assembly (check_roles=false) —
  2402→                                // inner ways sometimes form the boundary or bridge gaps.
  2403→                                // Filter retry results: discard rings with duplicate coords
  2404→                                // (figure-8 shapes from merging holes with outer boundary).
  2405→                                if (rings.empty()) {
  2406→                                    auto retry = assemble_outer_rings(rel.members, data.way_geometries, true);
  2407→                                    auto ck = [](double lat, double lng) -> int64_t {
  2408→                                        int32_t ilat = static_cast<int32_t>(lat * 1e7 + (lat >= 0 ? 0.5 : -0.5));
  2409→                                        int32_t ilng = static_cast<int32_t>(lng * 1e7 + (lng >= 0 ? 0.5 : -0.5));
  2410→                                        return (static_cast<int64_t>(ilat) << 32) | static_cast<uint32_t>(ilng);
  2411→                                    };
  2412→                                    for (auto& ring : retry) {
  2413→                                        if (!ring_has_duplicate_coords(ring, ck)) {
  2414→                                            rings.push_back(std::move(ring));
  2415→                                        }
  2416→                                    }
  2417→                                }
  2418→
  2419→                                // Diagnostic: log relations with 0 rings produced
  2420→                                if (rings.empty() && !rel.members.empty()) {
  2421→                                    int total_ways = 0, found = 0, missing = 0;
  2422→                                    for (const auto& [way_id, role] : rel.members) {
  2423→                                        total_ways++;
  2424→                                        auto it = data.way_geometries.find(way_id);
  2425→                                        if (it != data.way_geometries.end() && !it->second.coords.empty()) {
  2426→                                            found++;
  2427→                                        } else {
  2428→                                            missing++;
  2429→                                        }

                                // If outer-only assembly failed, retry with all ways.
                                // Osmium ignores roles during assembly (check_roles=false) —
                                // inner ways sometimes form the boundary or bridge gaps.
                                // Filter retry results: discard rings with duplicate coords
                                // (figure-8 shapes from merging holes with outer boundary).
                                if (rings.empty()) {
                                    auto retry = assemble_outer_rings(rel.members, data.way_geometries, true);
                                    auto ck = [](double lat, double lng) -> int64_t {
                                        int32_t ilat = static_cast<int32_t>(lat * 1e7 + (lat >= 0 ? 0.5 : -0.5));
                                        int32_t ilng = static_cast<int32_t>(lng * 1e7 + (lng >= 0 ? 0.5 : -0.5));
                                        return (static_cast<int64_t>(ilat) << 32) | static_cast<uint32_t>(ilng);
                                    };
                                    for (auto& ring : retry) {
                                        if (!ring_has_duplicate_coords(ring, ck)) {
                                            rings.push_back(std::move(ring));
                                        }
                                    }
                                }

                                // Diagnostic: log relations with 0 rings produced
                                if (rings.empty() && !rel.members.empty()) {
                                    int total_ways = 0, found = 0, missing = 0;
                                    for (const auto& [way_id, role] : rel.members) {
                                        total_ways++;
                                        auto it = data.way_geometries.find(way_id);
                                        if (it != data.way_geometries.end() && !it->second.coords.empty()) {
                                            found++;
                                        } else {
                                            missing++;
                                        }


Edit

                                    auto retry = assemble_outer_rings(rel.members, data.way_geometries, true);
                                    auto ck = [](double lat, double lng) -> int64_t {
                                        int32_t ilat = static_cast<int32_t>(lat * 1e7 + (lat >= 0 ? 0.5 : -0.5));
                                        int32_t ilng = static_cast<int32_t>(lng * 1e7 + (lng >= 0 ? 0.5 : -0.5));
                                        return (static_cast<int64_t>(ilat) << 32) | static_cast<uint32_t>(ilng);
                                    };
                                    for (auto& ring : retry) {
                                        if (!ring_has_duplicate_coords(ring, ck)) {

                                    auto retry = assemble_outer_rings(rel.members, data.way_geometries, true);
                                    for (auto& ring : retry) {
                                        if (!ring_has_duplicate_coords(ring)) {