Skip to content

Conversation

Copy link

Copilot AI commented Oct 10, 2025

Tested:
First is with xlim and ylim, 2nd is xlim, 3rd is ylim, 4th is none specified

image

Description

This PR implements automatic conversion of xlim and ylim parameters to Web Mercator (EPSG:3857) when tiles=True is used without geo=True, making the behavior consistent with how data coordinates are automatically converted.

Problem

Previously, when using tiles=True with geographic data (lat/lon coordinates), hvPlot would automatically convert the data coordinates to Web Mercator, but xlim and ylim parameters were not converted. This meant users had to manually calculate Web Mercator values for axis limits, which was inconvenient and error-prone:

# Before: Users had to manually convert xlim/ylim
df = pd.DataFrame({'lon': [-120, -100, -80], 'lat': [30, 35, 40]})
# xlim/ylim in lat/lon would not match the converted data coordinates
df.hvplot.points('lon', 'lat', tiles=True, xlim=(-130, -70), ylim=(25, 45))  # Wrong scale!

Solution

Now xlim and ylim are automatically converted to Web Mercator when appropriate, just like the data coordinates:

# After: Just use lat/lon coordinates - hvPlot handles the conversion automatically
df = pd.DataFrame({'lon': [-120, -100, -80], 'lat': [30, 35, 40]})
df.hvplot.points('lon', 'lat', tiles=True, xlim=(-130, -70), ylim=(25, 45))  # Works perfectly!

Implementation Details

The conversion logic:

  1. Checks data bounds first: Before converting xlim/ylim, the code checks if the data coordinates are within typical lat/lon bounds (-180 <= lon <= 360, -90 <= lat <= 90). This prevents false positives where small numerical values might be within those ranges but represent non-geographic data.

  2. Converts only when appropriate: Conversion happens only when:

    • tiles=True is set
    • geo=False (not using explicit geo mode)
    • projection is not False (automatic projection not disabled)
    • Data coordinates are within lat/lon bounds
    • xlim/ylim values are within lat/lon bounds
  3. Uses proper projection math:

    • Easting (x) depends only on longitude: easting = longitude × origin_shift / 180
    • Northing (y) depends only on latitude: northing = log(tan((90 + latitude) × π / 360)) × origin_shift / π

Examples

Geographic data (automatic conversion):

# San Francisco area
sf_data = pd.DataFrame({
    'lon': [-122.45, -122.42, -122.40],
    'lat': [37.75, 37.77, 37.79]
})
plot = sf_data.hvplot.points('lon', 'lat', tiles=True, 
                             xlim=(-122.5, -122.3), 
                             ylim=(37.7, 37.85))
# xlim and ylim are automatically converted to Web Mercator

Non-geographic data (no conversion):

# Scientific measurements
data = pd.DataFrame({'x': [1000, 2000, 3000], 'y': [500, 600, 700]})
plot = data.hvplot.points('x', 'y', tiles=True, 
                          xlim=(900, 3100), 
                          ylim=(400, 800))
# xlim and ylim remain unchanged (data is outside lat/lon bounds)

Testing

Added comprehensive tests covering:

  • ✅ Conversion with geographic data and tiles
  • ✅ No conversion without tiles
  • ✅ No conversion when data is outside lat/lon bounds
  • ✅ Edge cases: negative/positive longitudes, equatorial/polar latitudes, boundary values
  • ✅ Partial specification (only xlim or only ylim)
  • projection=False disables conversion

All tests pass and the implementation is fully backwards compatible with existing behavior.

Closes

Fixes the issue described in the problem statement where xlim/ylim were not automatically converted to Web Mercator when using tiles with lat/lon data.

Original prompt

This section details on the original issue you should resolve

<issue_title>Automatically convert xlim/ylim to Web Mercator when possible</issue_title>
<issue_description>We recently added automatic projection of lat/lon coordinates to Web Mercator when tiles=True so users can easily plot geodatasets in these classic coordinates (e.g. GPS). We should also automatically convert xlim and ylim.</issue_description>

Comments on the Issue (you are @copilot in this section)

Fixes #1617


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI changed the title [WIP] Add automatic conversion of xlim and ylim to Web Mercator Automatically convert xlim/ylim to Web Mercator when tiles=True Oct 10, 2025
Copilot AI requested a review from ahuang11 October 10, 2025 16:37
Copilot finished work on behalf of ahuang11 October 10, 2025 16:37
@ahuang11 ahuang11 requested a review from maximlt October 10, 2025 18:14
@ahuang11 ahuang11 marked this pull request as ready for review October 10, 2025 18:14
@maximlt
Copy link
Member

maximlt commented Oct 14, 2025

I forgot a comment. Could you please document this feature? I think a simple mention in the xlim/ylim section would be fine, and a real example in the tiles section. I don't think the docstring needs to be updated.

@ahuang11
Copy link
Collaborator

Okay added docs

hvplot/util.py Outdated
) from Exception(*errors)


def is_within_latlon_bounds(data, x, y):
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 please make these functions in util.py private by prepending their name with an underscore? That's the pattern I have adopted for new functions.

Copy link
Collaborator

@ahuang11 ahuang11 Oct 22, 2025

Choose a reason for hiding this comment

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

Hmm, I'm on the fence about making them private. Maybe is_within_latlon_bounds but others I can see people calling it, e.g. from holoviews.util.transform import lon_lat_to_easting_northing. I typically reserve underscores for private methods, less utils, or else it should be a private method?

hvplot/util.py Outdated
max_x = np.max(data[x])
min_y = np.min(data[y])
max_y = np.max(data[y])
except (KeyError, ValueError, TypeError):
Copy link
Member

Choose a reason for hiding this comment

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

What cases do these exceptions cover?

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think the goal was for it to gracefully fail so this auto-conversion doesn't block the user for anything unexpected--now I simply just use Exception and warn on exception.

"id": "8c428430",
"metadata": {},
"source": [
"If `xlim` and `ylim` are in `lat`/`lon` coordinates, they will be automatically transformed to Web Mercator coordinates."
Copy link
Member

Choose a reason for hiding this comment

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

Not sure we should document things this way in the gallery. Which is why I suggested doing it in the tiles example section, as this behavior is only enabled when tiles is enabled. For this example, I simply suggest updating the original snippet with xlim=..., ylim=....

Copy link
Collaborator

Choose a reason for hiding this comment

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

Thanks for clarifying, that makes sense. I wasn’t really sure what “a real example in the tiles section” referred to since I haven't worked in hvplot docs for a while, so just a heads up a quick link to the hvPlot docs section would be helpful for future comments / contributors! 🙇🏻

Copy link
Member

Choose a reason for hiding this comment

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

Good point! 👍

@maximlt
Copy link
Member

maximlt commented Oct 15, 2025

@ahuang11 sorry I thought I submitted my review yesterday, but no!

@ahuang11 ahuang11 requested review from Azaya89 and maximlt October 22, 2025 23:29
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.

Automatically convert xlim/ylim to Web Mercator when possible

4 participants