Skip to content

missing region border in plots #110

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

Merged
merged 2 commits into from
Apr 6, 2021
Merged

Conversation

wsavran
Copy link
Collaborator

@wsavran wsavran commented Apr 1, 2021

- added kwargs to plot_basemap to apply 'fast' projection and central latitude
- added 'fast' as an option to the plot_args of plot_spatial_dataset and plot_catalog
- updated docstrings
@codecov-io
Copy link

codecov-io commented Apr 1, 2021

Codecov Report

Merging #110 (ff0e4da) into master (fa789f0) will not change coverage.
The diff coverage is n/a.

Impacted file tree graph

@@           Coverage Diff           @@
##           master     #110   +/-   ##
=======================================
  Coverage   53.25%   53.25%           
=======================================
  Files          19       19           
  Lines        3091     3091           
  Branches      445      445           
=======================================
  Hits         1646     1646           
  Misses       1352     1352           
  Partials       93       93           

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update fa789f0...ff0e4da. Read the comment docs.

@wsavran wsavran changed the title fixes #109: missing region border missing region border in plots Apr 1, 2021

Returns:
:class:`matplotlib.pyplot.ax` object

"""
if ax is None:
fig = pyplot.figure()
ax = fig.add_subplot(111, projection=ccrs.PlateCarree(central_longitude=0.0))
ax = fig.add_subplot(111, projection=projection)
if apprx:
Copy link
Contributor

Choose a reason for hiding this comment

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

maybe add & projection == ccrs.PlateCarree()? (not needed if you are sure that plot_basemap() will never be called by a user)

Copy link
Collaborator

Choose a reason for hiding this comment

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

the default arg is projection=ccrs.PlateCarree. I believe its good as Bill left it, to accept different projections. Also the default platecarree looks good, for quick plotting without worrying about projections.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

i think plot_basemap will probably be called by other users especially if they are trying to plot catalogs on top of forecasts or other data sets. is there a downside to letting users choose projections in the plot_basemap function?

Copy link
Contributor

Choose a reason for hiding this comment

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

What I meant is: the 'fast'/apprx "projection" works by rescaling the axes. This rescaling is incompatible with any other "true" (non-PlateCaree) projection, which should be caught by adding & projection == ccrs.PlateCarree() to the if statement.

Copy link
Collaborator Author

@wsavran wsavran Apr 3, 2021

Choose a reason for hiding this comment

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

thats a good point. in the other functions i handled that case by forcing projection=ccrs.PlateCarree(). for example, in plot_catalog(). not sure why i didn't do that in basemap. also, if the user chooses the fast option, we should force PlateCarree().

now that im thinking about it, there are some other parameters that might lose continuity between these cartopy enhanced calls, eg, extent

Copy link
Collaborator

@pabloitu pabloitu left a comment

Choose a reason for hiding this comment

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

In func plot_basemap, line

if set_global:
an else is missing:

else:
        ax.set_extent(extents=extent, crs=ccrs.PlateCarree())

so plot_basemap can define the extent, and do not waste downloading time of unneeded basemaps


Returns:
:class:`matplotlib.pyplot.ax` object

"""
if ax is None:
fig = pyplot.figure()
ax = fig.add_subplot(111, projection=ccrs.PlateCarree(central_longitude=0.0))
ax = fig.add_subplot(111, projection=projection)
if apprx:
Copy link
Collaborator

Choose a reason for hiding this comment

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

the default arg is projection=ccrs.PlateCarree. I believe its good as Bill left it, to accept different projections. Also the default platecarree looks good, for quick plotting without worrying about projections.

@pabloitu pabloitu force-pushed the bill_fix109_plots branch from 2780d2c to a604a98 Compare April 2, 2021 20:47
@pabloitu
Copy link
Collaborator

pabloitu commented Apr 2, 2021

I did a failed commit, reversed it, and the test with jma_catalog_reader is not working anymore (don't understand why really). Sorry the mess.

@wsavran
Copy link
Collaborator Author

wsavran commented Apr 3, 2021

no worries @pabloitu. i think it is something to do with obspy and nothing that you did. it seems to be failing on an imported docstring. see below.

tests/test_JmaCsvCatalog.py:2: in <module>
    import csep
csep/__init__.py:6: in <module>
    from csep.core import forecasts
csep/core/forecasts.py:13: in <module>
    from csep.core.catalogs import AbstractBaseCatalog
csep/core/catalogs.py:18: in <module>
    from csep.utils.comcat import SummaryEvent
csep/utils/comcat.py:19: in <module>
    from obspy.core.event import read_events
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/obspy/__init__.py:60: in <module>
    _add_format_plugin_table(read, "waveform", "read", numspaces=4)
/opt/hostedtoolcache/Python/3.7.10/x64/lib/python3.7/site-packages/obspy/core/util/base.py:562: in _add_format_plugin_table
    if '%s' in func.__doc__:
E   TypeError: argument of type 'NoneType' is not iterable

if set_global:
ax.set_global()
else:
ax.set_extent(extents=extent, crs=ccrs.PlateCarree()) # Defined extent always in lat/lon
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

should the crs here be projection and not ccrs.PlateCarree()

Copy link
Contributor

Choose a reason for hiding this comment

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

According to the docs: "Set the extent (x0, x1, y0, y1) of the map in the given coordinate system. [scitools.org.uk/cartopy/docs/...]. By default (crs=None), it would take over the projection of the axis (i.e. projection), but since the extent will be - in our case - (always?) specified in the same coordinate system as the original data (i.e. PlateCaree()), it is good as is.

See also stackoverflow.com.

if apprx:
# Re-aspect plot (only for plain matplotlib plots, or when using PlateCarree)
LATKM = 110.574
ax.set_aspect(1 / (LATKM / 111.320 * numpy.cos(numpy.deg2rad(central_latitude))))
Copy link
Contributor

Choose a reason for hiding this comment

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

I just noticed that it can be simplified:

ax.set_aspect(111.320 * numpy.cos(numpy.deg2rad(central_latitude)) / LATKM)

Btw: here a reference for why/how this works: stackoverflow.com/a/1253545. I use it essentially for every geographic operation because it allows to go from lat-lon to relative distances in km in a very concise way.
So all in all, you may revise this section to:

        # Set plot aspect according to local longitude-to-latitude length ratio
        # (only compatible with plain PlateCarree "projection")
        LATKM = 110.574  # length of a ° of latitude [km] [https://stackoverflow.com/a/1253545]
        ax.set_aspect(111.320 * numpy.cos(numpy.deg2rad(central_latitude)) / LATKM)

Copy link
Contributor

Choose a reason for hiding this comment

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

Damn. The simplification is wrong; it should be:

ax.set_aspect(111.320 / LATKM / numpy.cos(numpy.deg2rad(central_latitude)))

added missing else statement to handle set_global flag in plot_basemap
@wsavran
Copy link
Collaborator Author

wsavran commented Apr 5, 2021

heres an example for these different options.

import matplotlib.pyplot as plt
import cartopy.crs as ccrs
from csep.utils.plots import plot_basemap

Plot using plot_basemap

ax = plot_basemap(
    'ESRI_terrain', [-100, 30, 0, 80], 
    projection=ccrs.Mercator(), 
    grid=True, grid_labels=True
)

Set extent using cartopy.crs.PlateCarree()

This is the current behavior of pycsep.

ax = plot_basemap(
    'ESRI_terrain', [-100, 30, 0, 80],
     projection=ccrs.Mercator(),
     grid=True,
     grid_labels=True
)
ax.set_extent([-100, 30, 0, 80], crs=ccrs.PlateCarree())

Set extent using axes projection

ax = plot_basemap(
    'ESRI_terrain', [-100, 30, 0, 80], 
     projection=ccrs.Mercator(), 
     grid=True, 
     grid_labels=True
)
ax.set_extent([-100, 30, 0, 80], crs=None)

output_6_0

Set extent using same projection as basemap

ax = plot_basemap(
    'ESRI_terrain', [-100, 30, 0, 80], 
     projection=ccrs.Mercator(), 
     grid=True, 
     grid_labels=True
)
ax.set_extent([-100, 30, 0, 80], crs=ccrs.Mercator())

output_8_0

@wsavran
Copy link
Collaborator Author

wsavran commented Apr 6, 2021

im merging this pr

@wsavran wsavran merged commit a860d08 into SCECcode:master Apr 6, 2021
@wsavran wsavran deleted the bill_fix109_plots branch April 6, 2021 15:08
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.

Missing region border when specifying a projection; In addition: a (faster) alternative to using projections
4 participants