Now I need to add the detection of closed admin ways in the worker loop. Let me find where the way processing happens (after highway/address processing).


  2188→                                                    local.street_nodes.push_back({
  2189→                                                        static_cast<float>(loc.lat()),
  2190→                                                        static_cast<float>(loc.lon())
  2191→                                                    });
  2192→                                                }
  2193→
  2194→                                                WayHeader header{};
  2195→                                                header.node_offset = node_offset;
  2196→                                                header.node_count = static_cast<uint8_t>(std::min(wnodes.size(), size_t(255)));
  2197→                                                header.name_id = 0; // placeholder, intern later
  2198→                                                local.ways.push_back(header);
  2199→                                                local.way_strings.push_back(name);
  2200→                                                local.deferred_ways.push_back({way_id, node_offset, header.node_count});
  2201→                                                local.way_count++;
  2202→                                            }
  2203→                                        }
  2204→                                    }
  2205→
  2206→                                    // Store way geometry only for admin boundary member ways
  2207→                                    if (parallel_admin && !wnodes.empty() && admin_way_ids.count(way.id())) {
  2208→                                        std::vector<std::pair<double,double>> geom;
  2209→                                        for (const auto& nr : wnodes) {
  2210→                                            try {
  2211→                                                auto loc = index.get(nr.positive_ref());
  2212→                                                if (loc.valid()) geom.push_back({loc.lat(), loc.lon()});
  2213→                                            } catch (...) {}
  2214→                                        }
  2215→                                        if (!geom.empty()) {
  2216→                                            int64_t first_nid = wnodes.front().positive_ref();
  2217→                                            int64_t last_nid = wnodes.back().positive_ref();

                                                    local.street_nodes.push_back({
                                                        static_cast<float>(loc.lat()),
                                                        static_cast<float>(loc.lon())
                                                    });
                                                }

                                                WayHeader header{};
                                                header.node_offset = node_offset;
                                                header.node_count = static_cast<uint8_t>(std::min(wnodes.size(), size_t(255)));
                                                header.name_id = 0; // placeholder, intern later
                                                local.ways.push_back(header);
                                                local.way_strings.push_back(name);
                                                local.deferred_ways.push_back({way_id, node_offset, header.node_count});
                                                local.way_count++;
                                            }
                                        }
                                    }

                                    // Store way geometry only for admin boundary member ways
                                    if (parallel_admin && !wnodes.empty() && admin_way_ids.count(way.id())) {
                                        std::vector<std::pair<double,double>> geom;
                                        for (const auto& nr : wnodes) {
                                            try {
                                                auto loc = index.get(nr.positive_ref());
                                                if (loc.valid()) geom.push_back({loc.lat(), loc.lon()});
                                            } catch (...) {}
                                        }
                                        if (!geom.empty()) {
                                            int64_t first_nid = wnodes.front().positive_ref();
                                            int64_t last_nid = wnodes.back().positive_ref();


  2218→                                            local.way_geoms.push_back({way.id(), std::move(geom), first_nid, last_nid});
  2219→                                        }
  2220→                                    }
  2221→                                }
  2222→                            }
  2223→
  2224→                            uint64_t done = blocks_done.fetch_add(1) + 1;
  2225→                            if (done % 1000 == 0) {
  2226→                                std::cerr << "  Processed " << done << " way blocks..." << std::endl;
  2227→                            }
  2228→                        }
  2229→                    });
  2230→                }
  2231→
  2232→                // Way reader (producer)
  2233→                {
  2234→                    while (auto buf = reader_ways.read()) {
  2235→                        {
  2236→                            std::unique_lock<std::mutex> lock(way_queue_mutex);
  2237→                            way_queue_cv.wait(lock, [&]{ return way_block_queue.size() < WAY_MAX_QUEUE; });
  2238→                            way_block_queue.push_back(std::move(buf));
  2239→                        }
  2240→                        way_queue_cv.notify_one();
  2241→                    }
  2242→                    reader_ways.close();

                                            local.way_geoms.push_back({way.id(), std::move(geom), first_nid, last_nid});
                                        }
                                    }
                                }
                            }

                            uint64_t done = blocks_done.fetch_add(1) + 1;
                            if (done % 1000 == 0) {
                                std::cerr << "  Processed " << done << " way blocks..." << std::endl;
                            }
                        }
                    });
                }

                // Way reader (producer)
                {
                    while (auto buf = reader_ways.read()) {
                        {
                            std::unique_lock<std::mutex> lock(way_queue_mutex);
                            way_queue_cv.wait(lock, [&]{ return way_block_queue.size() < WAY_MAX_QUEUE; });
                            way_block_queue.push_back(std::move(buf));
                        }
                        way_queue_cv.notify_one();
                    }
                    reader_ways.close();


I need to add the closed-way admin detection after the admin way geometry storage (line 2220), still inside the way processing loop. Let me add it.


Edit

                                    // Store way geometry only for admin boundary member ways
                                    if (parallel_admin && !wnodes.empty() && admin_way_ids.count(way.id())) {
                                        std::vector<std::pair<double,double>> geom;
                                        for (const auto& nr : wnodes) {
                                            try {
                                                auto loc = index.get(nr.positive_ref());
                                                if (loc.valid()) geom.push_back({loc.lat(), loc.lon()});
                                            } catch (...) {}
                                        }
                                        if (!geom.empty()) {
                                            int64_t first_nid = wnodes.front().positive_ref();
                                            int64_t last_nid = wnodes.back().positive_ref();
                                            local.way_geoms.push_back({way.id(), std::move(geom), first_nid, last_nid});
                                        }
                                    }

                                    // Store way geometry only for admin boundary member ways
                                    if (parallel_admin && !wnodes.empty() && admin_way_ids.count(way.id())) {
                                        std::vector<std::pair<double,double>> geom;
                                        for (const auto& nr : wnodes) {
                                            try {
                                                auto loc = index.get(nr.positive_ref());
                                                if (loc.valid()) geom.push_back({loc.lat(), loc.lon()});
                                            } catch (...) {}
                                        }
                                        if (!geom.empty()) {
                                            int64_t first_nid = wnodes.front().positive_ref();
                                            int64_t last_nid = wnodes.back().positive_ref();
                                            local.way_geoms.push_back({way.id(), std::move(geom), first_nid, last_nid});
                                        }
                                    }

                                    // Handle closed ways that are admin boundaries themselves
                                    // (not part of a multipolygon relation, but standalone polygons)
                                    if (parallel_admin) {
                                        const char* boundary = way.tags()["boundary"];
                                        if (boundary) {
                                            bool is_admin = (std::strcmp(boundary, "administrative") == 0);
                                            bool is_postal = (std::strcmp(boundary, "postal_code") == 0);
                                            if ((is_admin || is_postal) && wnodes.size() >= 4 &&
                                                wnodes.front().ref() == wnodes.back().ref()) {
                                                // Closed way forming an admin polygon
                                                uint8_t al = 0;
                                                if (is_admin) {
                                                    const char* level_str = way.tags()["admin_level"];
                                                    if (level_str) al = static_cast<uint8_t>(std::atoi(level_str));
                                                } else {
                                                    al = 11;
                                                }
                                                if (al >= 2 && al <= 11 && (kMaxAdminLevel == 0 || al <= kMaxAdminLevel)) {
                                                    const char* aname = way.tags()["name"];
                                                    if (!aname && is_postal) aname = way.tags()["postal_code"];
                                                    if (aname || is_postal) {
                                                        std::vector<std::pair<double,double>> verts;
                                                        bool all_valid = true;
                                                        for (const auto& nr : wnodes) {
                                                            try {
                                                                auto loc = index.get(nr.positive_ref());
                                                                if (loc.valid()) {
                                                                    verts.push_back({loc.lat(), loc.lon()});
                                                                } else { all_valid = false; break; }
                                                            } catch (...) { all_valid = false; break; }
                                                        }
                                                        if (all_valid && verts.size() >= 3) {
                                                            std::string cc;
                                                            if (al == 2) {
                                                                const char* iso = way.tags()["ISO3166-1:alpha2"];
                                                                if (iso) cc = iso;
                                                            }
                                                            local.closed_way_admins.push_back({
                                                                std::move(verts),
                                                                aname ? aname : "",
                                                                al, std::move(cc)
                                                            });
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }