Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor fork handling in guidance #3264

Merged
merged 4 commits into from
Jan 7, 2017
Merged

Conversation

chaupow
Copy link
Member

@chaupow chaupow commented Nov 8, 2016

Issue

This PR works towards #3099

Tasklist

  • refactor TurnHandler::findFork() code
  • refactor TurnHandler::isObviousOfTwo()
  • revise definition of forks and TurnHandler::findFork()
  • refactor IntersectionHandler::assignFork()
  • add regression / cucumber cases (see docs/testing.md)
  • review
  • adjust for comments

Tasklist unrelated to this PR (but arose during this work)

  • rename struct Intersection in route_step.hpp
  • revise BasicTurnType
  • move isObviousByRoadClass in turn_handler.hpp to a dedicated .compatible() in road_class (road class refactor)
  • refactor struct Fork to use a begin and end iterator instead of right and left

Code Review Todolist

@chaupow chaupow force-pushed the refactor/guidance_fork_handling branch from 5287c76 to 58537a1 Compare November 8, 2016 15:46
@chaupow chaupow changed the title Refactor/guidance fork handling Refactor fork handling in guidance Nov 8, 2016
// `ConnectedRoads` is a relative view of an intersection by an incoming edge.
// `ConnectedRoads` are streets at an intersection ordered from from sharp right counter-clockwise to
// sharp left where `connected_roads[0]` is _always_ a u-turn
struct ConnectedRoads final : public std::vector<ConnectedRoad>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the change in the API here? This will break all users throughout osrm-backend.

Also semantically it it an intersection represented as ordered roads.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reverted

// _at_ `node_based_graph.GetTarget(via_edge)`
// _from_ `NodeID`
// _over_ `via_edge`
// _from where_ there are other `connected_roads`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is _at_ and _from where_ all about?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated comment to make it less confusing

// Checks whether a turn from `via_edge` onto `road` is a turn
// - on a ramp
// - a continue
// - or a normal turn
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved to unrelated todo

{
break;
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can express this and above more clearly using a adjacent-ish stdlib algorithm

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done. thanks!


const bool valid_indices = 0 < right && right < left;
BOOST_ASSERT(0 < right);
BOOST_ASSERT(right < left);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change from a check to an assert: does this always hold?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes. (hopefully.) yes.


// there cannot be a fork of more than three streets
std::size_t size_of_fork = left - right + 1;
const bool not_more_than_three_old = (left - right) <= 2;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dead code

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks!

@daniel-j-h
Copy link
Member

daniel-j-h commented Nov 8, 2016

Looking at your refactored findFork:

  • What is the return type? A std::pair<std::size_t, std::size_t> expresses no semantic meaning. Looks like those are indices into the Intersection array. What if findFork fails, what's the return type then? What about the following: either return an iterator pair, or create a local struct Fork { .. }; and change to optional<Fork> findFork(..);.
  • Does findFork has to work with all settings or can you put a pre-condition in to only handle specific scenarios, e.g. the Intersection array has to have a size of at least 3 (1 uturn + 2 for fork).
  • What about refactoring this into its own findClosestToStraight (feel free to change all namings mentioned here) function and then using e.g. a min stdlib function.
  • Looks like we only handle this branch. What about inverting the condition and returning early if it's not met. Otherwise we need to carry around an extra level of indentation. Related: please clang-format-3.8 your code.
  • The leftmost / rightmost code here is duplicated. Should be able to 1/ put it into its own function and 2/ write once, run on fwd iterators and then again on reverse iterators. You can probably use adjacent find from the stdlib for this.
  • Why?
  • Make member function: 1 2 3.
  • Use the GeoJSON printer and check what you classify as fork e.g. on Berlin.
  • Then check this heuristic.

Copy link

@MoKob MoKob left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The actual change is completely hidden in the unrelated changes.

These should, at the least, be separated from each other and I feel we should really discuss this ConnectedRoads change, as I don't really agree with it.

@@ -27,7 +27,7 @@ struct IntersectionPrinter
// renders the used coordinate locations for all entries/as well as the resulting
// intersection-classification
util::json::Array operator()(const NodeID intersection_node,
const extractor::guidance::Intersection &intersection,
const extractor::guidance::ConnectedRoads &intersection,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we split unrelated changes like these into a dedicated PR?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will do.


std::vector<TurnOperation>
transformIntersectionIntoTurns(const Intersection &intersection) const;
transformIntersectionIntoTurns(const ConnectedRoads &intersection) const;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you rename it, than intersection in the name here does not make any sense.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reverted

@@ -64,8 +65,8 @@ class TurnAnalysis
const SliproadHandler sliproad_handler;

// Utility function, setting basic turn types. Prepares for normal turn handling.
Intersection
setTurnTypes(const NodeID from, const EdgeID via_edge, Intersection intersection) const;
ConnectedRoads
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should add OSRM_ATTR_WARN_UNUSED here.

Intersection MergeSegregatedRoads(const NodeID intersection_node,
Intersection intersection) const;
ConnectedRoads MergeSegregatedRoads(const NodeID intersection_node,
ConnectedRoads intersection) const;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if it's important to have it named ConnectedRoads to you, then we should also change the name intersection into connected_roads to not loose sync between type and name. Right now (Intersection intersection) connects the variable to its type

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reverted

//
// |
// |
// (connected_roads[i])
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comments out of sync with name below (connected_roads vs intersection)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

corrected

@@ -25,7 +25,7 @@ namespace guidance
{

std::pair<util::guidance::EntryClass, util::guidance::BearingClass>
classifyIntersection(Intersection intersection);
classifyIntersection(ConnectedRoads intersection);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, if we name it roads, the connection between intersection and connected roads is not clear.

Sure, a vector<ConnectedRoad> is a list of ConnectedRoads, but so is std::set<ConnectedRoad> or std::list<ConnectedRoad>. I don't really like removing the additional information that is inherently given by having a specifically sorted list of Roads. Especially with the added functionality.

It's a dedicated class, not simply a plain collection.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reverted

@chaupow
Copy link
Member Author

chaupow commented Nov 9, 2016

Regarding Intersection vs ConnectedRoads:

The main reason for the change is that there is another struct Intersection in route_step.hpp and I thought that might be confusing.

As the discussed Intersectionis basically a vector of <ConnectedRoad>, @TheMarex and me thought that ConnectedRoads makes sense and is also inline with the naming conventions with some other vectors and connected_roads[0] or connected_roads.size() is pretty intuitive to me.

However, if you think we should rename it to something more similar to Intersection I'm open to it and I’ll undo the Intersection changes.

@MoKob
Copy link

MoKob commented Nov 9, 2016

Capturing from chat:

in short: yes, we should to stuff about intersection vs intersection. But I feel renaming intersection in engine should be the way to go, since it's only a output part

[12:21]
by adding more functionality to intersection in preprocessing (e.g. findClosestTurn), we are moving away from a pure collection

[12:22]
ConnectedRoads is a really bad choice for me, since it would be valid for a set, a vector, a map, a list, basically any collection type

[12:24]
intersection.size() vs connected_roads.size() does not improve readability to me. The size of an intersection conveys some meaning, size of connected roads is (especially since s vs no s is easily lost) way less meaningful to me

[12:25]
you could easily confuse .size() with the size of a road (e.g. width)

also you have the sorting criterium which implies meaning. With the addition of .valid(), it moves away form a pure collection to an ordered list of items. So there is more meaning to it rather than just a random collection of entries

In total: We are renaming the route-step intersection. Name up for discussion.

@TheMarex
Copy link
Member

in short: yes, we should to stuff about intersection vs intersection. But I feel renaming intersection in engine should be the way to go, since it's only a output part

Being on the output part is actually the prime reason I would argue against remaining it. We always refer to this object in the docs as Intersection object and having parity with docs and internal class names makes it quite easy to orient yourself in the API code. Changing anything that is touched by people besides internal developers always leads to confusion. In context of #3228 this might get even more confusing.

You could maybe use IntersectionModel or InternalIntersection or something similar to resolve the name conflict.

@chaupow chaupow force-pushed the refactor/guidance_fork_handling branch from 58537a1 to 6660a73 Compare November 14, 2016 15:48
@chaupow
Copy link
Member Author

chaupow commented Nov 15, 2016

I did some work on this PR, things happened so far:

  • commented code that was necessary to understand how fork handling works
  • refactor findFork()
  • slightly refactor other functions and files that are strongly related to findFork()

what these change not do:

  • changes of the behaviour of findFork(); refactor was only done regarding code readibility
  • refactor of code outside findFork()

With these thigns in mind, would you look at the code again before I continue work here? Thanks a lot, lot, lot! @daniel-j-h @MoKob

Copy link
Member

@daniel-j-h daniel-j-h left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left some comments on the PR - also please see #3264 (comment)

// `angle` is given counter-clockwise:
// 0 = uturn, 90 = right, 180 = straight, 270 = left
// `bearing` is the direction in clockwise angle from true north after taking the turn:
// 0 = heading north, 90 = east, 180 = south, 270 = west<<<<<<< HEAD
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<<<< HEAD

// if no entry was found that forbids entry, the intersection entries in the range are all
// valid.
return true;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you make this iterator based, the previous ~15 lines are

return all_of(first, last, mem_fn(&T::allowed));

@@ -45,6 +45,8 @@ std::size_t IntersectionHandler::countValid(const Intersection &intersection) co
});
}

// Checks whether a turn from `via_edge` onto `road` is a turn, on a ramp, a continue, or a normal
// turn
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, does this comment say the only TurnTypes the function handles is Turn, OnRamp, Continue?

@@ -45,9 +45,26 @@ class TurnHandler : public IntersectionHandler
Intersection intersection) const override final;

private:
struct Fork;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This only forward declares Fork making it an incomplete type in this header

@@ -68,8 +85,7 @@ class TurnHandler : public IntersectionHandler
handleDistinctConflict(const EdgeID via_edge, ConnectedRoad &left, ConnectedRoad &right) const;

// Classification
std::pair<std::size_t, std::size_t> findFork(const EdgeID via_edge,
const Intersection &intersection) const;
Fork findFork(const EdgeID via_edge, const Intersection &intersection) const;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but here you're trying to use an incomplete type by-value as return type.

}
}
return true;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you express these two nested raw for loops via stdlib algorithms?
The outer one looks like a for each, the inner one like a any-of.

// Checks whether a three-way-intersection coming from `via_edge` is a fork
// with `intersection` as described as in #IntersectionExplanation@intersection_handler.hpp
TurnHandler::Fork TurnHandler::findFork(const EdgeID via_edge,
const Intersection &intersection) const
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if intersection is empty (the UTurn road only)? Is size = 3 a pre-condition?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's not a pre-condition but if intersection.size()<3 it will now return boost::none

// they cannot be fork candidates
if ((left == right) || ((left - right + 1) > 3))
{
return Fork::getEmptyFork();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As said, the default constructor already does that.

const std::size_t right;
const std::size_t left;
const std::size_t size;
const bool valid;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

data + boolean valid is more or less what optional<T> is for - just make it a plain old struct T { U data; }; and then use optional<T> and check with if (myoptional) use(*myoptional);

// check if all entries in the fork range allow entry
const bool only_valid_entries = intersection.hasValidEntries(right, left);

// TODO check whether 2*NARROW_TURN is too large
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where did the 2 * narrow turn angle go?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no idea. It was never there when I touched it. I looked into the history in master a bit and couldnt find it either. @MoKob you remember something about this or can I remove the comment?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like the comment is out of date. Feel free to remove.

@chaupow chaupow requested a review from MoKob December 9, 2016 16:44
bool hasValidEntries(Intersection::iterator first, Intersection::iterator last) const;

Base::iterator findClosestTurn(double angle);
Base::const_iterator findClosestTurn(double angle) const;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of the functions here should no longer be attached to Intersection or IntersectionView - but instead check EnableIntersectionOps<T>. If you have the time for it, read up about the CRTP pattern.

We need this since even though this creates a inheritance hierarchy

struct base {};
struct derived : base {};

the following types stand in no relationship to each other

using BaseVec = vector<base>;
using DerivedVec = vector<derived>;

using the CRTP pattern we inject the functionality into both collection types.

Intersection::iterator right;
Intersection::iterator left;
const std::size_t size;
Fork(Intersection::iterator right, Intersection::iterator left);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this work on Intersection or IntersectionView - or both?

Check the data these types hold and decide.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fork handling is only called on Intersections (in the dedicated handlers), just as the sliproad handler. Right now this is correct. We could think about how we could update this behaviour, but right no I'd say for the scope of this PR having Intersections here is correct.


return all_of(first, last + 1, [&](const ConnectedRoad &road) { return road.entry_allowed; });
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comes from the messed up rebase.

// The Turn Operation indicates what is exposed to the outside of the turn analysis.
//
// `angle` is given counter-clockwise:
// 0 = uturn, 90 = right, 180 = straight, 270 = left
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the intersection shape itself does not know about where you come from. It does not offer an angle property.

// A representation of intermediate intersections
// A representation of intermediate intersections:
// An intersection is an ordered list of connected roads ordered from from sharp right
// counter-clockwise to sharp left where `intersection[0]` is always a u-turn
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the intersection in the output is a different object than the intersection in the preprocessing. Here the concept of Connected Roads is unknown. We offer available turn bearings (in all directions, not angles) and allowed entry flags.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ugh. changed this. don't know how this happened

Intersection::iterator right;
Intersection::iterator left;
const std::size_t size;
Fork(Intersection::iterator right, Intersection::iterator left);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fork handling is only called on Intersections (in the dedicated handlers), just as the sliproad handler. Right now this is correct. We could think about how we could update this behaviour, but right no I'd say for the scope of this PR having Intersections here is correct.

bool isObviousOfTwo(const EdgeID via_edge,
const ConnectedRoad &road,
const ConnectedRoad &other) const;

bool hasObvious(const EdgeID &via_edge, const Fork) const;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this the replacement of the above function? Also Fork could probably benefit from a reference, due to its size.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No this is not a replacement, but yes reference sounds good.

struct Fork
{
Intersection::iterator right;
Intersection::iterator left;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since we are using iterators, wouldn't it be better to follow the stdlib paradigm for begin, end here? I'd prefer a begin / end approach at least, but given that Fork is wrapped into an optional, I'd be alright with left/right. However, left and right requires implicit knowledge about the order. So we could maybe use ccw_right and ccw_left to indicate the order here as well? This way you know which direction to iterate over.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moving to new issue

@@ -68,8 +85,7 @@ class TurnHandler : public IntersectionHandler
handleDistinctConflict(const EdgeID via_edge, ConnectedRoad &left, ConnectedRoad &right) const;

// Classification
std::pair<std::size_t, std::size_t> findFork(const EdgeID via_edge,
const Intersection &intersection) const;
boost::optional<Fork> findFork(const EdgeID via_edge, Intersection &intersection) const;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you maybe add documentation on what the difference between findFork and findLeftmost... versions are?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point. I also renamed findLeftAndRightmostForkCandidates to findForkCandidatesByGeometry which is hopefully a better name because we're just looking at angles and the geometric relations between the roads here.

using namespace osrm::extractor::guidance;
// given two adjacent roads and `road1` being a candidate for a fork,
// return false, if next road `road2` is also a fork candidate or
// return true, if `road2` is not a suitable fork candidate and thus, `road1` the outermost fork
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

adjacent == any specific order? I'd assume they are still in ccw order? Would be nice to specify this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

// if all roads are part of a fork, set `candidate` to the last road
else
{
return outermost - 1;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we would use the begin/end paradigm I mentioned above, we wouldn't need these distinctions here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moving to new issue

}
}

struct StraightestTurnAtIntersection
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

adding a dedicated struct here seems wrong to me. the straightmost turn at an instersection can be found with getClosestTurn(STRAIGHT_ANGLE) for any intersection and is an iterator into the intersection. It's deviation can easily be computed whenever necessary by angularDeviation(itr.angle,STRAIGHT_ANGLE).

The dedicated struct is overkill in my personal opinion. Is it used often enough to justify the definition?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After checking: given that it is used twice, I feel that introducing a dedicated struct here only adds confusion. Having the indirection using an id instead of the iterator findClosestTurn returns does only add confusion in my view.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch. thanks!

// returns a tuple {i, angle_dev} of the road at the intersection that is closest to going straight:
// `intersection[i]` is the actual road
// `angle_dev` is the angle between going straight and taking the road `intersection[i]`
StraightestTurnAtIntersection findClosestToStraight(const Intersection &intersection)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can be removed completely and replaced with findClosestTurn(STRAIGHT_ANGLE) in intersection

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch. thanks!

@chaupow chaupow force-pushed the refactor/guidance_fork_handling branch 2 times, most recently from aaf2397 to 32dcbb5 Compare December 15, 2016 14:57
@chaupow chaupow mentioned this pull request Dec 15, 2016
@chaupow chaupow force-pushed the refactor/guidance_fork_handling branch from 32dcbb5 to 138616d Compare December 15, 2016 15:06
Copy link

@MoKob MoKob left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one tiny item left. otherwise 👍

{
BOOST_ASSERT(end < self()->end());

return all_of(begin, end, [](const ConnectedRoad &road) { return road.entry_allowed; });
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should use type auto, since we can also call it on an intersectionView which does not have a connected road.

@chaupow chaupow force-pushed the refactor/guidance_fork_handling branch from 138616d to 88a568d Compare December 15, 2016 15:49
Copy link
Member

@daniel-j-h daniel-j-h left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A single Travis build is failing with

Disallowing sources: ubuntu-toolchain-r-test

Might be a blip. Please restart and check again.

{
auto comp = makeCompareAngularDeviation(angle);
return std::min_element(self()->begin(), self()->end(), comp);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-const overload for the function above?

There is a trick involving a const-cast so you don't have to duplicate code.
Search around for how to implement const / non-const overloads.

Not sure if we need it here ..

bool hasAllValidEntries(const iteratorT begin, const iteratorT end) const
{
return all_of(begin, end, [](const ConnectedRoad &road) { return road.entry_allowed; });
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

std::all_of and not all_of - the reason the unqualified all_of works is argument-dependant lookup (ADL, great to know, read up a bit about it): if the iterators are in the std namespace all_of is searched in the std namespace, too.

Check std::all_ofs docs: it requires an InputIt (make yourself comfortable with the different iterator concepts), which basically means you can run over it once. Because you are using std::all_of your iterators now also have to be (at least) InputIterators. You could assert that at compile time via

static_assert(std::is_base_of<std::input_iterator_tag, typename std::iterator_traits<Iter>::iterator_category>::value, "")

but what you usually do is to make the template's type just InputIt, as in

template <typename InputIt> void fn(InputIt first, InputIt last);

(C++ Concepts will change this in the future and enforce the static assert automatically)

Finally, don't hard code ConnectedRoad as parameter here: the reason we're having the EnableOps CRTP struct is because you don't know if it's a ConnectedRoad or a ViewData. Make it generic.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks 🙇

template <typename Iterator>
Iterator findOutermostForkCandidate(const Iterator start, const Iterator end)
{
const auto outermost = std::adjacent_find(start, end, isOutermostForkCandidate);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ForwardIt requirement, as explained above


// a wrapper to handle road indices of forks at intersections
TurnHandler::Fork::Fork(Intersection::iterator right, Intersection::iterator left)
: right(right), left(left), size((int)(left - right) + 1)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why int? And why signed at all?

if (turn_is_perfectly_straight && in_data.name_id != EMPTY_NAMEID &&
road_data.name_id != EMPTY_NAMEID && same_name)
via_data.name_id, road_data.name_id, name_table, street_name_suffix_table);
if (turn_is_perfectly_straight && via_data.name_id != EMPTY_NAMEID && same_name)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait, the empty name id check has to happen before the requiresNameAnnounced above

for (auto road = fork.right; road < fork.left; ++road)
{
if (isObviousOfTwo(via_edge, *road, *(road + 1)) ||
isObviousOfTwo(via_edge, *(road + 1), *road))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is +1 here? Is this always safe to do?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is, and it is ugly and I tagged it in another issue to fix this #3457 😞

const Intersection &intersection) const
// finds a fork candidate by just looking at the geometry and angle of an intersection
boost::optional<TurnHandler::Fork>
TurnHandler::findForkCandidatesByGeometry(Intersection &intersection) const
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you ever modify the intersection? If yes, why? If no => const!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, maybe you can help me here cause I was having trouble with this part anyways: If I make intersection const, all iterators I can get are const-iterators. The function is supposed to return a Fork struct with iterators to the beginning and end of the fork end those iterators cannot be const-iterators cause later on I want to modify their instructions. The only solution I got working was to make the intersection non-const, thus resulting in that code.

you have any idea how to fix that?

return std::all_of(fork.right, fork.left + 1, [&](ConnectedRoad &base) {
const auto base_class = node_based_graph.GetEdgeData(base.eid).road_classification;
// check that there is no turn obvious == check that all turns are non-onvious
return std::all_of(fork.right, fork.left + 1, [&](ConnectedRoad &compare) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the second loop have to exclude the road from the first road? Otherwise you're doing a self-comparison?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if it's a self comparison, the inner loop will evaluate return [...] || compare.eid == base.eid;

// makes sure that the fork is isolated from other neighbouring streets on the left and
// right side
const auto next =
(fork->left + 1) == intersection.end() ? intersection.begin() : (fork->left + 1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 safe to do?

Copy link
Member Author

@chaupow chaupow Jan 1, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's safe but will also be fixed by #3457

right > 0 &&
angularDeviation(intersection[right].angle, intersection[right - 1].angle) >=
GROUP_ANGLE;
angularDeviation(fork->right->angle, (fork->right - 1)->angle) >= GROUP_ANGLE;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same

@chaupow chaupow force-pushed the refactor/guidance_fork_handling branch from 88a568d to d462ec2 Compare January 5, 2017 16:15
@chaupow chaupow force-pushed the refactor/guidance_fork_handling branch from d462ec2 to e69c905 Compare January 5, 2017 17:06
@chaupow chaupow force-pushed the refactor/guidance_fork_handling branch from e69c905 to 696bfdb Compare January 6, 2017 12:47
@chaupow chaupow merged commit f313cb9 into master Jan 7, 2017
@chaupow chaupow deleted the refactor/guidance_fork_handling branch January 9, 2017 16:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants