matplotlib / basemap

Plot on map projections (with coastlines and political boundaries) using matplotlib
MIT License
780 stars 392 forks source link

Bug in hammer projection with lon_0>0 #463

Closed William-gregory closed 1 year ago

William-gregory commented 5 years ago

When using Basemap 'hammer' projection with lon_0 set to anything greater than 0 degrees, the map is plotted as the mirror image of reality. Setting lon_0 to negative values (like -180) works fine, but I don't want to do this as then none of my data would fit this projection and I would have to re-grid everything.

I've subsequently uninstalled & reinstalled anaconda3 (now have python 3.7) and have also reinstalled Basemap (v.1.2.0), and am still getting the same problem.

Here is the code:

import matplotlib as mpl from mpl_toolkits.basemap import Basemap import matplotlib.pyplot as plt

m = Basemap(projection='hammer', resolution = 'l', lon_0=180)

fig = plt.figure(figsize=(12,12)) m.drawcoastlines(linewidth=0.3) m.fillcontinents(color='lightgrey',lake_color='white') m.drawmapboundary(fill_color='white') plt.show()

hammer_proj

ghzuo commented 5 years ago

I have the same problem in Basemap 'robin' projection. I had updated the basemap to 1.2.1 and matplotlib 3.1.

And I think there is another problem is relative to this problem. When lon_0 is not equal 0, all the background functions, like drawlsmask(), bluemarble(), ..., are useless.

pochedls commented 5 years ago

Same issue with eck4 using basemap 1.2.0, matplotlib 3.0.3, python 3.7.1.

@William-gregory / @ghzuo - were you able to use a different version to circumvent this issue?

WeatherGod commented 5 years ago

Most likely, the issue is related to pyproj4 or proj4 as most of the work for transforms are off-loaded to proj4.

On Thu, Aug 15, 2019 at 7:52 PM pochedls notifications@github.com wrote:

Same issue with eck4 using basemap 1.2.0, matplotlib 3.0.3, python 3.7.1.

@William-gregory https://github.com/William-gregory / @ghzuo https://github.com/ghzuo - were you able to use a different version to circumvent this issue?

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/matplotlib/basemap/issues/463?email_source=notifications&email_token=AACHF6FVJSIHRPGOI5LZ6W3QEXT2NA5CNFSM4HYWK6GKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD4NJOIQ#issuecomment-521836322, or mute the thread https://github.com/notifications/unsubscribe-auth/AACHF6D3UOT3UOKJROGOCX3QEXT2NANCNFSM4HYWK6GA .

gkvallis commented 4 years ago

I also find that, in order for the code to even run (in an anaconda environment), I need to preface it with:

import os import conda conda_file_dir = conda.file conda_dir = conda_file_dir.split('lib')[0] proj_lib = os.path.join(os.path.join(conda_dir, 'share'), 'proj') os.environ["PROJ_LIB"] = proj_lib

Previously, this preface was not needed. I do not know whether this is related to the bug the OP reported. The inverted images occur with many of the map projections, but not for 'cyl', which is just a lat-long projection.

sbonaime commented 4 years ago

Any idea to solve this strange problem ? With lon_0 not equal to 0, my map is completely empty !

from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
m = Basemap(projection='robin', lon_0=10, resolution='l')
m.etopo()
plt.show()
Capture d’écran 2020-11-08 à 08-11-20 00 08 27
pochedls commented 4 years ago

@sbonaime - I have largely transitioned to cartopy, but I recall using a negative value, e.g., lon_0=-180, gave me something that looked right.

sbonaime commented 4 years ago

@pochedls I should update my old script ! with lon_0=0, my map is ok but I need lon_0=15... Any workaround like installling an "old" lib ?

pochedls commented 4 years ago

@sbonaime - I assume it would be possible to address this with an older library (perhaps just creating a conda environment with an older version of basemap would work). lon_0=-345 might work?

sbonaime commented 4 years ago

@pochedls lon_0=-345 is not working... Maybe I should take some time to update to cartopy. Thanks

PhillCli commented 2 years ago

So, I've hit a similar issue, code I've used to reproduce, blue-marble with robin projection

from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt

m = Basemap(projection="robin", lon_0=0, resolution=None)
m.bluemarble()
plt.show()

Expected output: image

It works on all tested base map versions - 1.1.0 - 1.3.4 only if I downgrade matplotlib to 3.4.3, 3.5.X release doesn't work with any version of base map I've tried.

TL;DR try downgrading matplotlib, ~3.4.3 worked for me

molinav commented 2 years ago

@PhillCli Thanks for the feedback and the hint about the matplotlib version. Do you have the full traceback that you get when using matplotlib 3.5.x? Probably some deprecated matplotlib syntax was removed in 3.5.x and basemap is still using the old syntax. I will take a look to it when I get some free time.

PhillCli commented 2 years ago

Do you have the full traceback that you get when using matplotlib 3.5.x?

It's a silent failure, the plot is generated but is completely empty except for an outline. Just like in this post comment

There is a warning, but it's also present in the working case:

Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).

EDIT: Another piece of hint is that it's related to projections, if the projection is removed background map is plotted correctly.

molinav commented 2 years ago

@PhillCli Removing the projection argument is equivalent to setting projection="cyl".

From your code snippet, if you try to do the following with the Robin projection:

plt.imshow(m._bm_rgba_warped)

you can see that the transformed image is actually there and it is transformed correctly (note that the image will appear upside down in the figure, but this also occurs in matplotlib 3.4.x, so I assume it is the normal behaviour). When setting projection="cyl" (or not defining it), the underlying image is stored in a different hidden attribute:

plt.imshow(m._bm_rgba)

So the problem does not seem to be with the projection transformations but with the rendering of the image by matplotlib. Furthermore, if I just add one line to your code snippet then the image is shown (using matplotlib version 3.5.3):

from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt

m = Basemap(projection="robin", lon_0=0, resolution=None)
im1 = m.bluemarble()
im1.axes.add_image(im1)
plt.show()

bluemarble_robin

The problem that you described propagates to all the Basemap methods related to drawing image backgrounds, since all of them rely on Basemap.warpimage, which in the end is calling Basemap.imshow to show the image.

molinav commented 2 years ago

Going a bit more into the im1 object from my line added, just a couple of outputs from an interactive session:

In [24]: type(im1.axes)
Out[24]: matplotlib.axes._subplots.AxesSubplot

In [25]: im1.axes.get_images()[0]
Out[25]: <matplotlib.image.AxesImage at 0x204343eaaf0>

In [26]: im1.axes.get_images()[0] is im1
Out[26]: True

so im1.axes actually has a list of images containing only im1, which is our rebel axes image. At least this looks normal to me.

PhillCli commented 2 years ago

@molinav Thanks for the investigation, I'm not that familiar with the internals of Basemap.

Good to know the image is still properly transformed, and just a matter of explicitly adding it to Axes object.

That would make sense that something about rendering changed in 3.5.3 that breaks the default Basemap imshow, wonder if that was also the case for the @sbonaime issue.

It might be valuable to update the docs I think that's where the snippet I tried to fix originated from here.

YilongWang commented 1 year ago

I have the similar issue with "moll" projection, and find a very rough solution. See if the steps described in #577 can solve your problem.

When using Basemap 'hammer' projection with lon_0 set to anything greater than 0 degrees, the map is plotted as the mirror image of reality. Setting lon_0 to negative values (like -180) works fine, but I don't want to do this as then none of my data would fit this projection and I would have to re-grid everything.

I've subsequently uninstalled & reinstalled anaconda3 (now have python 3.7) and have also reinstalled Basemap (v.1.2.0), and am still getting the same problem.

Here is the code:

import matplotlib as mpl from mpl_toolkits.basemap import Basemap import matplotlib.pyplot as plt

m = Basemap(projection='hammer', resolution = 'l', lon_0=180)

fig = plt.figure(figsize=(12,12)) m.drawcoastlines(linewidth=0.3) m.fillcontinents(color='lightgrey',lake_color='white') m.drawmapboundary(fill_color='white') plt.show()

hammer_proj

YilongWang commented 1 year ago

I have the similar issue with "moll" projection, and find a very rough solution. See if the steps described in #577 can solve your problem.

Any idea to solve this strange problem ? With lon_0 not equal to 0, my map is completely empty !

from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
m = Basemap(projection='robin', lon_0=10, resolution='l')
m.etopo()
plt.show()
Capture d’écran 2020-11-08 à 08-11-20 00 08 27
YilongWang commented 1 year ago

I have the similar issue with "moll" projection, and find a very rough solution. See if the steps described in #577 can solve your problem. In basemap-1.3.7, Line 3206, you can add ",fill_color='none'" for limb2, the modified code looks like:

draw another filled patch, with no boundary.

            limb2 = self.drawmapboundary(linewidth=0, ax=ax,fill_color='none')
            self._mapboundarydrawn = limb2

If seems limb2 is not necessary any more in current version of basemap.

@molinav Thanks for the investigation, I'm not that familiar with the internals of Basemap.

Good to know the image is still properly transformed, and just a matter of explicitly adding it to Axes object.

That would make sense that something about rendering changed in 3.5.3 that breaks the default Basemap imshow, wonder if that was also the case for the @sbonaime issue.

It might be valuable to update the docs I think that's where the snippet I tried to fix originated from here.

molinav commented 1 year ago

Tracing back with Python 3.7, the failure related to the flipped coastlines occurs in the transition of the pyproj dependency from version 1.9.6 into 2.0.0, when pyproj migrated from PROJ 4.x into PROJ 6.0. Something changed between these two PROJ versions.

molinav commented 1 year ago

I close this issue as complete after applying the patch proposed by @YilongWang. If any problem persists, please free to reopen this issue or to create a new one.