-
Notifications
You must be signed in to change notification settings - Fork 219
Feature/get turn info add retrieve policy #531
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
Feature/get turn info add retrieve policy #531
Conversation
which will later also retrieve pk/qk and next point
independent w.r.t. p/q and, when retrieving the *k points, it is way more convenient if splitted.
Also pass retrieve policy for ring
which is now redundant.
replacing it with retrieve policy (only where necessary)
(preparation step for removal of *k methods)
Because it is not called from specific get_turn_info context And uses all points in non typical order (e.g. qi, pj, pk i/o pi, pj, pk)
by direction code (made for that purpose)
done in non typical order
for spike calculation
Therefore the robust_retriever should store its result
Interesting. Regarding future plans, what for will be additional points The PR looks good to me. I'm only wondering why you've made certain naming decisions:
|
Right, it is ready again, all tests pass (now in debug mode - to verify assertions) |
// (that is: range2.at(0) to range1.at(0), denoted as x) | ||
int const side_pj_q2 = sides.apply(range2.at(1), range2.at(2), range1.at(1)); | ||
int const side_pj_x = sides.apply(range2.at(0), range1.at(0), range1.at(1)); | ||
int const side_qk_x = sides.apply(range2.at(0), range1.at(0), range2.at(2)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is probably me who wrote it. I don't remember exactly why turns are checked this way, I'd have to analyse the whole file and maybe debug. However I may point you in a right direction right now. This is probably for turns where linear geometries are involved, correct? With linear geometries the turns have slightly different meaning than in areal/areal case. For instance the equality doesn't mean that the whole segment was the same but that the intersection point was equal to one of the points of the geometry. Turns hold additional data indicating whether prior to the intersection point the segments were collinear or are crossing at the intersection point, etc. AFAIR this had something to do with the fact that linear geometries are not ordered like areal and that from various geometrically equal sets of segments I needed the same set of turns, no matter the direction of the segments. E.g. for areal/areal one relative direction could result in i/i operations but with reverse segments the operations would be x/x. And this is true for all other operations. But linear turns would be the same.
So AFAIR parts of this code are there to transform ordered areal turns into unordered linear turns. Sometimes additional info had to be calculated. I didn't check this for sure but this might have been such case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Furthermore, this is the code calculating turns for endpoints, i.e. the first or the last point of a linestring. There is no such thing in areal geometries. However in this case we have to know how the other geometry behaves at the endpoint, when one geometry ends or begins. And since the geometry either begins or ends at intersection point maybe this is why "non-segment" points from other geometries are considered.
std::pair<operation_type, operation_type> | ||
operations = operations_of_equal(side_calc); | ||
operations = operations_of_equal(side_pi_q2, side_pi_x, side_qk_x); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So unless we know what's happening exactly we should probably be careful about changing this code. But if all of the tests pass with this change it should be ok I think.
// If pk is collinear with qj-qk, they continue collinearly. | ||
// This can be on either side of p1 (== q1), or collinear | ||
// The second condition checks if they do not continue | ||
// oppositely | ||
if ( side_pk_q2 == 0 && side_pk_p == side_qk_p ) | ||
if ( side_1 == 0 && side_2 == side_3 ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Out of curiosity, why did you decide to change the names of variables to less meaningful? Above I see a comment referencing specific points so this condition looks unrelated now.
Looks good, I'm only worried about the replacement of |
IMO it is not less clear. The code was using named sides, but substituting them with completely other sides, even from different sources. I was really surprised by that and consider it as wrong. In this new version you can a bit better see what is happening there, which is still not 100% clear, but IMO more clear than just call encapsulated magic in the wrong way. I had to do it in really small steps to avoid breaking anything, it took me quite some time but all is equivalent as it was before, and all tests pass of course. |
@awulkiew About your question, related to the new method of using things as range_q.at(0):
I was quite used to i,j and k - but I noticed that @vissarion kind of objected to this way of working, and I think I agree with him. So having range-like methods as at(0) (I also considered just subscripting) make it more clear which point is meant, then just alphabetical order i,j,k,l (l, so L, would have been new, but would have been really confusing with 1). So to answer your question directly, yes, I think that at(0) etc is more readable to me than .i() |
About your question
Because the names were wrong. It used names like "pk" - while, as you mentioned, there is indeed no "pk" in end-points. It is another point, and which one depends on the situation. It is called 5 or 6 times in different ways, I could not come up with a common name, also because I don't understand all the tiny details of this code. |
Finally, about your comment:
I did not realize they were unordered, and I don't think they are. Is that really the case? But for sure, I understand that on the end-points there is different behaviour, and different decisions should be made. What I don't understand (at least not understood) was that side calculator was used, a points were inserted for the k-points, and all looked like there were general calculations happening, but behind the screens there were sides calculated totally different from the normal cases (and called like the normal cases). That was my incentive to change it, besides, of course, the need to pass a range instead of points (which I could not do here) and to access the ranges differently, and pass them differently, as was coming from review remarks... Also, this code was called from both linear/linear and linear/areal. Are there bulk tests for these cases, inside or outside (at MySQL side) our codebase? I'm curious if it just always works... Of course I'm willing to give more meaningful names if I know them, and to understand the code better, and refactor some more things if necessary - but for now I thought this was already quite a step and, as a step itself, consistent. |
We have the tests here: So debugging them, stepping inside and seeing in which scenarios which sides are calculated here and which points are taken into account should give us the answer. I'll check it out too. Btw, if you're using Visual Studio you could use the extension I wrote to easily see the geometries, I don't know if you know about it (https://marketplace.visualstudio.com/items?itemName=AdamWulkiewicz.GraphicalDebugging). |
Ok, so this is the code handling a case when the intersection point is at the first or last point of the linestring (geometry P) and the second point of a segment of the other linestring or ring (geometry Q). So as usual we want to check which segment has to be followed when intersection or union is calculated. Because there is no 3rd point of the first geometry (and I probably wanted to use existing utility for this) we have to generate some point and it's sufficient to take the first point of Q. E.g. in this case: and these are the new 2 blue segments for P (let's call it P') with the added first point from Q and 2 segments of Q (green): So now we can check the sides "the regular way" using the existing tools, i.e. With that said in L/L case (2 linear geometries) 2 sides checks would be enough: Pi or Pj WRT Q1 and Q2 (so
|
I edited the comment to make it more clear what I'm talking about ;) I don't suggest that this code should be refactored/cleaned as a part of this PR. I just wanted to help understanding it. Anyway, I agree with merging it together with possible change of names of variables. So this would probably be the key:
|
One more clarification, by "unordered" I meant that linear geometries are not CW nor CCW and that there is no interior or exterior on left or right side of linear geometry. That regardless of the relative direction of this geometry the methods and operations are the same. They allow to simply follow the linear geometry and generate intersection or union or stop at |
Thanks a lot for your clarifications! I'll continue later. |
Thanks again for your comments and clear pictures. Sure, I know your debugging tools, certainly. But I'm always using QtCreator now - and not Visual Studio. I remember that you did have a tool for that too, but (at that time) it did slow things down, sometimes giving issues, and besides that I usually have the need to add more and more debug information, so therefore I rely on SVG creation and do not visualize geometries themselves in the IDE. It now becomes clear to me why a point of Q was substituted for P. But I still think it was mainly a trick to be able to use the side calculator. That need is gone now, because (by applying ranges) it became clear that it was messing names up (I don't mean implementation, but just the names, as you confirmed). For which you provide the key, as far as I can see, and I could use those names (in the calling functions, so i/o x. But note that they are called as, for example the last one: Or maybe I did not understand you completely... |
I'm playing with the code in develop and replacing the second parameter of Anyway, I also tried to remove
and at line 482:
of course in |
Right, I think I tried the same thing (but with errors...) |
Overlay tests also pass for me. In
I also changed the names of points passed to |
Do I understand correctly that you do not get the errors with the code as it is now, correct? Only after making changes? And you suggest I play with it after the merging? If that's the case I agree. |
Sure, that is what I suggested. So I did not get the errors as it is now. |
Follow-up: #540 |
All right - I was tired, probably not a typo but tried one of them, not the pair together. So obviously that didn't work... Thanks, will go to that new PR |
This PR (actually my first) adds a retrieve policy, and passes it to get_turn_info.
This policy retrieves a point known as "k" (pk / qk), the first non-duplicate point coming consecutively after the points "i" and "j" (two points from two segments p and q, part of the intersection of two segments, so these points are pi,pj and qi,qj).
In the past points pk/qk were just passed. But now we need another point (pl/ql) and it is not convenient to add that. Therefore the policy is introduced.
An additional advantage is that point "k" is now retrieved lazily, on demand. Often that point is not necessary. It is actually only necessary if there are collinearies. And often that is not the case. So in all those cases, performance should increase just a little bit.
This PR adds the policy and retrieves point "k" using the method .get(). In another PR I will add an argument to get, to be able to retrieve also the subsequente point, as described above. But for now this PR is large enough.
It is also related to side calculation, the place where that point is used, to check whether it is located left/right or collinear to a segment.
To my surprise, also non-typical usage of side calculation were introduced in the implementations for linear intersections, and the typical order is changed there. Therefore I copied the originals there - they cannot use the policy because they don't use the "k" point. Actually there should be looked at, because it is looking suspicious, but we can do that afterwards.
I'm curious to your findings.