[generator] Handle mini-roundabouts in the middle of the road.

This commit is contained in:
Olga Khlopkova 2020-01-30 13:10:58 +03:00 committed by Maksim Andrianov
parent d89a435545
commit 65316de3aa
3 changed files with 204 additions and 95 deletions

View file

@ -97,7 +97,7 @@ UNIT_TEST(TrimSegment_Vertical)
m2::PointD const a(2.0, -1.0);
m2::PointD const b(2.0, 3.0);
double const dist = 1.0;
m2::PointD const point = TrimSegment(a, b, dist);
m2::PointD const point = GetPointAtDistFromTarget(a /* source */, b /* target */, dist);
m2::PointD const pointPlan(2.0, 2.0);
TEST(AlmostEqualAbs(point, pointPlan, kMwmPointAccuracy), ());
}
@ -107,7 +107,7 @@ UNIT_TEST(TrimSegment_VerticalNegative)
m2::PointD const a(-3.0, -5.0);
m2::PointD const b(-3.0, 6.0);
double const dist = 4.0;
m2::PointD const point = TrimSegment(a, b, dist);
m2::PointD const point = GetPointAtDistFromTarget(a /* source */, b /* target */, dist);
m2::PointD const pointPlan(-3.0, 2.0);
TEST(AlmostEqualAbs(point, pointPlan, kMwmPointAccuracy), ());
}
@ -117,7 +117,7 @@ UNIT_TEST(TrimSegment_ExceptionalCase)
m2::PointD const a(1.0, 2.0);
m2::PointD const b(2.0, 3.0);
double const dist = 10.0;
m2::PointD const point = TrimSegment(a, b, dist);
m2::PointD const point = GetPointAtDistFromTarget(a /* source */, b /* target */, dist);
TEST(AlmostEqualAbs(point, a, kMwmPointAccuracy), ());
}
@ -170,7 +170,8 @@ UNIT_TEST(TrimSegment_Radius3)
m2::PointD const pointRoundabout(15.0, 17.0);
double const r = 3.0;
m2::PointD const nextPointOnRoad = TrimSegment(pointOnRoad, pointRoundabout, r);
m2::PointD const nextPointOnRoad = GetPointAtDistFromTarget(
pointOnRoad /* source */, pointRoundabout /* target */, r /* dist */);
double const dist = DistanceOnPlain(nextPointOnRoad, pointRoundabout);
TestRunCmpNumbers(dist, r);
}
@ -209,7 +210,8 @@ UNIT_TEST(Manage_MiniRoundabout_1Road)
kMwmPointAccuracy),
());
m2::PointD newPointOnRoad = TrimSegment(nearest, center, r);
m2::PointD const newPointOnRoad =
GetPointAtDistFromTarget(nearest /* source */, center /* target */, r /* dist */);
AddPointToCircle(circlePlain, newPointOnRoad);
std::vector<m2::PointD> const circlePlainExpected{
@ -237,19 +239,22 @@ UNIT_TEST(Manage_MiniRoundabout_4Roads)
auto circlePlain = PointToPolygon(center, r, 6, 30.0);
AddPointToCircle(circlePlain, TrimSegment(mercator::FromLatLon(stationRoadNode.m_lat,
stationRoadNode.m_lon),
center, r));
AddPointToCircle(circlePlain, TrimSegment(mercator::FromLatLon(plaughRoundaboutNode.m_lat,
plaughRoundaboutNode.m_lon),
center, r));
AddPointToCircle(circlePlain, TrimSegment(mercator::FromLatLon(stationRoadLeftNode.m_lat,
stationRoadLeftNode.m_lon),
center, r));
AddPointToCircle(circlePlain, GetPointAtDistFromTarget(
mercator::FromLatLon(stationRoadNode.m_lat,
stationRoadNode.m_lon) /* source */,
center /* target */, r /* dist */));
AddPointToCircle(circlePlain, GetPointAtDistFromTarget(
mercator::FromLatLon(plaughRoundaboutNode.m_lat,
plaughRoundaboutNode.m_lon) /* source */,
center /* target */, r /* dist */));
AddPointToCircle(circlePlain, GetPointAtDistFromTarget(
mercator::FromLatLon(stationRoadLeftNode.m_lat,
stationRoadLeftNode.m_lon) /* source */,
center /* target */, r /* dist */));
AddPointToCircle(circlePlain,
TrimSegment(mercator::FromLatLon(plaughRoundaboutRightNode.m_lat,
plaughRoundaboutRightNode.m_lon),
center, r));
GetPointAtDistFromTarget(mercator::FromLatLon(plaughRoundaboutRightNode.m_lat,
plaughRoundaboutRightNode.m_lon),
center, r));
std::vector<m2::PointD> const circlePlainExpected{
{-0.47381, 60.67520}, {-0.47383, 60.67521}, {-0.47384, 60.67521}, {-0.47385, 60.67520},

View file

@ -16,6 +16,45 @@
namespace
{
double constexpr kDefaultRadiusMeters = 5.0;
/// \brief Moves |it| on the |road| range one step from current |it| element.
bool MoveIterAwayFromRoundabout(feature::FeatureBuilder::PointSeq::iterator & it,
feature::FeatureBuilder::PointSeq const & road)
{
bool middlePoint = false;
if (it == road.begin())
{
++it;
}
else if (it + 1 == road.end())
{
--it;
}
else
{
// Mini-roundabout is on the middle of the road so we need to insert 2 points to the roundabout
// circle and create additional surrogate road.
--it;
middlePoint = true;
}
return middlePoint;
}
void UpdateFeatureGeometry(feature::FeatureBuilder::PointSeq const & seq,
feature::FeatureBuilder & fb)
{
fb.ResetGeometry();
for (auto const & p : seq)
fb.AddPoint(p);
}
feature::FeatureBuilder::PointSeq::iterator GetIterOnRoad(m2::PointD const & point,
feature::FeatureBuilder::PointSeq & road)
{
return std::find_if(road.begin(), road.end(), [&point](m2::PointD const & pointOnRoad) {
return base::AlmostEqualAbs(pointOnRoad, point, kMwmPointAccuracy);
});
}
}
namespace generator
@ -73,44 +112,86 @@ void MiniRoundaboutTransformer::UpdateRoadType(FeatureParams::Types const & foun
}
}
bool MiniRoundaboutTransformer::AddRoundaboutToRoad(m2::PointD const & center,
std::vector<m2::PointD> & roundabout,
feature::FeatureBuilder::PointSeq & road)
feature::FeatureBuilder CreateFb(std::vector<m2::PointD> const & way, uint64_t id,
uint32_t roadType, bool isRoundabout = true)
{
auto itPointOnRoad =
std::find_if(road.begin(), road.end(), [&center](m2::PointD const & pointOnRoad) {
return base::AlmostEqualAbs(pointOnRoad, center, kMwmPointAccuracy);
});
feature::FeatureBuilder fb;
fb.SetOsmId(base::MakeOsmWay(id));
fb.SetLinear();
CHECK(itPointOnRoad != road.end(),
("Could not find mini_roundabout on the road ", mercator::ToLatLon(center)));
UpdateFeatureGeometry(way, fb);
auto itPointUpd = itPointOnRoad;
if (itPointOnRoad == road.begin())
{
++itPointOnRoad;
}
else if (itPointOnRoad + 1 == road.end())
{
--itPointOnRoad;
}
else // Roundabout is on the middle of the road so we need to insert 2 points
{
m2::PointD const leftPointOnRoad = TrimSegment(*(itPointOnRoad - 1), center, m_radiusMercator);
if (AlmostEqualAbs(leftPointOnRoad, *(itPointOnRoad - 1), kMwmPointAccuracy))
return false;
if (!isRoundabout)
return fb;
AddPointToCircle(roundabout, leftPointOnRoad);
itPointOnRoad = road.insert(itPointOnRoad, leftPointOnRoad);
itPointUpd = itPointOnRoad + 1;
itPointOnRoad += 2;
}
fb.AddPoint(way[0]);
m2::PointD const nextPointOnRoad = TrimSegment(*itPointOnRoad, center, m_radiusMercator);
if (AlmostEqualAbs(nextPointOnRoad, *itPointOnRoad, kMwmPointAccuracy))
static uint32_t const roundaboutType = classif().GetTypeByPath({"junction", "roundabout"});
fb.AddType(roundaboutType);
static uint32_t const defaultRoadType = classif().GetTypeByPath({"highway", "tertiary"});
fb.AddType(roadType == 0 ? defaultRoadType : roadType);
return fb;
}
feature::FeatureBuilder::PointSeq MiniRoundaboutTransformer::CreateSurrogateRoad(
RoundaboutUnit const & roundaboutOnRoad, std::vector<m2::PointD> & roundaboutCircle,
feature::FeatureBuilder::PointSeq & road,
feature::FeatureBuilder::PointSeq::iterator & itPointUpd)
{
feature::FeatureBuilder::PointSeq surrogateRoad(itPointUpd, road.end());
auto itPointOnSurrogateRoad = surrogateRoad.begin();
auto itPointSurrogateUpd = itPointOnSurrogateRoad;
++itPointOnSurrogateRoad;
m2::PointD const nextPointOnSurrogateRoad = GetPointAtDistFromTarget(
*itPointOnSurrogateRoad /* source */, roundaboutOnRoad.m_location /* target */,
m_radiusMercator /* dist */);
if (AlmostEqualAbs(nextPointOnSurrogateRoad, *itPointOnSurrogateRoad, kMwmPointAccuracy))
return {};
AddPointToCircle(roundaboutCircle, nextPointOnSurrogateRoad);
*itPointSurrogateUpd = nextPointOnSurrogateRoad;
road.erase(itPointUpd + 1, road.end());
return surrogateRoad;
}
bool MiniRoundaboutTransformer::AddRoundaboutToRoad(RoundaboutUnit const & roundaboutOnRoad,
std::vector<m2::PointD> & roundaboutCircle,
feature::FeatureBuilder::PointSeq & road,
std::vector<feature::FeatureBuilder> & newRoads)
{
auto const roundaboutCenter = roundaboutOnRoad.m_location;
auto itPointUpd = GetIterOnRoad(roundaboutCenter, road);
CHECK(itPointUpd != road.end(), ());
auto itPointNearRoundabout = itPointUpd;
bool const isMiddlePoint = MoveIterAwayFromRoundabout(itPointNearRoundabout, road);
m2::PointD const nextPointOnRoad =
GetPointAtDistFromTarget(*itPointNearRoundabout /* source */, roundaboutCenter /* target */,
m_radiusMercator /* dist */);
if (AlmostEqualAbs(nextPointOnRoad, *itPointNearRoundabout, kMwmPointAccuracy))
return false;
AddPointToCircle(roundabout, nextPointOnRoad);
if (isMiddlePoint)
{
auto const surrogateRoad =
CreateSurrogateRoad(roundaboutOnRoad, roundaboutCircle, road, itPointUpd);
if (surrogateRoad.size() < 2)
return false;
auto fbSurrogateRoad =
CreateFb(surrogateRoad, roundaboutOnRoad.m_roadId, 0, false /* isRoundabout */);
for (auto const & t : roundaboutOnRoad.m_roadTypes)
fbSurrogateRoad.AddType(t);
newRoads.push_back(std::move(fbSurrogateRoad));
}
AddPointToCircle(roundaboutCircle, nextPointOnRoad);
*itPointUpd = nextPointOnRoad;
return true;
}
@ -128,40 +209,16 @@ std::unordered_map<base::GeoObjectId, size_t> GetFeaturesHashMap(
return fbsIdToIndex;
}
void UpdateRoadGeometry(feature::FeatureBuilder::PointSeq const & road,
feature::FeatureBuilder & fb)
{
fb.ResetGeometry();
for (auto const & p : road)
fb.AddPoint(p);
}
feature::FeatureBuilder CreateRoundaboutFb(std::vector<m2::PointD> const & way, uint64_t wayId,
uint32_t roadType)
{
feature::FeatureBuilder fbRoundabout;
fbRoundabout.SetLinear();
for (auto const & point : way)
fbRoundabout.AddPoint(point);
fbRoundabout.AddPoint(way[0]);
fbRoundabout.SetOsmId(base::MakeOsmWay(wayId));
static uint32_t const roundaboutType = classif().GetTypeByPath({"junction", "roundabout"});
fbRoundabout.AddType(roundaboutType);
static uint32_t const defaultRoadType = classif().GetTypeByPath({"highway", "tertiary"});
fbRoundabout.AddType(roadType == 0 ? defaultRoadType : roadType);
return fbRoundabout;
}
void MiniRoundaboutTransformer::ProcessRoundabouts(
feature::CountriesFilesIndexAffiliation const & affiliation,
std::vector<feature::FeatureBuilder> & fbs)
{
std::vector<feature::FeatureBuilder> fbsRoundabouts;
fbsRoundabouts.reserve(m_roundabouts.size());
// Some mini-roundabouts are mapped in the middle of the road. These roads should be split
// in two parts on the opposite sides of the roundabout. New roads are saved in |fbsRoads|.
std::vector<feature::FeatureBuilder> fbsRoads;
fbsRoads.reserve(m_roundabouts.size());
std::unordered_map<base::GeoObjectId, size_t> fbsIdToIndex = GetFeaturesHashMap(fbs);
@ -173,10 +230,10 @@ void MiniRoundaboutTransformer::ProcessRoundabouts(
bool allRoadsInOneMwm = true;
bool foundRoad = false;
for (auto const & wayId : rb.m_ways)
{
base::GeoObjectId geoWayId = base::MakeOsmWay(wayId);
// Way affiliated to the current mini_roundabout.
auto pairIdIndex = fbsIdToIndex.find(geoWayId);
if (pairIdIndex == fbsIdToIndex.end())
@ -189,38 +246,68 @@ void MiniRoundaboutTransformer::ProcessRoundabouts(
allRoadsInOneMwm = false;
break;
}
auto itRoad = fbs.begin() + i;
auto road = itRoad->GetOuterGeometry();
auto road = fbs[i].GetOuterGeometry();
if (GetIterOnRoad(center, road) == road.end())
{
bool foundSurrogateRoad = false;
for (itRoad = fbsRoads.begin(); itRoad != fbsRoads.end(); ++itRoad)
{
if (itRoad->GetMostGenericOsmId() != geoWayId)
continue;
if (!AddRoundaboutToRoad(center, circlePlain, road))
road = itRoad->GetOuterGeometry();
if (GetIterOnRoad(center, road) == road.end())
continue;
foundSurrogateRoad = true;
break;
}
if (!foundSurrogateRoad)
{
LOG(LERROR, ("Not found road for mini_roundabout", mercator::FromLatLon(rb.m_coord)));
continue;
}
}
RoundaboutUnit roundaboutOnRoad = {wayId, center, itRoad->GetTypes()};
if (!AddRoundaboutToRoad(roundaboutOnRoad, circlePlain, road, fbsRoads))
continue;
UpdateRoadGeometry(road, fbs[i]);
UpdateRoadType(fbs[i].GetTypes(), roadType);
UpdateFeatureGeometry(road, *itRoad);
UpdateRoadType(itRoad->GetTypes(), roadType);
foundRoad = true;
}
if (!allRoadsInOneMwm || !foundRoad)
continue;
fbsRoundabouts.push_back(CreateRoundaboutFb(circlePlain, rb.m_id, roadType));
fbsRoundabouts.push_back(CreateFb(circlePlain, rb.m_id, roadType));
}
LOG(LINFO, ("Transformed", fbsRoundabouts.size(), "mini_roundabouts to roundabouts.", "Added",
fbsRoads.size(), "surrogate roads."));
// Adding new roundabouts to the features.
fbs.insert(fbs.end(), std::make_move_iterator(fbsRoundabouts.begin()),
std::make_move_iterator(fbsRoundabouts.end()));
LOG(LINFO, ("Transformed", fbsRoundabouts.size(), "mini_roundabouts to roundabouts"));
fbs.insert(fbs.end(), std::make_move_iterator(fbsRoads.begin()),
std::make_move_iterator(fbsRoads.end()));
}
double DistanceOnPlain(m2::PointD const & a, m2::PointD const & b) { return a.Length(b); }
m2::PointD TrimSegment(m2::PointD const & segPoint, m2::PointD const & target, double r)
m2::PointD GetPointAtDistFromTarget(m2::PointD const & source, m2::PointD const & target,
double dist)
{
double const len = DistanceOnPlain(segPoint, target);
if (len < r)
return segPoint;
double const len = DistanceOnPlain(source, target);
if (len < dist)
return source;
double const k = (len - r) / r;
return (segPoint + target * k) / (1.0 + k);
double const k = (len - dist) / dist;
return (source + target * k) / (1.0 + k);
}
std::vector<m2::PointD> PointToPolygon(m2::PointD const & center, double radiusMercator,

View file

@ -18,6 +18,13 @@
namespace generator
{
struct RoundaboutUnit
{
uint64_t m_roadId = 0;
m2::PointD m_location;
FeatureParams::Types m_roadTypes;
};
class MiniRoundaboutTransformer
{
public:
@ -36,9 +43,18 @@ private:
/// \brief Sets |road_type| to |found_type| if it is a more important road type.
void UpdateRoadType(FeatureParams::Types const & foundTypes, uint32_t & roadType);
/// \brief Creates new point and add it to |roundabout| circle and to the |road|.
bool AddRoundaboutToRoad(m2::PointD const & center, std::vector<m2::PointD> & roundabout,
feature::FeatureBuilder::PointSeq & road);
/// \brief Splits |road| in two parts: part before the |roundabout| and after.
/// Returns second road to |newRoads| - the artificial one.
feature::FeatureBuilder::PointSeq CreateSurrogateRoad(
RoundaboutUnit const & roundaboutOnRoad, std::vector<m2::PointD> & roundaboutCircle,
feature::FeatureBuilder::PointSeq & road,
feature::FeatureBuilder::PointSeq::iterator & itPointUpd);
/// \brief Creates new point and adds it to |roundabout| circle and to the |road|.
bool AddRoundaboutToRoad(RoundaboutUnit const & roundaboutOnRoad,
std::vector<m2::PointD> & roundaboutCircle,
feature::FeatureBuilder::PointSeq & road,
std::vector<feature::FeatureBuilder> & newRoads);
std::vector<MiniRoundaboutInfo> m_roundabouts;
double const m_radiusMercator = 0.0;
@ -47,9 +63,10 @@ private:
/// \brief Calculates Euclidean distance between 2 points on plane.
double DistanceOnPlain(m2::PointD const & a, m2::PointD const & b);
/// \returns The point that is located on the segment (|segPoint|, |target|) and lies in |r| or less
/// from |target|.
m2::PointD TrimSegment(m2::PointD const & segPoint, m2::PointD const & target, double r);
/// \returns The point that is located on the segment (|source|, |target|) and lies in |dist|
/// or less from |target|.
m2::PointD GetPointAtDistFromTarget(m2::PointD const & source, m2::PointD const & target,
double dist);
/// \brief Creates a regular polygon with |verticesCount| inscribed in a circle with |center| and
/// |radiusMercator|. The polygon is rotated by an angle |initAngleDeg| CCW.