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

Clip Voronoi regions in cvt_archive_heatmap #356

Merged
merged 3 commits into from
Sep 7, 2023
Merged

Conversation

btjanaka
Copy link
Member

@btjanaka btjanaka commented Sep 7, 2023

Description

The current cvt_archive_heatmap relies on faraway points to draw the polygons at the edges of the Voronoi diagram. Thus, if a user zooms out, they will see that the heatmap actually has polygons going beyond it, like below. In practice, we rarely notice this because the cvt_archive_heatmap sets the axis limits to be the bounds of the archive, so the outer regions are hidden.

cvt heatmap

However, there may be times when this behavior is undesirable, e.g., drawing two cvt archives next to each other. Thus, this PR makes it possible to clip the bounds of the Voronoi diagram, like so:

cvt heatmap

We accomplish this by taking each polygon and computing its intersection with the archive’s bounding box. Intersections are computed via the shapely library: https://shapely.readthedocs.io/ Furthermore, by using shapely, it is possible to clip to arbitrary shapes:

cvt heatmap

There are some tradeoffs to turning this feature on by default, namely:

  1. It may be more “intuitive” to have infinite regions at the edge of the archive, as that is how the archive works in practice — namely, points outside the archive bounds are inserted into cells at the edges.
  2. Computing the intersection of all the polygons with the bounding box (or bounding shape) can be somewhat time-consuming. However, after running the script in Speed up 2D cvt_archive_heatmap by order of magnitude #355 with clip=True, I found that the runtime for 10,000 cells was still ~2.3 sec (much slower than the 0.6 sec in that PR, but still very fast).

For these reasons, we will keep this feature turned off by default via the clip parameter to cvt_archive_heatmap .

Note that this PR introduces a dependency on shapely, and yet the feature we use shapely for will be turned off by default since clip=False. However, I believe this is acceptable because shapely is a well-supported library that is also lightweight (~2MB), so it will not be a burden to users.

TODO

  • Add shapely to deps
  • Add shapely to pinned reqs
  • Add shapely to intersphinx
  • Implement clipping
  • Add flag to turn clipping on and off
  • Add test for heatmap with and without clipping
  • Enable clipping to an arbitrary polygon
  • Add test for clipping to arbitrary polygon, with and without holes

Questions

Status

  • I have read the guidelines in
    CONTRIBUTING.md
  • I have formatted my code using yapf
  • I have tested my code by running pytest
  • I have linted my code with pylint
  • I have added a one-line description of my change to the changelog in
    HISTORY.md
  • This PR is ready to go

@btjanaka btjanaka merged commit 9a14d17 into master Sep 7, 2023
18 checks passed
@btjanaka btjanaka deleted the cvt-heatmap-clip branch September 7, 2023 05:57
@btjanaka
Copy link
Member Author

btjanaka commented Sep 18, 2023

I recently learned of a potential alternative to using shapely -- namely, using matplotlib's built-in clip_box and clip_path parameters. However, my understanding is that these parameters operate in pixel space rather than the x-y space of the plot (example: https://matplotlib.org/stable/gallery/images_contours_and_fields/image_clip_path.html), making it harder to implement this solution.

The following code seems to work for performing a clip with Matplotlib:

        coll = matplotlib.collections.PolyCollection(
            vertices,
            edgecolors=ec,
            facecolors=facecolors,
            linewidths=lw,
            rasterized=rasterized,
        )
        ax.add_collection(coll)
        coll.set_clip_box(
            matplotlib.transforms.TransformedBbox(
                matplotlib.transforms.Bbox([lower_bounds, upper_bounds]),
                ax.transData))

However, this seems to perform clipping at a pixel level rather than properly clipping the shapes -- notice how the edges in the plot below have no lines drawn at the edges:

cvt

For example, notice this corner where there are no black lines around the edges:

image

It may be possible to get around this by drawing rectangles etc. etc., but that seems rather hacky.

Thus, we will continue to use shapely.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant