-
Notifications
You must be signed in to change notification settings - Fork 371
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
fix treatment of spine paths with explicit path-codes #2362
Conversation
In case the GeoSpine path represents a multi-path, don't attemt to close it since this would convert the multi-path into a single closed path resulting in unwanted connector-lines between the individual polygons.
5e3889d
to
7bca0a3
Compare
@greglucas
To keep your changes for single closed paths, I've implemented the fix so that it only triggers if the path actually represents a multi-path (e.g. if it contains more than 1 |
Tests fail with |
Is there a way to do something like: One other thought is that perhaps the previous fix was actually a non-ideal one where This should get a test somehow, can you modify your example figure output to count moveto/close before/after this change? |
Closing is hereby performed by appending the first vertex with a CLOSEPOLY code rather than treating the last vertex as CLOSEPOLY to avoid gaps at the corner.
Hey, @greglucas, thanks for the quick response!
I had a quick look... it seems that using ...
elif closed and len(vertices):
codes = np.empty(len(vertices), dtype=self.code_type)
codes[0] = self.MOVETO
codes[1:-1] = self.LINETO
codes[-1] = self.CLOSEPOLY I agree that this might cause problems if the last vertex and the first should actually be connected.
I've now updated the PR to perform a more complete check and make sure that the path (and all potential sub-paths) are properly closed with the start-vertex.
For the currently implemented method I don't think it's necessary to count since If you agree with my proposed solution, I can write a test for |
Test-failure is again just a download-issue. |
@raphaelquast , some tests would be good regardless of the solution I think! You've definitely found some issues, so codifying those with tests would be great. I think this is good. I do have some concern that this is in a potential hot-loop when zooming/panning around, but the fix looks like the "proper" solution to account for the various cases. |
@greglucas I've now added some basic unittests to check proper handling of single- and multi-path boundaries (based on image-comparison) |
Test failures are again unrelated but I don't seem to have the rights to trigger a manual re-run. |
Hey, just checking in to see what's missing to get this merged?
|
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.
Sorry, been really busy on other projects the past month and lost track of this, thanks for the ping!
CLA
Yes, you should sign it again. There is a new CLA on all SciTools contributions now. It is a much simpler process and should take place immediately instead of needing a human to review it.
Code
Did you look at this as an option for closing the polygons? https://matplotlib.org/stable/api/path_api.html#matplotlib.path.Path.to_polygons
Specifically, this comment catches my eye saying any unclosed polys will be closed:
If closed_only is True (default), only closed polygons, with the last point being the same as the first point, will be returned. Any unclosed polylines in the path will be explicitly closed.
Testing
Are you able to put all of those tests into a single image with subplots to reduce the number of image comparisons/binary data you're adding?
Is there a way to test this without image comparisons. Our image comparisons are always fragile, so I'm wondering if you can say something like "before this change the boundary path had only 1 CLOSEPOLY, now it should have 5".
@raphaelquast friendly ping in case you had a chance to look into |
Hey @greglucas I had a quick look so far but nothing concrete. In general Concerning the tests: Its of course possible to check if the updated polygon-representation contains enough |
Hey @greglucas @staticmethod
def _ensure_path_closed(path):
"""Method to ensure that a path contains only closed sub-paths."""
polygons = path.to_polygons()
codes, vertices = [], []
for p in polygons:
vertices.extend([p[0], *p])
codes.extend([
mpath.Path.MOVETO,
*[mpath.Path.LINETO]*(len(p) - 1),
mpath.Path.CLOSEPOLY])
return mpath.Path(vertices, codes) However I realized that even though the docs claim that no path simplification is performed, the resulting output suggests otherwise... (I did not look in the corresponding c-code) Here's a gif showing the above The docs of
but the default ( I'm not sure if this is a bug or if this is the intended behavior but in both cases it does not look like it's doing what we want it to do. |
It looks like the path is allowed to simplify still as it enters that method. If you put in |
Hey, indeed that did the job! My final question now is if we should "reset" the initial_should_simplify = path.should_simplify
path.should_simplify = False
polygons = path.to_polygons(closed_only=False)
path.should_simplify = initial_should_simplify |
👍 The context manager sounds like a good idea to me. |
`path.should_simplify` is temporarily set to `False` to avoid unwanted path-simplification applied in `to_polygons`.
@greglucas I now pushed a new implementation using |
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.
Thanks @raphaelquast, I like the use of to_polygons()
👍 Just some minor nits at this point.
Co-authored-by: Greg Lucas <[email protected]>
@greglucas
However, this unfortunately causes a lot of other image tests to fail. While checking the resulting changes, I believe that this has in fact a mostly positive impact on the exported images! To clarify I've selected some of the most important changes I've found so far: [click to show] some image comparison results(LEFT: baseline, RIGHT: new result)
Its quite unfortunate that this has such broad effects, but it seems like it's worth investigating what's going on here... |
We just keep going deeper here :( It looks like the path is cleaned to remove duplicates with the Original path input: Path(array([[-180., -90.],
[-180., -90.],
[-180., 90.],
[ 180., 90.],
[ 180., -90.],
[-180., -90.]]), array([ 1, 2, 2, 2, 2, 79], dtype=uint8)) After your new method: Path(array([[-180., -90.],
[-180., 90.],
[ 180., 90.],
[ 180., -90.],
[-180., -90.]]), array([ 1, 2, 2, 2, 79], dtype=uint8)) Some of the original images do look better to me because they do a better job at clipping and not "leaking" the colors into the boundary. So maybe the best course of action is to just bump the tolerances ever so slightly up 0.02-0.03 for most of them it looks like? |
Yes I know... I really didn't expect that this final change would result in such a wide range of effects... I did some more tests to get to the bottom of this: If we run
On GitHub tests seem to succeed |
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.
Looks like the final failing test is because this needs to be marked as a network test.
Otherwise I think this is good to go and will be a nice addition.
Co-authored-by: Greg Lucas <[email protected]>
Awesome! One final thought... cartopy/lib/cartopy/mpl/geoaxes.py Line 231 in 2d4cef2
|
That also seems reasonable to add this capability there. Do you want to do that here or in a follow-up PR? (I'm not sure where we'd want the |
Since at this stage it's just about agreeing on the right place for I definitely agree that However, If you think it's better to put it in |
I don't have a major preference either way. It just seemed like it is similar to the other path handling in that location, but we are only using it in this file so that is fine too. Either one seems reasonable. |
OK, all done! In the end I decided to go with your suggestion... it seems like a better place for the method in case it is used somewhere else as well in the future. |
I guess the one failing test needs a re-run.. |
There is a branch protection rule on the Contributor License Agreement, can you sign that as well? |
Oh, I totally forgot that I need to re-sign this... done! |
Awesome, thank you @raphaelquast for the persistence and nice addition! |
Sure! Thanks for your valuable inputs! |
Hey, quick question :-) |
Rationale
The current implementation of the
adjust_location
method ofGeoSpines
assumes that the spine-path is a single connected path and potential path-codes are ignored.This causes problems if multi-paths are used as bounaries to clip the map (e.g. with
ax.set_boundary()
.... for example:Implications
With the proposed changes, path-codes are preserved and multi-path clipping works as expected