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

Build and run with gap closing


make[2]: *** [CMakeFiles/build-index.dir/build.make:79: CMakeFiles/build-index.dir/src/build_index.cpp.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:87: CMakeFiles/build-index.dir/all] Error 2
make: *** [Makefile:91: all] Error 2
  Assembling admin polygons in parallel (40080 relations, 112858 way geometries)...
  Parallel admin assembly complete: 43194 polygon rings from 40080 relations.
Done reading:
  43194 admin polygon rings
Waiting for admin polygon S2 covering to complete...
Admin polygon S2 covering complete (6103 cells).
admin index: 6103 cells, 43194 polygons
Done.


Build failed but the old binary ran. Let me check the error.


ssh node3 "cd /tmp/traccar-geocoder/build && make 2>&1 | grep error | head -5" 2>&1

Check compilation error


/tmp/traccar-geocoder/builder/src/build_index.cpp:525:28: error: ‘outer_ways’ was not declared in this scope
/tmp/traccar-geocoder/builder/src/build_index.cpp:530:27: error: ‘WayRef’ was not declared in this scope
/tmp/traccar-geocoder/builder/src/build_index.cpp:530:33: error: template argument 1 is invalid
/tmp/traccar-geocoder/builder/src/build_index.cpp:530:33: error: template argument 2 is invalid
/tmp/traccar-geocoder/builder/src/build_index.cpp:545:37: error: invalid types ‘const int[std::tuple_element<0, const std::pair<long unsigned int, bool> >::type {aka const long unsigned int}]’ for array subscript


I still have the `WayRef` struct referenced but didn't include it. Let me check the code structure.


525:    std::vector<bool> used(outer_ways.size(), false);
530:        const std::vector<WayRef>& ways;
562:    BacktrackState state{outer_ways, coord_adj, used, coord_key};
564:    for (size_t start_idx = 0; start_idx < outer_ways.size(); start_idx++) {
567:        const auto* sg = outer_ways[start_idx].geom;
584:                const auto& coords = outer_ways[wi].geom->coords;
604:    for (size_t start_idx = 0; start_idx < outer_ways.size(); start_idx++) {
608:        const auto* sg = outer_ways[start_idx].geom;
625:            for (size_t ci = 0; ci < outer_ways.size(); ci++) {
627:                const auto* cg = outer_ways[ci].geom;
645:            const auto& coords = outer_ways[best_idx].geom->coords;

[Showing results with pagination = limit: 20, offset: 0]


The WayRef struct and outer_ways vector were in the section I replaced. I need to add them back before the graph building.


   468→static std::vector<std::vector<std::pair<double,double>>> assemble_outer_rings(
   469→    const std::vector<std::pair<int64_t, std::string>>& members,
   470→    const std::unordered_map<int64_t, ParsedData::WayGeometry>& way_geoms)
   471→{
   472→    // Segment-level ring assembly (matching osmium's approach):
   473→    // 1. Decompose all outer ways into segments (consecutive node pairs)
   474→    // 2. Build graph: coordinate → set of adjacent coordinates
   475→    // 3. Trace closed rings through the graph
   476→
   477→    // Coordinate key matching osmium's Location (int32_t nanodegrees)
   478→    auto coord_key = [](double lat, double lng) -> int64_t {
   479→        int32_t ilat = static_cast<int32_t>(lat * 1e7 + (lat >= 0 ? 0.5 : -0.5));
   480→        int32_t ilng = static_cast<int32_t>(lng * 1e7 + (lng >= 0 ? 0.5 : -0.5));
   481→        return (static_cast<int64_t>(ilat) << 32) | static_cast<uint32_t>(ilng);
   482→    };
   483→
   484→    // Collect all node coordinates from outer ways
   485→    // Each segment is an edge in the graph: (coord_key_A, coord_key_B, lat_B, lng_B)
   486→    struct Edge {
   487→        int64_t target_key;
   488→        double target_lat, target_lng;
   489→    };
   490→    std::unordered_map<int64_t, std::vector<Edge>> graph;
   491→    // Track all edges for "used" marking: edge = pair of coord keys (smaller first)
   492→    struct EdgeId {
   493→        int64_t a, b;
   494→        bool operator==(const EdgeId& o) const { return a == o.a && b == o.b; }
   495→    };
   496→    struct EdgeIdHash {
   497→        size_t operator()(const EdgeId& e) const {
   498→            return std::hash<int64_t>()(e.a) ^ (std::hash<int64_t>()(e.b) << 32);
   499→        }
   500→    };
   501→    // Count of available edges between each pair of nodes
   502→    std::unordered_map<EdgeId, int, EdgeIdHash> edge_count;
   503→
   504→    auto make_edge_id = [](int64_t a, int64_t b) -> EdgeId {
   505→        return a <= b ? EdgeId{a, b} : EdgeId{b, a};
   506→    };
   507→
   508→    for (const auto& [way_id, role] : members) {
   509→        if (role != "outer" && !role.empty()) continue;
   510→        auto it = way_geoms.find(way_id);
   511→        if (it == way_geoms.end() || it->second.coords.size() < 2) continue;
   512→
   513→        const auto& coords = it->second.coords;
   514→        for (size_t i = 0; i + 1 < coords.size(); i++) {
   515→            int64_t k1 = coord_key(coords[i].first, coords[i].second);
   516→            int64_t k2 = coord_key(coords[i+1].first, coords[i+1].second);
   517→            if (k1 == k2) continue; // skip zero-length segments
   518→
   519→            graph[k1].push_back({k2, coords[i+1].first, coords[i+1].second});
   520→            graph[k2].push_back({k1, coords[i].first, coords[i].second});
   521→            edge_count[make_edge_id(k1, k2)]++;
   522→        }
   523→    }
   524→
   525→    std::vector<bool> used(outer_ways.size(), false);
   526→    std::vector<std::vector<std::pair<double,double>>> rings;
   527→

static std::vector<std::vector<std::pair<double,double>>> assemble_outer_rings(
    const std::vector<std::pair<int64_t, std::string>>& members,
    const std::unordered_map<int64_t, ParsedData::WayGeometry>& way_geoms)
{
    // Segment-level ring assembly (matching osmium's approach):
    // 1. Decompose all outer ways into segments (consecutive node pairs)
    // 2. Build graph: coordinate → set of adjacent coordinates
    // 3. Trace closed rings through the graph

    // Coordinate key matching osmium's Location (int32_t nanodegrees)
    auto coord_key = [](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);
    };

    // Collect all node coordinates from outer ways
    // Each segment is an edge in the graph: (coord_key_A, coord_key_B, lat_B, lng_B)
    struct Edge {
        int64_t target_key;
        double target_lat, target_lng;
    };
    std::unordered_map<int64_t, std::vector<Edge>> graph;
    // Track all edges for "used" marking: edge = pair of coord keys (smaller first)
    struct EdgeId {
        int64_t a, b;
        bool operator==(const EdgeId& o) const { return a == o.a && b == o.b; }
    };
    struct EdgeIdHash {
        size_t operator()(const EdgeId& e) const {
            return std::hash<int64_t>()(e.a) ^ (std::hash<int64_t>()(e.b) << 32);
        }
    };
    // Count of available edges between each pair of nodes
    std::unordered_map<EdgeId, int, EdgeIdHash> edge_count;

    auto make_edge_id = [](int64_t a, int64_t b) -> EdgeId {
        return a <= b ? EdgeId{a, b} : EdgeId{b, a};
    };

    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() || it->second.coords.size() < 2) continue;

        const auto& coords = it->second.coords;
        for (size_t i = 0; i + 1 < coords.size(); i++) {
            int64_t k1 = coord_key(coords[i].first, coords[i].second);
            int64_t k2 = coord_key(coords[i+1].first, coords[i+1].second);
            if (k1 == k2) continue; // skip zero-length segments

            graph[k1].push_back({k2, coords[i+1].first, coords[i+1].second});
            graph[k2].push_back({k1, coords[i].first, coords[i].second});
            edge_count[make_edge_id(k1, k2)]++;
        }
    }

    std::vector<bool> used(outer_ways.size(), false);
    std::vector<std::vector<std::pair<double,double>>> rings;


I see — the code still has the segment graph building but then tries to use `outer_ways` which was removed. Let me rewrite this properly — use the whole-way approach with coordinate matching (which worked at 43,192) plus gap closing.


Edit

    // Segment-level ring assembly (matching osmium's approach):
    // 1. Decompose all outer ways into segments (consecutive node pairs)
    // 2. Build graph: coordinate → set of adjacent coordinates
    // 3. Trace closed rings through the graph

    // Coordinate key matching osmium's Location (int32_t nanodegrees)
    auto coord_key = [](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);
    };

    // Collect all node coordinates from outer ways
    // Each segment is an edge in the graph: (coord_key_A, coord_key_B, lat_B, lng_B)
    struct Edge {
        int64_t target_key;
        double target_lat, target_lng;
    };
    std::unordered_map<int64_t, std::vector<Edge>> graph;
    // Track all edges for "used" marking: edge = pair of coord keys (smaller first)
    struct EdgeId {
        int64_t a, b;
        bool operator==(const EdgeId& o) const { return a == o.a && b == o.b; }
    };
    struct EdgeIdHash {
        size_t operator()(const EdgeId& e) const {
            return std::hash<int64_t>()(e.a) ^ (std::hash<int64_t>()(e.b) << 32);
        }
    };
    // Count of available edges between each pair of nodes
    std::unordered_map<EdgeId, int, EdgeIdHash> edge_count;

    auto make_edge_id = [](int64_t a, int64_t b) -> EdgeId {
        return a <= b ? EdgeId{a, b} : EdgeId{b, a};
    };

    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() || it->second.coords.size() < 2) continue;

        const auto& coords = it->second.coords;
        for (size_t i = 0; i + 1 < coords.size(); i++) {
            int64_t k1 = coord_key(coords[i].first, coords[i].second);
            int64_t k2 = coord_key(coords[i+1].first, coords[i+1].second);
            if (k1 == k2) continue; // skip zero-length segments

            graph[k1].push_back({k2, coords[i+1].first, coords[i+1].second});
            graph[k2].push_back({k1, coords[i].first, coords[i].second});
            edge_count[make_edge_id(k1, k2)]++;
        }
    }

    std::vector<bool> used(outer_ways.size(), false);

    // Coordinate-based ring assembly with backtracking and gap closing
    struct WayRef {
        int64_t way_id;
        const ParsedData::WayGeometry* geom;
    };
    std::vector<WayRef> outer_ways;
    for (const auto& [way_id, role] : members) {
        if (role == "outer" || role.empty()) {
            auto it = way_geoms.find(way_id);
            if (it != way_geoms.end() && !it->second.coords.empty()) {
                outer_ways.push_back({way_id, &it->second});
            }
        }
    }

    // Coordinate key matching osmium's Location (int32_t nanodegrees)
    auto coord_key = [](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);
    };

    // Build adjacency by coordinate endpoints
    std::unordered_map<int64_t, std::vector<std::pair<size_t, bool>>> coord_adj;
    for (size_t i = 0; i < outer_ways.size(); i++) {
        const auto* g = outer_ways[i].geom;
        auto& first = g->coords.front();
        auto& last = g->coords.back();
        coord_adj[coord_key(first.first, first.second)].push_back({i, false});
        coord_adj[coord_key(last.first, last.second)].push_back({i, true});
    }

    std::vector<bool> used(outer_ways.size(), false);