示例#1
0
void output_pgsql_t::pgsql_out_way(osmium::Way const &way, taglist_t *tags,
                                   bool polygon, bool roads)
{
    if (polygon && way.is_closed()) {
        auto wkb = m_builder.get_wkb_polygon(way);
        if (!wkb.empty()) {
            expire.from_wkb(wkb.c_str(), way.id());
            if (m_enable_way_area) {
                char tmp[32];
                auto const area =
                    m_options.reproject_area
                        ? ewkb::parser_t(wkb).get_area<reprojection>(
                              m_options.projection.get())
                        : ewkb::parser_t(wkb)
                              .get_area<osmium::geom::IdentityProjection>();
                snprintf(tmp, sizeof(tmp), "%g", area);
                tags->push_override(tag_t("way_area", tmp));
            }
            m_tables[t_poly]->write_row(way.id(), *tags, wkb);
        }
    } else {
        double const split_at = m_options.projection->target_latlon() ? 1 : 100 * 1000;
        for (auto const &wkb : m_builder.get_wkb_line(way.nodes(), split_at)) {
            expire.from_wkb(wkb.c_str(), way.id());
            m_tables[t_line]->write_row(way.id(), *tags, wkb);
            if (roads) {
                m_tables[t_roads]->write_row(way.id(), *tags, wkb);
            }
        }

    }
}
示例#2
0
/*
COPY planet_osm (osm_id, name, place, landuse, leisure, "natural", man_made, waterway, highway, railway, amenity, tourism, learning, bu
ilding, bridge, layer, way) FROM stdin;
198497  Bedford Road    \N      \N      \N      \N      \N      \N      residential     \N      \N      \N      \N      \N      \N    \N       0102000020E610000004000000452BF702B342D5BF1C60E63BF8DF49406B9C4D470037D5BF5471E316F3DF4940DFA815A6EF35D5BF9AE95E27F5DF4940B41EB
E4C1421D5BF24D06053E7DF4940
212696  Oswald Road     \N      \N      \N      \N      \N      \N      minor   \N      \N      \N      \N      \N      \N      \N    0102000020E610000004000000467D923B6C22D5BFA359D93EE4DF4940B3976DA7AD11D5BF84BBB376DBDF4940997FF44D9A06D5BF4223D8B8FEDF49404D158C4AEA04D
5BF5BB39597FCDF4940
*/
int output_pgsql_t::pgsql_out_way(osmid_t id, taglist_t &outtags,
                                  const nodelist_t &nodes,
                                  int polygon, int roads)
{
     /* Split long ways after around 1 degree or 100km */
    double split_at;
    if (m_options.projection->target_latlon())
        split_at = 1;
    else
        split_at = 100 * 1000;

    char tmp[32];
    auto wkbs = builder.get_wkb_split(nodes, polygon, split_at);
    for (const auto& wkb: wkbs) {
        /* FIXME: there should be a better way to detect polygons */
        if (wkb.is_polygon()) {
            expire.from_nodes_poly(nodes, id);
            if ((wkb.area > 0.0) && m_enable_way_area) {
                snprintf(tmp, sizeof(tmp), "%g", wkb.area);
                outtags.push_override(tag_t("way_area", tmp));
            }
            m_tables[t_poly]->write_row(id, outtags, wkb.geom);
        } else {
            expire.from_nodes_line(nodes);
            m_tables[t_line]->write_row(id, outtags, wkb.geom);
            if (roads)
                m_tables[t_roads]->write_row(id, outtags, wkb.geom);
        }
    }

    return 0;
}
示例#3
0
/*
COPY planet_osm (osm_id, name, place, landuse, leisure, "natural", man_made, waterway, highway, railway, amenity, tourism, learning, bu
ilding, bridge, layer, way) FROM stdin;
198497  Bedford Road    \N      \N      \N      \N      \N      \N      residential     \N      \N      \N      \N      \N      \N    \N       0102000020E610000004000000452BF702B342D5BF1C60E63BF8DF49406B9C4D470037D5BF5471E316F3DF4940DFA815A6EF35D5BF9AE95E27F5DF4940B41EB
E4C1421D5BF24D06053E7DF4940
212696  Oswald Road     \N      \N      \N      \N      \N      \N      minor   \N      \N      \N      \N      \N      \N      \N    0102000020E610000004000000467D923B6C22D5BFA359D93EE4DF4940B3976DA7AD11D5BF84BBB376DBDF4940997FF44D9A06D5BF4223D8B8FEDF49404D158C4AEA04D
5BF5BB39597FCDF4940
*/
int output_pgsql_t::pgsql_out_way(osmid_t id, const taglist_t &tags, const nodelist_t &nodes, int exists)
{
    int polygon = 0, roads = 0;
    double split_at;

    /* If the flag says this object may exist already, delete it first */
    if (exists) {
        pgsql_delete_way_from_output(id);
        // TODO: this now only has an effect when called from the iterate_ways
        // call-back, so we need some alternative way to trigger this within
        // osmdata_t.
        const idlist_t rel_ids = m_mid->relations_using_way(id);
        for (idlist_t::const_iterator itr = rel_ids.begin();
             itr != rel_ids.end(); ++itr) {
            rels_pending_tracker->mark(*itr);
        }
    }

    taglist_t outtags;
    if (m_tagtransform->filter_way_tags(tags, &polygon, &roads, *m_export_list.get(),
                                        outtags))
        return 0;
    /* Split long ways after around 1 degree or 100km */
    if (m_options.projection->get_proj_id() == PROJ_LATLONG)
        split_at = 1;
    else
        split_at = 100 * 1000;

    tag_t *areatag = 0;
    auto wkbs = builder.get_wkb_split(nodes, polygon, split_at);
    for (const auto& wkb: wkbs) {
        /* FIXME: there should be a better way to detect polygons */
        if (wkb.is_polygon()) {
            expire->from_nodes_poly(nodes, id);
            if ((wkb.area > 0.0) && m_enable_way_area) {
                char tmp[32];
                snprintf(tmp, sizeof(tmp), "%g", wkb.area);
                if (!areatag) {
                    outtags.push_dedupe(tag_t("way_area", tmp));
                    areatag = outtags.find("way_area");
                } else
                    areatag->value = tmp;
            }
            m_tables[t_poly]->write_row(id, outtags, wkb.geom);
        } else {
            expire->from_nodes_line(nodes);
            m_tables[t_line]->write_row(id, outtags, wkb.geom);
            if (roads)
                m_tables[t_roads]->write_row(id, outtags, wkb.geom);
        }
    }

    return 0;
}
示例#4
0
/*
COPY planet_osm (osm_id, name, place, landuse, leisure, "natural", man_made, waterway, highway, railway, amenity, tourism, learning, bu
ilding, bridge, layer, way) FROM stdin;
198497  Bedford Road    \N      \N      \N      \N      \N      \N      residential     \N      \N      \N      \N      \N      \N    \N       0102000020E610000004000000452BF702B342D5BF1C60E63BF8DF49406B9C4D470037D5BF5471E316F3DF4940DFA815A6EF35D5BF9AE95E27F5DF4940B41EB
E4C1421D5BF24D06053E7DF4940
212696  Oswald Road     \N      \N      \N      \N      \N      \N      minor   \N      \N      \N      \N      \N      \N      \N    0102000020E610000004000000467D923B6C22D5BFA359D93EE4DF4940B3976DA7AD11D5BF84BBB376DBDF4940997FF44D9A06D5BF4223D8B8FEDF49404D158C4AEA04D
5BF5BB39597FCDF4940
*/
int output_pgsql_t::pgsql_out_way(osmid_t id, taglist_t &outtags,
                                  const nodelist_t &nodes,
                                  int polygon, int roads)
{
    /* Split long ways after around 1 degree or 100km */
    double split_at;
    if (m_options.projection->get_proj_id() == PROJ_LATLONG)
        split_at = 1;
    else
        split_at = 100 * 1000;

    tag_t *areatag = 0;
    auto wkbs = builder.get_wkb_split(nodes, polygon, split_at);
    for (const auto& wkb: wkbs) {
        /* FIXME: there should be a better way to detect polygons */
        if (wkb.is_polygon()) {
            expire->from_nodes_poly(nodes, id);
            if ((wkb.area > 0.0) && m_enable_way_area) {
                char tmp[32];
                snprintf(tmp, sizeof(tmp), "%g", wkb.area);
                if (!areatag) {
                    outtags.push_dedupe(tag_t("way_area", tmp));
                    areatag = outtags.find("way_area");
                } else
                    areatag->value = tmp;
            }
            m_tables[t_poly]->write_row(id, outtags, wkb.geom);
        } else {
            expire->from_nodes_line(nodes);
            m_tables[t_line]->write_row(id, outtags, wkb.geom);
            if (roads)
                m_tables[t_roads]->write_row(id, outtags, wkb.geom);
        }
    }

    return 0;
}
void place_tag_processor::process_tags(const taglist_t &tags)
{
    bool placeadmin = false;
    bool placehouse = false;
    bool placebuilding = false;
    const tag_t *place = 0;
    const tag_t *junction = 0;
    const tag_t *landuse = 0;
    bool isnamed = false;
    bool isinterpolation = false;
    const std::string *house_nr = 0;
    const std::string *conscr_nr = 0;
    const std::string *street_nr = 0;

    clear();
    src = &tags;

    for (const auto& item: tags) {
        if (boost::ends_with(item.key, "source")) {
            // ignore
        } else if (item.key == "name:prefix" ||
                   item.key == "name:botanical" ||
                   boost::ends_with(item.key, "wikidata")) {
            extratags.push_back(&item);
        } else if (item.key == "ref" ||
                   item.key == "int_ref" ||
                   item.key == "nat_ref" ||
                   item.key == "reg_ref" ||
                   item.key == "loc_ref" ||
                   item.key == "old_ref" ||
                   item.key == "iata" ||
                   item.key == "icao" ||
                   item.key == "operator" ||
                   item.key == "pcode" ||
                   boost::starts_with(item.key, "pcode:")) {
            names.push_back(&item);
        } else if (item.key == "name" ||
                   boost::starts_with(item.key, "name:") ||
                   item.key == "int_name" ||
                   boost::starts_with(item.key, "int_name:") ||
                   item.key == "nat_name" ||
                   boost::starts_with(item.key, "nat_name:") ||
                   item.key == "reg_name" ||
                   boost::starts_with(item.key, "reg_name:") ||
                   item.key == "loc_name" ||
                   boost::starts_with(item.key, "loc_name:") ||
                   item.key == "old_name" ||
                   boost::starts_with(item.key, "old_name:") ||
                   item.key == "alt_name" ||
                   boost::starts_with(item.key, "alt_name:") ||
                   boost::starts_with(item.key, "alt_name_") ||
                   item.key == "official_name" ||
                   boost::starts_with(item.key, "official_name:") ||
                   item.key == "place_name" ||
                   boost::starts_with(item.key, "place_name:") ||
                   item.key == "short_name" ||
                   boost::starts_with(item.key, "short_name:") ||
                   item.key == "brand") {
            names.push_back(&item);
            isnamed = true;
        } else if (item.key == "addr:housename") {
            names.push_back(&item);
            placehouse = true;
        } else if (item.key == "emergency") {
            if (item.value != "fire_hydrant" &&
                item.value != "yes" &&
                item.value != "no")
                places.push_back(item);
        } else if (item.key == "tourism" ||
                   item.key == "historic" ||
                   item.key == "military") {
            if (item.value != "no" && item.value != "yes")
                places.push_back(item);
        } else if (item.key == "natural") {
            if (item.value != "no" &&
                item.value != "yes" &&
                item.value != "coastline")
                places.push_back(item);
        } else if (item.key == "landuse") {
            if (item.value == "cemetry")
                places.push_back(item);
            else
                landuse = &item;
        } else if (item.key == "highway") {
            if (item.value == "footway") {
                auto *footway = tags.get("footway");
                if (footway == nullptr || *footway != "sidewalk")
                    places.push_back(item);
            } else if (item.value != "no" &&
                item.value != "turning_circle" &&
                item.value != "mini_roundabout" &&
                item.value != "noexit" &&
                item.value != "crossing")
                places.push_back(item);
        } else if (item.key == "railway") {
            if (item.value != "level_crossing" &&
                item.value != "no")
                places.push_back(item);
        } else if (item.key == "man_made") {
            if (item.value != "survey_point" &&
                item.value != "cutline")
                places.push_back(item);
        } else if (item.key == "aerialway") {
            if (item.value != "pylon" &&
                item.value != "no")
                places.push_back(item);
        } else if (item.key == "boundary") {
            if (item.value == "administrative")
                placeadmin = true;
            places.push_back(item);
        } else if (item.key == "aeroway" ||
                   item.key == "amenity" ||
                   item.key == "boundary" ||
                   item.key == "bridge" ||
                   item.key == "craft" ||
                   item.key == "leisure" ||
                   item.key == "office" ||
                   item.key == "shop" ||
                   item.key == "tunnel" ||
                   item.key == "mountain_pass") {
            if (item.value != "no")
            {
                places.push_back(item);
            }
        } else if (item.key == "waterway") {
            if (item.value != "riverbank")
                places.push_back(item);
        } else if (item.key == "place") {
            place = &item;
        } else if (item.key == "junction") {
            junction = &item;
        } else if (item.key == "addr:interpolation") {
            housenumber.clear();
            escape(item.value, housenumber);
            isinterpolation = true;
        } else if (item.key == "addr:housenumber") {
            house_nr = &item.value;
            placehouse = true;
        } else if (item.key == "addr:conscriptionnumber") {
            conscr_nr = &item.value;
            placehouse = true;
        } else if (item.key == "addr:streetnumber") {
            street_nr = &item.value;
            placehouse = true;
        } else if (item.key == "addr:street") {
            street = &item.value;
        } else if (item.key == "addr:place") {
            addr_place = &item.value;
        } else if (item.key == "postal_code" ||
                   item.key == "postcode" ||
                   item.key == "addr:postcode" ||
                   item.key == "tiger:zip_left" ||
                   item.key == "tiger:zip_right") {
            if (!postcode)
                postcode = &item.value;
        } else if (item.key == "country_code" ||
                   item.key == "ISO3166-1" ||
                   item.key == "is_in:country_code" ||
                   item.key == "addr:country" ||
                   item.key == "addr:country_code") {
            if (item.value.length() == 2)
                countrycode = &item.value;
        } else if (boost::starts_with(item.key, "addr:") ||
                   item.key == "is_in" ||
                   boost::starts_with(item.key, "is_in:") ||
                   item.key == "tiger:county") {
            address.push_back(&item);
        } else if (item.key == "admin_level") {
            admin_level = atoi(item.value.c_str());
            if (admin_level <= 0 || admin_level > 100)
                admin_level = 100;
        } else if (item.key == "tracktype" ||
                   item.key == "traffic_calming" ||
                   item.key == "service" ||
                   item.key == "cuisine" ||
                   item.key == "capital" ||
                   item.key == "dispensing" ||
                   item.key == "religion" ||
                   item.key == "denomination" ||
                   item.key == "sport" ||
                   item.key == "internet_access" ||
                   item.key == "lanes" ||
                   item.key == "surface" ||
                   item.key == "smoothness" ||
                   item.key == "width" ||
                   item.key == "est_width" ||
                   item.key == "incline" ||
                   item.key == "opening_hours" ||
                   item.key == "collection_times" ||
                   item.key == "service_times" ||
                   item.key == "disused" ||
                   item.key == "wheelchair" ||
                   item.key == "sac_scale" ||
                   item.key == "trail_visibility" ||
                   item.key == "mtb:scale" ||
                   item.key == "mtb:description" ||
                   item.key == "wood" ||
                   item.key == "drive_through" ||
                   item.key == "drive_in" ||
                   item.key == "access" ||
                   item.key == "vehicle" ||
                   item.key == "bicyle" ||
                   item.key == "foot" ||
                   item.key == "goods" ||
                   item.key == "hgv" ||
                   item.key == "motor_vehicle" ||
                   item.key == "motor_car" ||
                   boost::starts_with(item.key, "access:") ||
                   boost::starts_with(item.key, "contact:") ||
                   boost::starts_with(item.key, "drink:") ||
                   item.key == "oneway" ||
                   item.key == "date_on" ||
                   item.key == "date_off" ||
                   item.key == "day_on" ||
                   item.key == "day_off" ||
                   item.key == "hour_on" ||
                   item.key == "hour_off" ||
                   item.key == "maxweight" ||
                   item.key == "maxheight" ||
                   item.key == "maxspeed" ||
                   item.key == "fee" ||
                   item.key == "toll" ||
                   boost::starts_with(item.key, "toll:") ||
                   item.key == "charge" ||
                   item.key == "population" ||
                   item.key == "description" ||
                   item.key == "image" ||
                   item.key == "attribution" ||
                   item.key == "fax" ||
                   item.key == "email" ||
                   item.key == "url" ||
                   item.key == "website" ||
                   item.key == "phone" ||
                   item.key == "real_ale" ||
                   item.key == "smoking" ||
                   item.key == "food" ||
                   item.key == "camera" ||
                   item.key == "brewery" ||
                   item.key == "locality" ||
                   item.key == "wikipedia" ||
                   boost::starts_with(item.key, "wikipedia:")) {
            extratags.push_back(&item);
        } else if (item.key == "building") {
            placebuilding = true;
        }
    }

    // skip some tags, if they don't have a proper name (ref doesn't count)
    if (!isnamed) {
        if (!places.empty())
            places.erase(std::remove_if(places.begin(), places.end(),
                                        UnnamedPredicate()),
                         places.end());
    }

    if (isinterpolation)
        places.push_back(tag_t("place", "houses"));

    if (place) {
        if (isinterpolation ||
             (placeadmin &&
              place ->value != "island" &&
              place ->value != "islet"))
            extratags.push_back(place);
        else
            places.push_back(*place);
    }

    if (isnamed && places.empty()) {
        if (junction)
            places.push_back(*junction);
        else if (landuse)
            places.push_back(*landuse);
    }

    if (places.empty()) {
        if (placebuilding && (!names.empty() || placehouse || postcode)) {
            places.push_back(tag_t("building", "yes"));
        } else if (placehouse) {
            places.push_back(tag_t("place", "house"));
        } else if (postcode) {
            places.push_back(tag_t("place", "postcode"));
        }
    }

    // housenumbers
    if (!isinterpolation) {
        if (street_nr && conscr_nr) {
            housenumber.clear();
            escape(*conscr_nr, housenumber);
            housenumber.append("/");
            escape(*street_nr, housenumber);
        } else if (conscr_nr) {
            housenumber.clear();
            escape(*conscr_nr, housenumber);
        } else if (street_nr) {
            housenumber.clear();
            escape(*street_nr, housenumber);
        } else if (house_nr) {
            housenumber.clear();
            escape(*house_nr, housenumber);
        }
    }

}
示例#6
0
int output_pgsql_t::pgsql_out_relation(osmid_t id, const taglist_t &rel_tags,
                                       const multinodelist_t &xnodes, const multitaglist_t & xtags,
                                       const idlist_t &xid, const rolelist_t &xrole,
                                       bool pending)
{
    if (xnodes.empty())
        return 0;

    int roads = 0;
    int make_polygon = 0;
    int make_boundary = 0;
    double split_at;

    std::vector<int> members_superseeded(xnodes.size(), 0);
    taglist_t outtags;

    //if its a route relation make_boundary and make_polygon will be false otherwise one or the other will be true
    if (m_tagtransform->filter_rel_member_tags(rel_tags, xtags, xrole,
            &(members_superseeded[0]), &make_boundary, &make_polygon, &roads,
            *m_export_list.get(), outtags)) {
        return 0;
    }

    /* Split long linear ways after around 1 degree or 100km (polygons not effected) */
    if (m_options.projection->get_proj_id() == PROJ_LATLONG)
        split_at = 1;
    else
        split_at = 100 * 1000;

    //this will either make lines or polygons (unless the lines arent a ring or are less than 3 pts) depending on the tag transform above
    //TODO: pick one or the other based on which we expect to care about
    auto wkbs  = builder.build_both(xnodes, make_polygon, m_options.enable_multi, split_at, id);

    if (wkbs.empty()) {
        return 0;
    }

    tag_t *areatag = 0;
    char tmp[32];
    for (const auto& wkb: wkbs) {
        expire->from_wkb(wkb.geom.c_str(), -id);
        /* FIXME: there should be a better way to detect polygons */
        if (wkb.is_polygon()) {
            if ((wkb.area > 0.0) && m_enable_way_area) {
                snprintf(tmp, sizeof(tmp), "%g", wkb.area);
                if (!areatag) {
                    outtags.push_dedupe(tag_t("way_area", tmp));
                    areatag = outtags.find("way_area");
                }
            }
            m_tables[t_poly]->write_row(-id, outtags, wkb.geom);
        } else {
            m_tables[t_line]->write_row(-id, outtags, wkb.geom);
            if (roads)
                m_tables[t_roads]->write_row(-id, outtags, wkb.geom);
        }
    }

    /* Tagtransform will have marked those member ways of the relation that
     * have fully been dealt with as part of the multi-polygon entry.
     * Set them in the database as done and delete their entry to not
     * have duplicates */
    //dont do this when working with pending relations as its not needed
    if (make_polygon) {
        for (size_t i=0; i < xid.size(); i++) {
            if (members_superseeded[i]) {
                pgsql_delete_way_from_output(xid[i]);
                if(!pending)
                    ways_done_tracker->mark(xid[i]);
            }
        }
    }

    // If the tag transform said the polygon looked like a boundary we want to make that as well
    // If we are making a boundary then also try adding any relations which form complete rings
    // The linear variants will have already been processed above
    if (make_boundary) {
        wkbs = builder.build_polygons(xnodes, m_options.enable_multi, id);
        for (const auto& wkb: wkbs) {
            expire->from_wkb(wkb.geom.c_str(), -id);
            if ((wkb.area > 0.0) && m_enable_way_area) {
                snprintf(tmp, sizeof(tmp), "%g", wkb.area);
                if (!areatag) {
                    outtags.push_dedupe(tag_t("way_area", tmp));
                    areatag = outtags.find("way_area");
                }
            }
            m_tables[t_poly]->write_row(-id, outtags, wkb.geom);
        }
    }

    return 0;
}
示例#7
0
/* This is the workhorse of pgsql_add_relation, split out because it is used as the callback for iterate relations */
int output_pgsql_t::pgsql_process_relation(osmium::Relation const &rel)
{
    taglist_t prefiltered_tags;
    if (m_tagtransform->filter_tags(rel, nullptr, nullptr, prefiltered_tags)) {
        return 1;
    }

    idlist_t xid2;
    for (auto const &m : rel.members()) {
        /* Need to handle more than just ways... */
        if (m.type() == osmium::item_type::way) {
            xid2.push_back(m.ref());
        }
    }

    buffer.clear();
    rolelist_t xrole;
    auto num_ways = m_mid->rel_way_members_get(rel, &xrole, buffer);

    if (num_ways == 0)
        return 0;

  int roads = 0;
  int make_polygon = 0;
  int make_boundary = 0;
  taglist_t outtags;

  // If it's a route relation make_boundary and make_polygon will be false
  // otherwise one or the other will be true.
  if (m_tagtransform->filter_rel_member_tags(prefiltered_tags, buffer, xrole,
                                             &make_boundary, &make_polygon,
                                             &roads, outtags)) {
      return 0;
  }

  for (auto &w : buffer.select<osmium::Way>()) {
      m_mid->nodes_get_list(&(w.nodes()));
  }

  // linear features and boundaries
  // Needs to be done before the polygon treatment below because
  // for boundaries the way_area tag may be added.
  if (!make_polygon) {
      double const split_at = m_options.projection->target_latlon() ? 1 : 100 * 1000;
      auto wkbs = m_builder.get_wkb_multiline(buffer, split_at);
      for (auto const &wkb : wkbs) {
          expire.from_wkb(wkb.c_str(), -rel.id());
          m_tables[t_line]->write_row(-rel.id(), outtags, wkb);
          if (roads)
              m_tables[t_roads]->write_row(-rel.id(), outtags, wkb);
      }
  }

  // multipolygons and boundaries
  if (make_boundary || make_polygon) {
      auto wkbs = m_builder.get_wkb_multipolygon(rel, buffer);

      char tmp[32];
      for (auto const &wkb : wkbs) {
          expire.from_wkb(wkb.c_str(), -rel.id());
          if (m_enable_way_area) {
              auto const area =
                  m_options.reproject_area
                      ? ewkb::parser_t(wkb).get_area<reprojection>(
                            m_options.projection.get())
                      : ewkb::parser_t(wkb)
                            .get_area<osmium::geom::IdentityProjection>();
              snprintf(tmp, sizeof(tmp), "%g", area);
              outtags.push_override(tag_t("way_area", tmp));
          }
          m_tables[t_poly]->write_row(-rel.id(), outtags, wkb);
      }
  }

  return 0;
}
示例#8
0
unsigned lua_tagtransform_t::filter_rel_member_tags(
    taglist_t const &rel_tags, multitaglist_t const &members_tags,
    rolelist_t const &member_roles, int *member_superseded, int *make_boundary,
    int *make_polygon, int *roads, export_list const &, taglist_t &out_tags,
    bool)
{
    lua_getglobal(L, m_rel_mem_func.c_str());

    lua_newtable(L); /* relations key value table */

    for (const auto &rel_tag : rel_tags) {
        lua_pushstring(L, rel_tag.key.c_str());
        lua_pushstring(L, rel_tag.value.c_str());
        lua_rawset(L, -3);
    }

    lua_newtable(L); /* member tags table */

    int idx = 1;
    for (const auto &member_tags : members_tags) {
        lua_pushnumber(L, idx++);
        lua_newtable(L); /* member key value table */
        for (const auto &member_tag : member_tags) {
            lua_pushstring(L, member_tag.key.c_str());
            lua_pushstring(L, member_tag.value.c_str());
            lua_rawset(L, -3);
        }
        lua_rawset(L, -3);
    }

    lua_newtable(L); /* member roles table */

    for (size_t i = 0; i < member_roles.size(); i++) {
        lua_pushnumber(L, i + 1);
        lua_pushstring(L, member_roles[i]);
        lua_rawset(L, -3);
    }

    lua_pushnumber(L, member_roles.size());

    if (lua_pcall(L, 4, 6, 0)) {
        fprintf(
            stderr,
            "Failed to execute lua function for relation tag processing: %s\n",
            lua_tostring(L, -1));
        /* lua function failed */
        return 1;
    }

    *roads = (int)lua_tointeger(L, -1);
    lua_pop(L, 1);
    *make_polygon = (int)lua_tointeger(L, -1);
    lua_pop(L, 1);
    *make_boundary = (int)lua_tointeger(L, -1);
    lua_pop(L, 1);

    lua_pushnil(L);
    for (size_t i = 0; i < members_tags.size(); i++) {
        if (lua_next(L, -2)) {
            member_superseded[i] = (int)lua_tointeger(L, -1);
            lua_pop(L, 1);
        } else {
            fprintf(stderr,
                    "Failed to read member_superseded from lua function\n");
        }
    }
    lua_pop(L, 2);

    lua_pushnil(L);
    while (lua_next(L, -2) != 0) {
        const char *key = lua_tostring(L, -2);
        const char *value = lua_tostring(L, -1);
        out_tags.push_back(tag_t(key, value));
        lua_pop(L, 1);
    }
    lua_pop(L, 1);

    unsigned filter = (unsigned)lua_tointeger(L, -1);

    lua_pop(L, 1);

    return filter;
}