Merge pull request #4193 from bykoianko/master-generating-route-altitude-chart-image

Generating route altitude chart image.
This commit is contained in:
ygorshenin 2016-09-08 19:30:11 +03:00 committed by GitHub
commit f2b4f7db60
27 changed files with 765 additions and 121 deletions

276
map/chart_generator.cpp Normal file
View 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
View 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

View file

@ -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);
}

View file

@ -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.
//@{

View file

@ -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 \

View 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

View file

@ -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 \

View file

@ -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;

View file

@ -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();

View file

@ -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;

View file

@ -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:

View file

@ -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. */

View file

@ -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.

View file

@ -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;
}

View file

@ -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,

View file

@ -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:

View file

@ -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

View file

@ -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)

View file

@ -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)

View file

@ -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;
};

View file

@ -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;
};

View file

@ -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)

View file

@ -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;

View file

@ -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);

View file

@ -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}};

View file

@ -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);

View file

@ -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);