<commit_before>// Copyright (c) 2013-2020 mogemimi. Distributed under the MIT license.

#pragma once

// The version numbers follow the [Semantic Versioning](http://semver.org/)
#define POMDOG_MAJOR_VERSION 0
#define POMDOG_MINOR_VERSION 1
#define POMDOG_PATCH_VERSION 6
<commit_msg>Bump version to 0.1.7<commit_after>// Copyright (c) 2013-2020 mogemimi. Distributed under the MIT license.

#pragma once

// The version numbers follow the [Semantic Versioning](http://semver.org/)
#define POMDOG_MAJOR_VERSION 0
#define POMDOG_MINOR_VERSION 1
#define POMDOG_PATCH_VERSION 7
<|endoftext|>


<commit_before>#ifndef _HDB_TRIE_H_
#define _HDB_TRIE_H_

#include <string>

#include <am/concept/DataType.h>
#include <hdb/HugeDB.h>

#include <am/db_trie/common_trie.h>

NS_IZENELIB_AM_BEGIN

template <typename CharType,
          typename NodeIDType,
          typename LockType>
class HdbTrieEdgeTable : public CommonEdgeTable<CharType, NodeIDType, LockType>
{
    typedef CommonEdgeTable<CharType, NodeIDType, LockType> CommonType;
    typedef HdbTrieEdgeTable<CharType, NodeIDType, LockType> ThisType;

public:

    typedef typename CommonType::EdgeTableKeyType EdgeTableKeyType;
    typedef typename CommonType::EdgeTableValueType EdgeTableValueType;

    typedef izenelib::hdb::ordered_hdb_fixed_no_delta<EdgeTableKeyType,
        EdgeTableValueType , LockType> DBType;

    HdbTrieEdgeTable(const std::string& name)
    : CommonType(name), db_(name) { }

    virtual ~HdbTrieEdgeTable() { }

    /**
     * @brief Open all db files, maps them to memory. Call it
     *        after config() and before any insert() or find() operations.
     */
    void open()
    {
        db_.setDegree(128);
        db_.setPageSize(8192);
        db_.setCachedRecordsNumber(2500000);
        db_.setMergeFactor(2);
        db_.open();
    }

    void flush()
    {
        db_.flush();
    }

    void close()
    {
        db_.close();
    }

    void optimize()
    {
        db_.optimize();
    }

    virtual void insertValue(const EdgeTableKeyType& key, const EdgeTableValueType & value)
    {
        db_.insertValue(key, value);
    }

    virtual bool getValue(const EdgeTableKeyType& key, EdgeTableValueType & value)
    {
        return db_.getValue(key, value);
    }

    virtual bool getValueBetween( std::vector<DataType<EdgeTableKeyType,EdgeTableValueType> > & results,
        const EdgeTableKeyType& start, const EdgeTableKeyType& end)
    {
        return db_.getValueBetween(results, start, end);
    }

    DBType& getDB()
    {
        return db_;
    }

private:

    DBType db_;
};

template <typename NodeIDType,
          typename UserDataType,
          typename LockType>
class HdbTrieDataTable : public CommonDataTable<NodeIDType, UserDataType, LockType>
{
    typedef CommonDataTable<NodeIDType, UserDataType, LockType> CommonType;
    typedef HdbTrieDataTable<NodeIDType, UserDataType, LockType> ThisType;

public:

    typedef typename CommonType::DataTableKeyType DataTableKeyType;
    typedef typename CommonType::DataTableValueType DataTableValueType;

    typedef izenelib::hdb::ordered_hdb_fixed_no_delta<
        DataTableKeyType,
        DataTableValueType,
        LockType> DBType;

    HdbTrieDataTable(const std::string& name)
    : CommonType(name), db_(name) { }

    virtual ~HdbTrieDataTable() { }

    /**
     * @brief Open all db files, maps them to memory. Call it
     *        after config() and before any insert() or find() operations.
     */
    void open()
    {
        db_.setDegree(32);
        db_.setPageSize(1024);
        db_.setCachedRecordsNumber(2500000);
        db_.setMergeFactor(2);
        db_.open();
    }

    void flush()
    {
        db_.flush();
    }

    void close()
    {
        db_.close();
    }

    void optimize()
    {
        db_.optimize();
    }

    unsigned int numItems()
    {
        return db_.numItems();
    }

    virtual void insertValue(const DataTableKeyType& key, const DataTableValueType & value)
    {
        db_.insertValue(key, value);
    }

    virtual void update(const DataTableKeyType& key, const DataTableValueType & value)
    {
        db_.update(key, value);
    }

    virtual bool getValue(const DataTableKeyType& key, DataTableValueType & value)
    {
        return db_.getValue(key, value);
    }

    DBType& getDB()
    {
        return db_;
    }

private:

    DBType db_;
};

/**
 * @brief Definitions for distributed tries on each partition.
 */
template <typename StringType,
          typename UserDataType,
          typename NodeIDType = uint64_t,
          typename LockType = izenelib::util::NullLock>
class HDBTrie
    : public CommonTrie<StringType,
                        UserDataType,
                        NodeIDType,
                        HdbTrieEdgeTable      <typename StringType::value_type,
                                               NodeIDType,
                                               LockType>,
                        HdbTrieDataTable      <NodeIDType,
                                               UserDataType,
                                               LockType> >
{
public:
    typedef HdbTrieEdgeTable      <typename StringType::value_type,
                                   NodeIDType,
                                   LockType>
            EdgeTableType;

    typedef HdbTrieDataTable      <NodeIDType, UserDataType, LockType>
            DataTableType;

    typedef CommonTrie <StringType, UserDataType, NodeIDType,
                EdgeTableType, DataTableType> CommonType;

    HDBTrie(const std::string name)
    :   CommonType(name) {}

    void openForWrite()
    {
        CommonType::open();
    }

    void openForRead()
    {
        CommonType::open();
    }

    virtual ~HDBTrie() {}
};

#define HDBTrie2 HDBTrie

NS_IZENELIB_AM_END

#endif
<commit_msg>fix hdb_trie<commit_after>#ifndef _HDB_TRIE_H_
#define _HDB_TRIE_H_

#include <string>

#include <am/concept/DataType.h>
#include <hdb/HugeDB.h>

#include <am/db_trie/common_trie.h>

NS_IZENELIB_AM_BEGIN

template <typename CharType,
          typename NodeIDType,
          typename LockType>
class HdbTrieEdgeTable : public CommonEdgeTable<CharType, NodeIDType, LockType>
{
    typedef CommonEdgeTable<CharType, NodeIDType, LockType> CommonType;
    typedef HdbTrieEdgeTable<CharType, NodeIDType, LockType> ThisType;

public:

    typedef typename CommonType::EdgeTableKeyType EdgeTableKeyType;
    typedef typename CommonType::EdgeTableValueType EdgeTableValueType;

    typedef izenelib::hdb::ordered_hdb_fixed_no_delta<EdgeTableKeyType,
        EdgeTableValueType , LockType> DBType;

    HdbTrieEdgeTable(const std::string& name)
    : CommonType(name), db_(name) { }

    virtual ~HdbTrieEdgeTable() { }

    /**
     * @brief Open all db files, maps them to memory. Call it
     *        after config() and before any insert() or find() operations.
     */
    void open()
    {
        db_.setDegree(128);
        db_.setPageSize(8192);
        db_.setCachedRecordsNumber(2500000);
        db_.setMergeFactor(2);
        db_.open();
    }

    void flush()
    {
        db_.flush();
    }

    void close()
    {
        db_.close();
    }

    void optimize()
    {
        db_.optimize();
    }

    virtual void insertValue(const EdgeTableKeyType& key, const EdgeTableValueType & value)
    {
        db_.insertValue(key, value);
    }

    virtual bool getValue(const EdgeTableKeyType& key, EdgeTableValueType & value)
    {
        return db_.getValue(key, value);
    }

    virtual bool getValueBetween( std::vector<DataType<EdgeTableKeyType,EdgeTableValueType> > & results,
        const EdgeTableKeyType& start, const EdgeTableKeyType& end)
    {
        return db_.getValueBetween(results, start, end);
    }

    DBType& getDB()
    {
        return db_;
    }

private:

    DBType db_;
};

template <typename NodeIDType,
          typename UserDataType,
          typename LockType>
class HdbTrieDataTable : public CommonDataTable<NodeIDType, UserDataType, LockType>
{
    typedef CommonDataTable<NodeIDType, UserDataType, LockType> CommonType;
    typedef HdbTrieDataTable<NodeIDType, UserDataType, LockType> ThisType;

public:

    typedef typename CommonType::DataTableKeyType DataTableKeyType;
    typedef typename CommonType::DataTableValueType DataTableValueType;

    typedef izenelib::hdb::ordered_hdb_no_delta<
        DataTableKeyType,
        DataTableValueType,
        LockType> DBType;

    HdbTrieDataTable(const std::string& name)
    : CommonType(name), db_(name) { }

    virtual ~HdbTrieDataTable() { }

    /**
     * @brief Open all db files, maps them to memory. Call it
     *        after config() and before any insert() or find() operations.
     */
    void open()
    {
        db_.setDegree(32);
        db_.setPageSize(1024);
        db_.setCachedRecordsNumber(2500000);
        db_.setMergeFactor(2);
        db_.open();
    }

    void flush()
    {
        db_.flush();
    }

    void close()
    {
        db_.close();
    }

    void optimize()
    {
        db_.optimize();
    }

    unsigned int numItems()
    {
        return db_.numItems();
    }

    virtual void insertValue(const DataTableKeyType& key, const DataTableValueType & value)
    {
        db_.insertValue(key, value);
    }

    virtual void update(const DataTableKeyType& key, const DataTableValueType & value)
    {
        db_.update(key, value);
    }

    virtual bool getValue(const DataTableKeyType& key, DataTableValueType & value)
    {
        return db_.getValue(key, value);
    }

    DBType& getDB()
    {
        return db_;
    }

private:

    DBType db_;
};

/**
 * @brief Definitions for distributed tries on each partition.
 */
template <typename StringType,
          typename UserDataType,
          typename NodeIDType = uint64_t,
          typename LockType = izenelib::util::NullLock>
class HDBTrie
    : public CommonTrie<StringType,
                        UserDataType,
                        NodeIDType,
                        HdbTrieEdgeTable      <typename StringType::value_type,
                                               NodeIDType,
                                               LockType>,
                        HdbTrieDataTable      <NodeIDType,
                                               UserDataType,
                                               LockType> >
{
public:
    typedef HdbTrieEdgeTable      <typename StringType::value_type,
                                   NodeIDType,
                                   LockType>
            EdgeTableType;

    typedef HdbTrieDataTable      <NodeIDType, UserDataType, LockType>
            DataTableType;

    typedef CommonTrie <StringType, UserDataType, NodeIDType,
                EdgeTableType, DataTableType> CommonType;

    HDBTrie(const std::string name)
    :   CommonType(name) {}

    void openForWrite()
    {
        CommonType::open();
    }

    void openForRead()
    {
        CommonType::open();
    }

    virtual ~HDBTrie() {}
};

#define HDBTrie2 HDBTrie

NS_IZENELIB_AM_END

#endif
<|endoftext|>


<commit_before>#ifndef ENGINE_API_ROUTE_HPP
#define ENGINE_API_ROUTE_HPP

#include "engine/api/base_api.hpp"
#include "engine/api/json_factory.hpp"
#include "engine/api/route_parameters.hpp"

#include "engine/datafacade/datafacade_base.hpp"

#include "engine/guidance/assemble_geometry.hpp"
#include "engine/guidance/assemble_leg.hpp"
#include "engine/guidance/assemble_overview.hpp"
#include "engine/guidance/assemble_route.hpp"
#include "engine/guidance/assemble_steps.hpp"
#include "engine/guidance/collapse_turns.hpp"
#include "engine/guidance/lane_processing.hpp"
#include "engine/guidance/post_processing.hpp"
#include "engine/guidance/verbosity_reduction.hpp"

#include "engine/internal_route_result.hpp"

#include "util/coordinate.hpp"
#include "util/integer_range.hpp"
#include "util/json_util.hpp"

#include <iterator>
#include <vector>

namespace osrm
{
namespace engine
{
namespace api
{

class RouteAPI : public BaseAPI
{
  public:
    RouteAPI(const datafacade::BaseDataFacade &facade_, const RouteParameters &parameters_)
        : BaseAPI(facade_, parameters_), parameters(parameters_)
    {
    }

    void MakeResponse(const InternalManyRoutesResult &raw_routes,
                      util::json::Object &response) const
    {
        BOOST_ASSERT(!raw_routes.routes.empty());

        util::json::Array jsRoutes;

        for (const auto &route : raw_routes.routes)
        {
            if (!route.is_valid())
                continue;

            jsRoutes.values.push_back(MakeRoute(route.segment_end_coordinates,
                                                route.unpacked_path_segments,
                                                route.source_traversed_in_reverse,
                                                route.target_traversed_in_reverse));
        }

        response.values["waypoints"] =
            BaseAPI::MakeWaypoints(raw_routes.routes[0].segment_end_coordinates);
        response.values["routes"] = std::move(jsRoutes);
        response.values["code"] = "Ok";
    }

  protected:
    template <typename ForwardIter>
    util::json::Value MakeGeometry(ForwardIter begin, ForwardIter end) const
    {
        if (parameters.geometries == RouteParameters::GeometriesType::Polyline)
        {
            return json::makePolyline<100000>(begin, end);
        }

        if (parameters.geometries == RouteParameters::GeometriesType::Polyline6)
        {
            return json::makePolyline<1000000>(begin, end);
        }

        BOOST_ASSERT(parameters.geometries == RouteParameters::GeometriesType::GeoJSON);
        return json::makeGeoJSONGeometry(begin, end);
    }

    template <typename GetFn>
    util::json::Array GetAnnotations(const guidance::LegGeometry &leg, GetFn Get) const
    {
        util::json::Array annotations_store;
        annotations_store.values.reserve(leg.annotations.size());

        for (const auto& step : leg.annotations)
        {
              annotations_store.values.push_back(Get(step));
        }

        return annotations_store;
    }

    util::json::Object MakeRoute(const std::vector<PhantomNodes> &segment_end_coordinates,
                                 const std::vector<std::vector<PathData>> &unpacked_path_segments,
                                 const std::vector<bool> &source_traversed_in_reverse,
                                 const std::vector<bool> &target_traversed_in_reverse) const
    {
        std::vector<guidance::RouteLeg> legs;
        std::vector<guidance::LegGeometry> leg_geometries;
        auto number_of_legs = segment_end_coordinates.size();
        legs.reserve(number_of_legs);
        leg_geometries.reserve(number_of_legs);

        for (auto idx : util::irange<std::size_t>(0UL, number_of_legs))
        {
            const auto &phantoms = segment_end_coordinates[idx];
            const auto &path_data = unpacked_path_segments[idx];

            const bool reversed_source = source_traversed_in_reverse[idx];
            const bool reversed_target = target_traversed_in_reverse[idx];

            auto leg_geometry = guidance::assembleGeometry(BaseAPI::facade,
                                                           path_data,
                                                           phantoms.source_phantom,
                                                           phantoms.target_phantom,
                                                           reversed_source,
                                                           reversed_target);
            auto leg = guidance::assembleLeg(facade,
                                             path_data,
                                             leg_geometry,
                                             phantoms.source_phantom,
                                             phantoms.target_phantom,
                                             reversed_target,
                                             parameters.steps);

            if (parameters.steps)
            {
                auto steps = guidance::assembleSteps(BaseAPI::facade,
                                                     path_data,
                                                     leg_geometry,
                                                     phantoms.source_phantom,
                                                     phantoms.target_phantom,
                                                     reversed_source,
                                                     reversed_target);

                /* Perform step-based post-processing.
                 *
                 * Using post-processing on basis of route-steps for a single leg at a time
                 * comes at the cost that we cannot count the correct exit for roundabouts.
                 * We can only emit the exit nr/intersections up to/starting at a part of the leg.
                 * If a roundabout is not terminated in a leg, we will end up with a
                 *enter-roundabout
                 * and exit-roundabout-nr where the exit nr is out of sync with the previous enter.
                 *
                 *         | S |
                 *         *   *
                 *  ----*        * ----
                 *                  T
                 *  ----*        * ----
                 *       V *   *
                 *         |   |
                 *         |   |
                 *
                 * Coming from S via V to T, we end up with the legs S->V and V->T. V-T will say to
                 *take
                 * the second exit, even though counting from S it would be the third.
                 * For S, we only emit `roundabout` without an exit number, showing that we enter a
                 *roundabout
                 * to find a via point.
                 * The same exit will be emitted, though, if we should start routing at S, making
                 * the overall response consistent.
                 *
                 * ⚠ CAUTION: order of post-processing steps is important
                 *    - handleRoundabouts must be called before collapseTurnInstructions that
                 *      expects post-processed roundabouts
                 */

                guidance::trimShortSegments(steps, leg_geometry);
                leg.steps = guidance::handleRoundabouts(std::move(steps));
                leg.steps = guidance::collapseTurnInstructions(std::move(leg.steps));
                leg.steps = guidance::anticipateLaneChange(std::move(leg.steps));
                leg.steps = guidance::buildIntersections(std::move(leg.steps));
                leg.steps = guidance::suppressShortNameSegments(std::move(leg.steps));
                leg.steps = guidance::assignRelativeLocations(std::move(leg.steps),
                                                              leg_geometry,
                                                              phantoms.source_phantom,
                                                              phantoms.target_phantom);
                leg_geometry = guidance::resyncGeometry(std::move(leg_geometry), leg.steps);
            }

            leg_geometries.push_back(std::move(leg_geometry));
            legs.push_back(std::move(leg));
        }

        auto route = guidance::assembleRoute(legs);
        boost::optional<util::json::Value> json_overview;
        if (parameters.overview != RouteParameters::OverviewType::False)
        {
            const auto use_simplification =
                parameters.overview == RouteParameters::OverviewType::Simplified;
            BOOST_ASSERT(use_simplification ||
                         parameters.overview == RouteParameters::OverviewType::Full);

            auto overview = guidance::assembleOverview(leg_geometries, use_simplification);
            json_overview = MakeGeometry(overview.begin(), overview.end());
        }

        std::vector<util::json::Value> step_geometries;
        for (const auto idx : util::irange<std::size_t>(0UL, legs.size()))
        {
            auto &leg_geometry = leg_geometries[idx];

            step_geometries.reserve(step_geometries.size() + legs[idx].steps.size());

            std::transform(
                legs[idx].steps.begin(),
                legs[idx].steps.end(),
                std::back_inserter(step_geometries),
                [this, &leg_geometry](const guidance::RouteStep &step) {
                    if (parameters.geometries == RouteParameters::GeometriesType::Polyline)
                    {
                        return static_cast<util::json::Value>(json::makePolyline<100000>(
                            leg_geometry.locations.begin() + step.geometry_begin,
                            leg_geometry.locations.begin() + step.geometry_end));
                    }

                    if (parameters.geometries == RouteParameters::GeometriesType::Polyline6)
                    {
                        return static_cast<util::json::Value>(json::makePolyline<1000000>(
                            leg_geometry.locations.begin() + step.geometry_begin,
                            leg_geometry.locations.begin() + step.geometry_end));
                    }

                    BOOST_ASSERT(parameters.geometries == RouteParameters::GeometriesType::GeoJSON);
                    return static_cast<util::json::Value>(json::makeGeoJSONGeometry(
                        leg_geometry.locations.begin() + step.geometry_begin,
                        leg_geometry.locations.begin() + step.geometry_end));
                });
        }

        std::vector<util::json::Object> annotations;

        // To maintain support for uses of the old default constructors, we check
        // if annotations property was set manually after default construction
        auto requested_annotations = parameters.annotations_type;
        if ((parameters.annotations == true) &&
            (parameters.annotations_type == RouteParameters::AnnotationsType::None))
        {
            requested_annotations = RouteParameters::AnnotationsType::All;
        }

        if (requested_annotations != RouteParameters::AnnotationsType::None)
        {
            for (const auto idx : util::irange<std::size_t>(0UL, leg_geometries.size()))
            {
                auto &leg_geometry = leg_geometries[idx];
                util::json::Object annotation;

                // AnnotationsType uses bit flags, & operator checks if a property is set
                if (parameters.annotations_type & RouteParameters::AnnotationsType::Speed)
                {
                    double prev_speed = 0;
                    annotation.values["speed"] = GetAnnotations(
                        leg_geometry, [&prev_speed](const guidance::LegGeometry::Annotation &anno) {
                            if (anno.duration < std::numeric_limits<double>::min())
                            {
                                return prev_speed;
                            }
                            else
                            {
                                auto speed = std::round(anno.distance / anno.duration * 10.) / 10.;
                                prev_speed = speed;
                                return util::json::clamp_float(speed);
                            }
                        });
                }

                if (requested_annotations & RouteParameters::AnnotationsType::Duration)
                {
                    annotation.values["duration"] = GetAnnotations(
                        leg_geometry, [](const guidance::LegGeometry::Annotation &anno) {
                            return anno.duration;
                        });
                }
                if (requested_annotations & RouteParameters::AnnotationsType::Distance)
                {
                    annotation.values["distance"] = GetAnnotations(
                        leg_geometry, [](const guidance::LegGeometry::Annotation &anno) {
                            return anno.distance;
                        });
                }
                if (requested_annotations & RouteParameters::AnnotationsType::Weight)
                {
                    annotation.values["weight"] = GetAnnotations(
                        leg_geometry,
                        [](const guidance::LegGeometry::Annotation &anno) { return anno.weight; });
                }
                if (requested_annotations & RouteParameters::AnnotationsType::Datasources)
                {
                    annotation.values["datasources"] = GetAnnotations(
                        leg_geometry, [](const guidance::LegGeometry::Annotation &anno) {
                            return anno.datasource;
                        });
                }
                if (requested_annotations & RouteParameters::AnnotationsType::Nodes)
                {
                    util::json::Array nodes;
                    nodes.values.reserve(leg_geometry.osm_node_ids.size());
                    for (const auto node_id : leg_geometry.osm_node_ids)
                    {
                          nodes.values.push_back(static_cast<std::uint64_t>(node_id));
                    }
                    annotation.values["nodes"] = std::move(nodes);
                }

                annotations.push_back(std::move(annotation));
            }
        }

        auto result = json::makeRoute(route,
                                      json::makeRouteLegs(std::move(legs),
                                                          std::move(step_geometries),
                                                          std::move(annotations)),
                                      std::move(json_overview),
                                      facade.GetWeightName());

        return result;
    }

    const RouteParameters &parameters;
};

} // ns api
} // ns engine
} // ns osrm

#endif
<commit_msg>Fix formatting<commit_after>#ifndef ENGINE_API_ROUTE_HPP
#define ENGINE_API_ROUTE_HPP

#include "engine/api/base_api.hpp"
#include "engine/api/json_factory.hpp"
#include "engine/api/route_parameters.hpp"

#include "engine/datafacade/datafacade_base.hpp"

#include "engine/guidance/assemble_geometry.hpp"
#include "engine/guidance/assemble_leg.hpp"
#include "engine/guidance/assemble_overview.hpp"
#include "engine/guidance/assemble_route.hpp"
#include "engine/guidance/assemble_steps.hpp"
#include "engine/guidance/collapse_turns.hpp"
#include "engine/guidance/lane_processing.hpp"
#include "engine/guidance/post_processing.hpp"
#include "engine/guidance/verbosity_reduction.hpp"

#include "engine/internal_route_result.hpp"

#include "util/coordinate.hpp"
#include "util/integer_range.hpp"
#include "util/json_util.hpp"

#include <iterator>
#include <vector>

namespace osrm
{
namespace engine
{
namespace api
{

class RouteAPI : public BaseAPI
{
  public:
    RouteAPI(const datafacade::BaseDataFacade &facade_, const RouteParameters &parameters_)
        : BaseAPI(facade_, parameters_), parameters(parameters_)
    {
    }

    void MakeResponse(const InternalManyRoutesResult &raw_routes,
                      util::json::Object &response) const
    {
        BOOST_ASSERT(!raw_routes.routes.empty());

        util::json::Array jsRoutes;

        for (const auto &route : raw_routes.routes)
        {
            if (!route.is_valid())
                continue;

            jsRoutes.values.push_back(MakeRoute(route.segment_end_coordinates,
                                                route.unpacked_path_segments,
                                                route.source_traversed_in_reverse,
                                                route.target_traversed_in_reverse));
        }

        response.values["waypoints"] =
            BaseAPI::MakeWaypoints(raw_routes.routes[0].segment_end_coordinates);
        response.values["routes"] = std::move(jsRoutes);
        response.values["code"] = "Ok";
    }

  protected:
    template <typename ForwardIter>
    util::json::Value MakeGeometry(ForwardIter begin, ForwardIter end) const
    {
        if (parameters.geometries == RouteParameters::GeometriesType::Polyline)
        {
            return json::makePolyline<100000>(begin, end);
        }

        if (parameters.geometries == RouteParameters::GeometriesType::Polyline6)
        {
            return json::makePolyline<1000000>(begin, end);
        }

        BOOST_ASSERT(parameters.geometries == RouteParameters::GeometriesType::GeoJSON);
        return json::makeGeoJSONGeometry(begin, end);
    }

    template <typename GetFn>
    util::json::Array GetAnnotations(const guidance::LegGeometry &leg, GetFn Get) const
    {
        util::json::Array annotations_store;
        annotations_store.values.reserve(leg.annotations.size());

        for (const auto &step : leg.annotations)
        {
            annotations_store.values.push_back(Get(step));
        }

        return annotations_store;
    }

    util::json::Object MakeRoute(const std::vector<PhantomNodes> &segment_end_coordinates,
                                 const std::vector<std::vector<PathData>> &unpacked_path_segments,
                                 const std::vector<bool> &source_traversed_in_reverse,
                                 const std::vector<bool> &target_traversed_in_reverse) const
    {
        std::vector<guidance::RouteLeg> legs;
        std::vector<guidance::LegGeometry> leg_geometries;
        auto number_of_legs = segment_end_coordinates.size();
        legs.reserve(number_of_legs);
        leg_geometries.reserve(number_of_legs);

        for (auto idx : util::irange<std::size_t>(0UL, number_of_legs))
        {
            const auto &phantoms = segment_end_coordinates[idx];
            const auto &path_data = unpacked_path_segments[idx];

            const bool reversed_source = source_traversed_in_reverse[idx];
            const bool reversed_target = target_traversed_in_reverse[idx];

            auto leg_geometry = guidance::assembleGeometry(BaseAPI::facade,
                                                           path_data,
                                                           phantoms.source_phantom,
                                                           phantoms.target_phantom,
                                                           reversed_source,
                                                           reversed_target);
            auto leg = guidance::assembleLeg(facade,
                                             path_data,
                                             leg_geometry,
                                             phantoms.source_phantom,
                                             phantoms.target_phantom,
                                             reversed_target,
                                             parameters.steps);

            if (parameters.steps)
            {
                auto steps = guidance::assembleSteps(BaseAPI::facade,
                                                     path_data,
                                                     leg_geometry,
                                                     phantoms.source_phantom,
                                                     phantoms.target_phantom,
                                                     reversed_source,
                                                     reversed_target);

                /* Perform step-based post-processing.
                 *
                 * Using post-processing on basis of route-steps for a single leg at a time
                 * comes at the cost that we cannot count the correct exit for roundabouts.
                 * We can only emit the exit nr/intersections up to/starting at a part of the leg.
                 * If a roundabout is not terminated in a leg, we will end up with a
                 *enter-roundabout
                 * and exit-roundabout-nr where the exit nr is out of sync with the previous enter.
                 *
                 *         | S |
                 *         *   *
                 *  ----*        * ----
                 *                  T
                 *  ----*        * ----
                 *       V *   *
                 *         |   |
                 *         |   |
                 *
                 * Coming from S via V to T, we end up with the legs S->V and V->T. V-T will say to
                 *take
                 * the second exit, even though counting from S it would be the third.
                 * For S, we only emit `roundabout` without an exit number, showing that we enter a
                 *roundabout
                 * to find a via point.
                 * The same exit will be emitted, though, if we should start routing at S, making
                 * the overall response consistent.
                 *
                 * ⚠ CAUTION: order of post-processing steps is important
                 *    - handleRoundabouts must be called before collapseTurnInstructions that
                 *      expects post-processed roundabouts
                 */

                guidance::trimShortSegments(steps, leg_geometry);
                leg.steps = guidance::handleRoundabouts(std::move(steps));
                leg.steps = guidance::collapseTurnInstructions(std::move(leg.steps));
                leg.steps = guidance::anticipateLaneChange(std::move(leg.steps));
                leg.steps = guidance::buildIntersections(std::move(leg.steps));
                leg.steps = guidance::suppressShortNameSegments(std::move(leg.steps));
                leg.steps = guidance::assignRelativeLocations(std::move(leg.steps),
                                                              leg_geometry,
                                                              phantoms.source_phantom,
                                                              phantoms.target_phantom);
                leg_geometry = guidance::resyncGeometry(std::move(leg_geometry), leg.steps);
            }

            leg_geometries.push_back(std::move(leg_geometry));
            legs.push_back(std::move(leg));
        }

        auto route = guidance::assembleRoute(legs);
        boost::optional<util::json::Value> json_overview;
        if (parameters.overview != RouteParameters::OverviewType::False)
        {
            const auto use_simplification =
                parameters.overview == RouteParameters::OverviewType::Simplified;
            BOOST_ASSERT(use_simplification ||
                         parameters.overview == RouteParameters::OverviewType::Full);

            auto overview = guidance::assembleOverview(leg_geometries, use_simplification);
            json_overview = MakeGeometry(overview.begin(), overview.end());
        }

        std::vector<util::json::Value> step_geometries;
        for (const auto idx : util::irange<std::size_t>(0UL, legs.size()))
        {
            auto &leg_geometry = leg_geometries[idx];

            step_geometries.reserve(step_geometries.size() + legs[idx].steps.size());

            std::transform(
                legs[idx].steps.begin(),
                legs[idx].steps.end(),
                std::back_inserter(step_geometries),
                [this, &leg_geometry](const guidance::RouteStep &step) {
                    if (parameters.geometries == RouteParameters::GeometriesType::Polyline)
                    {
                        return static_cast<util::json::Value>(json::makePolyline<100000>(
                            leg_geometry.locations.begin() + step.geometry_begin,
                            leg_geometry.locations.begin() + step.geometry_end));
                    }

                    if (parameters.geometries == RouteParameters::GeometriesType::Polyline6)
                    {
                        return static_cast<util::json::Value>(json::makePolyline<1000000>(
                            leg_geometry.locations.begin() + step.geometry_begin,
                            leg_geometry.locations.begin() + step.geometry_end));
                    }

                    BOOST_ASSERT(parameters.geometries == RouteParameters::GeometriesType::GeoJSON);
                    return static_cast<util::json::Value>(json::makeGeoJSONGeometry(
                        leg_geometry.locations.begin() + step.geometry_begin,
                        leg_geometry.locations.begin() + step.geometry_end));
                });
        }

        std::vector<util::json::Object> annotations;

        // To maintain support for uses of the old default constructors, we check
        // if annotations property was set manually after default construction
        auto requested_annotations = parameters.annotations_type;
        if ((parameters.annotations == true) &&
            (parameters.annotations_type == RouteParameters::AnnotationsType::None))
        {
            requested_annotations = RouteParameters::AnnotationsType::All;
        }

        if (requested_annotations != RouteParameters::AnnotationsType::None)
        {
            for (const auto idx : util::irange<std::size_t>(0UL, leg_geometries.size()))
            {
                auto &leg_geometry = leg_geometries[idx];
                util::json::Object annotation;

                // AnnotationsType uses bit flags, & operator checks if a property is set
                if (parameters.annotations_type & RouteParameters::AnnotationsType::Speed)
                {
                    double prev_speed = 0;
                    annotation.values["speed"] = GetAnnotations(
                        leg_geometry, [&prev_speed](const guidance::LegGeometry::Annotation &anno) {
                            if (anno.duration < std::numeric_limits<double>::min())
                            {
                                return prev_speed;
                            }
                            else
                            {
                                auto speed = std::round(anno.distance / anno.duration * 10.) / 10.;
                                prev_speed = speed;
                                return util::json::clamp_float(speed);
                            }
                        });
                }

                if (requested_annotations & RouteParameters::AnnotationsType::Duration)
                {
                    annotation.values["duration"] = GetAnnotations(
                        leg_geometry, [](const guidance::LegGeometry::Annotation &anno) {
                            return anno.duration;
                        });
                }
                if (requested_annotations & RouteParameters::AnnotationsType::Distance)
                {
                    annotation.values["distance"] = GetAnnotations(
                        leg_geometry, [](const guidance::LegGeometry::Annotation &anno) {
                            return anno.distance;
                        });
                }
                if (requested_annotations & RouteParameters::AnnotationsType::Weight)
                {
                    annotation.values["weight"] = GetAnnotations(
                        leg_geometry,
                        [](const guidance::LegGeometry::Annotation &anno) { return anno.weight; });
                }
                if (requested_annotations & RouteParameters::AnnotationsType::Datasources)
                {
                    annotation.values["datasources"] = GetAnnotations(
                        leg_geometry, [](const guidance::LegGeometry::Annotation &anno) {
                            return anno.datasource;
                        });
                }
                if (requested_annotations & RouteParameters::AnnotationsType::Nodes)
                {
                    util::json::Array nodes;
                    nodes.values.reserve(leg_geometry.osm_node_ids.size());
                    for (const auto node_id : leg_geometry.osm_node_ids)
                    {
                        nodes.values.push_back(static_cast<std::uint64_t>(node_id));
                    }
                    annotation.values["nodes"] = std::move(nodes);
                }

                annotations.push_back(std::move(annotation));
            }
        }

        auto result = json::makeRoute(route,
                                      json::makeRouteLegs(std::move(legs),
                                                          std::move(step_geometries),
                                                          std::move(annotations)),
                                      std::move(json_overview),
                                      facade.GetWeightName());

        return result;
    }

    const RouteParameters &parameters;
};

} // ns api
} // ns engine
} // ns osrm

#endif
<|endoftext|>


<commit_before>// Lose Face - An open source face recognition project
// Copyright (C) 2008-2009 David Capello
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// * Redistributions of source code must retain the above copyright
//   notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
//   notice, this list of conditions and the following disclaimer in
//   the documentation and/or other materials provided with the
//   distribution.
// * Neither the name of the authors nor the names of its contributors
//   may be used to endorse or promote products derived from this
//   software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.

#include <cstdio>
#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <string>

#define VECTOR_WITH_IO
#define SAVE_DATA 1

#ifndef M_PI
#define M_PI		3.14159265358979323846
#endif

#include "Ann.h"
// #include "Chrono.h"

void train_until_mse_is_reached(Mlp& net, Backpropagation& bp, PatternSet& set,
				double target_mse, int show_epochs)
{
  int i = 0;
  while (net.calcMSE(set) >= target_mse) {
    std::random_shuffle(set.begin(), set.end());
    bp.train(set);
    if (show_epochs > 0 && !((i++)%show_epochs))
      std::cout << "MSE=" << net.calcMSE(set) << std::endl;
  }
  std::cout << "MSE=" << net.calcMSE(set) << std::endl;
}

void train_net_adapting_learning_rate(Mlp& net, Backpropagation& bp, PatternSet& set)
{
  int i = 1;
  double lastE, E = net.calcMSE(set);
  Mlp lastNet;

  while (E > 0.1) {
    std::random_shuffle(set.begin(), set.end());

    lastE = E;
    lastNet = net;

    bp.train(set);

    E = net.calcMSE(set);
    while (E > lastE) {
      bp.setLearningRate(bp.getLearningRate()/2.0);
      net = lastNet;
      bp.train(set);
      E = net.calcMSE(set);

      if (!((i++)%1000))
	std::cout << "MSE=" << net.calcMSE(set)
		  << " LR=" << bp.getLearningRate() << std::endl;
    }
    bp.setLearningRate(bp.getLearningRate()*2.0);
  }
}

void normalize_inputs(PatternSet& set,
		      double min_value,
		      double max_value,
		      Vector& min_vector,
		      Vector& max_vector)
{
  if (!set.empty()) {
    min_vector = set[0].getInput();
    max_vector = set[0].getInput();

    for (size_t c=1; c<set.size(); c++) {
      for (size_t i=0; i<set[c].getInput().size(); i++) {
	if (min_vector(i) > set[c].getInput(i)) min_vector(i) = set[c].getInput(i);
	if (max_vector(i) < set[c].getInput(i)) max_vector(i) = set[c].getInput(i);
      }
    }

    for (size_t c=0; c<set.size(); c++)
      for (size_t i=0; i<set[c].getInput().size(); i++) {
	set[c].setInput(i,
			min_value + ((max_value - min_value) *
				     (set[c].getInput(i) - min_vector(i)) /
				     (     max_vector(i) - min_vector(i))));
      }
  }
}

void test_logical_op(const std::string& name,
		     int a_x0, int a_x1, int a_y0,
		     int b_x0, int b_x1, int b_y0,
		     int c_x0, int c_x1, int c_y0,
		     int d_x0, int d_x1, int d_y0)
{
  std::cout << name << std::endl;

  Mlp net(2, 3, 1);
  net.initRandom(0.0, 1.0);

  Backpropagation bp(net);
  bp.setLearningRate(0.25);
  bp.setMomentum(0.9);

  PatternSet set;
  for (int c=0; c<4; c++) {
    Pattern pat(net.createInput(),
		net.createOutput());
    set.push_back(pat);
  }

  set[0].setInput(0, a_x0);
  set[0].setInput(1, a_x1);
  set[0].setOutput(0, a_y0);

  set[1].setInput(0, b_x0);
  set[1].setInput(1, b_x1);
  set[1].setOutput(0, b_y0);

  set[2].setInput(0, c_x0);
  set[2].setInput(1, c_x1);
  set[2].setOutput(0, c_y0);

  set[3].setInput(0, d_x0);
  set[3].setInput(1, d_x1);
  set[3].setOutput(0, d_y0);

  train_until_mse_is_reached(net, bp, set, 0.001, 10000);

  for (size_t p=0; p<set.size(); p++) {
    Vector hidden, output;
    net.recall(set[p].getInput(), hidden, output);

    std::cout << "  " << set[p].getInput()
	      << " => " << output << std::endl;
  }

#if SAVE_DATA
  {
    std::ofstream f((name + ".dat").c_str());
    for (double x=0.0; x<=1.0; x+=0.01) {
      for (double y=0.0; y<=1.0; y+=0.01) {
	Vector input(2);
	Vector hidden, output;
	input(0) = x;
	input(1) = y;
	net.recall(input, hidden, output);
	f << input(0) << "\t" << input(1) << "\t" << output(0) << "\n";
      }
    }
  }
#endif
}

void test_grid()
{
  std::cout << "grid" << std::endl;

  Mlp net(2, 16, 1);
  net.initRandom(0.0, 1.0);

  Backpropagation bp(net);
  bp.setLearningRate(0.1);
  bp.setMomentum(0.8);

  PatternSet set;
  for (int y=0; y<=2; ++y) {
    for (int x=0; x<=2; ++x) {
      Pattern pat(net.createInput(),
		  net.createOutput());
      pat.setInput(0, x / 2.0);
      pat.setInput(1, y / 2.0);
      pat.setOutput(0, ((x>y?x-y:y-x)&1) ? 0.0: 1.0);
      set.push_back(pat);
    }
  }

  train_until_mse_is_reached(net, bp, set, 0.01, 1000);

  for (size_t p=0; p<set.size(); p++) {
    Vector hidden, output;
    net.recall(set[p].getInput(), hidden, output);

    std::cout << "  " << set[p].getInput()
	      << " => " << output << std::endl;
  }

#if SAVE_DATA
  {
    std::ofstream f("grid.dat");
    for (double x=0.0; x<=1.0; x+=0.01) {
      for (double y=0.0; y<=1.0; y+=0.01) {
	Vector input(2);
	Vector hidden, output;
	input(0) = x;
	input(1) = y;
	net.recall(input, hidden, output);
	f << input(0) << "\t" << input(1) << "\t" << output(0) << "\n";
      }
    }
  }
#endif
}

void test_sin()
{
  std::cout << "sin(x)" << std::endl;

  Mlp net(1, 10, 1);
  net.setHiddenActivationFunction(Tansig());
  net.initRandom(-1.0, 1.0);

  Backpropagation bp(net);
  bp.setLearningRate(0.6);
  bp.setMomentum(0.1);

  PatternSet set;
  for (int c=0; c<=100; c++) {
    Pattern pat(net.createInput(),
		net.createOutput());

    double x = (M_PI*8) * static_cast<double>(c) / 100.0 - M_PI*4;
    pat.setInput(0, x);
    pat.setOutput(0, std::sin(x));
    set.push_back(pat);
  }

  train_until_mse_is_reached(net, bp, set, 0.01, 100);

#if SAVE_DATA
  {
    std::ofstream f("sin.dat");
    for (int c=-2000; c<4000; ++c) {
      Vector input(1);
      Vector hidden, output;
      input(0) = (M_PI*2) * static_cast<double>(c) / 1000.0 - M_PI;
      net.recall(input, hidden, output);
      f << input(0) << "\t" << output(0) << "\n";
    }
  }
#endif
}

void test_iris()
{
  std::cout << "iris" << std::endl;

  Mlp net(4, 3, 3);
  net.initRandom(-1.0, 1.0);

  Backpropagation bp(net);
  bp.setLearningRate(0.0001);
  // bp.setLearningRate(0.25);
  // bp.setMomentum(0.9);

  std::ifstream f("iris.csv");
  PatternSet set;
  char buf[256];

  while (f.getline(buf, sizeof(buf)).good()) {
    std::istringstream str(buf);

    Pattern pat(net.createInput(),
		net.createOutput());
    char comma;

    for (int c=0; c<4; ++c) {
      double input = 0.0;
      str >> input >> comma;
      pat.setInput(c, input);
    }

    int output;
    str >> output;

    for (int c=0; c<3; ++c)
      pat.setOutput(c, (c == output) ? 1.0: 0.0);

    set.push_back(pat);
  }

  // std::cout << "Patterns: " << set.size() << std::endl << std::endl;
  // for (int p=0; p<set.size(); ++p)
  //   std::cout << "Pattern["<<p<<"]: " << set[p].input << " => " << set[p].output << std::endl;

  // normalize patterns
  Vector min_vector;
  Vector max_vector;
  normalize_inputs(set, 0.0, 1.0, min_vector, max_vector);

  // go to training
  int max = 0;
  int bestEpoch = 0;
  Mlp theNet;
  for (int c=0; c<1000; ++c) {
    std::random_shuffle(set.begin(), set.end());
    bp.train(set);

    // performance
    int good = 0;
    for (size_t p=0; p<set.size(); ++p) {
      Vector hidden, output;
      net.recall(set[p].getInput(), hidden, output);

      if (output.getMaxPos() == set[p].getOutput().getMaxPos())
	++good;
    }
    if (max < good) {
      max = good;
      bestEpoch = c;
      theNet = net;
    }
    // std::cout << "Epoch=" << c << " Hits=" << ((double)good / (double)set.size()) << " MSE=" << net.calcMSE(set) << std::endl;
  }
  std::cout << "Best Epoch=" << bestEpoch << " Hits=" << ((double)max / (double)set.size()) << std::endl;

  Matrix distribution(3, 3);
  distribution.zero();
  for (size_t p=0; p<set.size(); ++p) {
    Vector hidden, output;
    theNet.recall(set[p].getInput(), hidden, output);
    ++distribution(set[p].getOutput().getMaxPos(), output.getMaxPos());
  }
  std::cout << "class |  0 |  1 |  2 |" << std::endl
	    << "------+----+----+----+" << std::endl;
  for (int i=0; i<3; ++i) {
    std::printf("   %2d | %2d | %2d | %2d |\n", i,
		distribution(i, 0),
		distribution(i, 1),
		distribution(i, 2));
  }
  std::cout << "------+----+----+----+" << std::endl;
}

int main()
{
  srand(1); test_logical_op("AND", 0,0,0, 0,1,0, 1,0,0, 1,1,1);
  srand(1); test_logical_op("OR",  0,0,0, 0,1,1, 1,0,1, 1,1,1);
  srand(1); test_logical_op("XOR", 0,0,0, 0,1,1, 1,0,1, 1,1,0);
  srand(1); test_grid();
  srand(1); test_sin(); // chrono_func(test_sin, "sin(x)");
  srand(1); test_iris();

  // DynamicMlp sinnet(1, 10, 1);
  // sinnet.hiddenFunc.setFunction<Tansig>();
  // srand(1); test_sin(sinnet); // chrono_func(test_sin, "sin(x)");
  // srand(1); test_iris<DynamicMlp>();

  // for (int s=0; s<10; ++s) {
  //   std::cout << "Seed " << s << std::endl;
  //   srand(s);
  //   test_iris();
  // }
  return 0;
}
<commit_msg>Fixed printf and cout problems.<commit_after>// Lose Face - An open source face recognition project
// Copyright (C) 2008-2009 David Capello
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// * Redistributions of source code must retain the above copyright
//   notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
//   notice, this list of conditions and the following disclaimer in
//   the documentation and/or other materials provided with the
//   distribution.
// * Neither the name of the authors nor the names of its contributors
//   may be used to endorse or promote products derived from this
//   software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.

#include <cstdio>
#include <iostream>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <string>

#define VECTOR_WITH_IO
#define SAVE_DATA 1

#ifndef M_PI
#define M_PI		3.14159265358979323846
#endif

#include "Ann.h"
// #include "Chrono.h"

void train_until_mse_is_reached(Mlp& net, Backpropagation& bp, PatternSet& set,
				double target_mse, int show_epochs)
{
  int i = 0;
  while (net.calcMSE(set) >= target_mse) {
    std::random_shuffle(set.begin(), set.end());
    bp.train(set);
    if (show_epochs > 0 && !((i++)%show_epochs))
      std::cout << "MSE=" << net.calcMSE(set) << std::endl;
  }
  std::cout << "MSE=" << net.calcMSE(set) << std::endl;
}

void train_net_adapting_learning_rate(Mlp& net, Backpropagation& bp, PatternSet& set)
{
  int i = 1;
  double lastE, E = net.calcMSE(set);
  Mlp lastNet;

  while (E > 0.1) {
    std::random_shuffle(set.begin(), set.end());

    lastE = E;
    lastNet = net;

    bp.train(set);

    E = net.calcMSE(set);
    while (E > lastE) {
      bp.setLearningRate(bp.getLearningRate()/2.0);
      net = lastNet;
      bp.train(set);
      E = net.calcMSE(set);

      if (!((i++)%1000))
	std::cout << "MSE=" << net.calcMSE(set)
		  << " LR=" << bp.getLearningRate() << std::endl;
    }
    bp.setLearningRate(bp.getLearningRate()*2.0);
  }
}

void normalize_inputs(PatternSet& set,
		      double min_value,
		      double max_value,
		      Vector& min_vector,
		      Vector& max_vector)
{
  if (!set.empty()) {
    min_vector = set[0].getInput();
    max_vector = set[0].getInput();

    for (size_t c=1; c<set.size(); c++) {
      for (size_t i=0; i<set[c].getInput().size(); i++) {
	if (min_vector(i) > set[c].getInput(i)) min_vector(i) = set[c].getInput(i);
	if (max_vector(i) < set[c].getInput(i)) max_vector(i) = set[c].getInput(i);
      }
    }

    for (size_t c=0; c<set.size(); c++)
      for (size_t i=0; i<set[c].getInput().size(); i++) {
	set[c].setInput(i,
			min_value + ((max_value - min_value) *
				     (set[c].getInput(i) - min_vector(i)) /
				     (     max_vector(i) - min_vector(i))));
      }
  }
}

void test_logical_op(const std::string& name,
		     int a_x0, int a_x1, int a_y0,
		     int b_x0, int b_x1, int b_y0,
		     int c_x0, int c_x1, int c_y0,
		     int d_x0, int d_x1, int d_y0)
{
  std::cout << name << std::endl;

  Mlp net(2, 3, 1);
  net.initRandom(0.0, 1.0);

  Backpropagation bp(net);
  bp.setLearningRate(0.25);
  bp.setMomentum(0.9);

  PatternSet set;
  for (int c=0; c<4; c++) {
    Pattern pat(net.createInput(),
		net.createOutput());
    set.push_back(pat);
  }

  set[0].setInput(0, a_x0);
  set[0].setInput(1, a_x1);
  set[0].setOutput(0, a_y0);

  set[1].setInput(0, b_x0);
  set[1].setInput(1, b_x1);
  set[1].setOutput(0, b_y0);

  set[2].setInput(0, c_x0);
  set[2].setInput(1, c_x1);
  set[2].setOutput(0, c_y0);

  set[3].setInput(0, d_x0);
  set[3].setInput(1, d_x1);
  set[3].setOutput(0, d_y0);

  train_until_mse_is_reached(net, bp, set, 0.001, 10000);

  for (size_t p=0; p<set.size(); p++) {
    Vector hidden, output;
    net.recall(set[p].getInput(), hidden, output);

    std::cout << "  " << set[p].getInput()
	      << " => " << output << std::endl;
  }

#if SAVE_DATA
  {
    std::ofstream f((name + ".dat").c_str());
    for (double x=0.0; x<=1.0; x+=0.01) {
      for (double y=0.0; y<=1.0; y+=0.01) {
	Vector input(2);
	Vector hidden, output;
	input(0) = x;
	input(1) = y;
	net.recall(input, hidden, output);
	f << input(0) << "\t" << input(1) << "\t" << output(0) << "\n";
      }
    }
  }
#endif
}

void test_grid()
{
  std::cout << "grid" << std::endl;

  Mlp net(2, 16, 1);
  net.initRandom(0.0, 1.0);

  Backpropagation bp(net);
  bp.setLearningRate(0.1);
  bp.setMomentum(0.8);

  PatternSet set;
  for (int y=0; y<=2; ++y) {
    for (int x=0; x<=2; ++x) {
      Pattern pat(net.createInput(),
		  net.createOutput());
      pat.setInput(0, x / 2.0);
      pat.setInput(1, y / 2.0);
      pat.setOutput(0, ((x>y?x-y:y-x)&1) ? 0.0: 1.0);
      set.push_back(pat);
    }
  }

  train_until_mse_is_reached(net, bp, set, 0.01, 1000);

  for (size_t p=0; p<set.size(); p++) {
    Vector hidden, output;
    net.recall(set[p].getInput(), hidden, output);

    std::cout << "  " << set[p].getInput()
	      << " => " << output << std::endl;
  }

#if SAVE_DATA
  {
    std::ofstream f("grid.dat");
    for (double x=0.0; x<=1.0; x+=0.01) {
      for (double y=0.0; y<=1.0; y+=0.01) {
	Vector input(2);
	Vector hidden, output;
	input(0) = x;
	input(1) = y;
	net.recall(input, hidden, output);
	f << input(0) << "\t" << input(1) << "\t" << output(0) << "\n";
      }
    }
  }
#endif
}

void test_sin()
{
  std::cout << "sin(x)\n";

  Mlp net(1, 10, 1);
  net.setHiddenActivationFunction(Tansig());
  net.initRandom(-1.0, 1.0);

  Backpropagation bp(net);
  bp.setLearningRate(0.6);
  bp.setMomentum(0.1);

  PatternSet set;
  for (int c=0; c<=100; c++) {
    Pattern pat(net.createInput(),
		net.createOutput());

    double x = (M_PI*8) * static_cast<double>(c) / 100.0 - M_PI*4;
    pat.setInput(0, x);
    pat.setOutput(0, std::sin(x));
    set.push_back(pat);
  }

  train_until_mse_is_reached(net, bp, set, 0.01, 100);

#if SAVE_DATA
  {
    std::ofstream f("sin.dat");
    for (int c=-2000; c<4000; ++c) {
      Vector input(1);
      Vector hidden, output;
      input(0) = (M_PI*2) * static_cast<double>(c) / 1000.0 - M_PI;
      net.recall(input, hidden, output);
      f << input(0) << "\t" << output(0) << "\n";
    }
  }
#endif
}

void test_iris()
{
  std::cout << "iris\n";

  Mlp net(4, 3, 3);
  net.initRandom(-1.0, 1.0);

  Backpropagation bp(net);
  bp.setLearningRate(0.0001);
  // bp.setLearningRate(0.25);
  // bp.setMomentum(0.9);

  std::ifstream f("iris.csv");
  PatternSet set;
  char buf[256];

  while (f.getline(buf, sizeof(buf)).good()) {
    std::istringstream str(buf);

    Pattern pat(net.createInput(),
		net.createOutput());
    char comma;

    for (int c=0; c<4; ++c) {
      double input = 0.0;
      str >> input >> comma;
      pat.setInput(c, input);
    }

    int output;
    str >> output;

    for (int c=0; c<3; ++c)
      pat.setOutput(c, (c == output) ? 1.0: 0.0);

    set.push_back(pat);
  }

  // std::cout << "Patterns: " << set.size() << "\n\n";
  // for (int p=0; p<set.size(); ++p)
  //   std::cout << "Pattern[" << p << "]: " << set[p].getInput() << " => " << set[p].getOutput() << "\n";

  // normalize patterns
  Vector min_vector;
  Vector max_vector;
  normalize_inputs(set, 0.0, 1.0, min_vector, max_vector);

  // go to training
  int max = 0;
  int bestEpoch = 0;
  Mlp theNet;
  for (int c=0; c<1000; ++c) {
    std::random_shuffle(set.begin(), set.end());
    bp.train(set);

    // performance
    int good = 0;
    for (size_t p=0; p<set.size(); ++p) {
      Vector hidden, output;
      net.recall(set[p].getInput(), hidden, output);

      if (output.getMaxPos() == set[p].getOutput().getMaxPos())
	++good;
    }
    if (max < good) {
      max = good;
      bestEpoch = c;
      theNet = net;
    }
    // std::cout << "Epoch=" << c << " Hits=" << ((double)good / (double)set.size()) << " MSE=" << net.calcMSE(set) << std::endl;
  }
  std::cout << "Best Epoch=" << bestEpoch << " Hits=" << ((double)max / (double)set.size()) << std::endl;

  Matrix distribution(3, 3);
  distribution.zero();
  for (size_t p=0; p<set.size(); ++p) {
    Vector hidden, output;
    theNet.recall(set[p].getInput(), hidden, output);
    ++distribution(set[p].getOutput().getMaxPos(), output.getMaxPos());
  }
  std::cout << "class |  0 |  1 |  2 |" << std::endl
	    << "------+----+----+----+" << std::endl;
  for (int i=0; i<3; ++i) {
    std::printf("   %2d | %2d | %2d | %2d |\n", i,
		(int)distribution(i, 0),
		(int)distribution(i, 1),
		(int)distribution(i, 2));
  }
  std::cout << "------+----+----+----+" << std::endl;
}

int main()
{
  srand(1); test_logical_op("AND", 0,0,0, 0,1,0, 1,0,0, 1,1,1);
  srand(1); test_logical_op("OR",  0,0,0, 0,1,1, 1,0,1, 1,1,1);
  srand(1); test_logical_op("XOR", 0,0,0, 0,1,1, 1,0,1, 1,1,0);
  srand(1); test_grid();
  srand(1); test_sin(); // chrono_func(test_sin, "sin(x)");
  srand(1); test_iris();

  // DynamicMlp sinnet(1, 10, 1);
  // sinnet.hiddenFunc.setFunction<Tansig>();
  // srand(1); test_sin(sinnet); // chrono_func(test_sin, "sin(x)");
  // srand(1); test_iris<DynamicMlp>();

  // for (int s=0; s<10; ++s) {
  //   std::cout << "Seed " << s << std::endl;
  //   srand(s);
  //   test_iris();
  // }
  return 0;
}
<|endoftext|>


<commit_before>#ifndef MATCH_HPP
#define MATCH_HPP

#include "engine/plugins/plugin_base.hpp"

#include "engine/map_matching/bayes_classifier.hpp"
#include "engine/object_encoder.hpp"
#include "engine/search_engine.hpp"
#include "engine/guidance/textual_route_annotation.hpp"
#include "engine/guidance/segment_list.hpp"
#include "engine/api_response_generator.hpp"
#include "engine/routing_algorithms/map_matching.hpp"
#include "util/coordinate_calculation.hpp"
#include "util/integer_range.hpp"
#include "util/json_logger.hpp"
#include "util/json_util.hpp"
#include "util/string_util.hpp"

#include <cstdlib>

#include <algorithm>
#include <memory>
#include <string>
#include <vector>

namespace osrm
{
namespace engine
{
namespace plugins
{

template <class DataFacadeT> class MapMatchingPlugin : public BasePlugin
{
    std::shared_ptr<SearchEngine<DataFacadeT>> search_engine_ptr;

    using SubMatching = routing_algorithms::SubMatching;
    using SubMatchingList = routing_algorithms::SubMatchingList;
    using CandidateLists = routing_algorithms::CandidateLists;
    using ClassifierT = map_matching::BayesClassifier<map_matching::LaplaceDistribution,
                                                      map_matching::LaplaceDistribution,
                                                      double>;
    using TraceClassification = ClassifierT::ClassificationT;

  public:
    MapMatchingPlugin(DataFacadeT *facade, const int max_locations_map_matching)
        : descriptor_string("match"), facade(facade),
          max_locations_map_matching(max_locations_map_matching),
          // the values were derived from fitting a laplace distribution
          // to the values of manually classified traces
          classifier(map_matching::LaplaceDistribution(0.005986, 0.016646),
                     map_matching::LaplaceDistribution(0.054385, 0.458432),
                     0.696774) // valid apriori probability
    {
        search_engine_ptr = std::make_shared<SearchEngine<DataFacadeT>>(facade);
    }

    virtual ~MapMatchingPlugin() {}

    const std::string GetDescriptor() const final override { return descriptor_string; }

    TraceClassification
    classify(const float trace_length, const float matched_length, const int removed_points) const
    {
        (void)removed_points; // unused

        const double distance_feature = -std::log(trace_length) + std::log(matched_length);

        // matched to the same point
        if (!std::isfinite(distance_feature))
        {
            return std::make_pair(ClassifierT::ClassLabel::NEGATIVE, 1.0);
        }

        const auto label_with_confidence = classifier.classify(distance_feature);

        return label_with_confidence;
    }

    CandidateLists getCandidates(
        const std::vector<util::FixedPointCoordinate> &input_coords,
        const std::vector<std::pair<const int, const boost::optional<int>>> &input_bearings,
        const double gps_precision,
        std::vector<double> &sub_trace_lengths)
    {
        CandidateLists candidates_lists;

        // assuming gps_precision is the standard deviation of a normal distribution that
        // models GPS noise (in this model), this should give us the correct search radius
        // with > 99% confidence
        double query_radius = 3 * gps_precision;
        double last_distance =
            util::coordinate_calculation::haversineDistance(input_coords[0], input_coords[1]);

        sub_trace_lengths.resize(input_coords.size());
        sub_trace_lengths[0] = 0;
        for (const auto current_coordinate : util::irange<std::size_t>(0, input_coords.size()))
        {
            bool allow_uturn = false;
            if (0 < current_coordinate)
            {
                last_distance = util::coordinate_calculation::haversineDistance(
                    input_coords[current_coordinate - 1], input_coords[current_coordinate]);

                sub_trace_lengths[current_coordinate] +=
                    sub_trace_lengths[current_coordinate - 1] + last_distance;
            }

            if (input_coords.size() - 1 > current_coordinate && 0 < current_coordinate)
            {
                double turn_angle = util::coordinate_calculation::computeAngle(
                    input_coords[current_coordinate - 1], input_coords[current_coordinate],
                    input_coords[current_coordinate + 1]);

                // sharp turns indicate a possible uturn
                if (turn_angle <= 90.0 || turn_angle >= 270.0)
                {
                    allow_uturn = true;
                }
            }

            // Use bearing values if supplied, otherwise fallback to 0,180 defaults
            auto bearing = input_bearings.size() > 0 ? input_bearings[current_coordinate].first : 0;
            auto range = input_bearings.size() > 0
                             ? (input_bearings[current_coordinate].second
                                    ? *input_bearings[current_coordinate].second
                                    : 10)
                             : 180;
            auto candidates = facade->NearestPhantomNodesInRange(input_coords[current_coordinate],
                                                                 query_radius, bearing, range);

            if (candidates.size() == 0)
            {
                break;
            }

            // sort by forward id, then by reverse id and then by distance
            std::sort(
                candidates.begin(), candidates.end(),
                [](const PhantomNodeWithDistance &lhs, const PhantomNodeWithDistance &rhs)
                {
                    return lhs.phantom_node.forward_node_id < rhs.phantom_node.forward_node_id ||
                           (lhs.phantom_node.forward_node_id == rhs.phantom_node.forward_node_id &&
                            (lhs.phantom_node.reverse_node_id < rhs.phantom_node.reverse_node_id ||
                             (lhs.phantom_node.reverse_node_id ==
                                  rhs.phantom_node.reverse_node_id &&
                              lhs.distance < rhs.distance)));
                });

            auto new_end = std::unique(
                candidates.begin(), candidates.end(),
                [](const PhantomNodeWithDistance &lhs, const PhantomNodeWithDistance &rhs)
                {
                    return lhs.phantom_node.forward_node_id == rhs.phantom_node.forward_node_id &&
                           lhs.phantom_node.reverse_node_id == rhs.phantom_node.reverse_node_id;
                });
            candidates.resize(new_end - candidates.begin());

            if (!allow_uturn)
            {
                const auto compact_size = candidates.size();
                for (const auto i : util::irange<std::size_t>(0, compact_size))
                {
                    // Split edge if it is bidirectional and append reverse direction to end of list
                    if (candidates[i].phantom_node.forward_node_id != SPECIAL_NODEID &&
                        candidates[i].phantom_node.reverse_node_id != SPECIAL_NODEID)
                    {
                        PhantomNode reverse_node(candidates[i].phantom_node);
                        reverse_node.forward_node_id = SPECIAL_NODEID;
                        candidates.push_back(
                            PhantomNodeWithDistance{reverse_node, candidates[i].distance});

                        candidates[i].phantom_node.reverse_node_id = SPECIAL_NODEID;
                    }
                }
            }

            // sort by distance to make pruning effective
            std::sort(candidates.begin(), candidates.end(),
                      [](const PhantomNodeWithDistance &lhs, const PhantomNodeWithDistance &rhs)
                      {
                          return lhs.distance < rhs.distance;
                      });

            candidates_lists.push_back(std::move(candidates));
        }

        return candidates_lists;
    }

    util::json::Object submatchingToJSON(const SubMatching &sub,
                                         const RouteParameters &route_parameters,
                                         const InternalRouteResult &raw_route)
    {
        util::json::Object subtrace;

        if (route_parameters.classify)
        {
            subtrace.values["confidence"] = sub.confidence;
        }

        auto response_generator = MakeApiResponseGenerator(facade);

        subtrace.values["hint_data"] = response_generator.BuildHintData(raw_route);

        if (route_parameters.geometry || route_parameters.print_instructions)
        {
            using SegmentList = guidance::SegmentList<DataFacadeT>;
            // Passing false to extract_alternative extracts the route.
            const constexpr bool EXTRACT_ROUTE = false;
            // by passing false to segment_list, we skip the douglas peucker simplification
            // and mark all segments as necessary within the generation process
            const constexpr bool NO_ROUTE_SIMPLIFICATION = false;
            SegmentList segment_list(raw_route, EXTRACT_ROUTE, route_parameters.zoom_level,
                                     NO_ROUTE_SIMPLIFICATION, facade);

            if (route_parameters.geometry)
            {
                subtrace.values["geometry"] =
                    response_generator.GetGeometry(route_parameters.compression, segment_list);
            }

            if (route_parameters.print_instructions)
            {
                subtrace.values["instructions"] =
                    guidance::AnnotateRoute<DataFacadeT>(segment_list.Get(), facade);
            }

            util::json::Object json_route_summary;
            json_route_summary.values["total_distance"] = segment_list.GetDistance();
            json_route_summary.values["total_time"] = segment_list.GetDuration();
            subtrace.values["route_summary"] = json_route_summary;
        }

        subtrace.values["indices"] = util::json::make_array(sub.indices);

        util::json::Array points;
        for (const auto &node : sub.nodes)
        {
            points.values.emplace_back(
                util::json::make_array(node.location.lat / COORDINATE_PRECISION,
                                       node.location.lon / COORDINATE_PRECISION));
        }
        subtrace.values["matched_points"] = points;

        util::json::Array names;
        for (const auto &node : sub.nodes)
        {
            names.values.emplace_back(facade->get_name_for_id(node.name_id));
        }
        subtrace.values["matched_names"] = names;

        return subtrace;
    }

    Status HandleRequest(const RouteParameters &route_parameters,
                         util::json::Object &json_result) final override
    {
        // enforce maximum number of locations for performance reasons
        if (max_locations_map_matching > 0 &&
            static_cast<int>(route_parameters.coordinates.size()) > max_locations_map_matching)
        {
            json_result.values["status_message"] = "Too many coodindates";
            return Status::Error;
        }

        // check number of parameters
        if (!check_all_coordinates(route_parameters.coordinates))
        {
            json_result.values["status_message"] = "Invalid coordinates";
            return Status::Error;
        }

        std::vector<double> sub_trace_lengths;
        const auto &input_coords = route_parameters.coordinates;
        const auto &input_timestamps = route_parameters.timestamps;
        const auto &input_bearings = route_parameters.bearings;
        if (input_timestamps.size() > 0 && input_coords.size() != input_timestamps.size())
        {
            json_result.values["status_message"] =
                "Number of timestamps does not match number of coordinates";
            return Status::Error;
        }

        if (input_bearings.size() > 0 && input_coords.size() != input_bearings.size())
        {
            json_result.values["status_message"] =
                "Number of bearings does not match number of coordinates";
            return Status::Error;
        }

        // at least two coordinates are needed for map matching
        if (static_cast<int>(input_coords.size()) < 2)
        {
            json_result.values["status_message"] = "At least two coordinates needed";
            return Status::Error;
        }

        const auto candidates_lists = getCandidates(
            input_coords, input_bearings, route_parameters.gps_precision, sub_trace_lengths);
        if (candidates_lists.size() != input_coords.size())
        {
            BOOST_ASSERT(candidates_lists.size() < input_coords.size());
            json_result.values["status_message"] =
                std::string("Could not find a matching segment for coordinate ") +
                std::to_string(candidates_lists.size());
            return Status::NoSegment;
        }

        // setup logging if enabled
        if (util::json::Logger::get())
            util::json::Logger::get()->initialize("matching");

        // call the actual map matching
        SubMatchingList sub_matchings;
        search_engine_ptr->map_matching(candidates_lists, input_coords, input_timestamps,
                                        route_parameters.matching_beta,
                                        route_parameters.gps_precision, sub_matchings);

        util::json::Array matchings;
        for (auto &sub : sub_matchings)
        {
            // classify result
            if (route_parameters.classify)
            {
                double trace_length =
                    sub_trace_lengths[sub.indices.back()] - sub_trace_lengths[sub.indices.front()];
                TraceClassification classification =
                    classify(trace_length, sub.length,
                             (sub.indices.back() - sub.indices.front() + 1) - sub.nodes.size());
                if (classification.first == ClassifierT::ClassLabel::POSITIVE)
                {
                    sub.confidence = classification.second;
                }
                else
                {
                    sub.confidence = 1 - classification.second;
                }
            }

            BOOST_ASSERT(sub.nodes.size() > 1);

            // FIXME we only run this to obtain the geometry
            // The clean way would be to get this directly from the map matching plugin
            InternalRouteResult raw_route;
            PhantomNodes current_phantom_node_pair;
            for (unsigned i = 0; i < sub.nodes.size() - 1; ++i)
            {
                current_phantom_node_pair.source_phantom = sub.nodes[i];
                current_phantom_node_pair.target_phantom = sub.nodes[i + 1];
                BOOST_ASSERT(current_phantom_node_pair.source_phantom.IsValid());
                BOOST_ASSERT(current_phantom_node_pair.target_phantom.IsValid());
                raw_route.segment_end_coordinates.emplace_back(current_phantom_node_pair);
            }
            search_engine_ptr->shortest_path(
                raw_route.segment_end_coordinates,
                std::vector<bool>(raw_route.segment_end_coordinates.size() + 1, true), raw_route);

            BOOST_ASSERT(raw_route.shortest_path_length != INVALID_EDGE_WEIGHT);

            matchings.values.emplace_back(submatchingToJSON(sub, route_parameters, raw_route));
        }

        if (util::json::Logger::get())
            util::json::Logger::get()->render("matching", json_result);
        json_result.values["matchings"] = matchings;

        if (sub_matchings.empty())
        {
            json_result.values["status_message"] = "Cannot find matchings";
            return Status::EmptyResult;
        }

        json_result.values["status_message"] = "Found matchings";
        return Status::Ok;
    }

  private:
    std::string descriptor_string;
    DataFacadeT *facade;
    int max_locations_map_matching;
    ClassifierT classifier;
};
}
}
}

#endif // MATCH_HPP
<commit_msg>Minor typo fix<commit_after>#ifndef MATCH_HPP
#define MATCH_HPP

#include "engine/plugins/plugin_base.hpp"

#include "engine/map_matching/bayes_classifier.hpp"
#include "engine/object_encoder.hpp"
#include "engine/search_engine.hpp"
#include "engine/guidance/textual_route_annotation.hpp"
#include "engine/guidance/segment_list.hpp"
#include "engine/api_response_generator.hpp"
#include "engine/routing_algorithms/map_matching.hpp"
#include "util/coordinate_calculation.hpp"
#include "util/integer_range.hpp"
#include "util/json_logger.hpp"
#include "util/json_util.hpp"
#include "util/string_util.hpp"

#include <cstdlib>

#include <algorithm>
#include <memory>
#include <string>
#include <vector>

namespace osrm
{
namespace engine
{
namespace plugins
{

template <class DataFacadeT> class MapMatchingPlugin : public BasePlugin
{
    std::shared_ptr<SearchEngine<DataFacadeT>> search_engine_ptr;

    using SubMatching = routing_algorithms::SubMatching;
    using SubMatchingList = routing_algorithms::SubMatchingList;
    using CandidateLists = routing_algorithms::CandidateLists;
    using ClassifierT = map_matching::BayesClassifier<map_matching::LaplaceDistribution,
                                                      map_matching::LaplaceDistribution,
                                                      double>;
    using TraceClassification = ClassifierT::ClassificationT;

  public:
    MapMatchingPlugin(DataFacadeT *facade, const int max_locations_map_matching)
        : descriptor_string("match"), facade(facade),
          max_locations_map_matching(max_locations_map_matching),
          // the values were derived from fitting a laplace distribution
          // to the values of manually classified traces
          classifier(map_matching::LaplaceDistribution(0.005986, 0.016646),
                     map_matching::LaplaceDistribution(0.054385, 0.458432),
                     0.696774) // valid apriori probability
    {
        search_engine_ptr = std::make_shared<SearchEngine<DataFacadeT>>(facade);
    }

    virtual ~MapMatchingPlugin() {}

    const std::string GetDescriptor() const final override { return descriptor_string; }

    TraceClassification
    classify(const float trace_length, const float matched_length, const int removed_points) const
    {
        (void)removed_points; // unused

        const double distance_feature = -std::log(trace_length) + std::log(matched_length);

        // matched to the same point
        if (!std::isfinite(distance_feature))
        {
            return std::make_pair(ClassifierT::ClassLabel::NEGATIVE, 1.0);
        }

        const auto label_with_confidence = classifier.classify(distance_feature);

        return label_with_confidence;
    }

    CandidateLists getCandidates(
        const std::vector<util::FixedPointCoordinate> &input_coords,
        const std::vector<std::pair<const int, const boost::optional<int>>> &input_bearings,
        const double gps_precision,
        std::vector<double> &sub_trace_lengths)
    {
        CandidateLists candidates_lists;

        // assuming gps_precision is the standard deviation of a normal distribution that
        // models GPS noise (in this model), this should give us the correct search radius
        // with > 99% confidence
        double query_radius = 3 * gps_precision;
        double last_distance =
            util::coordinate_calculation::haversineDistance(input_coords[0], input_coords[1]);

        sub_trace_lengths.resize(input_coords.size());
        sub_trace_lengths[0] = 0;
        for (const auto current_coordinate : util::irange<std::size_t>(0, input_coords.size()))
        {
            bool allow_uturn = false;
            if (0 < current_coordinate)
            {
                last_distance = util::coordinate_calculation::haversineDistance(
                    input_coords[current_coordinate - 1], input_coords[current_coordinate]);

                sub_trace_lengths[current_coordinate] +=
                    sub_trace_lengths[current_coordinate - 1] + last_distance;
            }

            if (input_coords.size() - 1 > current_coordinate && 0 < current_coordinate)
            {
                double turn_angle = util::coordinate_calculation::computeAngle(
                    input_coords[current_coordinate - 1], input_coords[current_coordinate],
                    input_coords[current_coordinate + 1]);

                // sharp turns indicate a possible uturn
                if (turn_angle <= 90.0 || turn_angle >= 270.0)
                {
                    allow_uturn = true;
                }
            }

            // Use bearing values if supplied, otherwise fallback to 0,180 defaults
            auto bearing = input_bearings.size() > 0 ? input_bearings[current_coordinate].first : 0;
            auto range = input_bearings.size() > 0
                             ? (input_bearings[current_coordinate].second
                                    ? *input_bearings[current_coordinate].second
                                    : 10)
                             : 180;
            auto candidates = facade->NearestPhantomNodesInRange(input_coords[current_coordinate],
                                                                 query_radius, bearing, range);

            if (candidates.size() == 0)
            {
                break;
            }

            // sort by forward id, then by reverse id and then by distance
            std::sort(
                candidates.begin(), candidates.end(),
                [](const PhantomNodeWithDistance &lhs, const PhantomNodeWithDistance &rhs)
                {
                    return lhs.phantom_node.forward_node_id < rhs.phantom_node.forward_node_id ||
                           (lhs.phantom_node.forward_node_id == rhs.phantom_node.forward_node_id &&
                            (lhs.phantom_node.reverse_node_id < rhs.phantom_node.reverse_node_id ||
                             (lhs.phantom_node.reverse_node_id ==
                                  rhs.phantom_node.reverse_node_id &&
                              lhs.distance < rhs.distance)));
                });

            auto new_end = std::unique(
                candidates.begin(), candidates.end(),
                [](const PhantomNodeWithDistance &lhs, const PhantomNodeWithDistance &rhs)
                {
                    return lhs.phantom_node.forward_node_id == rhs.phantom_node.forward_node_id &&
                           lhs.phantom_node.reverse_node_id == rhs.phantom_node.reverse_node_id;
                });
            candidates.resize(new_end - candidates.begin());

            if (!allow_uturn)
            {
                const auto compact_size = candidates.size();
                for (const auto i : util::irange<std::size_t>(0, compact_size))
                {
                    // Split edge if it is bidirectional and append reverse direction to end of list
                    if (candidates[i].phantom_node.forward_node_id != SPECIAL_NODEID &&
                        candidates[i].phantom_node.reverse_node_id != SPECIAL_NODEID)
                    {
                        PhantomNode reverse_node(candidates[i].phantom_node);
                        reverse_node.forward_node_id = SPECIAL_NODEID;
                        candidates.push_back(
                            PhantomNodeWithDistance{reverse_node, candidates[i].distance});

                        candidates[i].phantom_node.reverse_node_id = SPECIAL_NODEID;
                    }
                }
            }

            // sort by distance to make pruning effective
            std::sort(candidates.begin(), candidates.end(),
                      [](const PhantomNodeWithDistance &lhs, const PhantomNodeWithDistance &rhs)
                      {
                          return lhs.distance < rhs.distance;
                      });

            candidates_lists.push_back(std::move(candidates));
        }

        return candidates_lists;
    }

    util::json::Object submatchingToJSON(const SubMatching &sub,
                                         const RouteParameters &route_parameters,
                                         const InternalRouteResult &raw_route)
    {
        util::json::Object subtrace;

        if (route_parameters.classify)
        {
            subtrace.values["confidence"] = sub.confidence;
        }

        auto response_generator = MakeApiResponseGenerator(facade);

        subtrace.values["hint_data"] = response_generator.BuildHintData(raw_route);

        if (route_parameters.geometry || route_parameters.print_instructions)
        {
            using SegmentList = guidance::SegmentList<DataFacadeT>;
            // Passing false to extract_alternative extracts the route.
            const constexpr bool EXTRACT_ROUTE = false;
            // by passing false to segment_list, we skip the douglas peucker simplification
            // and mark all segments as necessary within the generation process
            const constexpr bool NO_ROUTE_SIMPLIFICATION = false;
            SegmentList segment_list(raw_route, EXTRACT_ROUTE, route_parameters.zoom_level,
                                     NO_ROUTE_SIMPLIFICATION, facade);

            if (route_parameters.geometry)
            {
                subtrace.values["geometry"] =
                    response_generator.GetGeometry(route_parameters.compression, segment_list);
            }

            if (route_parameters.print_instructions)
            {
                subtrace.values["instructions"] =
                    guidance::AnnotateRoute<DataFacadeT>(segment_list.Get(), facade);
            }

            util::json::Object json_route_summary;
            json_route_summary.values["total_distance"] = segment_list.GetDistance();
            json_route_summary.values["total_time"] = segment_list.GetDuration();
            subtrace.values["route_summary"] = json_route_summary;
        }

        subtrace.values["indices"] = util::json::make_array(sub.indices);

        util::json::Array points;
        for (const auto &node : sub.nodes)
        {
            points.values.emplace_back(
                util::json::make_array(node.location.lat / COORDINATE_PRECISION,
                                       node.location.lon / COORDINATE_PRECISION));
        }
        subtrace.values["matched_points"] = points;

        util::json::Array names;
        for (const auto &node : sub.nodes)
        {
            names.values.emplace_back(facade->get_name_for_id(node.name_id));
        }
        subtrace.values["matched_names"] = names;

        return subtrace;
    }

    Status HandleRequest(const RouteParameters &route_parameters,
                         util::json::Object &json_result) final override
    {
        // enforce maximum number of locations for performance reasons
        if (max_locations_map_matching > 0 &&
            static_cast<int>(route_parameters.coordinates.size()) > max_locations_map_matching)
        {
            json_result.values["status_message"] = "Too many coordinates";
            return Status::Error;
        }

        // check number of parameters
        if (!check_all_coordinates(route_parameters.coordinates))
        {
            json_result.values["status_message"] = "Invalid coordinates";
            return Status::Error;
        }

        std::vector<double> sub_trace_lengths;
        const auto &input_coords = route_parameters.coordinates;
        const auto &input_timestamps = route_parameters.timestamps;
        const auto &input_bearings = route_parameters.bearings;
        if (input_timestamps.size() > 0 && input_coords.size() != input_timestamps.size())
        {
            json_result.values["status_message"] =
                "Number of timestamps does not match number of coordinates";
            return Status::Error;
        }

        if (input_bearings.size() > 0 && input_coords.size() != input_bearings.size())
        {
            json_result.values["status_message"] =
                "Number of bearings does not match number of coordinates";
            return Status::Error;
        }

        // at least two coordinates are needed for map matching
        if (static_cast<int>(input_coords.size()) < 2)
        {
            json_result.values["status_message"] = "At least two coordinates needed";
            return Status::Error;
        }

        const auto candidates_lists = getCandidates(
            input_coords, input_bearings, route_parameters.gps_precision, sub_trace_lengths);
        if (candidates_lists.size() != input_coords.size())
        {
            BOOST_ASSERT(candidates_lists.size() < input_coords.size());
            json_result.values["status_message"] =
                std::string("Could not find a matching segment for coordinate ") +
                std::to_string(candidates_lists.size());
            return Status::NoSegment;
        }

        // setup logging if enabled
        if (util::json::Logger::get())
            util::json::Logger::get()->initialize("matching");

        // call the actual map matching
        SubMatchingList sub_matchings;
        search_engine_ptr->map_matching(candidates_lists, input_coords, input_timestamps,
                                        route_parameters.matching_beta,
                                        route_parameters.gps_precision, sub_matchings);

        util::json::Array matchings;
        for (auto &sub : sub_matchings)
        {
            // classify result
            if (route_parameters.classify)
            {
                double trace_length =
                    sub_trace_lengths[sub.indices.back()] - sub_trace_lengths[sub.indices.front()];
                TraceClassification classification =
                    classify(trace_length, sub.length,
                             (sub.indices.back() - sub.indices.front() + 1) - sub.nodes.size());
                if (classification.first == ClassifierT::ClassLabel::POSITIVE)
                {
                    sub.confidence = classification.second;
                }
                else
                {
                    sub.confidence = 1 - classification.second;
                }
            }

            BOOST_ASSERT(sub.nodes.size() > 1);

            // FIXME we only run this to obtain the geometry
            // The clean way would be to get this directly from the map matching plugin
            InternalRouteResult raw_route;
            PhantomNodes current_phantom_node_pair;
            for (unsigned i = 0; i < sub.nodes.size() - 1; ++i)
            {
                current_phantom_node_pair.source_phantom = sub.nodes[i];
                current_phantom_node_pair.target_phantom = sub.nodes[i + 1];
                BOOST_ASSERT(current_phantom_node_pair.source_phantom.IsValid());
                BOOST_ASSERT(current_phantom_node_pair.target_phantom.IsValid());
                raw_route.segment_end_coordinates.emplace_back(current_phantom_node_pair);
            }
            search_engine_ptr->shortest_path(
                raw_route.segment_end_coordinates,
                std::vector<bool>(raw_route.segment_end_coordinates.size() + 1, true), raw_route);

            BOOST_ASSERT(raw_route.shortest_path_length != INVALID_EDGE_WEIGHT);

            matchings.values.emplace_back(submatchingToJSON(sub, route_parameters, raw_route));
        }

        if (util::json::Logger::get())
            util::json::Logger::get()->render("matching", json_result);
        json_result.values["matchings"] = matchings;

        if (sub_matchings.empty())
        {
            json_result.values["status_message"] = "Cannot find matchings";
            return Status::EmptyResult;
        }

        json_result.values["status_message"] = "Found matchings";
        return Status::Ok;
    }

  private:
    std::string descriptor_string;
    DataFacadeT *facade;
    int max_locations_map_matching;
    ClassifierT classifier;
};
}
}
}

#endif // MATCH_HPP
<|endoftext|>