示例#1
0
void RequestHandler::HandleRequest(const http::request &current_request, http::reply &current_reply)
{
    if (!service_handler)
    {
        current_reply = http::reply::stock_reply(http::reply::internal_server_error);
        util::Log(logWARNING) << "No service handler registered." << std::endl;
        return;
    }

    // parse command
    try
    {
        TIMER_START(request_duration);
        std::string request_string;
        util::URIDecode(current_request.uri, request_string);
        util::Log(logDEBUG) << "req: " << request_string;

        auto api_iterator = request_string.begin();
        auto maybe_parsed_url = api::parseURL(api_iterator, request_string.end());
        ServiceHandler::ResultT result;

        // check if the was an error with the request
        if (maybe_parsed_url && api_iterator == request_string.end())
        {

            const engine::Status status =
                service_handler->RunQuery(*std::move(maybe_parsed_url), result);
            if (status != engine::Status::Ok)
            {
                // 4xx bad request return code
                current_reply.status = http::reply::bad_request;
            }
            else
            {
                BOOST_ASSERT(status == engine::Status::Ok);
            }
        }
        else
        {
            const auto position = std::distance(request_string.begin(), api_iterator);
            BOOST_ASSERT(position >= 0);
            const auto context_begin =
                request_string.begin() + ((position < 3) ? 0 : (position - 3UL));
            BOOST_ASSERT(context_begin >= request_string.begin());
            const auto context_end = request_string.begin() +
                                     std::min<std::size_t>(position + 3UL, request_string.size());
            BOOST_ASSERT(context_end <= request_string.end());
            std::string context(context_begin, context_end);

            current_reply.status = http::reply::bad_request;
            result = util::json::Object();
            auto &json_result = result.get<util::json::Object>();
            json_result.values["code"] = "InvalidUrl";
            json_result.values["message"] = "URL string malformed close to position " +
                                            std::to_string(position) + ": \"" + context + "\"";
        }

        current_reply.headers.emplace_back("Access-Control-Allow-Origin", "*");
        current_reply.headers.emplace_back("Access-Control-Allow-Methods", "GET");
        current_reply.headers.emplace_back("Access-Control-Allow-Headers",
                                           "X-Requested-With, Content-Type");
        if (result.is<util::json::Object>())
        {
            current_reply.headers.emplace_back("Content-Type", "application/json; charset=UTF-8");
            current_reply.headers.emplace_back("Content-Disposition",
                                               "inline; filename=\"response.json\"");

            util::json::render(current_reply.content, result.get<util::json::Object>());
        }
        else
        {
            BOOST_ASSERT(result.is<std::string>());
            current_reply.content.resize(result.get<std::string>().size());
            std::copy(result.get<std::string>().cbegin(),
                      result.get<std::string>().cend(),
                      current_reply.content.begin());

            current_reply.headers.emplace_back("Content-Type", "application/x-protobuf");
        }

        // set headers
        current_reply.headers.emplace_back("Content-Length",
                                           std::to_string(current_reply.content.size()));

        if (!std::getenv("DISABLE_ACCESS_LOGGING"))
        {
            // deactivated as GCC apparently does not implement that, not even in 4.9
            // std::time_t t = std::time(nullptr);
            // util::Log() << std::put_time(std::localtime(&t), "%m-%d-%Y
            // %H:%M:%S") <<
            //     " " << current_request.endpoint.to_string() << " " <<
            //     current_request.referrer << ( 0 == current_request.referrer.length() ? "- " :" ")
            //     <<
            //     current_request.agent << ( 0 == current_request.agent.length() ? "- " :" ") <<
            //     request;

            time_t ltime;
            struct tm *time_stamp;
            TIMER_STOP(request_duration);

            ltime = time(nullptr);
            time_stamp = localtime(&ltime);
            // log timestamp
            util::Log() << (time_stamp->tm_mday < 10 ? "0" : "") << time_stamp->tm_mday << "-"
                        << (time_stamp->tm_mon + 1 < 10 ? "0" : "") << (time_stamp->tm_mon + 1)
                        << "-" << 1900 + time_stamp->tm_year << " "
                        << (time_stamp->tm_hour < 10 ? "0" : "") << time_stamp->tm_hour << ":"
                        << (time_stamp->tm_min < 10 ? "0" : "") << time_stamp->tm_min << ":"
                        << (time_stamp->tm_sec < 10 ? "0" : "") << time_stamp->tm_sec << " "
                        << TIMER_MSEC(request_duration) << "ms "
                        << current_request.endpoint.to_string() << " " << current_request.referrer
                        << (0 == current_request.referrer.length() ? "- " : " ")
                        << current_request.agent
                        << (0 == current_request.agent.length() ? "- " : " ")
                        << current_reply.status << " " //
                        << request_string;
        }
    }
    catch (const std::exception &e)
    {
        current_reply = http::reply::stock_reply(http::reply::internal_server_error);
        util::Log(logWARNING) << "[server error] code: " << e.what()
                              << ", uri: " << current_request.uri;
    }
}
示例#2
0
int main(int argc, char *argv[])
{
    LogPolicy::GetInstance().Unmute();
    try
    {
        // enable logging
        if (argc < 3)
        {
            SimpleLogger().Write(logWARNING) << "usage:\n" << argv[0]
                                             << " <osrm> <osrm.restrictions>";
            return -1;
        }

        SimpleLogger().Write() << "Using restrictions from file: " << argv[2];
        std::ifstream restriction_ifstream(argv[2], std::ios::binary);
        const FingerPrint fingerprint_orig;
        FingerPrint fingerprint_loaded;
        restriction_ifstream.read((char *)&fingerprint_loaded, sizeof(FingerPrint));

        // check fingerprint and warn if necessary
        if (!fingerprint_loaded.TestGraphUtil(fingerprint_orig))
        {
            SimpleLogger().Write(logWARNING) << argv[2] << " was prepared with a different build. "
                                                           "Reprocess to get rid of this warning.";
        }

        if (!restriction_ifstream.good())
        {
            throw osrm::exception("Could not access <osrm-restrictions> files");
        }
        uint32_t usable_restrictions = 0;
        restriction_ifstream.read((char *)&usable_restrictions, sizeof(uint32_t));
        restriction_list.resize(usable_restrictions);

        // load restrictions
        if (usable_restrictions > 0)
        {
            restriction_ifstream.read((char *)&(restriction_list[0]),
                                      usable_restrictions * sizeof(TurnRestriction));
        }
        restriction_ifstream.close();

        std::ifstream input_stream(argv[1], std::ifstream::in | std::ifstream::binary);
        if (!input_stream.is_open())
        {
            throw osrm::exception("Cannot open osrm file");
        }

        // load graph data
        std::vector<ImportEdge> edge_list;
        const NodeID number_of_nodes = readBinaryOSRMGraphFromStream(input_stream,
                                                                     edge_list,
                                                                     bollard_node_list,
                                                                     traffic_lights_list,
                                                                     &coordinate_list,
                                                                     restriction_list);
        input_stream.close();


        BOOST_ASSERT_MSG(restriction_list.size() == usable_restrictions,
                         "size of restriction_list changed");

        SimpleLogger().Write() << restriction_list.size() << " restrictions, "
                               << bollard_node_list.size() << " bollard nodes, "
                               << traffic_lights_list.size() << " traffic lights";

        traffic_lights_list.clear();
        traffic_lights_list.shrink_to_fit();

        // Building an node-based graph
        DeallocatingVector<TarjanEdge> graph_edge_list;
        for (const NodeBasedEdge &input_edge : edge_list)
        {
            if (input_edge.source == input_edge.target)
            {
                continue;
            }

            if (input_edge.forward)
            {
                graph_edge_list.emplace_back(input_edge.source,
                                       input_edge.target,
                                       (std::max)((int)input_edge.weight, 1),
                                       input_edge.name_id);
            }
            if (input_edge.backward)
            {
                graph_edge_list.emplace_back(input_edge.target,
                                       input_edge.source,
                                       (std::max)((int)input_edge.weight, 1),
                                       input_edge.name_id);
            }
        }
        edge_list.clear();
        edge_list.shrink_to_fit();
        BOOST_ASSERT_MSG(0 == edge_list.size() && 0 == edge_list.capacity(),
                         "input edge vector not properly deallocated");

        tbb::parallel_sort(graph_edge_list.begin(), graph_edge_list.end());
        auto graph = std::make_shared<TarjanDynamicGraph>(number_of_nodes, graph_edge_list);
        edge_list.clear();
        edge_list.shrink_to_fit();

        SimpleLogger().Write() << "Starting SCC graph traversal";

        RestrictionMap restriction_map(restriction_list);
        auto tarjan = osrm::make_unique<TarjanSCC<TarjanDynamicGraph>>(graph,
                                                                       restriction_map,
                                                                       bollard_node_list);
        tarjan->run();
        SimpleLogger().Write() << "identified: " << tarjan->get_number_of_components()
                           << " many components";
        SimpleLogger().Write() << "identified " << tarjan->get_size_one_count() << " SCCs of size 1";

        // output
        TIMER_START(SCC_RUN_SETUP);

        // remove files from previous run if exist
        DeleteFileIfExists("component.dbf");
        DeleteFileIfExists("component.shx");
        DeleteFileIfExists("component.shp");

        Percent p(graph->GetNumberOfNodes());

        OGRRegisterAll();

        const char *pszDriverName = "ESRI Shapefile";
        OGRSFDriver *poDriver =
            OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName(pszDriverName);
        if (nullptr == poDriver)
        {
            throw osrm::exception("ESRI Shapefile driver not available");
        }
        OGRDataSource *poDS = poDriver->CreateDataSource("component.shp", nullptr);

        if (nullptr == poDS)
        {
            throw osrm::exception("Creation of output file failed");
        }

        OGRSpatialReference *poSRS = new OGRSpatialReference();
        poSRS->importFromEPSG(4326);

        OGRLayer *poLayer = poDS->CreateLayer("component", poSRS, wkbLineString, nullptr);

        if (nullptr == poLayer)
        {
            throw osrm::exception("Layer creation failed.");
        }
        TIMER_STOP(SCC_RUN_SETUP);
        SimpleLogger().Write() << "shapefile setup took " << TIMER_MSEC(SCC_RUN_SETUP)/1000. << "s";

        uint64_t total_network_distance = 0;
        p.reinit(graph->GetNumberOfNodes());
        TIMER_START(SCC_OUTPUT);
        for (const NodeID source : osrm::irange(0u, graph->GetNumberOfNodes()))
        {
            p.printIncrement();
            for (const auto current_edge : graph->GetAdjacentEdgeRange(source))
            {
                const TarjanDynamicGraph::NodeIterator target = graph->GetTarget(current_edge);

                if (source < target || graph->EndEdges(target) == graph->FindEdge(target, source))
                {
                    total_network_distance +=
                        100 * FixedPointCoordinate::ApproximateEuclideanDistance(
                                  coordinate_list[source].lat,
                                  coordinate_list[source].lon,
                                  coordinate_list[target].lat,
                                  coordinate_list[target].lon);

                    BOOST_ASSERT(current_edge != SPECIAL_EDGEID);
                    BOOST_ASSERT(source != SPECIAL_NODEID);
                    BOOST_ASSERT(target != SPECIAL_NODEID);

                    const unsigned size_of_containing_component =
                        std::min(tarjan->get_component_size(source),
                                 tarjan->get_component_size(target));

                    // edges that end on bollard nodes may actually be in two distinct components
                    if (size_of_containing_component < 1000)
                    {
                        OGRLineString lineString;
                        lineString.addPoint(coordinate_list[source].lon / COORDINATE_PRECISION,
                                            coordinate_list[source].lat / COORDINATE_PRECISION);
                        lineString.addPoint(coordinate_list[target].lon / COORDINATE_PRECISION,
                                            coordinate_list[target].lat / COORDINATE_PRECISION);

                        OGRFeature *poFeature = OGRFeature::CreateFeature(poLayer->GetLayerDefn());

                        poFeature->SetGeometry(&lineString);
                        if (OGRERR_NONE != poLayer->CreateFeature(poFeature))
                        {
                            throw osrm::exception("Failed to create feature in shapefile.");
                        }
                        OGRFeature::DestroyFeature(poFeature);
                    }
                }
            }
        }
        OGRSpatialReference::DestroySpatialReference(poSRS);
        OGRDataSource::DestroyDataSource(poDS);
        TIMER_STOP(SCC_OUTPUT);
        SimpleLogger().Write() << "generating output took: " << TIMER_MSEC(SCC_OUTPUT)/1000. << "s";

        SimpleLogger().Write() << "total network distance: "
                               << (uint64_t)total_network_distance / 100 / 1000. << " km";

        SimpleLogger().Write() << "finished component analysis";
    }
    catch (const std::exception &e)
    {
        SimpleLogger().Write(logWARNING) << "[exception] " << e.what();
    }
    return 0;
}
示例#3
0
void Benchmark(BenchStaticRTree &rtree, unsigned num_queries)
{
    std::mt19937 mt_rand(RANDOM_SEED);
    std::uniform_int_distribution<> lat_udist(WORLD_MIN_LAT, WORLD_MAX_LAT);
    std::uniform_int_distribution<> lon_udist(WORLD_MIN_LON, WORLD_MAX_LON);
    std::vector<FixedPointCoordinate> queries;
    for (unsigned i = 0; i < num_queries; i++)
    {
        queries.emplace_back(FixedPointCoordinate(lat_udist(mt_rand), lon_udist(mt_rand)));
    }

    {
        const unsigned num_results = 5;
        std::cout << "#### IncrementalFindPhantomNodeForCoordinate : " << num_results
                  << " phantom nodes"
                  << "\n";

        TIMER_START(query_phantom);
        std::vector<PhantomNode> phantom_node_vector;
        for (const auto &q : queries)
        {
            phantom_node_vector.clear();
            rtree.IncrementalFindPhantomNodeForCoordinate(q, phantom_node_vector, 3, num_results);
            phantom_node_vector.clear();
            rtree.IncrementalFindPhantomNodeForCoordinate(q, phantom_node_vector, 17, num_results);
        }
        TIMER_STOP(query_phantom);

        std::cout << "Took " << TIMER_MSEC(query_phantom) << " msec for " << num_queries
                  << " queries."
                  << "\n";
        std::cout << TIMER_MSEC(query_phantom) / ((double)num_queries) << " msec/query."
                  << "\n";

        std::cout << "#### LocateClosestEndPointForCoordinate"
                  << "\n";
    }

    TIMER_START(query_endpoint);
    FixedPointCoordinate result;
    for (const auto &q : queries)
    {
        rtree.LocateClosestEndPointForCoordinate(q, result, 3);
    }
    TIMER_STOP(query_endpoint);

    std::cout << "Took " << TIMER_MSEC(query_endpoint) << " msec for " << num_queries << " queries."
              << "\n";
    std::cout << TIMER_MSEC(query_endpoint) / ((double)num_queries) << " msec/query."
              << "\n";

    std::cout << "#### FindPhantomNodeForCoordinate"
              << "\n";

    TIMER_START(query_node);
    for (const auto &q : queries)
    {
        PhantomNode phantom;
        rtree.FindPhantomNodeForCoordinate(q, phantom, 3);
    }
    TIMER_STOP(query_node);

    std::cout << "Took " << TIMER_MSEC(query_node) << " msec for " << num_queries << " queries."
              << "\n";
    std::cout << TIMER_MSEC(query_node) / ((double)num_queries) << " msec/query."
              << "\n";

    {
        const unsigned num_results = 1;
        std::cout << "#### IncrementalFindPhantomNodeForCoordinate : " << num_results
                  << " phantom nodes"
                  << "\n";

        TIMER_START(query_phantom);
        std::vector<PhantomNode> phantom_node_vector;
        for (const auto &q : queries)
        {
            phantom_node_vector.clear();
            rtree.IncrementalFindPhantomNodeForCoordinate(q, phantom_node_vector, 3, num_results);
            phantom_node_vector.clear();
            rtree.IncrementalFindPhantomNodeForCoordinate(q, phantom_node_vector, 17, num_results);
        }
        TIMER_STOP(query_phantom);

        std::cout << "Took " << TIMER_MSEC(query_phantom) << " msec for " << num_queries
                  << " queries."
                  << "\n";
        std::cout << TIMER_MSEC(query_phantom) / ((double)num_queries) << " msec/query."
                  << "\n";

        std::cout << "#### LocateClosestEndPointForCoordinate"
                  << "\n";
    }
}
    void Run()
    {
        TIMER_START(SCC_RUN);
        const NodeID max_node_id = m_graph->GetNumberOfNodes();

        // The following is a hack to distinguish between stuff that happens
        // before the recursive call and stuff that happens after
        std::stack<TarjanStackFrame> recursion_stack;
        // true = stuff before, false = stuff after call
        std::stack<NodeID> tarjan_stack;
        std::vector<TarjanNode> tarjan_node_list(max_node_id);
        unsigned component_index = 0, size_of_current_component = 0;
        unsigned index = 0;
        std::vector<bool> processing_node_before_recursion(max_node_id, true);
        for (const NodeID node : util::irange(0u, max_node_id))
        {
            if (SPECIAL_NODEID == components_index[node])
            {
                recursion_stack.emplace(TarjanStackFrame(node, node));
            }

            while (!recursion_stack.empty())
            {
                TarjanStackFrame currentFrame = recursion_stack.top();
                const NodeID u = currentFrame.parent;
                const NodeID v = currentFrame.v;
                recursion_stack.pop();

                const bool before_recursion = processing_node_before_recursion[v];

                if (before_recursion && tarjan_node_list[v].index != UINT_MAX)
                {
                    continue;
                }

                if (before_recursion)
                {
                    // Mark frame to handle tail of recursion
                    recursion_stack.emplace(currentFrame);
                    processing_node_before_recursion[v] = false;

                    // Mark essential information for SCC
                    tarjan_node_list[v].index = index;
                    tarjan_node_list[v].low_link = index;
                    tarjan_stack.push(v);
                    tarjan_node_list[v].on_stack = true;
                    ++index;

                    for (const auto current_edge : m_graph->GetAdjacentEdgeRange(v))
                    {
                        const auto vprime = m_graph->GetTarget(current_edge);

                        if (SPECIAL_NODEID == tarjan_node_list[vprime].index)
                        {
                            recursion_stack.emplace(TarjanStackFrame(vprime, v));
                        }
                        else
                        {
                            if (tarjan_node_list[vprime].on_stack &&
                                tarjan_node_list[vprime].index < tarjan_node_list[v].low_link)
                            {
                                tarjan_node_list[v].low_link = tarjan_node_list[vprime].index;
                            }
                        }
                    }
                }
                else
                {
                    processing_node_before_recursion[v] = true;
                    tarjan_node_list[u].low_link =
                        std::min(tarjan_node_list[u].low_link, tarjan_node_list[v].low_link);
                    // after recursion, lets do cycle checking
                    // Check if we found a cycle. This is the bottom part of the recursion
                    if (tarjan_node_list[v].low_link == tarjan_node_list[v].index)
                    {
                        NodeID vprime;
                        do
                        {
                            vprime = tarjan_stack.top();
                            tarjan_stack.pop();
                            tarjan_node_list[vprime].on_stack = false;
                            components_index[vprime] = component_index;
                            ++size_of_current_component;
                        } while (v != vprime);

                        component_size_vector.emplace_back(size_of_current_component);

                        if (size_of_current_component > 1000)
                        {
                            util::SimpleLogger().Write() << "large component [" << component_index
                                                         << "]=" << size_of_current_component;
                        }

                        ++component_index;
                        size_of_current_component = 0;
                    }
                }
            }
        }

        TIMER_STOP(SCC_RUN);
        util::SimpleLogger().Write() << "SCC run took: " << TIMER_MSEC(SCC_RUN) / 1000. << "s";

        size_one_counter = std::count_if(component_size_vector.begin(),
                                         component_size_vector.end(),
                                         [](unsigned value) { return 1 == value; });
    }
示例#5
0
int main(int argc, char *argv[])
{
    std::vector<osrm::extractor::QueryNode> coordinate_list;
    osrm::util::LogPolicy::GetInstance().Unmute();

    // enable logging
    if (argc < 2)
    {
        osrm::util::Log(logWARNING) << "usage:\n" << argv[0] << " <osrm>";
        return EXIT_FAILURE;
    }

    std::vector<osrm::tools::TarjanEdge> graph_edge_list;
    auto number_of_nodes =
        osrm::tools::loadGraph(std::string(argv[1]), coordinate_list, graph_edge_list);

    tbb::parallel_sort(graph_edge_list.begin(), graph_edge_list.end());
    const auto graph = std::make_shared<osrm::tools::TarjanGraph>(number_of_nodes, graph_edge_list);
    graph_edge_list.clear();
    graph_edge_list.shrink_to_fit();

    osrm::util::Log() << "Starting SCC graph traversal";

    auto tarjan = std::make_unique<osrm::extractor::TarjanSCC<osrm::tools::TarjanGraph>>(graph);
    tarjan->Run();
    osrm::util::Log() << "identified: " << tarjan->GetNumberOfComponents() << " many components";
    osrm::util::Log() << "identified " << tarjan->GetSizeOneCount() << " size 1 SCCs";

    // output
    TIMER_START(SCC_RUN_SETUP);

    // remove files from previous run if exist
    osrm::tools::deleteFileIfExists("component.dbf");
    osrm::tools::deleteFileIfExists("component.shx");
    osrm::tools::deleteFileIfExists("component.shp");

    OGRRegisterAll();

    const char *psz_driver_name = "ESRI Shapefile";
    auto *po_driver = OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName(psz_driver_name);
    if (nullptr == po_driver)
    {
        throw osrm::util::exception("ESRI Shapefile driver not available" + SOURCE_REF);
    }
    auto *po_datasource = po_driver->CreateDataSource("component.shp", nullptr);

    if (nullptr == po_datasource)
    {
        throw osrm::util::exception("Creation of output file failed" + SOURCE_REF);
    }

    auto *po_srs = new OGRSpatialReference();
    po_srs->importFromEPSG(4326);

    auto *po_layer = po_datasource->CreateLayer("component", po_srs, wkbLineString, nullptr);

    if (nullptr == po_layer)
    {
        throw osrm::util::exception("Layer creation failed." + SOURCE_REF);
    }
    TIMER_STOP(SCC_RUN_SETUP);
    osrm::util::Log() << "shapefile setup took " << TIMER_MSEC(SCC_RUN_SETUP) / 1000. << "s";

    TIMER_START(SCC_OUTPUT);
    uint64_t total_network_length = 0;
    {
        osrm::util::UnbufferedLog log;
        log << "Constructing geometry ";
        osrm::util::Percent percentage(log, graph->GetNumberOfNodes());
        for (const NodeID source : osrm::util::irange(0u, graph->GetNumberOfNodes()))
        {
            percentage.PrintIncrement();
            for (const auto current_edge : graph->GetAdjacentEdgeRange(source))
            {
                const auto target = graph->GetTarget(current_edge);

                if (source < target || SPECIAL_EDGEID == graph->FindEdge(target, source))
                {
                    total_network_length +=
                        100 * osrm::util::coordinate_calculation::greatCircleDistance(
                                  coordinate_list[source], coordinate_list[target]);

                    BOOST_ASSERT(current_edge != SPECIAL_EDGEID);
                    BOOST_ASSERT(source != SPECIAL_NODEID);
                    BOOST_ASSERT(target != SPECIAL_NODEID);

                    const unsigned size_of_containing_component =
                        std::min(tarjan->GetComponentSize(tarjan->GetComponentID(source)),
                                 tarjan->GetComponentSize(tarjan->GetComponentID(target)));

                    // edges that end on bollard nodes may actually be in two distinct components
                    if (size_of_containing_component < 1000)
                    {
                        OGRLineString line_string;
                        line_string.addPoint(static_cast<double>(osrm::util::toFloating(
                                                 coordinate_list[source].lon)),
                                             static_cast<double>(osrm::util::toFloating(
                                                 coordinate_list[source].lat)));
                        line_string.addPoint(static_cast<double>(osrm::util::toFloating(
                                                 coordinate_list[target].lon)),
                                             static_cast<double>(osrm::util::toFloating(
                                                 coordinate_list[target].lat)));

                        OGRFeature *po_feature =
                            OGRFeature::CreateFeature(po_layer->GetLayerDefn());

                        po_feature->SetGeometry(&line_string);
                        if (OGRERR_NONE != po_layer->CreateFeature(po_feature))
                        {
                            throw osrm::util::exception("Failed to create feature in shapefile." +
                                                        SOURCE_REF);
                        }
                        OGRFeature::DestroyFeature(po_feature);
                    }
                }
            }
        }
    }
    OGRSpatialReference::DestroySpatialReference(po_srs);
    OGRDataSource::DestroyDataSource(po_datasource);
    TIMER_STOP(SCC_OUTPUT);
    osrm::util::Log() << "generating output took: " << TIMER_MSEC(SCC_OUTPUT) / 1000. << "s";

    osrm::util::Log() << "total network distance: "
                      << static_cast<uint64_t>(total_network_length / 100 / 1000.) << " km";

    osrm::util::Log() << "finished component analysis";
    return EXIT_SUCCESS;
}