// necessary static util functions for stxxl's sorting
 static InternalExtractorEdge min_osm_value()
 {
     return InternalExtractorEdge(MIN_OSM_NODEID,
                                  MIN_OSM_NODEID,
                                  0,
                                  WeightData(),
                                  false,
                                  false,
                                  false,
                                  false,
                                  true,
                                  TRAVEL_MODE_INACCESSIBLE,
                                  false,
                                  INVALID_LANE_DESCRIPTIONID,
                                  guidance::RoadClassificationData());
 }
 // necessary static util functions for stxxl's sorting
 static InternalExtractorEdge min_osm_value()
 {
     return InternalExtractorEdge(MIN_OSM_NODEID,
                                  MIN_OSM_NODEID,
                                  0,
                                  WeightData(),
                                  false, // forward
                                  false, // backward
                                  false, // roundabout
                                  false, // circular
                                  true,  // can be startpoint
                                  TRAVEL_MODE_INACCESSIBLE,
                                  false,
                                  INVALID_LANE_DESCRIPTIONID,
                                  guidance::RoadClassification());
 }
/** warning: caller needs to take care of synchronization! */
void ExtractorCallbacks::wayFunction(ExtractionWay &parsed_way) {
    if((0 < parsed_way.speed) || (0 < parsed_way.duration)) { //Only true if the way is specified by the speed profile
        if(UINT_MAX == parsed_way.id){
            DEBUG("found bogus way with id: " << parsed_way.id << " of size " << parsed_way.path.size());
            return;
        }

        if(0 < parsed_way.duration) {
         //TODO: iterate all way segments and set duration corresponding to the length of each segment
            parsed_way.speed = parsed_way.duration/(parsed_way.path.size()-1);
        }

        if(FLT_EPSILON >= fabs(-1. - parsed_way.speed)){
            DEBUG("found way with bogus speed, id: " << parsed_way.id);
            return;
        }

        //Get the unique identifier for the street name
        const StringMap::const_iterator string_map_iterator = stringMap->find(parsed_way.name);
        if(stringMap->end() == string_map_iterator) {
            parsed_way.nameID = externalMemory->nameVector.size();
            externalMemory->nameVector.push_back(parsed_way.name);
            stringMap->insert(std::make_pair(parsed_way.name, parsed_way.nameID));
        } else {
            parsed_way.nameID = string_map_iterator->second;
        }

        if(ExtractionWay::opposite == parsed_way.direction) {
            std::reverse( parsed_way.path.begin(), parsed_way.path.end() );
            parsed_way.direction = ExtractionWay::oneway;
        }

        const bool split_bidirectional_edge = (parsed_way.backward_speed > 0) && (parsed_way.speed != parsed_way.backward_speed);

        for(std::vector< NodeID >::size_type n = 0; n < parsed_way.path.size()-1; ++n) {
            externalMemory->allEdges.push_back(
                    InternalExtractorEdge(parsed_way.path[n],
                            parsed_way.path[n+1],
                            parsed_way.type,
                            (split_bidirectional_edge ? ExtractionWay::oneway : parsed_way.direction),
                            parsed_way.speed,
                            parsed_way.nameID,
                            parsed_way.roundabout,
                            parsed_way.ignoreInGrid,
                            (0 < parsed_way.duration),
                            parsed_way.isAccessRestricted
                    )
            );
            externalMemory->usedNodeIDs.push_back(parsed_way.path[n]);
        }
        externalMemory->usedNodeIDs.push_back(parsed_way.path.back());

        //The following information is needed to identify start and end segments of restrictions
        externalMemory->wayStartEndVector.push_back(_WayIDStartAndEndEdge(parsed_way.id, parsed_way.path[0], parsed_way.path[1], parsed_way.path[parsed_way.path.size()-2], parsed_way.path.back()));

        if(split_bidirectional_edge) { //Only true if the way should be split
            std::reverse( parsed_way.path.begin(), parsed_way.path.end() );
            for(std::vector< NodeID >::size_type n = 0; n < parsed_way.path.size()-1; ++n) {
                externalMemory->allEdges.push_back(
                        InternalExtractorEdge(parsed_way.path[n],
                                parsed_way.path[n+1],
                                parsed_way.type,
                                ExtractionWay::oneway,
                                parsed_way.backward_speed,
                                parsed_way.nameID,
                                parsed_way.roundabout,
                                parsed_way.ignoreInGrid,
                                (0 < parsed_way.duration),
                                parsed_way.isAccessRestricted,
                                (ExtractionWay::oneway == parsed_way.direction)
                        )
                );
            }
            externalMemory->wayStartEndVector.push_back(_WayIDStartAndEndEdge(parsed_way.id, parsed_way.path[0], parsed_way.path[1], parsed_way.path[parsed_way.path.size()-2], parsed_way.path.back()));
        }
    }
}
/**
 * Takes the geometry contained in the ```input_way``` and the tags computed
 * by the lua profile inside ```parsed_way``` and computes all edge segments.
 *
 * Depending on the forward/backwards weights the edges are split into forward
 * and backward edges.
 *
 * warning: caller needs to take care of synchronization!
 */
void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const ExtractionWay &parsed_way)
{
    if (((0 >= parsed_way.forward_speed) ||
         (TRAVEL_MODE_INACCESSIBLE == parsed_way.forward_travel_mode)) &&
        ((0 >= parsed_way.backward_speed) ||
         (TRAVEL_MODE_INACCESSIBLE == parsed_way.backward_travel_mode)) &&
        (0 >= parsed_way.duration))
    { // Only true if the way is specified by the speed profile
        return;
    }

    if (input_way.nodes().size() <= 1)
    { // safe-guard against broken data
        return;
    }

    if (std::numeric_limits<decltype(input_way.id())>::max() == input_way.id())
    {
        util::SimpleLogger().Write(logDEBUG) << "found bogus way with id: " << input_way.id()
                                             << " of size " << input_way.nodes().size();
        return;
    }

    InternalExtractorEdge::WeightData forward_weight_data;
    InternalExtractorEdge::WeightData backward_weight_data;

    if (0 < parsed_way.duration)
    {
        const unsigned num_edges = (input_way.nodes().size() - 1);
        // FIXME We devide by the numer of nodes here, but should rather consider
        // the length of each segment. We would eigther have to compute the length
        // of the whole way here (we can't: no node coordinates) or push that back
        // to the container and keep a reference to the way.
        forward_weight_data.duration = parsed_way.duration / num_edges;
        forward_weight_data.type = InternalExtractorEdge::WeightType::WAY_DURATION;
        backward_weight_data.duration = parsed_way.duration / num_edges;
        backward_weight_data.type = InternalExtractorEdge::WeightType::WAY_DURATION;
    }
    else
    {
        if (parsed_way.forward_speed > 0 &&
            parsed_way.forward_travel_mode != TRAVEL_MODE_INACCESSIBLE)
        {
            forward_weight_data.speed = parsed_way.forward_speed;
            forward_weight_data.type = InternalExtractorEdge::WeightType::SPEED;
        }
        if (parsed_way.backward_speed > 0 &&
            parsed_way.backward_travel_mode != TRAVEL_MODE_INACCESSIBLE)
        {
            backward_weight_data.speed = parsed_way.backward_speed;
            backward_weight_data.type = InternalExtractorEdge::WeightType::SPEED;
        }
    }

    if (forward_weight_data.type == InternalExtractorEdge::WeightType::INVALID &&
        backward_weight_data.type == InternalExtractorEdge::WeightType::INVALID)
    {
        util::SimpleLogger().Write(logDEBUG) << "found way with bogus speed, id: "
                                             << input_way.id();
        return;
    }

    // FIXME this need to be moved into the profiles
    const char *data = input_way.get_value_by_key("highway");
    guidance::RoadClassificationData road_classification;
    if (data)
    {
        road_classification.road_class = guidance::functionalRoadClassFromTag(data);
    }

    const auto laneStringToDescription = [](std::string lane_string) -> TurnLaneDescription {
        if (lane_string.empty())
            return {};

        TurnLaneDescription lane_description;

        typedef boost::tokenizer<boost::char_separator<char>> tokenizer;
        boost::char_separator<char> sep("|", "", boost::keep_empty_tokens);
        boost::char_separator<char> inner_sep(";", "");
        tokenizer tokens(lane_string, sep);

        const constexpr std::size_t num_osm_tags = 11;
        const constexpr char *osm_lane_strings[num_osm_tags] = {"none",
                                                                "through",
                                                                "sharp_left",
                                                                "left",
                                                                "slight_left",
                                                                "slight_right",
                                                                "right",
                                                                "sharp_right",
                                                                "reverse",
                                                                "merge_to_left",
                                                                "merge_to_right"};
        const constexpr TurnLaneType::Mask masks_by_osm_string[num_osm_tags + 1] = {
            TurnLaneType::none,
            TurnLaneType::straight,
            TurnLaneType::sharp_left,
            TurnLaneType::left,
            TurnLaneType::slight_left,
            TurnLaneType::slight_right,
            TurnLaneType::right,
            TurnLaneType::sharp_right,
            TurnLaneType::uturn,
            TurnLaneType::merge_to_left,
            TurnLaneType::merge_to_right,
            TurnLaneType::empty}; // fallback, if string not found

        for (auto iter = tokens.begin(); iter != tokens.end(); ++iter)
        {
            tokenizer inner_tokens(*iter, inner_sep);
            guidance::TurnLaneType::Mask lane_mask = inner_tokens.begin() == inner_tokens.end()
                                                         ? TurnLaneType::none
                                                         : TurnLaneType::empty;
            for (auto token_itr = inner_tokens.begin(); token_itr != inner_tokens.end();
                 ++token_itr)
            {
                auto position = std::find(osm_lane_strings, osm_lane_strings + num_osm_tags, *token_itr);
                const auto translated_mask =
                    masks_by_osm_string[std::distance(osm_lane_strings, position)];
                if (translated_mask == TurnLaneType::empty)
                {
                    // if we have unsupported tags, don't handle them
                    util::SimpleLogger().Write(logDEBUG) << "Unsupported lane tag found: \""
                                                         << *token_itr << "\"";
                    return {};
                }
                BOOST_ASSERT((lane_mask & translated_mask) == 0); // make sure the mask is valid
                lane_mask |= translated_mask;
            }
            // add the lane to the description
            lane_description.push_back(lane_mask);
        }
        return lane_description;
    };

    // convert the lane description into an ID and, if necessary, remembr the description in the
    // description_map
    const auto requestId = [&](std::string lane_string) {
        if (lane_string.empty())
            return INVALID_LANE_DESCRIPTIONID;
        TurnLaneDescription lane_description = laneStringToDescription(std::move(lane_string));

        const auto lane_description_itr = lane_description_map.find(lane_description);
        if (lane_description_itr == lane_description_map.end())
        {
            const LaneDescriptionID new_id =
                boost::numeric_cast<LaneDescriptionID>(lane_description_map.size());
            lane_description_map[lane_description] = new_id;

            // since we are getting a new ID, we can augment the current offsets

            // and store the turn lane masks, sadly stxxl does not support insert
            for (const auto mask : lane_description)
                external_memory.turn_lane_masks.push_back(mask);

            external_memory.turn_lane_offsets.push_back(external_memory.turn_lane_offsets.back() +
                                                        lane_description.size());

            return new_id;
        }
        else
        {
            return lane_description_itr->second;
        }
    };

    // Deduplicates street names and street destination names based on the street_map map.
    // In case we do not already store the name, inserts (name, id) tuple and return id.
    // Otherwise fetches the id based on the name and returns it without insertion.
    const auto turn_lane_id_forward = requestId(parsed_way.turn_lanes_forward);
    const auto turn_lane_id_backward = requestId(parsed_way.turn_lanes_backward);

    const constexpr auto MAX_STRING_LENGTH = 255u;
    // Get the unique identifier for the street name
    // Get the unique identifier for the street name and destination
    const auto name_iterator = string_map.find(MapKey(parsed_way.name, parsed_way.destinations));
    unsigned name_id = EMPTY_NAMEID;
    if (string_map.end() == name_iterator)
    {
        const auto name_length = std::min<unsigned>(MAX_STRING_LENGTH, parsed_way.name.size());
        const auto destinations_length =
            std::min<unsigned>(MAX_STRING_LENGTH, parsed_way.destinations.size());
        const auto pronunciation_length =
            std::min<unsigned>(MAX_STRING_LENGTH, parsed_way.pronunciation.size());

        // name_offsets already has an offset of a new name, take the offset index as the name id
        name_id = external_memory.name_offsets.size() - 1;

        external_memory.name_char_data.reserve(external_memory.name_char_data.size() + name_length
                                               + destinations_length + pronunciation_length);

        std::copy(parsed_way.name.c_str(),
                  parsed_way.name.c_str() + name_length,
                  std::back_inserter(external_memory.name_char_data));
        external_memory.name_offsets.push_back(external_memory.name_char_data.size());

        std::copy(parsed_way.destinations.c_str(),
                  parsed_way.destinations.c_str() + destinations_length,
                  std::back_inserter(external_memory.name_char_data));
        external_memory.name_offsets.push_back(external_memory.name_char_data.size());

        std::copy(parsed_way.pronunciation.c_str(),
                  parsed_way.pronunciation.c_str() + pronunciation_length,
                  std::back_inserter(external_memory.name_char_data));
        external_memory.name_offsets.push_back(external_memory.name_char_data.size());

        auto k = MapKey{parsed_way.name, parsed_way.destinations};
        auto v = MapVal{name_id};
        string_map.emplace(std::move(k), std::move(v));
    }
    else
    {
        name_id = name_iterator->second;
    }

    const bool split_edge = (parsed_way.forward_speed > 0) &&
                            (TRAVEL_MODE_INACCESSIBLE != parsed_way.forward_travel_mode) &&
                            (parsed_way.backward_speed > 0) &&
                            (TRAVEL_MODE_INACCESSIBLE != parsed_way.backward_travel_mode) &&
                            ((parsed_way.forward_speed != parsed_way.backward_speed) ||
                             (parsed_way.forward_travel_mode != parsed_way.backward_travel_mode) ||
                             (turn_lane_id_forward != turn_lane_id_backward));

    external_memory.used_node_id_list.reserve(external_memory.used_node_id_list.size() +
                                              input_way.nodes().size());

    std::transform(input_way.nodes().begin(),
                   input_way.nodes().end(),
                   std::back_inserter(external_memory.used_node_id_list),
                   [](const osmium::NodeRef &ref) { return OSMNodeID{static_cast<std::uint64_t>(ref.ref())}; });

    const bool is_opposite_way = TRAVEL_MODE_INACCESSIBLE == parsed_way.forward_travel_mode;

    // traverse way in reverse in this case
    if (is_opposite_way)
    {
        BOOST_ASSERT(split_edge == false);
        BOOST_ASSERT(parsed_way.backward_travel_mode != TRAVEL_MODE_INACCESSIBLE);
        util::for_each_pair(
            input_way.nodes().crbegin(),
            input_way.nodes().crend(),
            [&](const osmium::NodeRef &first_node, const osmium::NodeRef &last_node) {
                external_memory.all_edges_list.push_back(
                    InternalExtractorEdge(OSMNodeID{static_cast<std::uint64_t>(first_node.ref())},
                                          OSMNodeID{static_cast<std::uint64_t>(last_node.ref())},
                                          name_id,
                                          backward_weight_data,
                                          true,
                                          false,
                                          parsed_way.roundabout,
                                          parsed_way.is_access_restricted,
                                          parsed_way.is_startpoint,
                                          parsed_way.backward_travel_mode,
                                          false,
                                          turn_lane_id_backward,
                                          road_classification));
            });

        external_memory.way_start_end_id_list.push_back(
            {OSMWayID{static_cast<std::uint32_t>(input_way.id())},
             OSMNodeID{static_cast<std::uint64_t>(input_way.nodes().back().ref())},
             OSMNodeID{static_cast<std::uint64_t>(input_way.nodes()[input_way.nodes().size() - 2].ref())},
             OSMNodeID{static_cast<std::uint64_t>(input_way.nodes()[1].ref())},
             OSMNodeID{static_cast<std::uint64_t>(input_way.nodes()[0].ref())}});
    }
    else
    {
        const bool forward_only =
            split_edge || TRAVEL_MODE_INACCESSIBLE == parsed_way.backward_travel_mode;
        util::for_each_pair(
            input_way.nodes().cbegin(),
            input_way.nodes().cend(),
            [&](const osmium::NodeRef &first_node, const osmium::NodeRef &last_node) {
                external_memory.all_edges_list.push_back(
                    InternalExtractorEdge(OSMNodeID{static_cast<std::uint64_t>(first_node.ref())},
                                          OSMNodeID{static_cast<std::uint64_t>(last_node.ref())},
                                          name_id,
                                          forward_weight_data,
                                          true,
                                          !forward_only,
                                          parsed_way.roundabout,
                                          parsed_way.is_access_restricted,
                                          parsed_way.is_startpoint,
                                          parsed_way.forward_travel_mode,
                                          split_edge,
                                          turn_lane_id_forward,
                                          road_classification));
            });
        if (split_edge)
        {
            BOOST_ASSERT(parsed_way.backward_travel_mode != TRAVEL_MODE_INACCESSIBLE);
            util::for_each_pair(
                input_way.nodes().cbegin(),
                input_way.nodes().cend(),
                [&](const osmium::NodeRef &first_node, const osmium::NodeRef &last_node) {
                    external_memory.all_edges_list.push_back(
                        InternalExtractorEdge(OSMNodeID{static_cast<std::uint64_t>(first_node.ref())},
                                              OSMNodeID{static_cast<std::uint64_t>(last_node.ref())},
                                              name_id,
                                              backward_weight_data,
                                              false,
                                              true,
                                              parsed_way.roundabout,
                                              parsed_way.is_access_restricted,
                                              parsed_way.is_startpoint,
                                              parsed_way.backward_travel_mode,
                                              true,
                                              turn_lane_id_backward,
                                              road_classification));
                });
        }

        external_memory.way_start_end_id_list.push_back(
            {OSMWayID{static_cast<std::uint32_t>(input_way.id())},
             OSMNodeID{static_cast<std::uint64_t>(input_way.nodes().back().ref())},
             OSMNodeID{static_cast<std::uint64_t>(input_way.nodes()[input_way.nodes().size() - 2].ref())},
             OSMNodeID{static_cast<std::uint64_t>(input_way.nodes()[1].ref())},
             OSMNodeID{static_cast<std::uint64_t>(input_way.nodes()[0].ref())}});
    }
}
 static InternalExtractorEdge max_value()
 {
     return InternalExtractorEdge(SPECIAL_NODEID, SPECIAL_NODEID, 0, 0, 0, false, false, false,
                                  false, TRAVEL_MODE_INACCESSIBLE, false);
 }
 // necessary static util functions for stxxl's sorting
 static InternalExtractorEdge min_value()
 {
     return InternalExtractorEdge(0, 0, 0, 0, 0, false, false, false, false,
                                  TRAVEL_MODE_INACCESSIBLE, false);
 }
Esempio n. 7
0
/** warning: caller needs to take care of synchronization! */
void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const ExtractionWay &parsed_way)
{
    if (((0 >= parsed_way.forward_speed) ||
         (TRAVEL_MODE_INACCESSIBLE == parsed_way.forward_travel_mode)) &&
        ((0 >= parsed_way.backward_speed) ||
         (TRAVEL_MODE_INACCESSIBLE == parsed_way.backward_travel_mode)) &&
        (0 >= parsed_way.duration))
    { // Only true if the way is specified by the speed profile
        return;
    }

    if (input_way.nodes().size() <= 1)
    { // safe-guard against broken data
        return;
    }

    if (std::numeric_limits<decltype(input_way.id())>::max() == input_way.id())
    {
        SimpleLogger().Write(logDEBUG) << "found bogus way with id: " << input_way.id()
                                       << " of size " << input_way.nodes().size();
        return;
    }
    if (0 < parsed_way.duration)
    {
        // TODO: iterate all way segments and set duration corresponding to the length of each
        // segment
        const_cast<ExtractionWay &>(parsed_way).forward_speed =
            parsed_way.duration / (input_way.nodes().size() - 1);
        const_cast<ExtractionWay &>(parsed_way).backward_speed =
            parsed_way.duration / (input_way.nodes().size() - 1);
    }

    if (std::numeric_limits<double>::epsilon() >= std::abs(-1. - parsed_way.forward_speed))
    {
        SimpleLogger().Write(logDEBUG) << "found way with bogus speed, id: " << input_way.id();
        return;
    }

    // Get the unique identifier for the street name
    const auto &string_map_iterator = string_map.find(parsed_way.name);
    unsigned name_id = external_memory.name_list.size();
    if (string_map.end() == string_map_iterator)
    {
        external_memory.name_list.push_back(parsed_way.name);
        string_map.insert(std::make_pair(parsed_way.name, name_id));
    }
    else
    {
        name_id = string_map_iterator->second;
    }

    const bool split_edge = (parsed_way.forward_speed > 0) &&
                            (TRAVEL_MODE_INACCESSIBLE != parsed_way.forward_travel_mode) &&
                            (parsed_way.backward_speed > 0) &&
                            (TRAVEL_MODE_INACCESSIBLE != parsed_way.backward_travel_mode) &&
                            ((parsed_way.forward_speed != parsed_way.backward_speed) ||
                             (parsed_way.forward_travel_mode != parsed_way.backward_travel_mode));

    auto pair_wise_segment_split =
        [&](const osmium::NodeRef &first_node, const osmium::NodeRef &last_node)
    {
        // SimpleLogger().Write() << "adding edge (" << first_node.ref() << "," <<
        // last_node.ref() << "), fwd speed: " << parsed_way.forward_speed;
        external_memory.all_edges_list.push_back(InternalExtractorEdge(
            (EdgeID)input_way.id(),
            first_node.ref(), last_node.ref(),
            ((split_edge || TRAVEL_MODE_INACCESSIBLE == parsed_way.backward_travel_mode)
                 ? ExtractionWay::oneway
                 : ExtractionWay::bidirectional),
            parsed_way.forward_speed, name_id, parsed_way.roundabout, parsed_way.ignore_in_grid,
            (0 < parsed_way.duration), parsed_way.is_access_restricted,
            parsed_way.forward_travel_mode, split_edge));
        external_memory.used_node_id_list.push_back(first_node.ref());
    };

    const bool is_opposite_way = TRAVEL_MODE_INACCESSIBLE == parsed_way.forward_travel_mode;
    if (is_opposite_way)
    {
        const_cast<ExtractionWay &>(parsed_way).forward_travel_mode =
            parsed_way.backward_travel_mode;
        const_cast<ExtractionWay &>(parsed_way).backward_travel_mode = TRAVEL_MODE_INACCESSIBLE;
        osrm::for_each_pair(input_way.nodes().crbegin(), input_way.nodes().crend(),
                            pair_wise_segment_split);
        external_memory.used_node_id_list.push_back(input_way.nodes().front().ref());
    }
    else
    {
        osrm::for_each_pair(input_way.nodes().cbegin(), input_way.nodes().cend(),
                            pair_wise_segment_split);
        external_memory.used_node_id_list.push_back(input_way.nodes().back().ref());
    }

    // The following information is needed to identify start and end segments of restrictions
    external_memory.way_start_end_id_list.push_back(
        {(EdgeID)input_way.id(),
         (NodeID)input_way.nodes()[0].ref(),
         (NodeID)input_way.nodes()[1].ref(),
         (NodeID)input_way.nodes()[input_way.nodes().size() - 2].ref(),
         (NodeID)input_way.nodes().back().ref()});

    if (split_edge)
    { // Only true if the way should be split
        BOOST_ASSERT(parsed_way.backward_travel_mode > 0);
        auto pair_wise_segment_split_2 =
            [&](const osmium::NodeRef &first_node, const osmium::NodeRef &last_node)
        {
            // SimpleLogger().Write() << "adding edge (" << last_node.ref() << "," <<
            // first_node.ref() << "), bwd speed: " << parsed_way.backward_speed;
            external_memory.all_edges_list.push_back(InternalExtractorEdge(
                (EdgeID)input_way.id(),
                last_node.ref(), first_node.ref(), ExtractionWay::oneway, parsed_way.backward_speed,
                name_id, parsed_way.roundabout, parsed_way.ignore_in_grid,
                (0 < parsed_way.duration), parsed_way.is_access_restricted,
                parsed_way.backward_travel_mode, split_edge));
        };

        if (is_opposite_way)
        {
            // SimpleLogger().Write() << "opposite2";
            osrm::for_each_pair(input_way.nodes().crbegin(), input_way.nodes().crend(),
                                pair_wise_segment_split_2);
            external_memory.used_node_id_list.push_back(input_way.nodes().front().ref());
        }
        else
        {
            osrm::for_each_pair(input_way.nodes().cbegin(), input_way.nodes().cend(),
                                pair_wise_segment_split_2);
            external_memory.used_node_id_list.push_back(input_way.nodes().back().ref());
        }

        external_memory.way_start_end_id_list.push_back(
            {(EdgeID)input_way.id(),
             (NodeID)input_way.nodes()[1].ref(),
             (NodeID)input_way.nodes()[0].ref(),
             (NodeID)input_way.nodes().back().ref(),
             (NodeID)input_way.nodes()[input_way.nodes().size() - 2].ref()});
    }
}
/**
 * Takes the geometry contained in the ```input_way``` and the tags computed
 * by the lua profile inside ```parsed_way``` and computes all edge segments.
 *
 * Depending on the forward/backwards weights the edges are split into forward
 * and backward edges.
 *
 * warning: caller needs to take care of synchronization!
 */
void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const ExtractionWay &parsed_way)
{
    if (((0 >= parsed_way.forward_speed) ||
         (TRAVEL_MODE_INACCESSIBLE == parsed_way.forward_travel_mode)) &&
        ((0 >= parsed_way.backward_speed) ||
         (TRAVEL_MODE_INACCESSIBLE == parsed_way.backward_travel_mode)) &&
        (0 >= parsed_way.duration))
    { // Only true if the way is specified by the speed profile
        return;
    }

    if (input_way.nodes().size() <= 1)
    { // safe-guard against broken data
        return;
    }

    if (std::numeric_limits<decltype(input_way.id())>::max() == input_way.id())
    {
        util::SimpleLogger().Write(logDEBUG) << "found bogus way with id: " << input_way.id()
                                             << " of size " << input_way.nodes().size();
        return;
    }

    InternalExtractorEdge::WeightData forward_weight_data;
    InternalExtractorEdge::WeightData backward_weight_data;

    if (0 < parsed_way.duration)
    {
        const unsigned num_edges = (input_way.nodes().size() - 1);
        // FIXME We devide by the numer of nodes here, but should rather consider
        // the length of each segment. We would eigther have to compute the length
        // of the whole way here (we can't: no node coordinates) or push that back
        // to the container and keep a reference to the way.
        forward_weight_data.duration = parsed_way.duration / num_edges;
        forward_weight_data.type = InternalExtractorEdge::WeightType::WAY_DURATION;
        backward_weight_data.duration = parsed_way.duration / num_edges;
        backward_weight_data.type = InternalExtractorEdge::WeightType::WAY_DURATION;
    }
    else
    {
        if (parsed_way.forward_speed > 0 &&
            parsed_way.forward_travel_mode != TRAVEL_MODE_INACCESSIBLE)
        {
            forward_weight_data.speed = parsed_way.forward_speed;
            forward_weight_data.type = InternalExtractorEdge::WeightType::SPEED;
        }
        if (parsed_way.backward_speed > 0 &&
            parsed_way.backward_travel_mode != TRAVEL_MODE_INACCESSIBLE)
        {
            backward_weight_data.speed = parsed_way.backward_speed;
            backward_weight_data.type = InternalExtractorEdge::WeightType::SPEED;
        }
    }

    if (forward_weight_data.type == InternalExtractorEdge::WeightType::INVALID &&
        backward_weight_data.type == InternalExtractorEdge::WeightType::INVALID)
    {
        util::SimpleLogger().Write(logDEBUG) << "found way with bogus speed, id: "
                                             << input_way.id();
        return;
    }

    // FIXME this need to be moved into the profiles
    const char *data = input_way.get_value_by_key("highway");
    guidance::RoadClassificationData road_classification;
    if (data)
    {
        road_classification.road_class = guidance::functionalRoadClassFromTag(data);
    }

    // Deduplicates street names and street destination names based on the street_map map.
    // In case we do not already store the name, inserts (name, id) tuple and return id.
    // Otherwise fetches the id based on the name and returns it without insertion.

    const constexpr auto MAX_STRING_LENGTH = 255u;

    // Get the unique identifier for the street name
    const auto string_map_iterator = string_map.find(parsed_way.name);
    unsigned name_id = external_memory.name_lengths.size();
    if (string_map.end() == string_map_iterator)
    {
        auto name_length = std::min<unsigned>(MAX_STRING_LENGTH, parsed_way.name.size());

        external_memory.name_char_data.reserve(name_id + name_length);
        std::copy(parsed_way.name.c_str(),
                  parsed_way.name.c_str() + name_length,
                  std::back_inserter(external_memory.name_char_data));

        external_memory.name_lengths.push_back(name_length);
        string_map.insert(std::make_pair(parsed_way.name, name_id));
    }
    else
    {
        name_id = string_map_iterator->second;
    }

    const bool split_edge = (parsed_way.forward_speed > 0) &&
                            (TRAVEL_MODE_INACCESSIBLE != parsed_way.forward_travel_mode) &&
                            (parsed_way.backward_speed > 0) &&
                            (TRAVEL_MODE_INACCESSIBLE != parsed_way.backward_travel_mode) &&
                            ((parsed_way.forward_speed != parsed_way.backward_speed) ||
                             (parsed_way.forward_travel_mode != parsed_way.backward_travel_mode));

    std::transform(input_way.nodes().begin(),
                   input_way.nodes().end(),
                   std::back_inserter(external_memory.used_node_id_list),
                   [](const osmium::NodeRef &ref) { return OSMNodeID(ref.ref()); });

    const bool is_opposite_way = TRAVEL_MODE_INACCESSIBLE == parsed_way.forward_travel_mode;

    // traverse way in reverse in this case
    if (is_opposite_way)
    {
        BOOST_ASSERT(split_edge == false);
        BOOST_ASSERT(parsed_way.backward_travel_mode != TRAVEL_MODE_INACCESSIBLE);
        util::for_each_pair(
            input_way.nodes().crbegin(),
            input_way.nodes().crend(),
            [&](const osmium::NodeRef &first_node, const osmium::NodeRef &last_node) {
                external_memory.all_edges_list.push_back(
                    InternalExtractorEdge(OSMNodeID(first_node.ref()),
                                          OSMNodeID(last_node.ref()),
                                          name_id,
                                          backward_weight_data,
                                          true,
                                          false,
                                          parsed_way.roundabout,
                                          parsed_way.is_access_restricted,
                                          parsed_way.is_startpoint,
                                          parsed_way.backward_travel_mode,
                                          false,
                                          road_classification));
            });

        external_memory.way_start_end_id_list.push_back(
            {OSMWayID(input_way.id()),
             OSMNodeID(input_way.nodes().back().ref()),
             OSMNodeID(input_way.nodes()[input_way.nodes().size() - 2].ref()),
             OSMNodeID(input_way.nodes()[1].ref()),
             OSMNodeID(input_way.nodes()[0].ref())});
    }
    else
    {
        const bool forward_only =
            split_edge || TRAVEL_MODE_INACCESSIBLE == parsed_way.backward_travel_mode;
        util::for_each_pair(
            input_way.nodes().cbegin(),
            input_way.nodes().cend(),
            [&](const osmium::NodeRef &first_node, const osmium::NodeRef &last_node) {
                external_memory.all_edges_list.push_back(
                    InternalExtractorEdge(OSMNodeID(first_node.ref()),
                                          OSMNodeID(last_node.ref()),
                                          name_id,
                                          forward_weight_data,
                                          true,
                                          !forward_only,
                                          parsed_way.roundabout,
                                          parsed_way.is_access_restricted,
                                          parsed_way.is_startpoint,
                                          parsed_way.forward_travel_mode,
                                          split_edge,
                                          road_classification));
            });
        if (split_edge)
        {
            BOOST_ASSERT(parsed_way.backward_travel_mode != TRAVEL_MODE_INACCESSIBLE);
            util::for_each_pair(
                input_way.nodes().cbegin(),
                input_way.nodes().cend(),
                [&](const osmium::NodeRef &first_node, const osmium::NodeRef &last_node) {
                    external_memory.all_edges_list.push_back(
                        InternalExtractorEdge(OSMNodeID(first_node.ref()),
                                              OSMNodeID(last_node.ref()),
                                              name_id,
                                              backward_weight_data,
                                              false,
                                              true,
                                              parsed_way.roundabout,
                                              parsed_way.is_access_restricted,
                                              parsed_way.is_startpoint,
                                              parsed_way.backward_travel_mode,
                                              true,
                                              road_classification));
                });
        }

        external_memory.way_start_end_id_list.push_back(
            {OSMWayID(input_way.id()),
             OSMNodeID(input_way.nodes().back().ref()),
             OSMNodeID(input_way.nodes()[input_way.nodes().size() - 2].ref()),
             OSMNodeID(input_way.nodes()[1].ref()),
             OSMNodeID(input_way.nodes()[0].ref())});
    }
}
/**
 * Takes the geometry contained in the ```input_way``` and the tags computed
 * by the lua profile inside ```parsed_way``` and computes all edge segments.
 *
 * Depending on the forward/backwards weights the edges are split into forward
 * and backward edges.
 *
 * warning: caller needs to take care of synchronization!
 */
void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const ExtractionWay &parsed_way)
{
    if (((0 >= parsed_way.forward_speed) ||
         (TRAVEL_MODE_INACCESSIBLE == parsed_way.forward_travel_mode)) &&
        ((0 >= parsed_way.backward_speed) ||
         (TRAVEL_MODE_INACCESSIBLE == parsed_way.backward_travel_mode)) &&
        (0 >= parsed_way.duration))
    { // Only true if the way is specified by the speed profile
        return;
    }

    if (input_way.nodes().size() <= 1)
    { // safe-guard against broken data
        return;
    }

    if (std::numeric_limits<decltype(input_way.id())>::max() == input_way.id())
    {
        SimpleLogger().Write(logDEBUG) << "found bogus way with id: " << input_way.id()
                                       << " of size " << input_way.nodes().size();
        return;
    }

    InternalExtractorEdge::WeightData forward_weight_data;
    InternalExtractorEdge::WeightData backward_weight_data;

    if (0 < parsed_way.duration)
    {
        const unsigned num_edges = (input_way.nodes().size() - 1);
        // FIXME We devide by the numer of nodes here, but should rather consider
        // the length of each segment. We would eigther have to compute the length
        // of the whole way here (we can't: no node coordinates) or push that back
        // to the container and keep a reference to the way.
        forward_weight_data.duration = parsed_way.duration / num_edges;
        forward_weight_data.type = InternalExtractorEdge::WeightType::WAY_DURATION;
        backward_weight_data.duration = parsed_way.duration / num_edges;
        backward_weight_data.type = InternalExtractorEdge::WeightType::WAY_DURATION;
    }
    else
    {
        if (parsed_way.forward_speed > 0 &&
            parsed_way.forward_travel_mode != TRAVEL_MODE_INACCESSIBLE)
        {
            forward_weight_data.speed = parsed_way.forward_speed;
            forward_weight_data.type = InternalExtractorEdge::WeightType::SPEED;
        }
        if (parsed_way.backward_speed > 0 &&
            parsed_way.backward_travel_mode != TRAVEL_MODE_INACCESSIBLE)
        {
            backward_weight_data.speed = parsed_way.backward_speed;
            backward_weight_data.type = InternalExtractorEdge::WeightType::SPEED;
        }
    }

    if (forward_weight_data.type == InternalExtractorEdge::WeightType::INVALID
     && backward_weight_data.type == InternalExtractorEdge::WeightType::INVALID)
    {
        SimpleLogger().Write(logDEBUG) << "found way with bogus speed, id: " << input_way.id();
        return;
    }

    // Get the unique identifier for the street name
    const auto &string_map_iterator = string_map.find(parsed_way.name);
    unsigned name_id = external_memory.name_list.size();
    if (string_map.end() == string_map_iterator)
    {
        external_memory.name_list.push_back(parsed_way.name);
        string_map.insert(std::make_pair(parsed_way.name, name_id));
    }
    else
    {
        name_id = string_map_iterator->second;
    }

    const bool split_edge = (parsed_way.forward_speed > 0) &&
                            (TRAVEL_MODE_INACCESSIBLE != parsed_way.forward_travel_mode) &&
                            (parsed_way.backward_speed > 0) &&
                            (TRAVEL_MODE_INACCESSIBLE != parsed_way.backward_travel_mode) &&
                            ((parsed_way.forward_speed != parsed_way.backward_speed) ||
                             (parsed_way.forward_travel_mode != parsed_way.backward_travel_mode));

    // lambda to add edge to container
    auto pair_wise_segment_split_forward =
        [&](const osmium::NodeRef &first_node, const osmium::NodeRef &last_node)
    {
        const bool forward_only = split_edge || TRAVEL_MODE_INACCESSIBLE == parsed_way.backward_travel_mode;
        external_memory.all_edges_list.push_back(InternalExtractorEdge(
            first_node.ref(), last_node.ref(), name_id, forward_weight_data,
            true, !forward_only, parsed_way.roundabout, parsed_way.ignore_in_grid,
            parsed_way.is_access_restricted, parsed_way.forward_travel_mode, split_edge));
        external_memory.used_node_id_list.push_back(first_node.ref());
    };

    const bool is_opposite_way = TRAVEL_MODE_INACCESSIBLE == parsed_way.forward_travel_mode;
    // traverse way in reverse in this case
    if (is_opposite_way)
    {
        // why don't we have to swap the parsed_way.forward/backward speed here as well
        const_cast<ExtractionWay &>(parsed_way).forward_travel_mode =
            parsed_way.backward_travel_mode;
        const_cast<ExtractionWay &>(parsed_way).backward_travel_mode = TRAVEL_MODE_INACCESSIBLE;
        osrm::for_each_pair(input_way.nodes().crbegin(), input_way.nodes().crend(),
                            pair_wise_segment_split_forward);
        external_memory.used_node_id_list.push_back(input_way.nodes().front().ref());
    }
    else
    {
        osrm::for_each_pair(input_way.nodes().cbegin(), input_way.nodes().cend(),
                            pair_wise_segment_split_forward);
        external_memory.used_node_id_list.push_back(input_way.nodes().back().ref());
    }

    // The following information is needed to identify start and end segments of restrictions
    external_memory.way_start_end_id_list.push_back(
        {(EdgeID)input_way.id(),
         (NodeID)input_way.nodes()[0].ref(),
         (NodeID)input_way.nodes()[1].ref(),
         (NodeID)input_way.nodes()[input_way.nodes().size() - 2].ref(),
         (NodeID)input_way.nodes().back().ref()});

    if (split_edge)
    { // Only true if the way should be split
        BOOST_ASSERT(parsed_way.backward_travel_mode != TRAVEL_MODE_INACCESSIBLE);
        auto pair_wise_segment_split_2 =
            [&](const osmium::NodeRef &first_node, const osmium::NodeRef &last_node)
        {
            external_memory.all_edges_list.push_back(InternalExtractorEdge(
                last_node.ref(), first_node.ref(), name_id, backward_weight_data,
                true, false, parsed_way.roundabout, parsed_way.ignore_in_grid,
                parsed_way.is_access_restricted, parsed_way.backward_travel_mode, split_edge));
            external_memory.used_node_id_list.push_back(last_node.ref());
        };

        if (is_opposite_way)
        {
            osrm::for_each_pair(input_way.nodes().crbegin(), input_way.nodes().crend(),
                                pair_wise_segment_split_2);
            external_memory.used_node_id_list.push_back(input_way.nodes().front().ref());
        }
        else
        {
            osrm::for_each_pair(input_way.nodes().cbegin(), input_way.nodes().cend(),
                                pair_wise_segment_split_2);
            external_memory.used_node_id_list.push_back(input_way.nodes().front().ref());
        }

        external_memory.way_start_end_id_list.push_back(
            {(EdgeID)input_way.id(),
             (NodeID)input_way.nodes()[1].ref(),
             (NodeID)input_way.nodes()[0].ref(),
             (NodeID)input_way.nodes().back().ref(),
             (NodeID)input_way.nodes()[input_way.nodes().size() - 2].ref()});
    }
}
 static InternalExtractorEdge max_osm_value()
 {
     return InternalExtractorEdge(MAX_OSM_NODEID, MAX_OSM_NODEID, 0, WeightData(), false, false,
                                  false, false, true, TRAVEL_MODE_INACCESSIBLE, false,
                                  guidance::RoadClassificationData());
 }