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

[Feature request] Adding ticks to geographic plots #125

Closed
664787022 opened this issue Mar 16, 2025 · 19 comments
Closed

[Feature request] Adding ticks to geographic plots #125

664787022 opened this issue Mar 16, 2025 · 19 comments

Comments

@664787022
Copy link

Thanks, @cvanelteren , for your continued development of Proplot into Ultraplot

Adding ticks to geographic plots is a practical feature in geoscience studies.

Although this feature was requested in Proplot, it was not merged into the codebase, see Support adding tick marks to geographic plots

Hopefully, @cvanelteren can add this feature to Ultraplot !

@cvanelteren
Copy link
Contributor

I believe this is already possible:

Image

import ultraplot as uplt, numpy as np

fig, axs = uplt.subplots(proj="cyl")
axs.format(
    land=True,
    labels=True,
    lonlines=20,
    latlines=20,
    gridminor=False,
    lonlim=(-140, 60),
    latlim=(-10, 50),
)
uplt.show(block=1)

@cvanelteren
Copy link
Contributor

Or more succinctly:

Image

import ultraplot as uplt, numpy as np
from itertools import product

fig, axs = uplt.subplots(ncols=2, nrows=2, proj="cyl")

settings = dict(
    land=True,
    labels=True,
    lonlines=20,
    latlines=20,
    lonlim=(-140, 60),
    latlim=(-10, 50),
)
for idx, (gridminor, gridmajor) in enumerate(product([True, False], [True, False])):
    settings["gridminor"] = gridminor
    settings["grid"] = gridmajor
    axs[idx].format(**settings)
uplt.show(block=1)

@cvanelteren
Copy link
Contributor

But perhaps I am missing something (not a geoscientist ;-)).

@cvanelteren
Copy link
Contributor

Ah wait I read this wrong, you want the ticklens not the grid to change.

@664787022
Copy link
Author

664787022 commented Mar 16, 2025

Ah wait I read this wrong, you want the ticklens not the grid to change.

Yes, ticks in geographic plots are used more frequently than gridlines, at least in my field. They help keep the map clean and visually appealing by avoiding clutter from multiple gridlines.

In some cases, we need to overlap contours. If we use gridlines simultaneously, there will be toooo many lines in the map.

Image

@cvanelteren
Copy link
Contributor

Image working on it ;-)

@cvanelteren
Copy link
Contributor

Is the attached img in #126 what you had in mind?

@664787022
Copy link
Author

Is the attached img in #126 what you had in mind?

Yes! It looks great! but im not sure if it goes well when using polar axes. For example uplot.subplot(proj="aeqd")

@cvanelteren
Copy link
Contributor

Do you have an example of how that would look like for polar axes? The ticks won't be equally spaced I am assuming.

@cvanelteren
Copy link
Contributor

cvanelteren commented Mar 16, 2025

TypeError: Cannot label AzimuthalEquidistant gridlines. Only PlateCarree gridlines are currently supported.

I think the labelling is non-trivial. This error is produced by cartopy when I attempt to force labeling on non PlateCarree projections. Will do some more digging to see if this can be achieved.

@cvanelteren
Copy link
Contributor

I'm super confused how the tickers are handled by matplotlib. The issue seems that for the projection above that the range is so big that it attempts to parse it evenly which generates too many ticks for matplotlib too handle. I attempted to limit these by overriding the ticker, but most function internally to matplotlib revert this conversion by making its own ticker which prevent passing parameters that could limit the tick locations and hence the error.

I am not too familiar with the internals of cartopy and how they divide these ranges. Luke also ran into this issue judging from the link above.

Input is appreciated on the PR.

@664787022
Copy link
Author

TypeError: Cannot label AzimuthalEquidistant gridlines. Only PlateCarree gridlines are currently supported.

I think the labelling is non-trivial. This error is produced by cartopy when I attempt to force labeling on non PlateCarree projections. Will do some more digging to see if this can be achieved.

Cartopy does not seem to support adding ticks for non-rectangular projections. I'm not sure whether it's possible to add ticks in a polar projection. However, we can now use ax.format(lontick=True, lattick=True) with ccrs.PlateCarree(), which I believe meets the needs of most use cases :-)

@cvanelteren
Copy link
Contributor

Are there any flat projections missing from the PR? See the function _is_flat_projection. Currently I have these:

    flat_names = [
        "PlateCarree",
        "Mercator",
        "LambertCylindrical",
        "Miller",
        "Robinson",
        "Mollweide",
        "EqualEarth",
    ]
    flat_names_basemap = [
        "basemap",
        "merc",
        "cyl",
        "rectilinear",
        "rect",
        "tmerc",
        "mill",
        "unknown",  # not sure why this is created on basemap but assume it is flat
    ]

@664787022
Copy link
Author

Are there any flat projections missing from the PR? See the function _is_flat_projection. Currently I have these:

flat_names = [
    "PlateCarree",
    "Mercator",
    "LambertCylindrical",
    "Miller",
    "Robinson",
    "Mollweide",
    "EqualEarth",
]
flat_names_basemap = [
    "basemap",
    "merc",
    "cyl",
    "rectilinear",
    "rect",
    "tmerc",
    "mill",
    "unknown",  # not sure why this is created on basemap but assume it is flat
]

I am not sure what "flat projection" refers to, but similar to Robinson, there are Hammer, Aitoff. You can find more in this cartopy projection list

@664787022
Copy link
Author

664787022 commented Mar 17, 2025

    # For cartopy
    if hasattr(proj, "name"):
        name = proj.name.lower()
    # For basemap:
    else:
        name = proj.projection.lower()
    return any(
        name == flat_name.lower() for flat_name in flat_names + flat_names_basemap
    )

I am a bit confused about this paragraph. proj.name.lower() always seems to return "unknown" in my case.

name = proj.__class__.__name__ 

works

@beckermr
Copy link
Collaborator

I think "flat" here is not a geometric statement. Instead what is meant is "a projection that you plot on a rectangle"?

If so we should find the proper term and use it in the code.

@cvanelteren
Copy link
Contributor

I used rectilinear now and modified the checks that should be name independent.

@cvanelteren
Copy link
Contributor

@664787022 with flat I referred to rectilinear, does my previous remark make sense now?

@cvanelteren
Copy link
Contributor

Feature is added in main. Will draft a release tag.

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

No branches or pull requests

3 participants