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

Integration with contextily to plot xyz basemaps #2115

Closed
weiji14 opened this issue Sep 12, 2022 · 5 comments · Fixed by #2394
Closed

Integration with contextily to plot xyz basemaps #2115

weiji14 opened this issue Sep 12, 2022 · 5 comments · Fixed by #2394
Assignees
Labels
feature request New feature wanted
Milestone

Comments

@weiji14
Copy link
Member

weiji14 commented Sep 12, 2022

Description of the desired feature

To allow for a greater variety of basemaps from e.g. OpenStreetMap, Stamen, MapBox, etc, it would be nice to integrate with contextily (part of the geopandas organization). This is motivated by https://forum.generic-mapping-tools.org/t/pygmt-vs-plotly/3250/10.

image

On the implementation, here are 2 ideas on how to integrate contextily basemaps with pygmt.

  1. Have an additional option under fig.basemap which allows selection of the basemap (e.g. OpenStreetMap, Stamen, Mapbox, etc) (Add Figure.tilemap to plot XYZ tile maps #2394, to be called fig.tilemap)
  2. Create a new load_basemap function under pygmt/datasets that would load the image (np.ndarray) and georeference it (to an xarray.DataArray), and then the user plots it using fig.grdimage. (done at Add function to load raster tile maps using contextily #2125, called load_tile_map)
  3. Maybe there's another more Pythonic way? Comment down below!

Either way, these would require:

  1. Wrapping around the contextily.bounds2img function at https://contextily.readthedocs.io/en/latest/reference.html#contextily.bounds2img. This returns a numpy.ndarray and a geographic extent.
  2. The numpy.ndarray needs to be georeferenced into an xarray.DataArray. I have some recent code at https://zen3geo.readthedocs.io/en/v0.4.0/object-detection-boxes.html#georeference-image-using-rioxarray to do that
  3. The xarray.DataArray will need to be plotted on the PyGMT map

Are you willing to help implement and maintain this feature? Yes, but discuss about the implementation first

@weiji14 weiji14 added help wanted Helping hands are appreciated feature request New feature wanted labels Sep 12, 2022
@seisman
Copy link
Member

seisman commented Sep 19, 2022

On the implementation, here are 2 ideas on how to integrate contextily basemaps with pygmt.

  1. Have an additional option under fig.basemap which allows selection of the basemap (e.g. OpenStreetMap, Stamen, Mapbox, etc)
  2. Create a new load_basemap function under pygmt/datasets that would load the image (np.ndarray) and georeference it (to an xarray.DataArray), and then the user plots it using fig.grdimage.
  3. Maybe there's another more Pythonic way? Comment down below!

Can we do both 1 and 2? I.e., having the load_basemap (maybe load_map_tiles?) function in pygmt/datasets and having a new parameter in Figure.basemap to call this function.

@weiji14
Copy link
Member Author

weiji14 commented Sep 19, 2022

On the implementation, here are 2 ideas on how to integrate contextily basemaps with pygmt.

  1. Have an additional option under fig.basemap which allows selection of the basemap (e.g. OpenStreetMap, Stamen, Mapbox, etc)
  2. Create a new load_basemap function under pygmt/datasets that would load the image (np.ndarray) and georeference it (to an xarray.DataArray), and then the user plots it using fig.grdimage.
  3. Maybe there's another more Pythonic way? Comment down below!

Can we do both 1 and 2? I.e., having the load_basemap (maybe load_map_tiles?) function in pygmt/datasets and having a new parameter in Figure.basemap to call this function.

That sounds like a neat idea. Start with (2) load_map_tiles first, and then do (1) new option in fig.basemap() (which will be a lot more complicated.

Actually, we'll also need to let fig.grdimage work with RGB (3 bands) instead of just 1 band. It works if we save the RGB image to a GeoTIFF first and tell fig.grdimage to plot the .tif file, but would be nicer to plot a 3 band xr.DataArray directly, xref #1555. Or we can use a tempfile 😉

@weiji14
Copy link
Member Author

weiji14 commented Sep 19, 2022

Started a Pull Request at #2125 for load_map_tiles load_tile_map (renamed based on #2125 (comment)).

To jump ahead a little bit, this is the code I'm currently using to plot the 3-band xarray.DataArray. The raster can be obtained from running the snippet in #2125 (comment).

import pygmt
import rioxarray

raster.rio.to_raster(raster_path="basemap.tif")

fig = pygmt.Figure()
fig.grdimage(grid="basemap.tif", frame=True)
fig.savefig("Singapore_stamen_map.png")
fig.show()

produces this Stamen Terrain map over Singapore

Singapore_stamen_map

@weiji14
Copy link
Member Author

weiji14 commented Mar 2, 2023

  1. Have an additional option under fig.basemap which allows selection of the basemap (e.g. OpenStreetMap, Stamen, Mapbox, etc)

After a bit of thinking, and as mentioned in in #2125 (comment), I'm considering creating a new fig.tilemap method instead of complicating fig.basemap further. This fig.tilemap method would essentially look like:

def tilemap(self, *, **kwargs):
    with GMTTempFile(suffix=".tif") as tmpfile:
        raster = pygmt.datasets.load_tile_map(...)
        raster.rio.to_raster(raster_path=tmpfile.name)
        fig.grdimage(grid=tmpfile.name, **kwargs)

Any thoughts on this implementation? Note that projections will also need to be handled, something mentioned in #2125 (comment). Also, the .rio.to_raster requires adding rioxarray as another optional dependency.

The tempfile is a bit of a hack to be honest, but is necessary until we can use grdimage to plot 3-band xarray.DataArrays directly, something to be done in #1555.

@weiji14 weiji14 self-assigned this Mar 2, 2023
@seisman
Copy link
Member

seisman commented Mar 2, 2023

After a bit of thinking, and as mentioned in in #2125 (comment), I'm considering creating a new fig.tilemap method instead of complicating fig.basemap further.

Yes, I aslo prefer fig.tilemap!

The tempfile is a bit of a hack to be honest, but is necessary until we can use grdimage to plot 3-band xarray.DataArrays directly, something to be done in #1555.

I agree.

@weiji14 weiji14 added this to the 0.9.0 milestone Mar 4, 2023
@seisman seisman removed the help wanted Helping hands are appreciated label Mar 22, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature request New feature wanted
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants