forked from organicmaps/organicmaps
Merge pull request #4193 from bykoianko/master-generating-route-altitude-chart-image
Generating route altitude chart image.
This commit is contained in:
commit
f2b4f7db60
27 changed files with 765 additions and 121 deletions
276
map/chart_generator.cpp
Normal file
276
map/chart_generator.cpp
Normal file
|
@ -0,0 +1,276 @@
|
|||
#include "map/chart_generator.hpp"
|
||||
|
||||
#include "base/assert.hpp"
|
||||
#include "base/math.hpp"
|
||||
|
||||
#include "std/algorithm.hpp"
|
||||
#include "std/fstream.hpp"
|
||||
|
||||
#include "3party/agg/agg_conv_curve.h"
|
||||
#include "3party/agg/agg_conv_stroke.h"
|
||||
#include "3party/agg/agg_path_storage.h"
|
||||
#include "3party/agg/agg_pixfmt_rgba.h"
|
||||
#include "3party/agg/agg_rasterizer_scanline_aa.h"
|
||||
#include "3party/agg/agg_renderer_primitives.h"
|
||||
#include "3party/agg/agg_renderer_scanline.h"
|
||||
#include "3party/agg/agg_scanline_p.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
template <class Color, class Order>
|
||||
struct BlendAdaptor
|
||||
{
|
||||
using order_type = Order;
|
||||
using color_type = Color;
|
||||
using TValueType = typename color_type::value_type;
|
||||
using TCalcType = typename color_type::calc_type;
|
||||
|
||||
enum
|
||||
{
|
||||
ScaleShift = color_type::base_shift,
|
||||
ScaleMask = color_type::base_mask,
|
||||
};
|
||||
|
||||
static AGG_INLINE void blend_pix(unsigned op, TValueType * p, unsigned cr, unsigned cg,
|
||||
unsigned cb, unsigned ca, unsigned cover)
|
||||
{
|
||||
using TBlendTable = agg::comp_op_table_rgba<Color, Order>;
|
||||
if (p[Order::A])
|
||||
{
|
||||
TBlendTable::g_comp_op_func[op](p, (cr * ca + ScaleMask) >> ScaleShift,
|
||||
(cg * ca + ScaleMask) >> ScaleShift,
|
||||
(cb * ca + ScaleMask) >> ScaleShift, ca, cover);
|
||||
}
|
||||
else
|
||||
{
|
||||
TBlendTable::g_comp_op_func[op](p, cr, cg, cb, ca, cover);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
agg::rgba8 GetLineColor(MapStyle mapStyle)
|
||||
{
|
||||
switch (mapStyle)
|
||||
{
|
||||
case MapStyleCount:
|
||||
LOG(LERROR, ("Wrong map style param."));
|
||||
// No need break or return here.
|
||||
case MapStyleDark:
|
||||
return agg::rgba8(255, 230, 140, 255);
|
||||
case MapStyleLight:
|
||||
case MapStyleClear:
|
||||
case MapStyleMerged:
|
||||
return agg::rgba8(30, 150, 240, 255);
|
||||
}
|
||||
}
|
||||
|
||||
agg::rgba8 GetCurveColor(MapStyle mapStyle)
|
||||
{
|
||||
switch (mapStyle)
|
||||
{
|
||||
case MapStyleCount:
|
||||
LOG(LERROR, ("Wrong map style param."));
|
||||
// No need break or return here.
|
||||
case MapStyleDark:
|
||||
return agg::rgba8(255, 230, 140, 20);
|
||||
case MapStyleLight:
|
||||
case MapStyleClear:
|
||||
case MapStyleMerged:
|
||||
return agg::rgba8(30, 150, 240, 20);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace maps
|
||||
{
|
||||
bool NormalizeChartData(vector<double> const & distanceDataM,
|
||||
feature::TAltitudes const & altitudeDataM, size_t resultPointCount,
|
||||
vector<double> & uniformAltitudeDataM)
|
||||
{
|
||||
double constexpr kEpsilon = 1e-6;
|
||||
|
||||
if (distanceDataM.size() != altitudeDataM.size())
|
||||
{
|
||||
LOG(LERROR, ("Altitude and distance data have different size."));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_sorted(distanceDataM.cbegin(), distanceDataM.cend()))
|
||||
{
|
||||
LOG(LERROR, ("Route segment distances are not sorted."));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (distanceDataM.empty() || resultPointCount == 0)
|
||||
{
|
||||
uniformAltitudeDataM.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
auto const calculateAltitude = [&](double distFormStartM) {
|
||||
if (distFormStartM <= distanceDataM.front())
|
||||
return static_cast<double>(altitudeDataM.front());
|
||||
if (distFormStartM >= distanceDataM.back())
|
||||
return static_cast<double>(altitudeDataM.back());
|
||||
|
||||
auto const lowerIt = lower_bound(distanceDataM.cbegin(), distanceDataM.cend(), distFormStartM);
|
||||
size_t const nextPointIdx = distance(distanceDataM.cbegin(), lowerIt);
|
||||
ASSERT_LESS(0, nextPointIdx, ("distFormStartM is greater than 0 but nextPointIdx == 0."));
|
||||
size_t const prevPointIdx = nextPointIdx - 1;
|
||||
|
||||
if (my::AlmostEqualAbs(distanceDataM[prevPointIdx], distanceDataM[nextPointIdx], kEpsilon))
|
||||
return static_cast<double>(altitudeDataM[prevPointIdx]);
|
||||
|
||||
double const k = (altitudeDataM[nextPointIdx] - altitudeDataM[prevPointIdx]) /
|
||||
(distanceDataM[nextPointIdx] - distanceDataM[prevPointIdx]);
|
||||
return static_cast<double>(altitudeDataM[prevPointIdx]) +
|
||||
k * (distFormStartM - distanceDataM[prevPointIdx]);
|
||||
};
|
||||
|
||||
double const routeLenM = distanceDataM.back();
|
||||
uniformAltitudeDataM.resize(resultPointCount);
|
||||
double const stepLenM = resultPointCount <= 1 ? 0.0 : routeLenM / (resultPointCount - 1);
|
||||
|
||||
for (size_t i = 0; i < resultPointCount; ++i)
|
||||
uniformAltitudeDataM[i] = calculateAltitude(static_cast<double>(i) * stepLenM);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GenerateYAxisChartData(uint32_t height, double minMetersPerPxl,
|
||||
vector<double> const & altitudeDataM, vector<double> & yAxisDataPxl)
|
||||
{
|
||||
if (altitudeDataM.empty())
|
||||
{
|
||||
yAxisDataPxl.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t constexpr kHeightIndentPxl = 2;
|
||||
uint32_t heightIndentPxl = kHeightIndentPxl;
|
||||
if (height <= 2 * kHeightIndentPxl)
|
||||
{
|
||||
LOG(LERROR, ("Chart height is less or equal than 2 * kHeightIndentPxl (", 2 * kHeightIndentPxl, ")"));
|
||||
heightIndentPxl = 0;
|
||||
}
|
||||
|
||||
auto const minMaxAltitudeIt = minmax_element(altitudeDataM.begin(), altitudeDataM.end());
|
||||
double const minAltM = *minMaxAltitudeIt.first;
|
||||
double const maxAltM = *minMaxAltitudeIt.second;
|
||||
double const deltaAltM = maxAltM - minAltM;
|
||||
uint32_t const drawHeightPxl = height - 2 * heightIndentPxl;
|
||||
double const metersPerPxl = max(minMetersPerPxl, deltaAltM / static_cast<double>(drawHeightPxl));
|
||||
if (metersPerPxl == 0.0)
|
||||
{
|
||||
LOG(LERROR, ("metersPerPxl == 0.0"));
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t const altitudeDataSize = altitudeDataM.size();
|
||||
yAxisDataPxl.resize(altitudeDataSize);
|
||||
for (size_t i = 0; i < altitudeDataSize; ++i)
|
||||
yAxisDataPxl[i] = height - heightIndentPxl - (altitudeDataM[i] - minAltM) / metersPerPxl;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GenerateChartByPoints(uint32_t width, uint32_t height, vector<m2::PointD> const & geometry,
|
||||
MapStyle mapStyle, vector<uint8_t> & frameBuffer)
|
||||
{
|
||||
frameBuffer.clear();
|
||||
if (width == 0 || height == 0)
|
||||
return;
|
||||
|
||||
agg::rgba8 const kBackgroundColor = agg::rgba8(255, 255, 255, 0);
|
||||
agg::rgba8 const kLineColor = GetLineColor(mapStyle);
|
||||
agg::rgba8 const kCurveColor = GetCurveColor(mapStyle);
|
||||
double constexpr kLineWidthPxl = 2.0;
|
||||
uint32_t constexpr kBPP = 4;
|
||||
|
||||
using TBlender = BlendAdaptor<agg::rgba8, agg::order_rgba>;
|
||||
using TPixelFormat = agg::pixfmt_custom_blend_rgba<TBlender, agg::rendering_buffer>;
|
||||
using TBaseRenderer = agg::renderer_base<TPixelFormat>;
|
||||
using TPrimitivesRenderer = agg::renderer_primitives<TBaseRenderer>;
|
||||
using TSolidRenderer = agg::renderer_scanline_aa_solid<TBaseRenderer>;
|
||||
using TPath = agg::poly_container_adaptor<vector<m2::PointD>>;
|
||||
using TStroke = agg::conv_stroke<TPath>;
|
||||
|
||||
agg::rendering_buffer renderBuffer;
|
||||
TPixelFormat pixelFormat(renderBuffer, agg::comp_op_src_over);
|
||||
TBaseRenderer baseRenderer(pixelFormat);
|
||||
|
||||
frameBuffer.assign(width * kBPP * height, 0);
|
||||
renderBuffer.attach(&frameBuffer[0], static_cast<unsigned>(width),
|
||||
static_cast<unsigned>(height), static_cast<int>(width * kBPP));
|
||||
|
||||
// Background.
|
||||
baseRenderer.reset_clipping(true);
|
||||
unsigned const op = pixelFormat.comp_op();
|
||||
pixelFormat.comp_op(agg::comp_op_src);
|
||||
baseRenderer.clear(kBackgroundColor);
|
||||
pixelFormat.comp_op(op);
|
||||
|
||||
agg::rasterizer_scanline_aa<> rasterizer;
|
||||
rasterizer.clip_box(0, 0, width, height);
|
||||
|
||||
if (geometry.empty())
|
||||
return; /* No chart line to draw. */
|
||||
|
||||
// Polygon under chart line.
|
||||
agg::path_storage underChartGeometryPath;
|
||||
underChartGeometryPath.move_to(geometry.front().x, static_cast<double>(height));
|
||||
for (auto const & p : geometry)
|
||||
underChartGeometryPath.line_to(p.x, p.y);
|
||||
underChartGeometryPath.line_to(geometry.back().x, static_cast<double>(height));
|
||||
underChartGeometryPath.close_polygon();
|
||||
|
||||
agg::conv_curve<agg::path_storage> curve(underChartGeometryPath);
|
||||
rasterizer.add_path(curve);
|
||||
agg::scanline32_p8 scanline;
|
||||
agg::render_scanlines_aa_solid(rasterizer, scanline, baseRenderer, kCurveColor);
|
||||
|
||||
// Chart line.
|
||||
TPath path_adaptor(geometry, false);
|
||||
TStroke stroke(path_adaptor);
|
||||
stroke.width(kLineWidthPxl);
|
||||
stroke.line_cap(agg::round_cap);
|
||||
stroke.line_join(agg::round_join);
|
||||
|
||||
rasterizer.add_path(stroke);
|
||||
agg::render_scanlines_aa_solid(rasterizer, scanline, baseRenderer, kLineColor);
|
||||
}
|
||||
|
||||
bool GenerateChart(uint32_t width, uint32_t height, vector<double> const & distanceDataM,
|
||||
feature::TAltitudes const & altitudeDataM, MapStyle mapStyle,
|
||||
vector<uint8_t> & frameBuffer)
|
||||
{
|
||||
if (distanceDataM.size() != altitudeDataM.size())
|
||||
{
|
||||
LOG(LERROR, ("The route is in inconsistent state. Size of altitudes is", altitudeDataM.size(),
|
||||
". Number of segment is", distanceDataM.size()));
|
||||
return false;
|
||||
}
|
||||
|
||||
vector<double> uniformAltitudeDataM;
|
||||
if (!NormalizeChartData(distanceDataM, altitudeDataM, width, uniformAltitudeDataM))
|
||||
return false;
|
||||
|
||||
vector<double> yAxisDataPxl;
|
||||
if (!GenerateYAxisChartData(height, 1.0 /* minMetersPerPxl */, uniformAltitudeDataM, yAxisDataPxl))
|
||||
return false;
|
||||
|
||||
size_t const uniformAltitudeDataSize = yAxisDataPxl.size();
|
||||
vector<m2::PointD> geometry(uniformAltitudeDataSize);
|
||||
|
||||
if (uniformAltitudeDataSize != 0)
|
||||
{
|
||||
double const oneSegLenPix =
|
||||
static_cast<double>(width) / (uniformAltitudeDataSize == 1 ? 1 : (uniformAltitudeDataSize - 1));
|
||||
for (size_t i = 0; i < uniformAltitudeDataSize; ++i)
|
||||
geometry[i] = m2::PointD(i * oneSegLenPix, yAxisDataPxl[i]);
|
||||
}
|
||||
|
||||
GenerateChartByPoints(width, height, geometry, mapStyle, frameBuffer);
|
||||
return true;
|
||||
}
|
||||
} // namespace maps
|
43
map/chart_generator.hpp
Normal file
43
map/chart_generator.hpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
#pragma once
|
||||
|
||||
#include "indexer/feature_altitude.hpp"
|
||||
#include "indexer/map_style.hpp"
|
||||
|
||||
#include "geometry/point2d.hpp"
|
||||
|
||||
#include "std/cstdint.hpp"
|
||||
#include "std/vector.hpp"
|
||||
|
||||
namespace maps
|
||||
{
|
||||
/// \brief fills uniformAltitudeDataM with altitude data which evenly distributed by
|
||||
/// |resultPointCount| points. |distanceDataM| and |altitudeDataM| form a curve of route altitude.
|
||||
/// This method is used to generalize and evenly distribute points of the chart.
|
||||
bool NormalizeChartData(vector<double> const & distanceDataM,
|
||||
feature::TAltitudes const & altitudeDataM, size_t resultPointCount,
|
||||
vector<double> & uniformAltitudeDataM);
|
||||
|
||||
/// \brief fills |yAxisDataPxl|. |yAxisDataPxl| is formed to pevent displaying
|
||||
/// big waves on the chart in case of small deviation in absolute values in |yAxisData|.
|
||||
/// \param height image chart height in pixels.
|
||||
/// \param minMetersInPixel minimum meter number per height pixel.
|
||||
/// \param altitudeDataM altitude data vector in meters.
|
||||
/// \param yAxisDataPxl Y-axis data of altitude chart in pixels.
|
||||
bool GenerateYAxisChartData(uint32_t height, double minMetersPerPxl,
|
||||
vector<double> const & altitudeDataM, vector<double> & yAxisDataPxl);
|
||||
|
||||
/// \brief generates chart image on a canvas with size |width|, |height| with |geometry|.
|
||||
/// (0, 0) is a left-top corner. X-axis goes down and Y-axis goes right.
|
||||
/// \param width is result image width in pixels.
|
||||
/// \param height is result image height in pixels.
|
||||
/// \param geometry is points which is used to draw a curve of the chart.
|
||||
/// \param mapStyle is a current map style.
|
||||
/// \param frameBuffer is a vector for a result image. It's resized in this method.
|
||||
/// It's filled with RGBA(8888) image date.
|
||||
void GenerateChartByPoints(uint32_t width, uint32_t height, vector<m2::PointD> const & geometry,
|
||||
MapStyle mapStyle, vector<uint8_t> & frameBuffer);
|
||||
|
||||
bool GenerateChart(uint32_t width, uint32_t height, vector<double> const & distanceDataM,
|
||||
feature::TAltitudes const & altitudeDataM, MapStyle mapStyle,
|
||||
vector<uint8_t> & frameBuffer);
|
||||
} // namespace maps
|
|
@ -1,4 +1,5 @@
|
|||
#include "map/framework.hpp"
|
||||
#include "map/chart_generator.hpp"
|
||||
#include "map/ge0_parser.hpp"
|
||||
#include "map/geourl_process.hpp"
|
||||
#include "map/gps_tracker.hpp"
|
||||
|
@ -2983,3 +2984,19 @@ bool Framework::OriginalFeatureHasDefaultName(FeatureID const & fid) const
|
|||
{
|
||||
return osm::Editor::Instance().OriginalFeatureHasDefaultName(fid);
|
||||
}
|
||||
|
||||
bool Framework::HasRouteAltitude() const { return m_routingSession.HasRouteAltitude(); }
|
||||
|
||||
bool Framework::GenerateRouteAltitudeChart(uint32_t width, uint32_t height,
|
||||
vector<uint8_t> & imageRGBAData) const
|
||||
{
|
||||
feature::TAltitudes altitudes;
|
||||
vector<double> segDistance;
|
||||
|
||||
if (!m_routingSession.GetRouteAltitudesAndDistancesM(segDistance, altitudes))
|
||||
return false;
|
||||
segDistance.insert(segDistance.begin(), 0.0);
|
||||
|
||||
return maps::GenerateChart(width, height, segDistance, altitudes,
|
||||
GetMapStyle(), imageRGBAData);
|
||||
}
|
||||
|
|
|
@ -725,6 +725,16 @@ public:
|
|||
void AllowAutoZoom(bool allowAutoZoom);
|
||||
void SaveAutoZoom(bool allowAutoZoom);
|
||||
|
||||
/// \returns true if altitude information along |m_route| is available and
|
||||
/// false otherwise.
|
||||
bool HasRouteAltitude() const;
|
||||
/// \brief Generates 4 bytes per point image (RGBA) and put the data to |imageRGBAData|.
|
||||
/// \returns If there is valid route info and returns true and false otherwise.
|
||||
/// \note If HasRouteAltitude() method returns true, GenerateRouteAltitudeChart(...)
|
||||
/// could return false if route was deleted or rebuilt between the calls.
|
||||
bool GenerateRouteAltitudeChart(uint32_t width, uint32_t height,
|
||||
vector<uint8_t> & imageRGBAData) const;
|
||||
|
||||
public:
|
||||
/// @name Editor interface.
|
||||
//@{
|
||||
|
|
|
@ -15,6 +15,7 @@ HEADERS += \
|
|||
booking_api.hpp \
|
||||
bookmark.hpp \
|
||||
bookmark_manager.hpp \
|
||||
chart_generator.hpp \
|
||||
displacement_mode_manager.hpp \
|
||||
feature_vec_model.hpp \
|
||||
framework.hpp \
|
||||
|
@ -38,6 +39,7 @@ SOURCES += \
|
|||
booking_api.cpp \
|
||||
bookmark.cpp \
|
||||
bookmark_manager.cpp \
|
||||
chart_generator.cpp \
|
||||
displacement_mode_manager.cpp \
|
||||
feature_vec_model.cpp \
|
||||
framework.cpp \
|
||||
|
|
234
map/map_tests/chart_generator_tests.cpp
Normal file
234
map/map_tests/chart_generator_tests.cpp
Normal file
|
@ -0,0 +1,234 @@
|
|||
#include "testing/testing.hpp"
|
||||
|
||||
#include "map/chart_generator.hpp"
|
||||
|
||||
#include "base/math.hpp"
|
||||
|
||||
#include "std/vector.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
double constexpr kEpsilon = 0.00001;
|
||||
uint32_t constexpr kBPP = 4;
|
||||
|
||||
bool AlmostEqualAbs(vector<double> const & v1, vector<double> const & v2)
|
||||
{
|
||||
if (v1.size() != v2.size())
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < v1.size(); ++i)
|
||||
{
|
||||
if (!my::AlmostEqualAbs(v1[i], v2[i], kEpsilon))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsColor(vector<uint8_t> const & frameBuffer, size_t startColorIdx, uint8_t expectedR,
|
||||
uint8_t expectedG, uint8_t expectedB, uint8_t expectedA)
|
||||
{
|
||||
CHECK_LESS_OR_EQUAL(startColorIdx + kBPP, frameBuffer.size(), ());
|
||||
|
||||
return frameBuffer[startColorIdx] == expectedR && frameBuffer[startColorIdx + 1] == expectedG &&
|
||||
frameBuffer[startColorIdx + 2] == expectedB && frameBuffer[startColorIdx + 3] == expectedA;
|
||||
}
|
||||
|
||||
void TestAngleColors(size_t width, size_t height, vector<uint8_t> const & frameBuffer,
|
||||
uint8_t expectedR, uint8_t expectedG, uint8_t expectedB, uint8_t expectedA)
|
||||
{
|
||||
TEST_EQUAL(frameBuffer.size(), width * height * kBPP, ());
|
||||
TEST(IsColor(frameBuffer, 0 /* startColorIdx */, expectedR, expectedG, expectedB, expectedA), ());
|
||||
TEST(IsColor(frameBuffer, kBPP * (width - 1) /* startColorIdx */, expectedR,
|
||||
expectedG, expectedB, expectedA), ());
|
||||
TEST(IsColor(frameBuffer, kBPP * height * (width - 1) /* startColorIdx */,
|
||||
expectedR, expectedG, expectedB, expectedA), ());
|
||||
TEST(IsColor(frameBuffer, kBPP * height * width - kBPP /* startColorIdx */,
|
||||
expectedR, expectedG, expectedB, expectedA), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(NormalizeChartData_SmokeTest)
|
||||
{
|
||||
vector<double> const distanceDataM = {0.0, 0.0, 0.0};
|
||||
feature::TAltitudes const altitudeDataM = {0, 0, 0};
|
||||
|
||||
vector<double> uniformAltitudeDataM;
|
||||
TEST(maps::NormalizeChartData(distanceDataM, altitudeDataM, 2 /* resultPointCount */, uniformAltitudeDataM),
|
||||
());
|
||||
|
||||
vector<double> const expectedUniformAltitudeDataM = {0.0, 0.0};
|
||||
TEST_EQUAL(expectedUniformAltitudeDataM, uniformAltitudeDataM, ());
|
||||
}
|
||||
|
||||
UNIT_TEST(NormalizeChartData_NoResultPointTest)
|
||||
{
|
||||
vector<double> const distanceDataM = {0.0, 0.0, 0.0};
|
||||
feature::TAltitudes const altitudeDataM = {0, 0, 0};
|
||||
|
||||
vector<double> uniformAltitudeDataM;
|
||||
TEST(maps::NormalizeChartData(distanceDataM, altitudeDataM, 0 /* resultPointCount */, uniformAltitudeDataM),
|
||||
());
|
||||
|
||||
TEST(uniformAltitudeDataM.empty(), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(NormalizeChartData_NoPointTest)
|
||||
{
|
||||
vector<double> const distanceDataM = {};
|
||||
feature::TAltitudes const altitudeDataM = {};
|
||||
|
||||
vector<double> uniformAltitudeDataM;
|
||||
TEST(maps::NormalizeChartData(distanceDataM, altitudeDataM, 2 /* resultPointCount */, uniformAltitudeDataM),
|
||||
());
|
||||
|
||||
TEST(uniformAltitudeDataM.empty(), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(NormalizeChartData_Test)
|
||||
{
|
||||
vector<double> const distanceDataM = {0.0, 2.0, 4.0, 6.0};
|
||||
feature::TAltitudes const altitudeDataM = {-9, 0, 9, 18};
|
||||
|
||||
vector<double> uniformAltitudeDataM;
|
||||
TEST(maps::NormalizeChartData(distanceDataM, altitudeDataM, 10 /* resultPointCount */, uniformAltitudeDataM),
|
||||
());
|
||||
|
||||
vector<double> const expectedUniformAltitudeDataM = {-9.0, -6.0, -3.0, 0.0, 3.0,
|
||||
6.0, 9.0, 12.0, 15.0, 18.0};
|
||||
TEST(AlmostEqualAbs(uniformAltitudeDataM, expectedUniformAltitudeDataM), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(GenerateYAxisChartData_SmokeTest)
|
||||
{
|
||||
vector<double> const altitudeDataM = {0.0, 0.0};
|
||||
vector<double> yAxisDataPxl;
|
||||
|
||||
TEST(maps::GenerateYAxisChartData(30 /* height */, 1.0 /* minMetersPerPxl */, altitudeDataM, yAxisDataPxl), ());
|
||||
vector<double> expecttedYAxisDataPxl = {28.0, 28.0};
|
||||
TEST(AlmostEqualAbs(yAxisDataPxl, expecttedYAxisDataPxl), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(GenerateYAxisChartData_EmptyAltitudeDataTest)
|
||||
{
|
||||
vector<double> const altitudeDataM = {};
|
||||
vector<double> yAxisDataPxl;
|
||||
|
||||
TEST(maps::GenerateYAxisChartData(30 /* height */, 1.0 /* minMetersPerPxl */, altitudeDataM, yAxisDataPxl), ());
|
||||
TEST(yAxisDataPxl.empty(), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(GenerateYAxisChartData_Test)
|
||||
{
|
||||
vector<double> const altitudeDataM = {0.0, 2.0, 0.0, -2.0, 1.0};
|
||||
vector<double> yAxisDataPxl;
|
||||
|
||||
TEST(maps::GenerateYAxisChartData(100 /* height */, 1.0 /* minMetersPerPxl */, altitudeDataM, yAxisDataPxl), ());
|
||||
vector<double> expecttedYAxisDataPxl = {96.0, 94.0, 96.0, 98.0, 95.0};
|
||||
TEST(AlmostEqualAbs(yAxisDataPxl, expecttedYAxisDataPxl), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(GenerateChartByPoints_NoGeometryTest)
|
||||
{
|
||||
vector<m2::PointD> const geometry = {};
|
||||
size_t constexpr width = 100;
|
||||
size_t constexpr height = 40;
|
||||
vector<uint8_t> frameBuffer;
|
||||
|
||||
maps::GenerateChartByPoints(width, height, geometry, MapStyleLight /* mapStyle */, frameBuffer);
|
||||
TestAngleColors(width, height, frameBuffer, 255 /* expectedR */, 255 /* expectedG */,
|
||||
255 /* expectedB */, 0 /* expectedA */);
|
||||
}
|
||||
|
||||
UNIT_TEST(GenerateChartByPoints_OnePointTest)
|
||||
{
|
||||
vector<m2::PointD> const geometry = {{20.0, 20.0}};
|
||||
size_t constexpr width = 40;
|
||||
size_t constexpr height = 40;
|
||||
vector<uint8_t> frameBuffer;
|
||||
|
||||
maps::GenerateChartByPoints(width, height, geometry, MapStyleLight /* mapStyle */, frameBuffer);
|
||||
TestAngleColors(width, height, frameBuffer, 255 /* expectedR */, 255 /* expectedG */,
|
||||
255 /* expectedB */, 0 /* expectedA */);
|
||||
}
|
||||
|
||||
UNIT_TEST(GenerateChartByPoints_Test)
|
||||
{
|
||||
vector<m2::PointD> const geometry = {{0.0, 0.0}, {10.0, 10.0}};
|
||||
|
||||
size_t constexpr width = 40;
|
||||
size_t constexpr height = 40;
|
||||
vector<uint8_t> frameBuffer;
|
||||
|
||||
maps::GenerateChartByPoints(width, height, geometry, MapStyleLight /* mapStyle */, frameBuffer);
|
||||
|
||||
TEST_EQUAL(frameBuffer.size(), width * height * kBPP, ());
|
||||
TEST(IsColor(frameBuffer, 0 /* startColorIdx */, 30 /* expectedR */, 150 /* expectedG */,
|
||||
240 /* expectedB */, 255 /* expectedA */),
|
||||
());
|
||||
TEST(IsColor(frameBuffer, kBPP * (width - 1) /* startColorIdx */, 255 /* expectedR */,
|
||||
255 /* expectedG */, 255 /* expectedB */, 0 /* expectedA */),
|
||||
());
|
||||
}
|
||||
|
||||
UNIT_TEST(GenerateChart_NoPointsTest)
|
||||
{
|
||||
size_t constexpr width = 50;
|
||||
vector<double> const distanceDataM = {};
|
||||
feature::TAltitudes const & altitudeDataM = {};
|
||||
vector<uint8_t> frameBuffer;
|
||||
|
||||
TEST(maps::GenerateChart(width, 50 /* height */, distanceDataM, altitudeDataM, MapStyleDark /* mapStyle */,
|
||||
frameBuffer),
|
||||
());
|
||||
TestAngleColors(width, 50 /* height */, frameBuffer, 255 /* expectedR */, 255 /* expectedG */,
|
||||
255 /* expectedB */, 0 /* expectedA */);
|
||||
}
|
||||
|
||||
UNIT_TEST(GenerateChart_OnePointTest)
|
||||
{
|
||||
size_t constexpr width = 50;
|
||||
size_t constexpr height = 50;
|
||||
vector<double> const distanceDataM = {0.0};
|
||||
feature::TAltitudes const & altitudeDataM = {0};
|
||||
vector<uint8_t> frameBuffer;
|
||||
|
||||
TEST(maps::GenerateChart(width, height, distanceDataM, altitudeDataM, MapStyleDark /* mapStyle */,
|
||||
frameBuffer),
|
||||
());
|
||||
TEST_EQUAL(frameBuffer.size(), width * height * kBPP, ());
|
||||
TEST(IsColor(frameBuffer, 0 /* startColorIdx */, 255 /* expectedR */, 255 /* expectedG */,
|
||||
255 /* expectedB */, 0 /* expectedA */), ());
|
||||
TEST(IsColor(frameBuffer, kBPP * (width - 1) /* startColorIdx */, 255 /* expectedR */,
|
||||
255 /* expectedG */, 255 /* expectedB */, 0 /* expectedA */), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(GenerateChart_EmptyRectTest)
|
||||
{
|
||||
size_t constexpr width = 0;
|
||||
vector<double> const distanceDataM = {};
|
||||
feature::TAltitudes const & altitudeDataM = {};
|
||||
vector<uint8_t> frameBuffer;
|
||||
|
||||
TEST(maps::GenerateChart(width, 50 /* height */, distanceDataM, altitudeDataM, MapStyleDark /* mapStyle */,
|
||||
frameBuffer),
|
||||
());
|
||||
TEST(frameBuffer.empty(), ());
|
||||
}
|
||||
|
||||
UNIT_TEST(GenerateChart_Test)
|
||||
{
|
||||
size_t constexpr width = 50;
|
||||
vector<double> const distanceDataM = {0.0, 100.0};
|
||||
feature::TAltitudes const & altitudeDataM = {0, 1000};
|
||||
vector<uint8_t> frameBuffer;
|
||||
|
||||
TEST(maps::GenerateChart(width, 50 /* height */, distanceDataM, altitudeDataM, MapStyleDark /* mapStyle */,
|
||||
frameBuffer),
|
||||
());
|
||||
TEST(IsColor(frameBuffer, 0 /* startColorIdx */, 255 /* expectedR */, 255 /* expectedG */,
|
||||
255 /* expectedB */, 0 /* expectedA */),
|
||||
());
|
||||
TEST(IsColor(frameBuffer, kBPP * 3 * width - kBPP /* startColorIdx */, 255 /* expectedR */,
|
||||
230 /* expectedG */, 140 /* expectedB */, 255 /* expectedA */),
|
||||
());
|
||||
}
|
||||
} // namespace
|
|
@ -38,8 +38,9 @@ SOURCES += \
|
|||
address_tests.cpp \
|
||||
booking_tests.cpp \
|
||||
bookmarks_test.cpp \
|
||||
chart_generator_tests.cpp \
|
||||
feature_getters_tests.cpp \
|
||||
ge0_parser_tests.cpp \
|
||||
ge0_parser_tests.cpp \
|
||||
geourl_test.cpp \
|
||||
gps_track_collection_test.cpp \
|
||||
gps_track_storage_test.cpp \
|
||||
|
|
|
@ -23,7 +23,7 @@ public:
|
|||
bool IsValid() const { return (m_current.IsValid() && m_poly.GetSize() > 1); }
|
||||
|
||||
m2::PolylineD const & GetPolyline() const { return m_poly; }
|
||||
|
||||
vector<double> const & GetSegDistanceM() const { return m_segDistance; }
|
||||
double GetTotalDistanceM() const;
|
||||
double GetDistanceFromBeginM() const;
|
||||
double GetDistanceToEndM() const;
|
||||
|
|
|
@ -56,16 +56,16 @@ public:
|
|||
|
||||
double GetPathLength() const override { return m_routeLength; }
|
||||
|
||||
m2::PointD const & GetStartPoint() const override
|
||||
Junction GetStartPoint() const override
|
||||
{
|
||||
CHECK(!m_routeEdges.empty(), ());
|
||||
return m_routeEdges.front().GetStartJunction().GetPoint();
|
||||
return m_routeEdges.front().GetStartJunction();
|
||||
}
|
||||
|
||||
m2::PointD const & GetEndPoint() const override
|
||||
Junction GetEndPoint() const override
|
||||
{
|
||||
CHECK(!m_routeEdges.empty(), ());
|
||||
return m_routeEdges.back().GetEndJunction().GetPoint();
|
||||
return m_routeEdges.back().GetEndJunction();
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -82,7 +82,7 @@ BicycleDirectionsEngine::BicycleDirectionsEngine(Index const & index) : m_index(
|
|||
|
||||
void BicycleDirectionsEngine::Generate(IRoadGraph const & graph, vector<Junction> const & path,
|
||||
Route::TTimes & times, Route::TTurns & turns,
|
||||
vector<m2::PointD> & routeGeometry,
|
||||
vector<Junction> & routeGeometry,
|
||||
my::Cancellable const & cancellable)
|
||||
{
|
||||
times.clear();
|
||||
|
@ -159,10 +159,7 @@ void BicycleDirectionsEngine::Generate(IRoadGraph const & graph, vector<Junction
|
|||
|
||||
LoadedPathSegment pathSegment;
|
||||
if (inFeatureId.IsValid())
|
||||
{
|
||||
LoadPathGeometry(inFeatureId, {prevJunction.GetPoint(), currJunction.GetPoint()},
|
||||
pathSegment);
|
||||
}
|
||||
LoadPathGeometry(inFeatureId, {prevJunction, currJunction}, pathSegment);
|
||||
|
||||
m_adjacentEdges.insert(make_pair(inFeatureId.m_index, move(adjacentEdges)));
|
||||
m_pathSegments.push_back(move(pathSegment));
|
||||
|
@ -172,6 +169,7 @@ void BicycleDirectionsEngine::Generate(IRoadGraph const & graph, vector<Junction
|
|||
RouterDelegate delegate;
|
||||
Route::TTimes turnAnnotationTimes;
|
||||
Route::TStreets streetNames;
|
||||
|
||||
MakeTurnAnnotation(resultGraph, delegate, routeGeometry, turns, turnAnnotationTimes, streetNames);
|
||||
}
|
||||
|
||||
|
@ -183,7 +181,7 @@ Index::FeaturesLoaderGuard & BicycleDirectionsEngine::GetLoader(MwmSet::MwmId co
|
|||
}
|
||||
|
||||
void BicycleDirectionsEngine::LoadPathGeometry(FeatureID const & featureId,
|
||||
vector<m2::PointD> const & path,
|
||||
vector<Junction> const & path,
|
||||
LoadedPathSegment & pathSegment)
|
||||
{
|
||||
pathSegment.Clear();
|
||||
|
|
|
@ -28,12 +28,12 @@ public:
|
|||
|
||||
// IDirectionsEngine override:
|
||||
void Generate(IRoadGraph const & graph, vector<Junction> const & path, Route::TTimes & times,
|
||||
Route::TTurns & turns, vector<m2::PointD> & routeGeometry,
|
||||
Route::TTurns & turns, vector<Junction> & routeGeometry,
|
||||
my::Cancellable const & cancellable) override;
|
||||
|
||||
private:
|
||||
Index::FeaturesLoaderGuard & GetLoader(MwmSet::MwmId const & id);
|
||||
void LoadPathGeometry(FeatureID const & featureId, vector<m2::PointD> const & path,
|
||||
void LoadPathGeometry(FeatureID const & featureId, vector<Junction> const & path,
|
||||
LoadedPathSegment & pathSegment);
|
||||
|
||||
TAdjacentEdgesMap m_adjacentEdges;
|
||||
|
|
|
@ -17,7 +17,7 @@ public:
|
|||
|
||||
virtual void Generate(IRoadGraph const & graph, vector<Junction> const & path,
|
||||
Route::TTimes & times, Route::TTurns & turns,
|
||||
vector<m2::PointD> & routeGeometry,
|
||||
vector<Junction> & routeGeometry,
|
||||
my::Cancellable const & cancellable) = 0;
|
||||
|
||||
protected:
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "routing/osrm_helpers.hpp"
|
||||
#include "routing/road_graph.hpp"
|
||||
#include "routing/turns.hpp"
|
||||
#include "routing/turn_candidate.hpp"
|
||||
|
||||
|
@ -25,7 +26,7 @@ struct FeatureGraphNode;
|
|||
*/
|
||||
struct LoadedPathSegment
|
||||
{
|
||||
vector<m2::PointD> m_path;
|
||||
vector<Junction> m_path;
|
||||
vector<turns::SingleLaneInfo> m_lanes;
|
||||
string m_name;
|
||||
TEdgeWeight m_weight; /*!< Time in seconds to pass the segment. */
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "routing/osrm_path_segment_factory.hpp"
|
||||
#include "routing/road_graph.hpp"
|
||||
#include "routing/routing_mapping.hpp"
|
||||
|
||||
#include "indexer/feature.hpp"
|
||||
|
@ -56,13 +57,13 @@ void LoadPathGeometry(buffer_vector<TSeg, 8> const & buffer, size_t startIndex,
|
|||
if (startIdx < endIdx)
|
||||
{
|
||||
for (auto idx = startIdx; idx <= endIdx; ++idx)
|
||||
loadPathGeometry.m_path.push_back(ft.GetPoint(idx));
|
||||
loadPathGeometry.m_path.emplace_back(ft.GetPoint(idx), feature::kDefaultAltitudeMeters);
|
||||
}
|
||||
else
|
||||
{
|
||||
// I use big signed type because endIdx can be 0.
|
||||
for (int64_t idx = startIdx; idx >= static_cast<int64_t>(endIdx); --idx)
|
||||
loadPathGeometry.m_path.push_back(ft.GetPoint(idx));
|
||||
loadPathGeometry.m_path.emplace_back(ft.GetPoint(idx), feature::kDefaultAltitudeMeters);
|
||||
}
|
||||
|
||||
// Load lanes if it is a last segment before junction.
|
||||
|
|
|
@ -153,9 +153,15 @@ public:
|
|||
|
||||
double GetPathLength() const override { return m_rawResult.shortestPathLength; }
|
||||
|
||||
m2::PointD const & GetStartPoint() const override { return m_rawResult.sourceEdge.segmentPoint; }
|
||||
Junction GetStartPoint() const override
|
||||
{
|
||||
return Junction(m_rawResult.sourceEdge.segmentPoint, feature::kDefaultAltitudeMeters);
|
||||
}
|
||||
|
||||
m2::PointD const & GetEndPoint() const override { return m_rawResult.targetEdge.segmentPoint; }
|
||||
Junction GetEndPoint() const override
|
||||
{
|
||||
return Junction(m_rawResult.targetEdge.segmentPoint, feature::kDefaultAltitudeMeters);
|
||||
}
|
||||
|
||||
OSRMRoutingResult(Index const & index, RoutingMapping & mapping, RawRoutingResult & result)
|
||||
: m_rawResult(result), m_index(index), m_routingMapping(mapping)
|
||||
|
@ -325,13 +331,18 @@ OsrmRouter::ResultCode OsrmRouter::MakeRouteFromCrossesPath(TCheckedPath const &
|
|||
Route::TTurns mwmTurnsDir;
|
||||
Route::TTimes mwmTimes;
|
||||
Route::TStreets mwmStreets;
|
||||
vector<m2::PointD> mwmPoints;
|
||||
vector<Junction> mwmJunctions;
|
||||
|
||||
OSRMRoutingResult resultGraph(*m_pIndex, *mwmMapping, routingResult);
|
||||
if (MakeTurnAnnotation(resultGraph, delegate, mwmPoints, mwmTurnsDir, mwmTimes, mwmStreets) != NoError)
|
||||
if (MakeTurnAnnotation(resultGraph, delegate, mwmJunctions, mwmTurnsDir, mwmTimes, mwmStreets) != NoError)
|
||||
{
|
||||
LOG(LWARNING, ("Can't load road path data from disk for", mwmMapping->GetCountryName()));
|
||||
return RouteNotFound;
|
||||
}
|
||||
|
||||
vector<m2::PointD> mwmPoints;
|
||||
JunctionsToPoints(mwmJunctions, mwmPoints);
|
||||
|
||||
// Connect annotated route.
|
||||
auto const pSize = static_cast<uint32_t>(points.size());
|
||||
for (auto turn : mwmTurnsDir)
|
||||
|
@ -366,9 +377,9 @@ OsrmRouter::ResultCode OsrmRouter::MakeRouteFromCrossesPath(TCheckedPath const &
|
|||
}
|
||||
|
||||
route.SetGeometry(points.begin(), points.end());
|
||||
route.SetTurnInstructions(turnsDir);
|
||||
route.SetSectionTimes(times);
|
||||
route.SetStreetNames(streets);
|
||||
route.SetTurnInstructions(move(turnsDir));
|
||||
route.SetSectionTimes(move(times));
|
||||
route.SetStreetNames(move(streets));
|
||||
return NoError;
|
||||
}
|
||||
|
||||
|
@ -494,19 +505,22 @@ OsrmRouter::ResultCode OsrmRouter::CalculateRoute(m2::PointD const & startPoint,
|
|||
Route::TTurns turnsDir;
|
||||
Route::TTimes times;
|
||||
Route::TStreets streets;
|
||||
vector<m2::PointD> points;
|
||||
vector<Junction> junctions;
|
||||
|
||||
OSRMRoutingResult resultGraph(*m_pIndex, *startMapping, routingResult);
|
||||
if (MakeTurnAnnotation(resultGraph, delegate, points, turnsDir, times, streets) != NoError)
|
||||
if (MakeTurnAnnotation(resultGraph, delegate, junctions, turnsDir, times, streets) != NoError)
|
||||
{
|
||||
LOG(LWARNING, ("Can't load road path data from disk!"));
|
||||
return RouteNotFound;
|
||||
}
|
||||
|
||||
vector<m2::PointD> points;
|
||||
JunctionsToPoints(junctions, points);
|
||||
|
||||
route.SetGeometry(points.begin(), points.end());
|
||||
route.SetTurnInstructions(turnsDir);
|
||||
route.SetSectionTimes(times);
|
||||
route.SetStreetNames(streets);
|
||||
route.SetTurnInstructions(move(turnsDir));
|
||||
route.SetSectionTimes(move(times));
|
||||
route.SetStreetNames(move(streets));
|
||||
|
||||
return NoError;
|
||||
}
|
||||
|
|
|
@ -20,14 +20,6 @@ bool HasType(uint32_t type, feature::TypesHolder const & types)
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Convert(vector<routing::Junction> const & path, vector<m2::PointD> & geometry)
|
||||
{
|
||||
size_t const pathSize = path.size();
|
||||
geometry.resize(pathSize);
|
||||
for (size_t i = 0; i < pathSize; ++i)
|
||||
geometry[i] = path[i].GetPoint();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace routing
|
||||
|
@ -42,7 +34,7 @@ PedestrianDirectionsEngine::PedestrianDirectionsEngine()
|
|||
|
||||
void PedestrianDirectionsEngine::Generate(IRoadGraph const & graph, vector<Junction> const & path,
|
||||
Route::TTimes & times, Route::TTurns & turns,
|
||||
vector<m2::PointD> & routeGeometry,
|
||||
vector<Junction> & routeGeometry,
|
||||
my::Cancellable const & cancellable)
|
||||
{
|
||||
times.clear();
|
||||
|
@ -64,7 +56,7 @@ void PedestrianDirectionsEngine::Generate(IRoadGraph const & graph, vector<Junct
|
|||
}
|
||||
|
||||
CalculateTurns(graph, routeEdges, turns, cancellable);
|
||||
Convert(path, routeGeometry);
|
||||
routeGeometry = path;
|
||||
}
|
||||
|
||||
void PedestrianDirectionsEngine::CalculateTurns(IRoadGraph const & graph,
|
||||
|
|
|
@ -12,7 +12,7 @@ public:
|
|||
|
||||
// IDirectionsEngine override:
|
||||
void Generate(IRoadGraph const & graph, vector<Junction> const & path, Route::TTimes & times,
|
||||
Route::TTurns & turns, vector<m2::PointD> & routeGeometry,
|
||||
Route::TTurns & turns, vector<Junction> & routeGeometry,
|
||||
my::Cancellable const & cancellable) override;
|
||||
|
||||
private:
|
||||
|
|
|
@ -265,4 +265,18 @@ private:
|
|||
|
||||
IRoadGraph::RoadInfo MakeRoadInfoForTesting(bool bidirectional, double speedKMPH,
|
||||
initializer_list<m2::PointD> const & points);
|
||||
|
||||
inline void JunctionsToPoints(vector<Junction> const & junctions, vector<m2::PointD> & points)
|
||||
{
|
||||
points.resize(junctions.size());
|
||||
for (size_t i = 0; i < junctions.size(); ++i)
|
||||
points[i] = junctions[i].GetPoint();
|
||||
}
|
||||
|
||||
inline void JunctionsToAltitudes(vector<Junction> const & junctions, feature::TAltitudes & altitudes)
|
||||
{
|
||||
altitudes.resize(junctions.size());
|
||||
for (size_t i = 0; i < junctions.size(); ++i)
|
||||
altitudes[i] = junctions[i].GetAltitude();
|
||||
}
|
||||
} // namespace routing
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "coding/reader_wrapper.hpp"
|
||||
|
||||
#include "indexer/feature.hpp"
|
||||
#include "indexer/feature_altitude.hpp"
|
||||
#include "indexer/ftypes_matcher.hpp"
|
||||
#include "indexer/index.hpp"
|
||||
|
||||
|
@ -19,6 +20,7 @@
|
|||
|
||||
#include "geometry/distance.hpp"
|
||||
|
||||
#include "std/algorithm.hpp"
|
||||
#include "std/queue.hpp"
|
||||
#include "std/set.hpp"
|
||||
|
||||
|
@ -241,16 +243,22 @@ void RoadGraphRouter::ReconstructRoute(vector<Junction> && path, Route & route,
|
|||
|
||||
Route::TTimes times;
|
||||
Route::TTurns turnsDir;
|
||||
vector<m2::PointD> geometry;
|
||||
vector<Junction> junctions;
|
||||
// @TODO(bykoianko) streetNames is not filled in Generate(). It should be done.
|
||||
Route::TStreets streetNames;
|
||||
if (m_directionsEngine)
|
||||
m_directionsEngine->Generate(*m_roadGraph, path, times, turnsDir, geometry, cancellable);
|
||||
m_directionsEngine->Generate(*m_roadGraph, path, times, turnsDir, junctions, cancellable);
|
||||
|
||||
route.SetGeometry(geometry.begin(), geometry.end());
|
||||
route.SetSectionTimes(times);
|
||||
route.SetTurnInstructions(turnsDir);
|
||||
route.SetStreetNames(streetNames);
|
||||
vector<m2::PointD> routeGeometry;
|
||||
JunctionsToPoints(junctions, routeGeometry);
|
||||
feature::TAltitudes altitudes;
|
||||
JunctionsToAltitudes(junctions, altitudes);
|
||||
|
||||
route.SetGeometry(routeGeometry.begin(), routeGeometry.end());
|
||||
route.SetSectionTimes(move(times));
|
||||
route.SetTurnInstructions(move(turnsDir));
|
||||
route.SetStreetNames(move(streetNames));
|
||||
route.SetAltitudes(move(altitudes));
|
||||
}
|
||||
|
||||
unique_ptr<IRouter> CreatePedestrianAStarRouter(Index & index, TCountryFileFn const & countryFileFn)
|
||||
|
|
|
@ -41,6 +41,7 @@ void Route::Swap(Route & rhs)
|
|||
swap(m_times, rhs.m_times);
|
||||
swap(m_streets, rhs.m_streets);
|
||||
m_absentCountries.swap(rhs.m_absentCountries);
|
||||
m_altitudes.swap(rhs.m_altitudes);
|
||||
}
|
||||
|
||||
void Route::AddAbsentCountry(string const & name)
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
#pragma once
|
||||
|
||||
#include "base/followed_polyline.hpp"
|
||||
#include "routing_settings.hpp"
|
||||
#include "turns.hpp"
|
||||
#include "routing/routing_settings.hpp"
|
||||
#include "routing/turns.hpp"
|
||||
|
||||
#include "indexer/feature_altitude.hpp"
|
||||
|
||||
#include "geometry/polyline2d.hpp"
|
||||
|
||||
#include "base/followed_polyline.hpp"
|
||||
|
||||
#include "std/vector.hpp"
|
||||
#include "std/set.hpp"
|
||||
#include "std/string.hpp"
|
||||
|
@ -52,21 +55,10 @@ public:
|
|||
Update();
|
||||
}
|
||||
|
||||
inline void SetTurnInstructions(TTurns & v)
|
||||
{
|
||||
swap(m_turns, v);
|
||||
}
|
||||
|
||||
inline void SetSectionTimes(TTimes & v)
|
||||
{
|
||||
swap(m_times, v);
|
||||
}
|
||||
|
||||
inline void SetStreetNames(TStreets & v)
|
||||
{
|
||||
swap(m_streets, v);
|
||||
}
|
||||
|
||||
inline void SetTurnInstructions(TTurns && v) { m_turns = move(v); }
|
||||
inline void SetSectionTimes(TTimes && v) { m_times = move(v); }
|
||||
inline void SetStreetNames(TStreets && v) { m_streets = move(v); }
|
||||
inline void SetAltitudes(feature::TAltitudes && v) { m_altitudes = move(v); }
|
||||
uint32_t GetTotalTimeSec() const;
|
||||
uint32_t GetCurrentTimeToEndSec() const;
|
||||
|
||||
|
@ -75,6 +67,8 @@ public:
|
|||
string const & GetRouterId() const { return m_router; }
|
||||
m2::PolylineD const & GetPoly() const { return m_poly.GetPolyline(); }
|
||||
TTurns const & GetTurns() const { return m_turns; }
|
||||
feature::TAltitudes const & GetAltitudes() const { return m_altitudes; }
|
||||
vector<double> const & GetSegDistanceM() const { return m_poly.GetSegDistanceM(); }
|
||||
void GetTurnsDistances(vector<double> & distances) const;
|
||||
string const & GetName() const { return m_name; }
|
||||
bool IsValid() const { return (m_poly.GetPolyline().GetSize() > 1); }
|
||||
|
@ -148,6 +142,7 @@ private:
|
|||
TTurns m_turns;
|
||||
TTimes m_times;
|
||||
TStreets m_streets;
|
||||
feature::TAltitudes m_altitudes;
|
||||
|
||||
mutable double m_currentTime;
|
||||
};
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "routing/loaded_path_segment.hpp"
|
||||
#include "routing/road_graph.hpp"
|
||||
#include "routing/turn_candidate.hpp"
|
||||
|
||||
#include "std/vector.hpp"
|
||||
|
@ -24,8 +25,8 @@ public:
|
|||
m2::PointD const & junctionPoint, size_t & ingoingCount,
|
||||
TurnCandidates & outgoingTurns) const = 0;
|
||||
virtual double GetPathLength() const = 0;
|
||||
virtual m2::PointD const & GetStartPoint() const = 0;
|
||||
virtual m2::PointD const & GetEndPoint() const = 0;
|
||||
virtual Junction GetStartPoint() const = 0;
|
||||
virtual Junction GetEndPoint() const = 0;
|
||||
|
||||
virtual ~IRoutingResult() = default;
|
||||
};
|
||||
|
|
|
@ -523,6 +523,29 @@ void RoutingSession::EmitCloseRoutingEvent() const
|
|||
alohalytics::Location::FromLatLon(lastGoodPoint.lat, lastGoodPoint.lon));
|
||||
}
|
||||
|
||||
bool RoutingSession::HasRouteAltitudeImpl() const
|
||||
{
|
||||
return m_route.GetAltitudes().size() == m_route.GetSegDistanceM().size() + 1;
|
||||
}
|
||||
|
||||
bool RoutingSession::HasRouteAltitude() const
|
||||
{
|
||||
threads::MutexGuard guard(m_routeSessionMutex);
|
||||
return HasRouteAltitudeImpl();
|
||||
}
|
||||
|
||||
bool RoutingSession::GetRouteAltitudesAndDistancesM(vector<double> & routeSegDistanceM,
|
||||
feature::TAltitudes & routeAltitudesM) const
|
||||
{
|
||||
threads::MutexGuard guard(m_routeSessionMutex);
|
||||
if (!m_route.IsValid() || !HasRouteAltitudeImpl())
|
||||
return false;
|
||||
|
||||
routeSegDistanceM = m_route.GetSegDistanceM();
|
||||
routeAltitudesM.assign(m_route.GetAltitudes().cbegin(), m_route.GetAltitudes().cend());
|
||||
return true;
|
||||
}
|
||||
|
||||
string DebugPrint(RoutingSession::State state)
|
||||
{
|
||||
switch (state)
|
||||
|
|
|
@ -105,6 +105,15 @@ public:
|
|||
inline void SetState(State state) { m_state = state; }
|
||||
|
||||
Route const & GetRoute() const { return m_route; }
|
||||
/// \returns true if altitude information along |m_route| is available and
|
||||
/// false otherwise.
|
||||
bool HasRouteAltitude() const;
|
||||
|
||||
/// \brief copies distance from route beginning to ends of route segments in meters and
|
||||
/// route altitude information to |routeSegDistanceM| and |routeAltitudes|.
|
||||
/// \returns true if there is valid route information. If the route is not valid returns false.
|
||||
bool GetRouteAltitudesAndDistancesM(vector<double> & routeSegDistanceM,
|
||||
feature::TAltitudes & routeAltitudesM) const;
|
||||
|
||||
State OnLocationPositionChanged(location::GpsInfo const & info, Index const & index);
|
||||
void GetRouteFollowingInfo(location::FollowingInfo & info) const;
|
||||
|
@ -165,6 +174,8 @@ private:
|
|||
void RemoveRoute();
|
||||
void RemoveRouteImpl();
|
||||
|
||||
bool HasRouteAltitudeImpl() const;
|
||||
|
||||
private:
|
||||
unique_ptr<AsyncRouter> m_router;
|
||||
Route m_route;
|
||||
|
|
|
@ -50,7 +50,7 @@ UNIT_TEST(DistanceToCurrentTurnTest)
|
|||
Route route("TestRouter");
|
||||
route.SetGeometry(kTestGeometry.begin(), kTestGeometry.end());
|
||||
vector<turns::TurnItem> turns(kTestTurns);
|
||||
route.SetTurnInstructions(turns);
|
||||
route.SetTurnInstructions(move(turns));
|
||||
|
||||
double distance;
|
||||
turns::TurnItem turn;
|
||||
|
@ -85,7 +85,7 @@ UNIT_TEST(NextTurnTest)
|
|||
Route route("TestRouter");
|
||||
route.SetGeometry(kTestGeometry.begin(), kTestGeometry.end());
|
||||
vector<turns::TurnItem> turns(kTestTurns);
|
||||
route.SetTurnInstructions(turns);
|
||||
route.SetTurnInstructions(move(turns));
|
||||
|
||||
double distance, nextDistance;
|
||||
turns::TurnItem turn;
|
||||
|
@ -114,7 +114,7 @@ UNIT_TEST(NextTurnsTest)
|
|||
Route route("TestRouter");
|
||||
route.SetGeometry(kTestGeometry.begin(), kTestGeometry.end());
|
||||
vector<turns::TurnItem> turns(kTestTurns);
|
||||
route.SetTurnInstructions(turns);
|
||||
route.SetTurnInstructions(move(turns));
|
||||
vector<turns::TurnItemDist> turnsDist;
|
||||
|
||||
{
|
||||
|
@ -165,9 +165,9 @@ UNIT_TEST(RouteNameTest)
|
|||
|
||||
route.SetGeometry(kTestGeometry.begin(), kTestGeometry.end());
|
||||
vector<turns::TurnItem> turns(kTestTurns);
|
||||
route.SetTurnInstructions(turns);
|
||||
route.SetTurnInstructions(move(turns));
|
||||
Route::TStreets names(kTestNames);
|
||||
route.SetStreetNames(names);
|
||||
route.SetStreetNames(move(names));
|
||||
|
||||
string name;
|
||||
route.GetCurrentStreetName(name);
|
||||
|
|
|
@ -123,11 +123,11 @@ UNIT_TEST(TestFixupTurns)
|
|||
m2::RectD const kSquareNearZero = MercatorBounds::MetresToXY(kSquareCenterLonLat.x,
|
||||
kSquareCenterLonLat.y, kHalfSquareSideMeters);
|
||||
// Removing a turn in case staying on a roundabout.
|
||||
vector<m2::PointD> const pointsMerc1 = {
|
||||
{ kSquareNearZero.minX(), kSquareNearZero.minY()},
|
||||
{ kSquareNearZero.minX(), kSquareNearZero.maxY() },
|
||||
{ kSquareNearZero.maxX(), kSquareNearZero.maxY() },
|
||||
{ kSquareNearZero.maxX(), kSquareNearZero.minY() }
|
||||
vector<Junction> const pointsMerc1 = {
|
||||
{{ kSquareNearZero.minX(), kSquareNearZero.minY() }, feature::kDefaultAltitudeMeters},
|
||||
{{ kSquareNearZero.minX(), kSquareNearZero.maxY() }, feature::kDefaultAltitudeMeters},
|
||||
{{ kSquareNearZero.maxX(), kSquareNearZero.maxY() }, feature::kDefaultAltitudeMeters},
|
||||
{{ kSquareNearZero.maxX(), kSquareNearZero.minY() }, feature::kDefaultAltitudeMeters},
|
||||
};
|
||||
// The constructor TurnItem(uint32_t idx, TurnDirection t, uint32_t exitNum = 0)
|
||||
// is used for initialization of vector<TurnItem> below.
|
||||
|
@ -143,10 +143,11 @@ UNIT_TEST(TestFixupTurns)
|
|||
TEST_EQUAL(turnsDir1, expectedTurnDir1, ());
|
||||
|
||||
// Merging turns which are close to each other.
|
||||
vector<m2::PointD> const pointsMerc2 = {
|
||||
{ kSquareNearZero.minX(), kSquareNearZero.minY()},
|
||||
{ kSquareCenterLonLat.x, kSquareCenterLonLat.y },
|
||||
{ kSquareNearZero.maxX(), kSquareNearZero.maxY() }};
|
||||
vector<Junction> const pointsMerc2 = {
|
||||
{{ kSquareNearZero.minX(), kSquareNearZero.minY()}, feature::kDefaultAltitudeMeters},
|
||||
{{ kSquareCenterLonLat.x, kSquareCenterLonLat.y }, feature::kDefaultAltitudeMeters},
|
||||
{{ kSquareNearZero.maxX(), kSquareNearZero.maxY() }, feature::kDefaultAltitudeMeters},
|
||||
};
|
||||
Route::TTurns turnsDir2 = {{0, TurnDirection::GoStraight},
|
||||
{1, TurnDirection::TurnLeft},
|
||||
{2, TurnDirection::ReachedYourDestination}};
|
||||
|
@ -157,10 +158,11 @@ UNIT_TEST(TestFixupTurns)
|
|||
TEST_EQUAL(turnsDir2, expectedTurnDir2, ());
|
||||
|
||||
// No turn is removed.
|
||||
vector<m2::PointD> const pointsMerc3 = {
|
||||
{ kSquareNearZero.minX(), kSquareNearZero.minY()},
|
||||
{ kSquareNearZero.minX(), kSquareNearZero.maxY() },
|
||||
{ kSquareNearZero.maxX(), kSquareNearZero.maxY() }};
|
||||
vector<Junction> const pointsMerc3 = {
|
||||
{{ kSquareNearZero.minX(), kSquareNearZero.minY()}, feature::kDefaultAltitudeMeters},
|
||||
{{ kSquareNearZero.minX(), kSquareNearZero.maxY() }, feature::kDefaultAltitudeMeters},
|
||||
{{ kSquareNearZero.maxX(), kSquareNearZero.maxY() }, feature::kDefaultAltitudeMeters},
|
||||
};
|
||||
Route::TTurns turnsDir3 = {{1, TurnDirection::TurnRight},
|
||||
{2, TurnDirection::ReachedYourDestination}};
|
||||
|
||||
|
|
|
@ -193,7 +193,7 @@ TurnDirection FindDirectionByAngle(vector<pair<double, TurnDirection>> const & l
|
|||
* shift belongs to a range [0, abs(end - start)].
|
||||
* \return an ingoing or outgoing point for a turn calculation.
|
||||
*/
|
||||
m2::PointD GetPointForTurn(vector<m2::PointD> const & path, m2::PointD const & junctionPoint,
|
||||
m2::PointD GetPointForTurn(vector<Junction> const & path, m2::PointD const & junctionPoint,
|
||||
size_t const maxPointsCount, double const minDistMeters,
|
||||
function<size_t(const size_t start, const size_t end, const size_t shift)> GetPointIndex)
|
||||
{
|
||||
|
@ -210,7 +210,7 @@ m2::PointD GetPointForTurn(vector<m2::PointD> const & path, m2::PointD const & j
|
|||
|
||||
for (size_t i = 1; i <= usedFtPntNum; ++i)
|
||||
{
|
||||
nextPoint = path[GetPointIndex(0, numSegPoints, i)];
|
||||
nextPoint = path[GetPointIndex(0, numSegPoints, i)].GetPoint();
|
||||
|
||||
// TODO The code below is a stub for compatability with older versions with this function.
|
||||
// Remove it, fix tests cases when it works (integration test
|
||||
|
@ -254,7 +254,7 @@ bool TurnInfo::IsSegmentsValid() const
|
|||
}
|
||||
|
||||
IRouter::ResultCode MakeTurnAnnotation(turns::IRoutingResult const & result,
|
||||
RouterDelegate const & delegate, vector<m2::PointD> & points,
|
||||
RouterDelegate const & delegate, vector<Junction> & junctions,
|
||||
Route::TTurns & turnsDir, Route::TTimes & times,
|
||||
Route::TStreets & streets)
|
||||
{
|
||||
|
@ -279,13 +279,13 @@ IRouter::ResultCode MakeTurnAnnotation(turns::IRoutingResult const & result,
|
|||
|
||||
// Street names. I put empty names too, to avoid freezing old street name while riding on
|
||||
// unnamed street.
|
||||
streets.emplace_back(max(points.size(), static_cast<size_t>(1)) - 1, loadedSegmentIt->m_name);
|
||||
streets.emplace_back(max(junctions.size(), static_cast<size_t>(1)) - 1, loadedSegmentIt->m_name);
|
||||
|
||||
// Turns information.
|
||||
if (!points.empty() && skipTurnSegments == 0)
|
||||
if (!junctions.empty() && skipTurnSegments == 0)
|
||||
{
|
||||
turns::TurnItem turnItem;
|
||||
turnItem.m_index = static_cast<uint32_t>(points.size() - 1);
|
||||
turnItem.m_index = static_cast<uint32_t>(junctions.size() - 1);
|
||||
|
||||
size_t segmentIndex = distance(loadedSegments.begin(), loadedSegmentIt);
|
||||
skipTurnSegments = CheckUTurnOnRoute(loadedSegments, segmentIndex, turnItem);
|
||||
|
@ -297,14 +297,14 @@ IRouter::ResultCode MakeTurnAnnotation(turns::IRoutingResult const & result,
|
|||
|
||||
#ifdef DEBUG
|
||||
double distMeters = 0.0;
|
||||
for (size_t k = lastIdx + 1; k < points.size(); ++k)
|
||||
distMeters += MercatorBounds::DistanceOnEarth(points[k - 1], points[k]);
|
||||
for (size_t k = lastIdx + 1; k < junctions.size(); ++k)
|
||||
distMeters += MercatorBounds::DistanceOnEarth(junctions[k - 1].GetPoint(), junctions[k].GetPoint());
|
||||
LOG(LDEBUG, ("Speed:", 3.6 * distMeters / nodeTimeSeconds, "kmph; Dist:", distMeters, "Time:",
|
||||
nodeTimeSeconds, "s", lastIdx, "e", points.size(), "source:",
|
||||
nodeTimeSeconds, "s", lastIdx, "e", junctions.size(), "source:",
|
||||
turnItem.m_sourceName, "target:", turnItem.m_targetName));
|
||||
lastIdx = points.size();
|
||||
lastIdx = junctions.size();
|
||||
#endif
|
||||
times.push_back(Route::TTimeItem(points.size(), estimatedTime));
|
||||
times.push_back(Route::TTimeItem(junctions.size(), estimatedTime));
|
||||
|
||||
// Lane information.
|
||||
if (turnItem.m_turn != turns::TurnDirection::NoTurn)
|
||||
|
@ -319,22 +319,22 @@ IRouter::ResultCode MakeTurnAnnotation(turns::IRoutingResult const & result,
|
|||
--skipTurnSegments;
|
||||
|
||||
// Path geometry.
|
||||
points.insert(points.end(), loadedSegmentIt->m_path.begin(), loadedSegmentIt->m_path.end());
|
||||
junctions.insert(junctions.end(), loadedSegmentIt->m_path.begin(), loadedSegmentIt->m_path.end());
|
||||
}
|
||||
|
||||
// Path found. Points will be replaced by start and end edges points.
|
||||
if (points.size() == 1)
|
||||
points.push_back(points.front());
|
||||
// Path found. Points will be replaced by start and end edges junctions.
|
||||
if (junctions.size() == 1)
|
||||
junctions.push_back(junctions.front());
|
||||
|
||||
if (points.size() < 2)
|
||||
if (junctions.size() < 2)
|
||||
return IRouter::ResultCode::RouteNotFound;
|
||||
|
||||
points.front() = result.GetStartPoint();
|
||||
points.back() = result.GetEndPoint();
|
||||
junctions.front() = result.GetStartPoint();
|
||||
junctions.back() = result.GetEndPoint();
|
||||
|
||||
times.push_back(Route::TTimeItem(points.size() - 1, estimatedTime));
|
||||
turnsDir.emplace_back(turns::TurnItem(static_cast<uint32_t>(points.size()) - 1, turns::TurnDirection::ReachedYourDestination));
|
||||
turns::FixupTurns(points, turnsDir);
|
||||
times.push_back(Route::TTimeItem(junctions.size() - 1, estimatedTime));
|
||||
turnsDir.emplace_back(turns::TurnItem(static_cast<uint32_t>(junctions.size()) - 1, turns::TurnDirection::ReachedYourDestination));
|
||||
turns::FixupTurns(junctions, turnsDir);
|
||||
|
||||
#ifdef DEBUG
|
||||
for (auto t : turnsDir)
|
||||
|
@ -349,7 +349,7 @@ IRouter::ResultCode MakeTurnAnnotation(turns::IRoutingResult const & result,
|
|||
{
|
||||
double dist = 0;
|
||||
for (size_t i = last + 1; i <= t.first; ++i)
|
||||
dist += MercatorBounds::DistanceOnEarth(points[i - 1], points[i]);
|
||||
dist += MercatorBounds::DistanceOnEarth(junctions[i - 1].GetPoint(), junctions[i].GetPoint());
|
||||
|
||||
double time = t.second - lastTime;
|
||||
|
||||
|
@ -376,23 +376,23 @@ double CalculateMercatorDistanceAlongPath(uint32_t startPointIndex, uint32_t end
|
|||
return mercatorDistanceBetweenTurns;
|
||||
}
|
||||
|
||||
void FixupTurns(vector<m2::PointD> const & points, Route::TTurns & turnsDir)
|
||||
void FixupTurns(vector<Junction> const & junctions, Route::TTurns & turnsDir)
|
||||
{
|
||||
double const kMergeDistMeters = 30.0;
|
||||
// For turns that are not EnterRoundAbout exitNum is always equal to zero.
|
||||
// If a turn is EnterRoundAbout exitNum is a number of turns between two points:
|
||||
// If a turn is EnterRoundAbout exitNum is a number of turns between two junctions:
|
||||
// (1) the route enters to the roundabout;
|
||||
// (2) the route leaves the roundabout;
|
||||
uint32_t exitNum = 0;
|
||||
// If a roundabout is worked up the roundabout value points to the turn
|
||||
// If a roundabout is worked up the roundabout value junctions to the turn
|
||||
// of the enter to the roundabout. If not, roundabout is equal to nullptr.
|
||||
TurnItem * roundabout = nullptr;
|
||||
|
||||
auto routeDistanceMeters = [&points](uint32_t start, uint32_t end)
|
||||
auto routeDistanceMeters = [&junctions](uint32_t start, uint32_t end)
|
||||
{
|
||||
double res = 0.0;
|
||||
for (uint32_t i = start + 1; i < end; ++i)
|
||||
res += MercatorBounds::DistanceOnEarth(points[i - 1], points[i]);
|
||||
res += MercatorBounds::DistanceOnEarth(junctions[i - 1].GetPoint(), junctions[i].GetPoint());
|
||||
return res;
|
||||
};
|
||||
|
||||
|
@ -568,11 +568,11 @@ void GetTurnDirection(IRoutingResult const & result, TurnInfo & turnInfo, TurnIt
|
|||
|
||||
ASSERT(!turnInfo.m_ingoing.m_path.empty(), ());
|
||||
ASSERT(!turnInfo.m_outgoing.m_path.empty(), ());
|
||||
ASSERT_LESS(MercatorBounds::DistanceOnEarth(turnInfo.m_ingoing.m_path.back(),
|
||||
turnInfo.m_outgoing.m_path.front()),
|
||||
ASSERT_LESS(MercatorBounds::DistanceOnEarth(turnInfo.m_ingoing.m_path.back().GetPoint(),
|
||||
turnInfo.m_outgoing.m_path.front().GetPoint()),
|
||||
kFeaturesNearTurnMeters, ());
|
||||
|
||||
m2::PointD const junctionPoint = turnInfo.m_ingoing.m_path.back();
|
||||
m2::PointD const junctionPoint = turnInfo.m_ingoing.m_path.back().GetPoint();
|
||||
m2::PointD const ingoingPoint = GetPointForTurn(turnInfo.m_ingoing.m_path, junctionPoint,
|
||||
kMaxPointsCount, kMinDistMeters,
|
||||
GetIngoingPointIndex);
|
||||
|
@ -592,7 +592,7 @@ void GetTurnDirection(IRoutingResult const & result, TurnInfo & turnInfo, TurnIt
|
|||
return;
|
||||
|
||||
ASSERT_GREATER(turnInfo.m_ingoing.m_path.size(), 1, ());
|
||||
m2::PointD const ingoingPointOneSegment = turnInfo.m_ingoing.m_path[turnInfo.m_ingoing.m_path.size() - 2];
|
||||
m2::PointD const ingoingPointOneSegment = turnInfo.m_ingoing.m_path[turnInfo.m_ingoing.m_path.size() - 2].GetPoint();
|
||||
TurnCandidates nodes;
|
||||
size_t ingoingCount;
|
||||
result.GetPossibleTurns(turnInfo.m_ingoing.m_nodeId, ingoingPointOneSegment, junctionPoint,
|
||||
|
@ -702,8 +702,8 @@ size_t CheckUTurnOnRoute(TUnpackedPathSegments const & segments,
|
|||
if (path[path.size() - 2] == checkedSegment.m_path[1])
|
||||
return 0;
|
||||
|
||||
m2::PointD const v1 = path[path.size() - 1] - path[path.size() - 2];
|
||||
m2::PointD const v2 = checkedSegment.m_path[1] - checkedSegment.m_path[0];
|
||||
m2::PointD const v1 = path[path.size() - 1].GetPoint() - path[path.size() - 2].GetPoint();
|
||||
m2::PointD const v2 = checkedSegment.m_path[1].GetPoint() - checkedSegment.m_path[0].GetPoint();
|
||||
|
||||
auto angle = ang::TwoVectorsAngle(m2::PointD::Zero(), v1, v2);
|
||||
|
||||
|
@ -711,7 +711,7 @@ size_t CheckUTurnOnRoute(TUnpackedPathSegments const & segments,
|
|||
return 0;
|
||||
|
||||
// Determine turn direction.
|
||||
m2::PointD const junctionPoint = masterSegment.m_path.back();
|
||||
m2::PointD const junctionPoint = masterSegment.m_path.back().GetPoint();
|
||||
m2::PointD const ingoingPoint = GetPointForTurn(masterSegment.m_path, junctionPoint,
|
||||
kMaxPointsCount, kMinDistMeters,
|
||||
GetIngoingPointIndex);
|
||||
|
|
|
@ -43,7 +43,7 @@ using TGetIndexFunction = function<size_t(pair<size_t, size_t>)>;
|
|||
* \return routing operation result code.
|
||||
*/
|
||||
IRouter::ResultCode MakeTurnAnnotation(turns::IRoutingResult const & result,
|
||||
RouterDelegate const & delegate, vector<m2::PointD> & points,
|
||||
RouterDelegate const & delegate, vector<Junction> & points,
|
||||
Route::TTurns & turnsDir, Route::TTimes & times,
|
||||
Route::TStreets & streets);
|
||||
|
||||
|
@ -72,7 +72,7 @@ double CalculateMercatorDistanceAlongPath(uint32_t startPointIndex, uint32_t end
|
|||
* \brief Selects lanes which are recommended for an end user.
|
||||
*/
|
||||
void SelectRecommendedLanes(Route::TTurns & turnsDir);
|
||||
void FixupTurns(vector<m2::PointD> const & points, Route::TTurns & turnsDir);
|
||||
void FixupTurns(vector<Junction> const & points, Route::TTurns & turnsDir);
|
||||
inline size_t GetFirstSegmentPointIndex(pair<size_t, size_t> const & p) { return p.first; }
|
||||
|
||||
TurnDirection InvertDirection(TurnDirection dir);
|
||||
|
|
Loading…
Add table
Reference in a new issue