diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 index bcdd6463d..eda8c29b3 --- a/.gitignore +++ b/.gitignore @@ -7,11 +7,12 @@ docs/build docs/_build docs/debug.log docs/.vscode -docs/generated -docs/api/autodoc_additional_props.rst +docs/source/generated +docs/source/auto_examples +docs/source/api/autodoc_additional_props.rst -docs/_tables -docs/_static/example_gifs_very_old +docs/source/_tables +docs/source/_static/example_gifs_very_old logos tests/Images @@ -32,3 +33,6 @@ _exclude* docs/jupyter_execute/* docs/.jupyter_cache/* docs/.virtual_documents/* + +# mac +.DS_Store diff --git a/docs/EOmaps_examples.rst b/docs/EOmaps_examples.rst deleted file mode 100644 index 64881c3bd..000000000 --- a/docs/EOmaps_examples.rst +++ /dev/null @@ -1,371 +0,0 @@ -.. include:: substitutions.rst - -.. _EOmaps_examples: - -🗺 EOmaps examples -================== - -... a collection of examples that show how to create beautiful interactive maps. - - -.. include:: example_galery.rst - - -.. _example_basics: - -Basic data visualization -------------------------- - -There are 3 basic steps required to visualize your data: - -1. Initialize a Maps-object with ``m = Maps()`` -2. Set the data and its specifications via ``m.set_data`` -3. Call ``m.plot_map()`` to generate the map! - -|toggleStart| - -.. literalinclude:: examples/example_basics.py - -|toggleEnd| - -.. image:: _static/example_images/example_basics.gif - :width: 75% - - -.. _example_customization: - -Customize the appearance of the plot ------------------------------------- - -- use ``m.set_plot_specs()`` to set the general appearance of the plot -- after creating the plot, you can access individual objects via ``m.figure.<...>`` … most importantly: - - - ``f`` : the matplotlib figure - - ``ax``, ``ax_cb``, ``ax_cb_plot`` : the axes used for plotting the map, colorbar and histogram - - ``gridspec``, ``cb_gridspec`` : the matplotlib GridSpec instances for the plot and the colorbar - - ``coll`` : the collection representing the data on the map - -|toggleStart| - -.. literalinclude:: examples/example_customization.py - -|toggleEnd| - -.. image:: _static/example_images/example_customization.png - :width: 75% - - -.. _example_multiple_maps: - -Data-classification and multiple Maps in a figure -------------------------------------------------- - -- Create grids of maps via ``MapsGrid`` -- | Classify your data via ``m.set_classify_specs(scheme, **kwargs)`` - | (using classifiers provided by the ``mapclassify`` module) -- | Add individual callback functions to each subplot via - | ``m.cb.click.attach``, ``m.cb.pick.attach`` -- | Share events between Maps-objects of the MapsGrid via - | ``mg.share_click_events()`` and ``mg.share_pick_events()`` - - -|toggleStart| - -.. literalinclude:: examples/example_multiple_maps.py - -|toggleEnd| - -.. image:: _static/example_images/example_multiple_maps.gif - :width: 75% - - -.. _example_callbacks: - -Callbacks - turn your maps into interactive widgets ---------------------------------------------------- - -- **Callback functions** can easily be attached to the plot to turn it - into an interactive plot-widget! - - - | there’s a nice list of (customizable) pre-defined callbacks accessible via: - | ``m.cb.click``, ``m.cb.pick``, ``m.cb.keypress`` and ``m.cb.dynamic`` - - - use ``annotate`` (and ``clear_annotations``) to create text-annotations - - use ``mark`` (and ``clear_markers``) to add markers - - use ``peek_layer`` (and ``switch_layer``) to compare multiple layers of data - - ... and many more: ``plot``, ``print_to_console``, ``get_values``, ``load`` ... - - - | ... but you can also define a custom one and connect it via - | ``m.cb.click.attach()`` (works also with ``pick`` and ``keypress``)! - -|toggleStart| - -.. literalinclude:: examples/example_callbacks.py - -|toggleEnd| - -.. image:: _static/example_images/example_callbacks.gif - :width: 75% - - -.. _example_overlays: - -Overlays, markers and annotations ---------------------------------- - -(… plot-generation might take a bit longer since overlays need to be downloaded first!) - -- add basic overlays with `m.add_overlay` -- add static annotations / markers with `m.add_annotation` and `m.add_marker` -- use “connected” Maps-objects to get multiple interactive data-layers! - -|toggleStart| - -.. literalinclude:: examples/example_overlays.py - -|toggleEnd| - -.. image:: _static/example_images/example_overlays.gif - :width: 75% - -The data displayed in the above gif is taken from: - - NaturalEarth (https://www.naturalearthdata.com/) - - -.. _example_webmaps: - -WebMap services and layer-switching ------------------------------------ - -- add WebMap services using ``m.add_wms`` and ``m.add_wmts`` -- compare different data-layers and WebMap services using ``m.cb.click.peek_layer`` and ``m.cb.keypress.switch_layer`` - -|toggleStart| - -.. literalinclude:: examples/example_webmaps.py - -|toggleEnd| - -.. image:: _static/example_images/example_webmaps.gif - :width: 75% - -The data displayed in the above gif is taken from: - - Sentinel-1 Global Backscatter Model (https://researchdata.tuwien.ac.at/records/n2d1v-gqb91) - - OpenStreetMap hosted by Mundialis (https://www.mundialis.de/en/ows-mundialis/) - - -.. _example_vector_data: - -Vector data - interactive geometries -------------------------------------- - -EOmaps can be used to assign callbacks to vektor-data (e.g. ``geopandas.GeoDataFrames``). - -- to make a GeoDataFrame pickable, first use ``m.add_gdf(picker_name="MyPicker")`` - - - now you can assign callbacks via ``m.cb.pick__MyPicker.attach...`` just as you - would do with the ordinary ``m.cb.click`` or ``m.cb.pick`` callbacks - -.. Note:: - For large datasets that are visualized as simple rectangles, ellipses etc. - it is recommended to use EOmaps to visualize the data with ``m.plot_map()`` - since the generation of the plot and the identification of the picked pixels - will be much faster! - - If the GeoDataFrame contains multiple different geometry types - (e.g. Lines, Patches, etc.) a unique pick-collection will be assigned - for each of the geometry types! - - -|toggleStart| - -.. literalinclude:: examples/example_vector_data.py - -|toggleEnd| - -.. image:: _static/example_images/example_vector_data.gif - :width: 75% - - -The data displayed in the above gif is taken from: - - NaturalEarth (https://www.naturalearthdata.com/) - - -.. _example_scalebars: - -Using Scalebars ---------------- - -EOmaps has a nice customizable scalebar feature! - - use ``s = m.add_scalebar(lon, lat, azim)`` to attach a scalebar to the plot - - once the scalebar is there, you can drag it around and change its - properties via ``s.set_position``, ``s.set_scale_props()``, - ``s.set_label_props()`` and ``s.set_patch_props()`` - -.. Note:: - You can also simply drag the scalebar with the mouse! - - - LEFT-click on it to make it interactive! - - RIGHT-click anywhere on the map to make it fixed again - - There are also some useful keyboard shortcuts you can use while the - scalebar is interactive - - - use ``+``/``-`` to rotate the scalebar - - use ``alt`` + ``+``/``-`` to set the text-offset - - use the ``arrow-keys`` to increase the frame-widths - - use ``alt`` + ``arrow-keys`` to decrease the frame-widths - - use ``delete`` to remove the scalebar from the plot - - -|toggleStart| - -.. literalinclude:: examples/example_scalebars.py - -|toggleEnd| - -.. image:: _static/example_images/example_scalebars.gif - :width: 75% - -The data displayed in the above gif is taken from: - - NaturalEarth (https://www.naturalearthdata.com/) - - - -.. _example_timeseries: - -Data analysis widgets - Timeseries and histograms -------------------------------------------------- - -Callback-functions can be used to trigger updates on other plots. -This example shows how to use EOmaps to analyze a database that is associated with a map. - -- create a grid of ``Maps`` objects and ordinary matplotlib axes via ``MapsGrid`` -- define a custom callback to update the plots if you click on the map - - -|toggleStart| - -.. literalinclude:: examples/example_timeseries.py - -|toggleEnd| - - -.. image:: _static/example_images/example_timeseries.gif - :width: 75% - - -.. _example_row_col_selector: - -Data analysis widgets - Select 1D slices of a 2D dataset --------------------------------------------------------- - -Use custom callback functions to perform arbitrary tasks on the data when clicking on the map. - -- Identify clicked row/col in a 2D dataset -- Highlight the found row and column using a new layer - -(requires EOmaps >= v3.1.4) - -|toggleStart| - -.. literalinclude:: examples/example_row_col_selector.py - -|toggleEnd| - - -.. image:: _static/example_images/example_row_col_selector.gif - :width: 75% - - -.. _example_inset_maps: - -Inset-maps - get a zoomed-in view on selected areas ---------------------------------------------------- - -Quickly create nice inset-maps to show details for specific regions. - -- the location and extent of the inset can be defined in any given crs - - - (or as a geodesic circle with a radius defined in meters) - -- the inset-map can have a different crs than the "parent" map - -(requires EOmaps >= v4.1) - -|toggleStart| - -.. literalinclude:: examples/example_inset_maps.py - -|toggleEnd| - - -.. image:: _static/example_images/example_inset_maps.png - :width: 75% - - -.. _example_lines: - -Lines and Annotations ---------------------- - -Draw lines defined by a set of anchor-points and add some nice annotations. - -Connect the anchor-points via: - -- geodesic lines -- straight lines -- reprojected straight lines defined in a given projection - - -(requires EOmaps >= v4.3.1) - -|toggleStart| - -.. literalinclude:: examples/example_lines.py - -|toggleEnd| - - -.. image:: _static/example_images/example_lines.png - :width: 75% - - -.. _example_gridlines: - -Gridlines and Grid Labels -------------------------- - -Draw custom grids and add grid labels. - -(requires EOmaps >= v6.5) - -|toggleStart| - -.. literalinclude:: examples/example_gridlines.py - -|toggleEnd| - - -.. image:: _static/example_images/example_gridlines.png - :width: 75% - -.. _example_contour: - -Contour plots and Contour Levels --------------------------------- - -Use the ``contour``-shape to draw contour-plots of regular (or irregular data) -or to indicate contour-levels on top of other plots. - -(requires EOmaps >= v7.1) - -|toggleStart| - -.. literalinclude:: examples/example_contour.py - -|toggleEnd| - - -.. image:: _static/example_images/example_contour.png - :width: 75% diff --git a/docs/Makefile b/docs/Makefile old mode 100644 new mode 100755 index d0c3cbf10..3747cf643 --- a/docs/Makefile +++ b/docs/Makefile @@ -12,6 +12,11 @@ BUILDDIR = build help: @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) +clean: + rm -rf $(BUILDDIR)/* + rm -rf $(SOURCEDIR)/auto_examples + rm -rf $(SOURCEDIR)/generated + .PHONY: help Makefile # Catch-all target: route all unknown targets to Sphinx using the new diff --git a/docs/_static/custom_css.css b/docs/_static/custom_css.css deleted file mode 100644 index b4ec9fb22..000000000 --- a/docs/_static/custom_css.css +++ /dev/null @@ -1,13 +0,0 @@ -.wy-nav-content { - max-width: 1200px !important; -} - - /* - Set colors used by sphinx-design info-boxes - For details, see: https://sphinx-design.readthedocs.io/en/latest/css_variables.html - */ -:root { - --sd-color-info: #b8c5c6; - --sd-color-info-highlight: #148a9c; - --sd-color-info-text: #000; -} diff --git a/docs/_static/example_images/example_lines.png b/docs/_static/example_images/example_lines.png deleted file mode 100644 index 3702eab50..000000000 Binary files a/docs/_static/example_images/example_lines.png and /dev/null differ diff --git a/docs/docs_env.yml b/docs/docs_env.yml old mode 100644 new mode 100755 index 42bc80cc3..568448f9e --- a/docs/docs_env.yml +++ b/docs/docs_env.yml @@ -8,11 +8,13 @@ dependencies: # --------------for building the docs - docutils =0.20.1 - - sphinx =7.2.6 + - sphinx =8.0.2 - sphinx-copybutton =0.5.2 - - myst-nb =1.0.0 - - sphinx-design =0.5.0 - - sphinx_rtd_theme =2.0.0 + - myst-nb =1.1.1 + - sphinx-design =0.6.1 + - pydata-sphinx-theme + - myst-sphinx-gallery = 0.2.2 + # install minimal dependencies to import eomaps - pip diff --git a/docs/example_galery.rst b/docs/example_galery.rst deleted file mode 100644 index 57ae25f15..000000000 --- a/docs/example_galery.rst +++ /dev/null @@ -1,87 +0,0 @@ -.. grid:: 2 3 4 5 - :gutter: 1 - - .. grid-item-card:: - :img-background: _static/example_images/example_basics.gif - :img-alt: Example 1 - Basic data visualization - :link: example_basics - :link-type: ref - - .. grid-item-card:: - :img-background: _static/example_images/example_customization.png - :img-alt: Example 2 - Customize the appearance of the map - :link: example_customization - :link-type: ref - - .. grid-item-card:: - :img-background: _static/example_images/example_multiple_maps.gif - :img-alt: Example 3 - Data-classification and multiple Maps in a figure - :link: example_multiple_maps - :link-type: ref - - .. grid-item-card:: - :img-background: _static/example_images/example_callbacks.gif - :img-alt: Example 4 - Callbacks - turn your maps into interactive widgets - :link: example_callbacks - :link-type: ref - - .. grid-item-card:: - :img-background: _static/example_images/example_overlays.gif - :img-alt: Example 5 - Overlays, markers and annotations - :link: example_overlays - :link-type: ref - - .. grid-item-card:: - :img-background: _static/example_images/example_webmaps.gif - :img-alt: Example 6 - WebMap services and layer-switching - :link: example_webmaps - :link-type: ref - - .. grid-item-card:: - :img-background: _static/example_images/example_vector_data.gif - :img-alt: Example 7 - Vector data - interactive geometries - :link: example_vector_data - :link-type: ref - - .. grid-item-card:: - :img-background: _static/example_images/example_scalebars.gif - :img-alt: Example 8 - Using Scalebars - :link: example_scalebars - :link-type: ref - - .. grid-item-card:: - :img-background: _static/example_images/example_timeseries.gif - :img-alt: Example 9 - Data analysis widgets - Timeseries and histograms - :link: example_timeseries - :link-type: ref - - .. grid-item-card:: - :img-background: _static/example_images/example_row_col_selector.gif - :img-alt: Example 10 - Data analysis widgets - Select 1D slices of a 2D dataset - :link: example_row_col_selector - :link-type: ref - - .. grid-item-card:: - :img-background: _static/example_images/example_inset_maps.png - :img-alt: Example 11 - Inset-maps - get a zoomed-in view on selected areas - :link: example_inset_maps - :link-type: ref - - .. grid-item-card:: - :img-background: _static/example_images/example_lines.png - :img-alt: Example 12 - Lines and Annotations - :text-align: center - :link: example_lines - :link-type: ref - - .. grid-item-card:: - :img-background: _static/example_images/example_gridlines.png - :img-alt: Example 13 - Gridlines and Grid Labels - :link: example_gridlines - :link-type: ref - - .. grid-item-card:: - :img-background: _static/example_images/example_contour.png - :img-alt: Example 14 - Contour plots and Contour Levels - :link: example_contour - :link-type: ref diff --git a/docs/examples/example_basics.py b/docs/examples/example_basics.py deleted file mode 100644 index 798a5bc70..000000000 --- a/docs/examples/example_basics.py +++ /dev/null @@ -1,32 +0,0 @@ -# EOmaps: A simple map - -from eomaps import Maps -import pandas as pd -import numpy as np - -# ----------- create some example-data -lon, lat = np.meshgrid(np.arange(-20, 40, 0.25), np.arange(30, 60, 0.25)) -data = pd.DataFrame( - dict(lon=lon.flat, lat=lat.flat, data_variable=np.sqrt(lon**2 + lat**2).flat) -) -data = data.sample(15000) # take 15000 random datapoints from the dataset -# ------------------------------------ - -m = Maps(crs=4326) -m.add_title("Click on the map to pick datapoints!") -m.add_feature.preset.ocean() -m.add_feature.preset.coastline() -m.set_data( - data=data, # a pandas-DataFrame holding the data & coordinates - parameter="data_variable", # the DataFrame-column you want to plot - x="lon", # the name of the DataFrame-column representing the x-coordinates - y="lat", # the name of the DataFrame-column representing the y-coordinates - crs=4326, # the coordinate-system of the x- and y- coordinates -) -m.plot_map() -m.add_colorbar(label="A dataset") - -c = m.add_compass((0.05, 0.86), scale=7, patch=None) - -m.cb.pick.attach.annotate() # attach a pick-annotation (on left-click) -m.add_logo() # add a logo diff --git a/docs/examples/example_callbacks.py b/docs/examples/example_callbacks.py deleted file mode 100644 index 7c59e1977..000000000 --- a/docs/examples/example_callbacks.py +++ /dev/null @@ -1,185 +0,0 @@ -# EOmaps example: Turn your maps into a powerful widgets - -from eomaps import Maps -import pandas as pd -import numpy as np - -# create some data -lon, lat = np.meshgrid(np.linspace(-20, 40, 50), np.linspace(30, 60, 50)) - -data = pd.DataFrame( - dict(lon=lon.flat, lat=lat.flat, data=np.sqrt(lon**2 + lat**2).flat) -) - -# --------- initialize a Maps object and plot a basic map -m = Maps(crs=3035, figsize=(10, 8)) -m.set_data(data=data, x="lon", y="lat", crs=4326) -m.ax.set_title("A clickable widget!") -m.set_shape.rectangles() - -m.set_classify_specs(scheme="EqualInterval", k=5) -m.add_feature.preset.coastline() -m.add_feature.preset.ocean() -m.plot_map() - -# add some static text -m.text( - 0.66, - 0.92, - ( - "Left-click: temporary annotations\n" - "Right-click: permanent annotations\n" - "Middle-click: clear permanent annotations" - ), - fontsize=10, - horizontalalignment="left", - verticalalignment="top", - color="k", - fontweight="bold", - bbox=dict(facecolor="w", alpha=0.75), -) - - -# --------- attach pre-defined CALLBACK functions --------- - -### add a temporary annotation and a marker if you left-click on a pixel -m.cb.pick.attach.mark( - button=1, - permanent=False, - fc=[0, 0, 0, 0.5], - ec="w", - ls="--", - buffer=2.5, - shape="ellipses", - zorder=1, -) -m.cb.pick.attach.annotate( - button=1, - permanent=False, - bbox=dict(boxstyle="round", fc="w", alpha=0.75), - zorder=999, -) -### save all picked values to a dict accessible via m.cb.get.picked_vals -m.cb.pick.attach.get_values(button=1) - -### add a permanent marker if you right-click on a pixel -m.cb.pick.attach.mark( - button=3, - permanent=True, - facecolor=[1, 0, 0, 0.5], - edgecolor="k", - buffer=1, - shape="rectangles", - zorder=1, -) - -### add a customized permanent annotation if you right-click on a pixel -def text(m, ID, val, pos, ind): - return f"ID={ID}" - - -m.cb.pick.attach.annotate( - button=3, - permanent=True, - bbox=dict(boxstyle="round", fc="r"), - text=text, - xytext=(10, 10), - zorder=2, # use zorder=2 to put the annotations on top of the markers -) - -### remove all permanent markers and annotations if you middle-click anywhere on the map -m.cb.pick.attach.clear_annotations(button=2) -m.cb.pick.attach.clear_markers(button=2) - -# --------- define a custom callback to update some text to the map -# (use a high zorder to draw the texts above all other things) -txt = m.text( - 0.5, - 0.35, - "You clicked on 0 pixels so far", - fontsize=15, - horizontalalignment="center", - verticalalignment="top", - color="w", - fontweight="bold", - animated=True, - zorder=99, - transform=m.ax.transAxes, -) -txt2 = m.text( - 0.18, - 0.9, - " lon / lat " + "\n", - fontsize=12, - horizontalalignment="right", - verticalalignment="top", - fontweight="bold", - animated=True, - zorder=99, - transform=m.ax.transAxes, -) - - -def cb1(m, pos, ID, val, **kwargs): - # update the text that indicates how many pixels we've clicked - nvals = len(m.cb.pick.get.picked_vals["ID"]) - txt.set_text( - f"You clicked on {nvals} pixel" - + ("s" if nvals > 1 else "") - + "!\n... and the " - + ("average " if nvals > 1 else "") - + f"value is {np.mean(m.cb.pick.get.picked_vals['val']):.3f}" - ) - - # update the list of lon/lat coordinates on the top left of the figure - d = m.data.loc[ID] - lonlat_list = txt2.get_text().splitlines() - if len(lonlat_list) > 10: - lonlat_txt = lonlat_list[0] + "\n" + "\n".join(lonlat_list[-10:]) + "\n" - else: - lonlat_txt = txt2.get_text() - txt2.set_text(lonlat_txt + f"{d['lon']:.2f} / {d['lat']:.2f}" + "\n") - - -m.cb.pick.attach(cb1, button=1, m=m) - - -def cb2(m, pos, val, **kwargs): - # plot a marker at the pixel-position - (l,) = m.ax.plot(*pos, marker="*", animated=True) - # add the custom marker to the blit-manager! - m.BM.add_artist(l) - - # print the value at the pixel-position - # use a low zorder so the text will be drawn below the temporary annotations - m.text( - pos[0], - pos[1] - 150000, - f"{val:.2f}", - horizontalalignment="center", - verticalalignment="bottom", - color=l.get_color(), - zorder=1, - transform=m.ax.transData, - ) - - -m.cb.pick.attach(cb2, button=3, m=m) - -# add a "target-indicator" on mouse-movement -m.cb.move.attach.mark(fc="r", ec="none", radius=10000, shape="geod_circles") -m.cb.move.attach.mark(fc="none", ec="r", radius=50000, shape="geod_circles") - -# add a colorbar -m.add_colorbar(hist_bins="bins", label="A classified dataset") -m.add_logo() - -m.apply_layout( - { - "figsize": [10.0, 8.0], - "0_map": [0.04375, 0.27717, 0.9125, 0.69566], - "1_cb": [0.01, 0.0, 0.98, 0.23377], - "1_cb_histogram_size": 0.8, - "2_logo": [0.825, 0.29688, 0.12, 0.06188], - } -) diff --git a/docs/examples/example_contour.py b/docs/examples/example_contour.py deleted file mode 100644 index 5806da34a..000000000 --- a/docs/examples/example_contour.py +++ /dev/null @@ -1,45 +0,0 @@ -# EOmaps example: Contour plots and contour levels - -from eomaps import Maps -import numpy as np - -# ------------- setup some random data -lon, lat = np.meshgrid(np.linspace(-180, 180, 200), np.linspace(-90, 90, 100)) -data = np.sqrt(lon**2 + lat**2) -name = "some parameter" - -# ------------- left map -m = Maps(ax=121) -m.add_title("Raster plot with contour-levels") -m.add_feature.preset.coastline() -# plot raster-data -m.set_data(data, lon, lat, parameter=name) -m.set_shape.raster() -m.plot_map() - -# layer to indicate contour-levels -m_cont = m.new_layer(inherit_data=True) -m_cont.set_shape.contour(filled=False) -m_cont.set_classify.EqualInterval(k=4) -m_cont.plot_map(colors=("r", "g", "b", "m")) - -# ------------- right map -m2 = m.new_map(ax=122, inherit_data=True) -m2.add_title("Filled contour plot") -m2.set_classify.EqualInterval(k=4) -m2.set_shape.contour(filled=True) -m2.plot_map(cmap="viridis") - -# add a colorbar and indicate contour-levels -cb = m.add_colorbar(label="Data histogram with indicated contour levels highlighted.") -cb.indicate_contours(m_cont) - -# apply a customized layout -layout = { - "figsize": [8.34, 3.9], - "0_map": [0.03652, 0.44167, 0.45, 0.48115], - "1_map": [0.51152, 0.44167, 0.45, 0.48115], - "2_cb": [-0.0125, 0.02673, 1.0125, 0.28055], - "2_cb_histogram_size": 0.76, -} -m.apply_layout(layout) diff --git a/docs/examples/example_customization.py b/docs/examples/example_customization.py deleted file mode 100644 index a859c3616..000000000 --- a/docs/examples/example_customization.py +++ /dev/null @@ -1,58 +0,0 @@ -# EOmaps example: Customize the appearance of the plot - -from eomaps import Maps -import pandas as pd -import numpy as np - -# ----------- create some example-data -lon, lat = np.meshgrid(np.arange(-30, 60, 0.25), np.arange(30, 60, 0.3)) -data = pd.DataFrame( - dict(lon=lon.flat, lat=lat.flat, data_variable=np.sqrt(lon**2 + lat**2).flat) -) -data = data.sample(3000) # take 3000 random datapoints from the dataset -# ------------------------------------ - -m = Maps(crs=3857, figsize=(9, 5)) -m.set_frame(rounded=0.2, lw=1.5, ec="midnightblue", fc="ivory") -m.text(0.5, 0.97, "What a nice figure", fontsize=12) - -m.add_feature.preset.ocean(fc="lightsteelblue") -m.add_feature.preset.coastline(lw=0.25) - -m.set_data(data=data, x="lon", y="lat", crs=4326) -m.set_shape.geod_circles(radius=30000) # plot geodesic-circles with 30 km radius -m.set_classify_specs( - scheme="UserDefined", bins=[35, 36, 37, 38, 45, 46, 47, 48, 55, 56, 57, 58] -) -m.plot_map( - edgecolor="k", # give shapes a black edgecolor - linewidth=0.5, # with a linewidth of 0.5 - cmap="RdYlBu", # use a red-yellow-blue colormap - vmin=35, # map colors to values between 35 and 60 - vmax=60, - alpha=0.75, # add some transparency -) - -# add a colorbar -m.add_colorbar( - label="some parameter", - hist_bins="bins", - hist_size=1, - hist_kwargs=dict(density=True), -) - -# add a y-label to the histogram -m.colorbar.ax_cb_plot.set_ylabel("The Y label") - -# add a logo to the plot -m.add_logo() - -m.apply_layout( - { - "figsize": [9.0, 5.0], - "0_map": [0.10154, 0.2475, 0.79692, 0.6975], - "1_cb": [0.20125, 0.0675, 0.6625, 0.135], - "1_cb_histogram_size": 1, - "2_logo": [0.87501, 0.09, 0.09999, 0.07425], - } -) diff --git a/docs/examples/example_gridlines.py b/docs/examples/example_gridlines.py deleted file mode 100644 index 0460ef310..000000000 --- a/docs/examples/example_gridlines.py +++ /dev/null @@ -1,71 +0,0 @@ -# EOmaps example: Customized gridlines - -from eomaps import Maps - -m = Maps(crs=Maps.CRS.Stereographic()) -m.subplots_adjust(left=0.1, right=0.9, bottom=0.1, top=0.9) - -m.add_feature.preset.ocean() -m.add_feature.preset.land() - -# draw a regular 5 degree grid -m.add_gridlines(5, lw=0.25, alpha=0.5) -# draw a grid with 20 degree longitude spacing and add labels -g = m.add_gridlines((20, None), c="b", n=500) -g.add_labels(offset=10, fontsize=8, c="b") -# draw a grid with 20 degree latitude spacing, add labels and exclude the 10° tick -g = m.add_gridlines((None, 20), c="g", n=500) -g.add_labels(where="l", offset=10, fontsize=8, c="g", exclude=[10]) -# explicitly highlight 10°N line and add a label on one side of the map -g = m.add_gridlines((None, [10]), c="indigo", n=500, lw=1.5) -g.add_labels(where="l", fontsize=12, fontweight="bold", c="indigo") - - -# ----------------- first inset-map -mi = m.new_inset_map(xy=(45, 45), radius=10, inset_crs=m.crs_plot) -mi.add_feature.preset.ocean() -mi.add_feature.preset.land() - -# draw a regular 1 degree grid -g = mi.add_gridlines((None, 1), c="g", lw=0.6) -# add some specific latitude gridlines and add labels -g = mi.add_gridlines((None, [40, 45, 50]), c="g", lw=2) -g.add_labels(where="lr", offset=7, fontsize=6, c="g") -# add some specific longitude gridlines and add labels -g = mi.add_gridlines(([40, 45, 50], None), c="b", lw=2) -g.add_labels(where="tb", offset=7, fontsize=6, c="b") - -mi.add_extent_indicator(m, fc="darkred", ec="none", alpha=0.5) -mi.add_indicator_line() - -# ----------------- second inset-map -mi = m.new_inset_map( - inset_crs=m.crs_plot, - xy=(-10, 10), - radius=20, - shape="rectangles", - boundary=dict(ec="k"), -) -mi.add_feature.preset.ocean() -mi.add_feature.preset.land() - -mi.add_extent_indicator(m, fc=".5", ec="none", alpha=0.5) -mi.add_indicator_line(c="k") - -# draw a regular 1 degree grid -g = mi.add_gridlines(5, lw=0.25) -# add some specific latitude gridlines and add labels -g = mi.add_gridlines((None, [0, 10, 25]), c="g", lw=2) -g.add_labels(where="l", fontsize=10, c="g") -# add some specific longitude gridlines and add labels -g = mi.add_gridlines(([-25, -10, 0], None), c="b", lw=2) -g.add_labels(where="t", fontsize=10, c="b") - -m.apply_layout( - { - "figsize": [7.39, 4.8], - "0_map": [0.025, 0.07698, 0.5625, 0.86602], - "1_inset_map": [0.7, 0.53885, 0.225, 0.41681], - "2_inset_map": [0.6625, 0.03849, 0.275, 0.42339], - } -) diff --git a/docs/examples/example_inset_maps.py b/docs/examples/example_inset_maps.py deleted file mode 100644 index 8f6df6515..000000000 --- a/docs/examples/example_inset_maps.py +++ /dev/null @@ -1,106 +0,0 @@ -# EOmaps example: How to create inset-maps - -from eomaps import Maps -import numpy as np - -m = Maps(Maps.CRS.Orthographic()) -m.add_feature.preset.coastline() # add some coastlines - -# ---------- create a new inset-map -# showing a 15 degree rectangle around the xy-point -mi1 = m.new_inset_map( - xy=(5, 45), - xy_crs=4326, - shape="rectangles", - radius=15, - plot_position=(0.75, 0.4), - plot_size=0.5, - inset_crs=4326, - boundary=dict(ec="r", lw=1), - indicate_extent=dict(fc=(1, 0, 0, 0.25)), -) - -mi1.add_indicator_line(m, marker="o") - -# populate the inset with some more detailed features -mi1.add_feature.preset("coastline", "ocean", "land", "countries", "urban_areas") - -# ---------- create another inset-map -# showing a 400km circle around the xy-point -mi2 = m.new_inset_map( - xy=(5, 45), - xy_crs=4326, - shape="geod_circles", - radius=400000, - plot_position=(0.25, 0.4), - plot_size=0.5, - inset_crs=3035, - boundary=dict(ec="g", lw=2), - indicate_extent=dict(fc=(0, 1, 0, 0.25)), -) -mi2.add_indicator_line(m, marker="o") - -# populate the inset with some features -mi2.add_feature.preset("ocean", "land") -mi2.add_feature.preset.urban_areas(zorder=1) - -# print some data on all of the maps - -x, y = np.meshgrid(np.linspace(-50, 50, 100), np.linspace(-30, 70, 100)) -data = x + y - -m.set_data(data, x, y, crs=4326) -m.set_classify.Quantiles(k=4) -m.plot_map(alpha=0.5, ec="none", set_extent=False) - -# use the same data and classification for the inset-maps -for m_i in [mi1, mi2]: - m_i.inherit_data(m) - m_i.inherit_classification(m) - -mi1.set_shape.ellipses(np.mean(m.shape.radius) / 2) -mi1.plot_map(alpha=0.75, ec="k", lw=0.5, set_extent=False) - -mi2.set_shape.ellipses(np.mean(m.shape.radius) / 2) -mi2.plot_map(alpha=1, ec="k", lw=0.5, set_extent=False) - - -# add an annotation for the second datapoint to the inset-map -mi2.add_annotation(ID=1, xytext=(-120, 80)) - -# indicate the extent of the second inset on the first inset -mi2.add_extent_indicator(mi1, ec="g", lw=2, fc="g", alpha=0.5, zorder=0) -mi2.add_indicator_line(mi1, marker="o") - -# add some additional text to the inset-maps -for m_i, txt, color in zip([mi1, mi2], ["epsg: 4326", "epsg: 3035"], ["r", "g"]): - txt = m_i.ax.text( - 0.5, - 0, - txt, - transform=m_i.ax.transAxes, - horizontalalignment="center", - bbox=dict(facecolor=color), - ) - # add the text-objects as artists to the blit-manager - m_i.BM.add_artist(txt) - -mi2.add_colorbar(hist_bins=20, margin=dict(bottom=-0.2), label="some parameter") -# move the inset map (and the colorbar) to a different location -mi2.set_inset_position(x=0.3) - -# share pick events -for mi in [m, mi1, mi2]: - mi.cb.pick.attach.annotate(text=lambda ID, val, **kwargs: f"ID={ID}\nval={val:.2f}") -m.cb.pick.share_events(mi1, mi2) - -m.apply_layout( - { - "figsize": [6.4, 4.8], - "0_map": [0.1625, 0.09, 0.675, 0.9], - "1_inset_map": [0.5625, 0.15, 0.375, 0.5], - "2_inset_map": [0.0875, 0.33338, 0.325, 0.43225], - "3_cb": [0.0875, 0.12, 0.4375, 0.12987], - "3_cb_histogram_size": 0.8, - } -) diff --git a/docs/examples/example_lines.py b/docs/examples/example_lines.py deleted file mode 100644 index b35920328..000000000 --- a/docs/examples/example_lines.py +++ /dev/null @@ -1,106 +0,0 @@ -# EOmaps example: drawing lines on a map - -from eomaps import Maps - -m = Maps(Maps.CRS.Mollweide(), figsize=(8, 4)) -m.add_feature.preset.ocean() -m.add_feature.preset.land() - -# get a few points for some basic lines -l1 = [(-135, 50), (45, 45), (123, 76)] -l2 = [(-120, -24), (-160, 34), (-153, -60), (-128, -82), (-24, -25), (-16, 35)] - -# define annotation-styles -bbox_style1 = dict( - xy_crs=4326, - fontsize=8, - bbox=dict(boxstyle="circle,pad=0.25", ec="b", fc="b", alpha=0.25), -) -bbox_style2 = dict( - xy_crs=4326, - fontsize=6, - bbox=dict(boxstyle="round,pad=0.25", ec="k", fc="r", alpha=0.5), - horizontalalignment="center", -) - -# -------- draw a line with 100 intermediate points per line-segment, -# mark anchor-points with "o" and every 10th intermediate point with "x" -m.add_line(l1, c="b", lw=0.5, marker="x", ms=3, markevery=10, n=100, mark_points="bo") - -m.add_annotation(xy=l1[0], text="start", xytext=(10, -20), **bbox_style1) -m.add_annotation(xy=l1[-1], text="end", xytext=(10, -30), **bbox_style1) - -# -------- draw a line with ~1km spacing between intermediate points per line-segment, -# mark anchor-points with "*" and every 1000th intermediate point with "." - -d_inter, d_tot = m.add_line( - l2, - c="r", - lw=0.5, - marker=".", - del_s=1000, - markevery=1000, - mark_points=dict(marker="*", fc="darkred", ec="k", lw=0.25, s=60, zorder=99), -) - -for i, (point, distance) in enumerate(zip(l2[:-1], d_tot)): - if i == 0: - t = "start" - else: - t = f"segment {i}" - - m.add_annotation( - xy=point, text=f"{t}\n{distance/1000:.0f}km", xytext=(10, 20), **bbox_style2 - ) - -m.add_annotation(xy=l2[-1], text="end", xytext=(10, 20), **bbox_style2) - - -# -------- show the effect of different connection-styles - -l3 = [(50, 20), (120, 20), (120, -30), (50, -30), (50, 20)] -l4 = [(55, 15), (115, 15), (115, -25), (55, -25), (55, 15)] -l5 = [(60, 10), (110, 10), (110, -20), (60, -20), (60, 10)] - -# -------- connect points via straight lines -m.add_line(l3, lw=0.75, ls="--", c="k", mark_points="k.") -m.add_annotation( - xy=l3[1], - fontsize=6, - xy_crs=4326, - text="geodesic lines", - xytext=(20, 10), - bbox=dict(ec="k", fc="w", ls="--"), -) - -# -------- connect points via lines that are straight in a given projection -m.add_line( - l4, - connect="straight_crs", - xy_crs=4326, - lw=1, - c="purple", - mark_points=dict(fc="purple", marker="."), -) -m.add_annotation( - xy=l4[1], - fontsize=6, - xy_crs=4326, - text="straight lines\nin epsg 4326", - xytext=(21, -10), - bbox=dict(ec="purple", fc="w"), -) - -# -------- connect points via geodesic lines -m.add_line(l5, connect="straight", lw=0.5, c="r", mark_points="r.") - -m.add_annotation( - xy=l5[1], - fontsize=6, - xy_crs=4326, - text="straight lines", - xytext=(24, -20), - bbox=dict(ec="r", fc="w"), -) - -m.add_logo() diff --git a/docs/examples/example_multiple_maps.py b/docs/examples/example_multiple_maps.py deleted file mode 100644 index 065ddaa8d..000000000 --- a/docs/examples/example_multiple_maps.py +++ /dev/null @@ -1,102 +0,0 @@ -# EOmaps example: Data-classification and multiple Maps in one figure - -from eomaps import Maps -import pandas as pd -import numpy as np - -# ----------- create some example-data -lon, lat = np.meshgrid(np.arange(-20, 40, 0.5), np.arange(30, 60, 0.5)) -data = pd.DataFrame( - dict(lon=lon.flat, lat=lat.flat, data_variable=np.sqrt(lon**2 + lat**2).flat) -) -data = data.sample(4000) # take 4000 random datapoints from the dataset -# ------------------------------------ - -# initialize a grid of Maps objects -m = Maps(ax=131, crs=4326, figsize=(11, 5)) -m2 = m.new_map(ax=132, crs=Maps.CRS.Stereographic()) -m3 = m.new_map(ax=133, crs=3035) - -# --------- set specs for the first map -m.text(0.5, 1.1, "epsg=4326", transform=m.ax.transAxes) -m.set_classify_specs(scheme="EqualInterval", k=10) - -# --------- set specs for the second map -m2.text(0.5, 1.1, "Stereographic", transform=m2.ax.transAxes) -m2.set_shape.rectangles() -m2.set_classify_specs(scheme="Quantiles", k=8) - -# --------- set specs for the third map -m3.text(0.5, 1.1, "epsg=3035", transform=m3.ax.transAxes) -m3.set_classify_specs( - scheme="StdMean", - multiples=[-1, -0.75, -0.5, -0.25, 0.25, 0.5, 0.75, 1], -) - -# --------- plot all maps and add colorbars to all maps -# set the data on ALL maps-objects of the grid -for m_i in [m, m2, m3]: - m_i.set_data(data=data, x="lon", y="lat", crs=4326) - m_i.plot_map() - m_i.add_colorbar(extend="neither") - - m_i.add_feature.preset.ocean() - m_i.add_feature.preset.land() - # add the coastline to all layers of the maps - m_i.add_feature.preset.coastline(layer="all") - - -# --------- add a new layer for the second axis -# NOTE: this layer is not visible by default but it can be shown by clicking -# on the layer-switcher utility buttons (bottom center of the figure) -# or by using `m2.show()` or via `m.show_layer("layer 2")` -m21 = m2.new_layer(layer="layer 2") -m21.inherit_data(m2) -m21.set_shape.delaunay_triangulation(mask_radius=0.5) -m21.set_classify_specs(scheme="Quantiles", k=4) -m21.plot_map(cmap="RdYlBu") -m21.add_colorbar(extend="neither") -# add an annotation that is only executed if "layer 2" is active -m21.cb.click.attach.annotate(text="callbacks are layer-sensitive!") - -# --------- add some callbacks to indicate the clicked data-point to all maps -for m_i in [m, m2, m3]: - m_i.cb.pick.attach.mark(fc="r", ec="none", buffer=1, permanent=True) - m_i.cb.pick.attach.mark(fc="none", ec="r", lw=1, buffer=5, permanent=True) - m_i.cb.move.attach.mark(fc="none", ec="k", lw=2, buffer=10, permanent=False) - -for m_i in [m, m2, m21, m3]: - # --------- rotate the ticks of the colorbars - m_i.colorbar.ax_cb.tick_params(rotation=90, labelsize=8) - # add logos - m_i.add_logo(size=0.05) - -# add an annotation-callback to the second map -m2.cb.pick.attach.annotate(text="the closest point is here!", zorder=99) - -# share click & pick-events between all Maps-objects of the MapsGrid -m.cb.move.share_events(m2, m3) -m.cb.pick.share_events(m2, m3) - -# --------- add a layer-selector widget -m.util.layer_selector(ncol=2, loc="lower center", draggable=False) - - -m.apply_layout( - { - "figsize": [11.0, 5.0], - "0_map": [0.015, 0.44, 0.3125, 0.34375], - "1_map": [0.35151, 0.363, 0.32698, 0.50973], - "2_map": [0.705, 0.44, 0.2875, 0.37872], - "3_cb": [0.05522, 0.0825, 0.2625, 0.2805], - "3_cb_histogram_size": 0.8, - "4_cb": [0.33625, 0.11, 0.3525, 0.2], - "4_cb_histogram_size": 0.8, - "5_cb": [0.72022, 0.0825, 0.2625, 0.2805], - "5_cb_histogram_size": 0.8, - "6_logo": [0.2725, 0.451, 0.05, 0.04538], - "7_logo": [0.625, 0.3795, 0.05, 0.04538], - "8_logo": [0.625, 0.3795, 0.05, 0.04538], - "9_logo": [0.93864, 0.451, 0.05, 0.04538], - } -) diff --git a/docs/examples/example_overlays.py b/docs/examples/example_overlays.py deleted file mode 100644 index c24bad766..000000000 --- a/docs/examples/example_overlays.py +++ /dev/null @@ -1,152 +0,0 @@ -# EOmaps example: Add overlays and indicators - -from eomaps import Maps -import pandas as pd -import numpy as np -import matplotlib.pyplot as plt -from matplotlib.patches import Patch - -# create some data -lon, lat = np.meshgrid(np.linspace(-20, 40, 100), np.linspace(30, 60, 100)) -data = pd.DataFrame( - dict( - lon=lon.flat, - lat=lat.flat, - param=(((lon - lon.mean()) ** 2 - (lat - lat.mean()) ** 2)).flat, - ) -) -data_OK = data[data.param >= 0] -data_OK.var = np.sqrt(data_OK.param) -data_mask = data[data.param < 0] - -# --------- initialize a Maps object and plot the data -m = Maps(Maps.CRS.Orthographic(), figsize=(10, 7)) -m.ax.set_title("Wooohoo, a flashy map-widget with static indicators!") -m.set_data(data=data_OK, x="lon", y="lat", crs=4326) -m.set_shape.rectangles(mesh=True) -m.set_classify_specs(scheme="Quantiles", k=10) -m.plot_map(cmap="Spectral_r") - -# ... add an "annotate" callback -cid = m.cb.click.attach.annotate(bbox=dict(alpha=0.75, color="w")) - -# - create a new layer and plot another dataset -m2 = m.new_layer() -m2.set_data(data=data_mask, x="lon", y="lat", crs=4326) -m2.set_shape.rectangles() -m2.plot_map(cmap="magma", set_extent=False) - -# create a new layer for some dynamically updated data -m3 = m.new_layer() -m3.set_data(data=data_OK.sample(1000), x="lon", y="lat", crs=4326) -m3.set_shape.ellipses(radius=25000, radius_crs=3857) - -# plot the map and set dynamic=True to allow continuous updates of the -# collection without re-drawing the background map -m3.plot_map( - cmap="gist_ncar", edgecolor="w", linewidth=0.25, dynamic=True, set_extent=False -) - -# define a callback that changes the values of the previously plotted dataset -# NOTE: this is not possible for the shapes: "shade_points" and "shade_raster"! -def callback(m, **kwargs): - # NOTE: Since we change the array of a dynamic collection, the changes will be - # reverted as soon as the background is re-drawn (e.g. on pan/zoom events) - selection = np.random.randint(0, len(m.data), 1000) - m.coll.set_array(data_OK.param.iloc[selection]) - - -# attach the callback (to update the dataset plotted on the Maps object "m3") -m.cb.click.attach(callback, m=m3, on_motion=True) - -# --------- add some basic overlays from NaturalEarth -m.add_feature.preset.coastline() -m.add_feature.preset.lakes() -m.add_feature.preset.rivers_lake_centerlines() -m.add_feature.preset.countries() -m.add_feature.preset.urban_areas() - -# add a customized legend -leg = m.ax.legend( - [ - Patch(fc="b"), - plt.Line2D([], [], c="b"), - Patch(fc="r"), - plt.Line2D([], [], c=".75"), - ], - ["lakes", "rivers", "urban areas", "countries"], - ncol=2, - loc="lower center", - facecolor="w", - framealpha=1, -) -# add the legend as artist to keep it on top -m.BM.add_artist(leg) - -# --------- add some fancy (static) indicators for selected pixels -mark_id = 6060 -for buffer in np.linspace(1, 5, 10): - m.add_marker( - ID=mark_id, - shape="ellipses", - radius="pixel", - fc=(1, 0, 0, 0.1), - ec="r", - buffer=buffer * 5, - n=100, # use 100 points to represent the ellipses - ) -m.add_marker( - ID=mark_id, shape="rectangles", radius="pixel", fc="g", ec="y", buffer=3, alpha=0.5 -) -m.add_marker( - ID=mark_id, shape="ellipses", radius="pixel", fc="k", ec="none", buffer=0.2 -) -m.add_annotation( - ID=mark_id, - text=f"Here's Vienna!\n... the data-value is={m.data.param.loc[mark_id]:.2f}", - xytext=(80, 70), - textcoords="offset points", - bbox=dict(boxstyle="round", fc="w", ec="r"), - horizontalalignment="center", - arrowprops=dict(arrowstyle="fancy", facecolor="r", connectionstyle="arc3,rad=0.35"), -) - -mark_id = 3324 -m.add_marker(ID=mark_id, shape="ellipses", radius=3, fc="none", ec="g", ls="--", lw=2) -m.add_annotation( - ID=mark_id, - text="", - xytext=(0, 98), - textcoords="offset points", - arrowprops=dict( - arrowstyle="fancy", facecolor="g", connectionstyle="arc3,rad=-0.25" - ), -) - -m.add_marker( - ID=mark_id, - shape="geod_circles", - radius=500000, - radius_crs=3857, - fc="none", - ec="b", - ls="--", - lw=2, -) - -m.add_annotation( - ID=mark_id, - text=( - "Here's the center of:\n" - + " $\\bullet$ a blue 'circle' with 50km radius\n" - + " $\\bullet$ a green 'circle' with 3deg radius" - ), - xytext=(-80, 100), - textcoords="offset points", - bbox=dict(boxstyle="round", fc="w", ec="k"), - horizontalalignment="left", - arrowprops=dict(arrowstyle="fancy", facecolor="w", connectionstyle="arc3,rad=0.35"), -) - -cb = m.add_colorbar(label="The Data", tick_precision=1) -m.add_logo() diff --git a/docs/examples/example_row_col_selector.py b/docs/examples/example_row_col_selector.py deleted file mode 100644 index 92402448d..000000000 --- a/docs/examples/example_row_col_selector.py +++ /dev/null @@ -1,113 +0,0 @@ -# EOmaps example: Select 1D slices of a 2D dataset - -from eomaps import Maps -import numpy as np - -# setup some random 2D data -lon, lat = np.meshgrid(np.linspace(-180, 180, 200), np.linspace(-90, 90, 100)) -data = np.sqrt(lon**2 + lat**2) + np.random.normal(size=lat.shape) ** 2 * 20 -name = "some parameter" -# ---------------------- - -# create a new map spanning the left row of a 2x2 grid -m = Maps(crs=Maps.CRS.InterruptedGoodeHomolosine(), ax=(2, 2, (1, 3)), figsize=(8, 5)) -m.add_feature.preset.coastline() -m.set_data(data, lon, lat, parameter=name) -m.set_classify_specs(Maps.CLASSIFIERS.NaturalBreaks, k=5) -m.plot_map() - -# create 2 ordinary matplotlib axes to show the selected data -ax_row = m.f.add_subplot(222) # 2x2 grid, top right -ax_row.set_xlabel("Longitude") -ax_row.set_ylabel(name) -ax_row.set_xlim(-185, 185) -ax_row.set_ylim(data.min(), data.max()) - -ax_col = m.f.add_subplot(224) # 2x2 grid, bottom right -ax_col.set_xlabel("Latitude") -ax_col.set_ylabel(name) -ax_col.set_xlim(-92.5, 92.5) -ax_col.set_ylim(data.min(), data.max()) - -# add a colorbar for the data -m.add_colorbar(label=name) -m.colorbar.ax_cb.tick_params(rotation=90) # rotate colorbar ticks 90° - -# add new layers to plot row- and column data -m2 = m.new_layer() -m2.set_shape.ellipses(m.shape.radius) - -m3 = m.new_layer() -m3.set_shape.ellipses(m.shape.radius) - - -# define a custom callback to indicate the clicked row/column -def cb(m, ind, ID, *args, **kwargs): - # get row and column from the data - # NOTE: "ind" always represents the index of the flattened array! - r, c = np.unravel_index(ind, m.data.shape) - - # ---- highlight the picked column - # use "dynamic=True" to avoid re-drawing the background on each pick - # use "set_extent=False" to avoid resetting the plot extent on each draw - m2.set_data(m.data_specs.data[:, c], m.data_specs.x[:, c], m.data_specs.y[:, c]) - m2.plot_map(fc="none", ec="b", set_extent=False, dynamic=True) - - # ---- highlight the picked row - m3.set_data(m.data_specs.data[r, :], m.data_specs.x[r, :], m.data_specs.y[r, :]) - m3.plot_map(fc="none", ec="r", set_extent=False, dynamic=True) - - # ---- plot the data for the selected column - (art0,) = ax_col.plot(m.data_specs.y[:, c], m.data_specs.data[:, c], c="b") - (art01,) = ax_col.plot( - m.data_specs.y[r, c], - m.data_specs.data[r, c], - c="k", - marker="o", - markerfacecolor="none", - ms=10, - ) - - # ---- plot the data for the selected row - (art1,) = ax_row.plot(m.data_specs.x[r, :], m.data_specs.data[r, :], c="r") - (art11,) = ax_row.plot( - m.data_specs.x[r, c], - m.data_specs.data[r, c], - c="k", - marker="o", - markerfacecolor="none", - ms=10, - ) - - # make all artists temporary (e.g. remove them on next pick) - # "m2.coll" represents the collection created by "m2.plot_map()" - for a in [art0, art01, art1, art11, m2.coll, m3.coll]: - m.cb.pick.add_temporary_artist(a) - - -# attach the custom callback -m.cb.pick.attach(cb, m=m) - -# ---- add a pick-annotation with a custom text -def text(ind, val, **kwargs): - r, c = np.unravel_index(ind, m.data.shape) - return ( - f"row/col = {r}/{c}\n" - f"lon/lat = {m.data_specs.x[r, c]:.2f}/{m.data_specs.y[r, c]:.2f}\n" - f"val = {val:.2f}" - ) - - -m.cb.pick.attach.annotate(text=text, fontsize=7) - -# apply a previously arranged layout (e.g. check "layout-editor" in the docs!) -m.apply_layout( - { - "figsize": [8, 5], - "0_map": [0.015, 0.49253, 0.51, 0.35361], - "1_": [0.60375, 0.592, 0.38, 0.392], - "2_": [0.60375, 0.096, 0.38, 0.392], - "3_cb": [0.025, 0.144, 0.485, 0.28], - "3_cb_histogram_size": 0.8, - } -) diff --git a/docs/examples/example_scalebars.py b/docs/examples/example_scalebars.py deleted file mode 100644 index 03af40a59..000000000 --- a/docs/examples/example_scalebars.py +++ /dev/null @@ -1,43 +0,0 @@ -# EOmaps example: Adding scalebars - what about distances? -from eomaps import Maps - -m = Maps(figsize=(9, 5)) -m.add_feature.preset.ocean(ec="k", scale="110m") - -s1 = m.add_scalebar((0, 45), 30, scale=10e5, n=8, preset="kr") - -s2 = m.add_scalebar( - (-11, -50), - -45, - scale=5e5, - n=10, - scale_props=dict(width=5, colors=("k", ".25", ".5", ".75", ".95")), - patch_props=dict(offsets=(1, 1.4, 1, 1), fc=(0.7, 0.8, 0.3, 1)), - label_props=dict( - offset=0.5, scale=1.4, every=5, weight="bold" # , family="Calibri" - ), -) - -s3 = m.add_scalebar( - (-120, -20), - 0, - scale=5e5, - n=10, - scale_props=dict(width=3, colors=(*["w", "darkred"] * 2, *["w"] * 5, "darkred")), - patch_props=dict(fc=(0.25, 0.25, 0.25, 0.8), ec="k", lw=0.5, offsets=(1, 1, 1, 2)), - label_props=dict( - every=(1, 4, 10), color="w", rotation=45, weight="bold" # , family="Impact" - ), - line_props=dict(color="w"), -) - -# it's also possible to update the properties of an existing scalebar -# via the setter-functions! -s4 = m.add_scalebar(n=10, preset="bw") -s4.set_scale_props(width=3, colors=[(1, 0.6, 0), (0, 0.5, 0.5)]) -s4.set_label_props(every=2) - -# NOTE that the last scalebar (s4) is automatically re-scaled and re-positioned -# on zoom events (the default if you don't provide an explicit scale & position)! - -m.add_logo() diff --git a/docs/examples/example_timeseries.py b/docs/examples/example_timeseries.py deleted file mode 100644 index 092c7e421..000000000 --- a/docs/examples/example_timeseries.py +++ /dev/null @@ -1,100 +0,0 @@ -# EOmaps example: Data analysis widgets - Interacting with a database - -from eomaps import Maps -import pandas as pd -import numpy as np - -# ============== create a random database ============= -length, Nlon, Nlat = 1000, 100, 50 -lonmin, lonmax, latmin, latmax = -70, 175, 0, 75 - -database = np.full((Nlon * Nlat, length), np.nan) -for i in range(Nlon * Nlat): - size = np.random.randint(1, length) - x = np.random.normal(loc=np.random.rand(), scale=np.random.rand(), size=size) - np.put(database, range(i * length, i * length + size), x) -lon, lat = np.meshgrid( - np.linspace(lonmin, lonmax, Nlon), np.linspace(latmin, latmax, Nlat) -) - -IDs = [f"point_{i}" for i in range(Nlon * Nlat)] -database = pd.DataFrame(database, index=IDs) -coords = pd.DataFrame(dict(lon=lon.flat, lat=lat.flat), index=IDs) - -# -------- calculate the number of values in each dataset -# (e.g. the data actually shown on the map) -data = pd.DataFrame(dict(count=database.count(axis=1), **coords)) -# ===================================================== - - -# initialize a map on top -m = Maps(ax=211) -m.add_feature.preset.ocean() -m.add_feature.preset.coastline() - -# initialize 2 matplotlib plot-axes below the map -ax_left = m.f.add_subplot(223) -ax_left.set_ylabel("data-values") -ax_left.set_xlabel("data-index") - -ax_right = m.f.add_subplot(224) -ax_right.set_ylabel("data-values") -ax_right.set_xlabel("histogram count") - -ax_left.sharey(ax_right) - -# -------- assign data to the map and plot it -m.set_data(data=data, x="lon", y="lat", crs=4326) -m.set_classify_specs( - scheme=Maps.CLASSIFIERS.UserDefined, - bins=[50, 100, 200, 400, 800], -) -m.set_shape.ellipses(radius=0.5) -m.plot_map() - - -# -------- define a custom callback function to update the plots -def update_plots(ID, **kwargs): - # get the data - x = database.loc[ID].dropna() - - # plot the lines and histograms - (l,) = ax_left.plot(x, lw=0.5, marker=".", c="C0") - cnt, val, art = ax_right.hist(x.values, bins=50, orientation="horizontal", fc="C0") - - # re-compute axis limits based on the new artists - ax_left.relim() - ax_right.relim() - ax_left.autoscale() - ax_right.autoscale() - - # add all artists as "temporary pick artists" so that they - # are removed when the next datapoint is selected - for a in [l, *art]: - m.cb.pick.add_temporary_artist(a) - - -# attach the custom callback (and some pre-defined callbacks) -m.cb.pick.attach(update_plots) -m.cb.pick.attach.annotate() -m.cb.pick.attach.mark(permanent=False, buffer=1, fc="none", ec="r") -m.cb.pick.attach.mark(permanent=False, buffer=2, fc="none", ec="r", ls=":") - -# add a colorbar -m.add_colorbar(0.25, label="Number of observations") -m.colorbar.ax_cb_plot.tick_params(labelsize=6) - -# add a logo -m.add_logo() - -m.apply_layout( - { - "figsize": [6.4, 4.8], - "0_map": [0.05625, 0.60894, 0.8875, 0.36594], - "1_": [0.12326, 0.11123, 0.35, 0.31667], - "2_": [0.58674, 0.11123, 0.35, 0.31667], - "3_cb": [0.12, 0.51667, 0.82, 0.06166], - "3_cb_histogram_size": 0.8, - "4_logo": [0.8125, 0.62333, 0.1212, 0.06667], - } -) diff --git a/docs/examples/example_vector_data.py b/docs/examples/example_vector_data.py deleted file mode 100644 index 2cb03523e..000000000 --- a/docs/examples/example_vector_data.py +++ /dev/null @@ -1,63 +0,0 @@ -# EOmaps example: Using geopandas - interactive shapes! - -from eomaps import Maps -import pandas as pd -import numpy as np - -# geopandas is used internally... the import is just here to show that! -import geopandas as gpd - -# ----------- create some example-data -lon, lat = np.meshgrid(np.linspace(-180, 180, 25), np.linspace(-90, 90, 25)) -data = pd.DataFrame( - dict(lon=lon.flat, lat=lat.flat, data=np.sqrt(lon**2 + lat**2).flat) -) - -# setup 2 maps with different projections next to each other -m = Maps(ax=121, crs=4326, figsize=(10, 5)) -m2 = Maps(f=m.f, ax=122, crs=Maps.CRS.Orthographic(45, 45)) - -# assign data to the Maps objects -m.set_data(data=data.sample(100), x="lon", y="lat", crs=4326, parameter="data") -m2.set_data(data=data, x="lon", y="lat", crs=4326) - -# fetch data (incl. metadata) for the "admin_0_countries" NaturalEarth feature -countries = m.add_feature.cultural.admin_0_countries.get_gdf(scale=50) - -for m_i in [m, m2]: - m_i.add_feature.preset.ocean() - - m_i.add_gdf( - countries, - picker_name="countries", - pick_method="contains", - val_key="NAME", - fc="none", - ec="k", - lw=0.5, - ) - - m_i.set_shape.rectangles(radius=3, radius_crs=4326) - m_i.plot_map(alpha=0.75, ec=(1, 1, 1, 0.5)) - - # attach a callback to highlite the rectangles - m_i.cb.pick.attach.mark(shape="rectangles", fc="none", ec="b", lw=2) - - # attach a callback to highlite the countries and indicate the names - picker = m_i.cb.pick["countries"] - picker.attach.highlight_geometry(fc="r", ec="k", lw=0.5, alpha=0.5) - picker.attach.annotate(text=lambda val, **kwargs: str(val)) - -# share pick events between the maps-objects -m.cb.pick.share_events(m2) -m.cb.pick["countries"].share_events(m2) - -m.add_logo() -m.apply_layout( - { - "figsize": [10.0, 5.0], - "0_map": [0.005, 0.25114, 0.5, 0.5], - "1_map": [0.5125, 0.0375, 0.475, 0.95], - "2_logo": [0.875, 0.01, 0.12, 0.09901], - } -) diff --git a/docs/examples/example_webmaps.py b/docs/examples/example_webmaps.py deleted file mode 100644 index 6d2456a8f..000000000 --- a/docs/examples/example_webmaps.py +++ /dev/null @@ -1,85 +0,0 @@ -# EOmaps example: WebMap services and layer-switching - -from eomaps import Maps -import numpy as np -import pandas as pd - -# ------ create some data -------- -lon, lat = np.meshgrid(np.linspace(-50, 50, 150), np.linspace(30, 60, 150)) -data = pd.DataFrame( - dict(lon=lon.flat, lat=lat.flat, data=np.sqrt(lon**2 + lat**2).flat) -) -# -------------------------------- - -m = Maps(Maps.CRS.GOOGLE_MERCATOR, layer="S1GBM_vv", figsize=(9, 4)) -# set the crs to GOOGLE_MERCATOR to avoid reprojecting the WebMap data -# (makes it a lot faster and it will also look much nicer!) - -# add S1GBM WebMap to the layer of this Maps-object -m.add_wms.S1GBM.add_layer.vv() - -# add OpenStreetMap on the currently invisible layer (OSM) -m.add_wms.OpenStreetMap.add_layer.default(layer="OSM") - -# create a new layer named "data" and plot some data -m2 = m.new_layer(layer="data") -m2.set_data(data=data.sample(5000), x="lon", y="lat", crs=4326) -m2.set_shape.geod_circles(radius=20000) -m2.plot_map() - - -# -------- CALLBACKS ---------- -# (use m.all to execute independent of the visible layer) -# on a left-click, show layers ("data", "OSM") in a rectangle -m.all.cb.click.attach.peek_layer(layer="OSM|data", how=0.2) - -# on a right-click, "swipe" the layers ("S1GBM_vv" and "data") from the left -m.all.cb.click.attach.peek_layer( - layer="S1GBM_vv|data", - how="left", - button=3, -) - -# switch between the layers by pressing the keys 0, 1 and 2 -m.all.cb.keypress.attach.switch_layer(layer="S1GBM_vv", key="0") -m.all.cb.keypress.attach.switch_layer(layer="OSM", key="1") -m.all.cb.keypress.attach.switch_layer(layer="data", key="2") - -# add a pick callback that is only executed if the "data" layer is visible -m2.cb.pick.attach.annotate(zorder=100) # use a high zorder to put it on top - -# ------ UTILITY WIDGETS -------- -# add a clickable widget to switch between layers -m.util.layer_selector( - loc="upper left", - ncol=3, - bbox_to_anchor=(0.01, 0.99), - layers=["OSM", "S1GBM_vv", "data"], -) -# add a slider to switch between layers -s = m.util.layer_slider( - pos=(0.5, 0.93, 0.38, 0.025), - color="r", - handle_style=dict(facecolor="r"), - txt_patch_props=dict(fc="w", ec="none", alpha=0.75, boxstyle="round, pad=.25"), -) - -# explicitly set the layers you want to use in the slider -# (Note: you can also use combinations of multiple existing layers!) -s.set_layers(["OSM", "S1GBM_vv", "data", "OSM|data{0.5}"]) - -# ------------------------------ - -m.add_logo() - -m.apply_layout( - { - "figsize": [9.0, 4.0], - "0_map": [0.00625, 0.01038, 0.9875, 0.97924], - "1_slider": [0.45, 0.93, 0.38, 0.025], - "2_logo": [0.865, 0.02812, 0.12, 0.11138], - } -) - -# fetch all layers before startup so that they are already cached -m.fetch_layers() diff --git a/docs/index.rst b/docs/index.rst deleted file mode 100644 index 139e19504..000000000 --- a/docs/index.rst +++ /dev/null @@ -1,305 +0,0 @@ -.. include:: substitutions.rst - -.. image:: _static/logo.png - :width: 40% - :align: center - :target: https://github.com/raphaelquast/EOmaps - -.. raw:: html - -
- Welcome to the documentation for EOmaps! -

- - -Here you can find detailed explanations on all the features and functionalities of EOmaps. - - -.. card:: Found a bug or got an idea for an interesting feature? - - .. grid:: 1 1 2 2 - :gutter: 1 - :margin: 0 - - .. grid-item-card:: :doc:`contribute` - :link: contribute - :link-type: doc - - Get all information you need to start contributing to EOmaps! - - .. grid-item-card:: GitHub - :link: https://github.com/raphaelquast/EOmaps/ - :link-type: url - - Open an issue or start a discussion on GitHub! - - - -Getting Started ---------------- - -To get started, have a look at the :doc:`installation` instructions and the :doc:`api_basics` section to learn how to use EOmaps! - -In addition, there is also the :doc:`api_companion_widget` GUI that can be used to interactively edit/compare/overlay maps and explore the features and functionalities. - - -Data Visualization ------------------- - -Want to visualize some data? Have a look at the :doc:`api_data_visualization` section to learn how to create beautiful maps with your datasets! - -EOmaps provides a variety of plot-shapes so you can select a shape that suits the structure, size and spatial representativeness of your data: - - .. include:: _shape_table.rst - -Map Features ------------- - -EOmaps provides many useful tools to customize your maps. - -.. grid:: 1 1 1 2 - :gutter: 1 - - .. grid-item-card:: :doc:`notebooks/naturalearth_features` - :link: notebooks/naturalearth_features - :link-type: doc - :shadow: none - - Add basic map features (coastlines, ocean-coloring etc. ) to the map. - - .. grid-item-card:: :doc:`api_webmaps` - :link: api_webmaps - :link-type: doc - :shadow: none - - Add imagery provided by WebMap services to the map. - - .. grid-item-card:: :doc:`notebooks/inset_maps` - :link: notebooks/inset_maps - :link-type: doc - :shadow: none - - Create zoomed-in views on specific regions of a map. - - .. grid-item-card:: :doc:`api_vector_data` - :link: api_vector_data - :link-type: doc - :shadow: none - - Add vector geometries to the map. - - .. grid-item-card:: :doc:`api_annotations_markers_etc` - :link: api_annotations_markers_etc - :link-type: doc - :shadow: none - - Add markers, annotations, lines, logos etc. to the map. - - .. grid-item-card:: :doc:`api_scalebar` - :link: api_scalebar - :link-type: doc - :shadow: none - - Add a scalebar to the map. - - .. grid-item-card:: :doc:`api_compass` - :link: api_compass - :link-type: doc - :shadow: none - - Add a compass (or North Arrow) to the map. - - .. grid-item-card:: :doc:`api_gridlines` - :link: api_gridlines - :link-type: doc - :shadow: none - - Add grid-lines (and optionally grid-labels) to the map. - - -Interactivity -------------- - -With a few lines of code, you can turn your maps into interactive data-analysis widgets! - -.. grid:: 1 1 1 2 - :gutter: 1 - - .. grid-item-card:: :doc:`api_companion_widget` - :link: api_companion_widget - :link-type: doc - :shadow: none - - A graphical user-interface to interact with the map. - - .. grid-item-card:: :doc:`api_callbacks` - :link: api_callbacks - :link-type: doc - :shadow: none - - Turn your maps into interactive data-analysis widgets. - - .. grid-item-card:: :doc:`api_layout_editor` - :link: api_layout_editor - :link-type: doc - :shadow: none - - Interactively re-arrange and re-size axes of a figure. - - .. grid-item-card:: :doc:`api_draw` - :link: api_draw - :link-type: doc - :shadow: none - - Interactively draw geometries on a map and export them as shapefiles. - - .. grid-item-card:: :doc:`api_utils` - :link: api_utils - :link-type: doc - :shadow: none - - A collection of utility widgets (layer-sliders, layer-selectors) - - .. grid-item-card:: :doc:`notebooks/widgets` - :link: notebooks/widgets - :link-type: doc - :shadow: none - - A collection of Jupyter Widgets (for Jupyter Notebooks) - - - - - -Miscellaneous -~~~~~~~~~~~~~ - -.. grid:: 1 1 1 2 - :gutter: 1 - - .. grid-item-card:: :doc:`api_logging` - :link: api_logging - :link-type: doc - :shadow: none - - Details on logging. - - .. grid-item-card:: :doc:`api_command_line_interface` - :link: api_command_line_interface - :link-type: doc - :shadow: none - - How to use the ``eomaps`` command-line interface. - - .. grid-item-card:: :doc:`api_read_data` - :link: api_companion_widget - :link-type: doc - :shadow: none - - Read data from NetCDF, GeoTIFF or CSV files. - - .. grid-item-card:: :doc:`api_misc` - :link: api_misc - :link-type: doc - :shadow: none - - Additional functions and properties that might come in handy. - - -API Reference -~~~~~~~~~~~~~ - -.. grid:: 1 1 1 2 - :gutter: 1 - - .. grid-item-card:: EOmaps API reference - :link: api/reference - :link-type: doc - :shadow: none - - Detailed information on the API of EOmaps. - - -Examples --------- - -Make sure to check out the :doc:`Examples ` for an overview of the capabilities (incl. source code)! - -.. include:: example_galery.rst - -.. toctree:: - :hidden: - :maxdepth: 1 - :caption: General - - installation - FAQ - -.. toctree:: - :hidden: - :maxdepth: 2 - :caption: Contribute - - contribute - -.. toctree:: - :hidden: - :maxdepth: 1 - :caption: How to use EOmaps - - api_basics - api_data_visualization - -.. toctree:: - :hidden: - :maxdepth: 1 - :caption: Map Features - - notebooks/inset_maps.ipynb - notebooks/naturalearth_features.ipynb - api_webmaps - api_vector_data - - api_annotations_markers_etc - api_scalebar - api_compass - api_gridlines - -.. toctree:: - :hidden: - :maxdepth: 1 - :caption: Interactivity - - api_callbacks - api_companion_widget - api_layout_editor - api_draw - notebooks/widgets.ipynb - api_utils - - -.. toctree:: - :hidden: - :maxdepth: 1 - :caption: Miscellaneous - - api_read_data - api_command_line_interface - api_logging - api_misc - - -.. toctree:: - :hidden: - :maxdepth: 2 - :caption: Examples - - EOmaps_examples - - -.. toctree:: - :hidden: - :maxdepth: 1 - :caption: API Reference - - api/reference diff --git a/docs/installation.md b/docs/installation.md deleted file mode 100644 index b0a249653..000000000 --- a/docs/installation.md +++ /dev/null @@ -1,84 +0,0 @@ -(installation)= -# 🐛 Installation - -```{contents} Contents: -:local: -:depth: 1 -``` - -The following sections provide information how to install **EOmaps**. - -- A quick tutorial on how to **get started from scratch** is available here: {ref}`quickstart_guide` -- More details on how to **configure your favorite IDE** to get the most out of **EOmaps** can be found in the {ref}`faq` section {ref}`configuring_the_editor`. -- If you want to know how to setup **EOmaps** for development, have a look at the {ref}`contribute` - - -## Installation with ``conda`` or ``mamba`` - -EOmaps is available on [conda-forge](https://anaconda.org/conda-forge/eomaps) and can be installed via: - -``` -conda install -c conda-forge eomaps -``` - -This will install all required dependencies as well as the optional dependencies ``pandas``, ``geopandas``, ``mapclassify``, ``datashader``, ``owslib``, ``requests`` and ``qtpy`` - - -:::{dropdown} Greatly speed up the installation! -:color: info -:icon: info -:open: -:margin: 3 - -Since the dependencies of EOmaps can be demanding to solve for ``conda``, it is **highly recommended** -that you use [mamba](https://github.com/mamba-org/mamba) to install EOmaps in a conda-environment! - -[mamba](https://github.com/mamba-org/mamba) is a reimplementation of the conda package manager in C++, capable of solving environments a lot faster. - -The recommended way to get started is to use [miniforge](https://github.com/conda-forge/miniforge), a minimalistic installer -that provides both ``conda`` and ``mamba``, pre-configured to use the ``conda-forge`` channel by default. -For other options, checkout the [mamba-docs](https://mamba.readthedocs.io/en/latest/installation/mamba-installation.html) - -Once ``mamba`` is installed, you just need to replace the term ``conda`` with ``mamba`` and you're good to go! - -``` -mamba install -c conda-forge eomaps -``` - -::: - - -## Installation with ``pip`` - - -EOmaps is also available on [pypi](https://pypi.org/project/eomaps/). - -To install EOmaps with a **minimal set of dependencies**, use: - -``` -pip install eomaps -``` - -### Optional dependencies - - -Some features ({ref}`webmap_layers`, {ref}`companion_widget`, etc.) require additional dependencies. -To use them you have to install the required dependency-groups: - -To get **all features of EOmaps**, you can use one of: - -- ``pip install eomaps[all]`` Install **ALL** requuired and optional dependencies -- ``pip install eomaps[all_nogui]`` Same as ``all`` but without installing the ``Qt`` GUI framework - - -In addition, you can use the following dependency-groups to activate only selected features: - -- ``pip install eomaps[wms]`` Add dependencies required to use {ref}`WebMap services ` -- ``pip install eomaps[gui]`` Add dependencies for ``Qt`` GUI framework (and the {ref}`CompanionWidget `) -- ``pip install eomaps[io]`` Add support for ``pandas``, ``xarray``, ``geopandas`` and ``rioxarray`` -- ``pip install eomaps[shade]`` Add capabilities to visualize extremely large datasets (via ``datashader``) -- ``pip install eomaps[classify]`` Add support for ``mapclassify`` to classify datasets - -It is also possible to combine dependency-groups, e.g.: ``pip install eomaps[wms, gui]``. - -A full list of all associated packages can be found in {ref}`setup_a_dev_env` or in the ``pyproject.toml`` file. diff --git a/docs/make.bat b/docs/make.bat old mode 100644 new mode 100755 index 3d64bb3aa..a84bd5ff5 --- a/docs/make.bat +++ b/docs/make.bat @@ -7,7 +7,7 @@ REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) -set SOURCEDIR=. +set SOURCEDIR=source set BUILDDIR=build if "%1" == "" goto help @@ -31,5 +31,9 @@ goto end :help %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +:clean +del /s /q %BUILDDIR%\* %SOURCEDIR%\auto_examples\* %SOURCEDIR%\generated\* + + :end popd diff --git a/docs/make2.bat b/docs/make2.bat old mode 100644 new mode 100755 index 3baa47fad..acb241398 --- a/docs/make2.bat +++ b/docs/make2.bat @@ -7,7 +7,7 @@ REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) -set SOURCEDIR=. +set SOURCEDIR=source set BUILDDIR=build if "%1" == "" goto help diff --git a/docs/FAQ.rst b/docs/source/FAQ.rst old mode 100644 new mode 100755 similarity index 100% rename from docs/FAQ.rst rename to docs/source/FAQ.rst diff --git a/docs/_shape_table.rst b/docs/source/_shape_table.rst old mode 100644 new mode 100755 similarity index 61% rename from docs/_shape_table.rst rename to docs/source/_shape_table.rst index 5a99687b5..074dbf6a2 --- a/docs/_shape_table.rst +++ b/docs/source/_shape_table.rst @@ -3,56 +3,56 @@ :gutter: 1 .. grid-item-card:: Ellipses - :img-bottom: _static/shape_imgs/ellipses.png + :img-bottom: /_static/shape_imgs/ellipses.png :link: shp_ellipses :link-type: ref .. grid-item-card:: Rectangles - :img-bottom: _static/shape_imgs/rectangles.png + :img-bottom: /_static/shape_imgs/rectangles.png :link: shp_rectangles :link-type: ref .. grid-item-card:: Geodesic Circles - :img-bottom: _static/shape_imgs/geod_circles.png + :img-bottom: /_static/shape_imgs/geod_circles.png :link: shp_geod_circles :link-type: ref .. grid-item-card:: Scatter Points - :img-bottom: _static/shape_imgs/scatter_points.png + :img-bottom: /_static/shape_imgs/scatter_points.png :link: shp_scatter :link-type: ref .. grid-item-card:: Contour - :img-bottom: _static/shape_imgs/contour_unfilled_filled.png + :img-bottom: /_static/shape_imgs/contour_unfilled_filled.png :link: shp_contour :link-type: ref .. grid-item-card:: Hexbin - :img-bottom: _static/shape_imgs/hexbin.png + :img-bottom: /_static/shape_imgs/hexbin.png :link: shp_hexbin :link-type: ref .. grid-item-card:: Voronoi Diagram - :img-bottom: _static/shape_imgs/voronoi_diagram.png + :img-bottom: /_static/shape_imgs/voronoi_diagram.png :link: shp_voronoi :link-type: ref .. grid-item-card:: Delaunay Triangulation - :img-bottom: _static/shape_imgs/delaunay_triangulation.png + :img-bottom: /_static/shape_imgs/delaunay_triangulation.png :link: shp_delaunay :link-type: ref .. grid-item-card:: Raster - :img-bottom: _static/shape_imgs/raster.png + :img-bottom: /_static/shape_imgs/raster.png :link: shp_raster :link-type: ref .. grid-item-card:: Shade Raster - :img-bottom: _static/shape_imgs/shade_raster.png + :img-bottom: /_static/shape_imgs/shade_raster.png :link: shp_shade_raster :link-type: ref .. grid-item-card:: Shade Points - :img-bottom: _static/shape_imgs/shade_points.png + :img-bottom: /_static/shape_imgs/shade_points.png :link: shp_shade_points :link-type: ref diff --git a/docs/source/_static/css/custom.css b/docs/source/_static/css/custom.css new file mode 100644 index 000000000..62b415824 --- /dev/null +++ b/docs/source/_static/css/custom.css @@ -0,0 +1,16 @@ +/* This file is used to override the default PyData theme styles */ + +html { + --pst-font-size-base: 17px; +} + +.bd-content { + flex-grow: 1; + max-width: 100%; +} +.bd-page-width { + max-width: 100rem; +} +.bd-main .bd-article-container { + max-width: 100%; +} diff --git a/docs/source/_static/css/gallery.css b/docs/source/_static/css/gallery.css new file mode 100644 index 000000000..73d329a7e --- /dev/null +++ b/docs/source/_static/css/gallery.css @@ -0,0 +1,18 @@ +/* This file is used to style the gallery page */ + +.myst-gallery-grid-item .sd-card-img-top { + margin: 10px auto; + background: none !important; + width: 90%; + display: block; +} +.myst-gallery-grid-item .sd-card-body { + margin: 1px auto; + display: block; +} + +.myst-gallery-grid-item .sd-card-title .reference { + color: var(--pst-color-on-surface); + font-size: var(--pst-font-size-h7); + font-weight: lighter; +} diff --git a/docs/_static/data_visualization/colorbar.gif b/docs/source/_static/data_visualization/colorbar.gif similarity index 100% rename from docs/_static/data_visualization/colorbar.gif rename to docs/source/_static/data_visualization/colorbar.gif diff --git a/docs/_static/data_visualization/colorbar_ticks.png b/docs/source/_static/data_visualization/colorbar_ticks.png similarity index 100% rename from docs/_static/data_visualization/colorbar_ticks.png rename to docs/source/_static/data_visualization/colorbar_ticks.png diff --git a/docs/_static/data_visualization/customize_plot.png b/docs/source/_static/data_visualization/customize_plot.png similarity index 100% rename from docs/_static/data_visualization/customize_plot.png rename to docs/source/_static/data_visualization/customize_plot.png diff --git a/docs/_static/data_visualization/dynamic_colorbar.gif b/docs/source/_static/data_visualization/dynamic_colorbar.gif similarity index 100% rename from docs/_static/data_visualization/dynamic_colorbar.gif rename to docs/source/_static/data_visualization/dynamic_colorbar.gif diff --git a/docs/_static/data_visualization/explicit_colors.png b/docs/source/_static/data_visualization/explicit_colors.png similarity index 100% rename from docs/_static/data_visualization/explicit_colors.png rename to docs/source/_static/data_visualization/explicit_colors.png diff --git a/docs/_static/data_visualization/plot_data.png b/docs/source/_static/data_visualization/plot_data.png similarity index 100% rename from docs/_static/data_visualization/plot_data.png rename to docs/source/_static/data_visualization/plot_data.png diff --git a/docs/_static/data_visualization/rgba_composite.png b/docs/source/_static/data_visualization/rgba_composite.png similarity index 100% rename from docs/_static/data_visualization/rgba_composite.png rename to docs/source/_static/data_visualization/rgba_composite.png diff --git a/docs/_static/data_visualization/uniform_colors.png b/docs/source/_static/data_visualization/uniform_colors.png similarity index 100% rename from docs/_static/data_visualization/uniform_colors.png rename to docs/source/_static/data_visualization/uniform_colors.png diff --git a/docs/source/_static/example_images/example_RGB.png b/docs/source/_static/example_images/example_RGB.png new file mode 100644 index 000000000..51ed3afc0 Binary files /dev/null and b/docs/source/_static/example_images/example_RGB.png differ diff --git a/docs/_static/example_images/example_basics.gif b/docs/source/_static/example_images/example_basics.gif similarity index 100% rename from docs/_static/example_images/example_basics.gif rename to docs/source/_static/example_images/example_basics.gif diff --git a/docs/_static/example_images/example_callbacks.gif b/docs/source/_static/example_images/example_callbacks.gif similarity index 100% rename from docs/_static/example_images/example_callbacks.gif rename to docs/source/_static/example_images/example_callbacks.gif diff --git a/docs/_static/example_images/example_contour.png b/docs/source/_static/example_images/example_contour.png similarity index 100% rename from docs/_static/example_images/example_contour.png rename to docs/source/_static/example_images/example_contour.png diff --git a/docs/_static/example_images/example_customization.png b/docs/source/_static/example_images/example_customization.png similarity index 100% rename from docs/_static/example_images/example_customization.png rename to docs/source/_static/example_images/example_customization.png diff --git a/docs/_static/example_images/example_gridlines.png b/docs/source/_static/example_images/example_gridlines.png similarity index 100% rename from docs/_static/example_images/example_gridlines.png rename to docs/source/_static/example_images/example_gridlines.png diff --git a/docs/_static/example_images/example_inset_maps.png b/docs/source/_static/example_images/example_inset_maps.png similarity index 100% rename from docs/_static/example_images/example_inset_maps.png rename to docs/source/_static/example_images/example_inset_maps.png diff --git a/docs/source/_static/example_images/example_lines.png b/docs/source/_static/example_images/example_lines.png new file mode 100644 index 000000000..932891497 Binary files /dev/null and b/docs/source/_static/example_images/example_lines.png differ diff --git a/docs/_static/example_images/example_multiple_maps.gif b/docs/source/_static/example_images/example_multiple_maps.gif similarity index 100% rename from docs/_static/example_images/example_multiple_maps.gif rename to docs/source/_static/example_images/example_multiple_maps.gif diff --git a/docs/_static/example_images/example_overlays.gif b/docs/source/_static/example_images/example_overlays.gif similarity index 100% rename from docs/_static/example_images/example_overlays.gif rename to docs/source/_static/example_images/example_overlays.gif diff --git a/docs/_static/example_images/example_row_col_selector.gif b/docs/source/_static/example_images/example_row_col_selector.gif similarity index 100% rename from docs/_static/example_images/example_row_col_selector.gif rename to docs/source/_static/example_images/example_row_col_selector.gif diff --git a/docs/_static/example_images/example_scalebars.gif b/docs/source/_static/example_images/example_scalebars.gif similarity index 100% rename from docs/_static/example_images/example_scalebars.gif rename to docs/source/_static/example_images/example_scalebars.gif diff --git a/docs/_static/example_images/example_timeseries.gif b/docs/source/_static/example_images/example_timeseries.gif similarity index 100% rename from docs/_static/example_images/example_timeseries.gif rename to docs/source/_static/example_images/example_timeseries.gif diff --git a/docs/_static/example_images/example_vector_data.gif b/docs/source/_static/example_images/example_vector_data.gif similarity index 100% rename from docs/_static/example_images/example_vector_data.gif rename to docs/source/_static/example_images/example_vector_data.gif diff --git a/docs/_static/example_images/example_webmaps.gif b/docs/source/_static/example_images/example_webmaps.gif similarity index 100% rename from docs/_static/example_images/example_webmaps.gif rename to docs/source/_static/example_images/example_webmaps.gif diff --git a/docs/source/_static/example_images/inset_maps/inset_maps1.png b/docs/source/_static/example_images/inset_maps/inset_maps1.png new file mode 100644 index 000000000..f375c698a Binary files /dev/null and b/docs/source/_static/example_images/inset_maps/inset_maps1.png differ diff --git a/docs/source/_static/example_images/inset_maps/inset_maps2.png b/docs/source/_static/example_images/inset_maps/inset_maps2.png new file mode 100644 index 000000000..fe427456d Binary files /dev/null and b/docs/source/_static/example_images/inset_maps/inset_maps2.png differ diff --git a/docs/source/_static/example_images/inset_maps/inset_maps3.png b/docs/source/_static/example_images/inset_maps/inset_maps3.png new file mode 100644 index 000000000..a72e751ec Binary files /dev/null and b/docs/source/_static/example_images/inset_maps/inset_maps3.png differ diff --git a/docs/source/_static/example_images/naturalearth_features/output1.png b/docs/source/_static/example_images/naturalearth_features/output1.png new file mode 100644 index 000000000..3ee0f078d Binary files /dev/null and b/docs/source/_static/example_images/naturalearth_features/output1.png differ diff --git a/docs/source/_static/example_images/naturalearth_features/output2.png b/docs/source/_static/example_images/naturalearth_features/output2.png new file mode 100644 index 000000000..cf8824ee4 Binary files /dev/null and b/docs/source/_static/example_images/naturalearth_features/output2.png differ diff --git a/docs/source/_static/example_images/naturalearth_features/output3.png b/docs/source/_static/example_images/naturalearth_features/output3.png new file mode 100644 index 000000000..39fa4d120 Binary files /dev/null and b/docs/source/_static/example_images/naturalearth_features/output3.png differ diff --git a/docs/source/_static/example_images/naturalearth_features/output4.png b/docs/source/_static/example_images/naturalearth_features/output4.png new file mode 100644 index 000000000..70b953275 Binary files /dev/null and b/docs/source/_static/example_images/naturalearth_features/output4.png differ diff --git a/docs/source/_static/example_images/naturalearth_features/output5.png b/docs/source/_static/example_images/naturalearth_features/output5.png new file mode 100644 index 000000000..d036e496f Binary files /dev/null and b/docs/source/_static/example_images/naturalearth_features/output5.png differ diff --git a/docs/_static/grids/grid1.png b/docs/source/_static/grids/grid1.png similarity index 100% rename from docs/_static/grids/grid1.png rename to docs/source/_static/grids/grid1.png diff --git a/docs/_static/grids/grid2.png b/docs/source/_static/grids/grid2.png similarity index 100% rename from docs/_static/grids/grid2.png rename to docs/source/_static/grids/grid2.png diff --git a/docs/_static/grids/grid3.png b/docs/source/_static/grids/grid3.png similarity index 100% rename from docs/_static/grids/grid3.png rename to docs/source/_static/grids/grid3.png diff --git a/docs/_static/grids/grid4.png b/docs/source/_static/grids/grid4.png similarity index 100% rename from docs/_static/grids/grid4.png rename to docs/source/_static/grids/grid4.png diff --git a/docs/_static/grids/grid5.png b/docs/source/_static/grids/grid5.png similarity index 100% rename from docs/_static/grids/grid5.png rename to docs/source/_static/grids/grid5.png diff --git a/docs/_static/grids/grid6.png b/docs/source/_static/grids/grid6.png similarity index 100% rename from docs/_static/grids/grid6.png rename to docs/source/_static/grids/grid6.png diff --git a/docs/_static/grids/grid7.png b/docs/source/_static/grids/grid7.png similarity index 100% rename from docs/_static/grids/grid7.png rename to docs/source/_static/grids/grid7.png diff --git a/docs/_static/inset_maps.png b/docs/source/_static/inset_maps.png similarity index 100% rename from docs/_static/inset_maps.png rename to docs/source/_static/inset_maps.png diff --git a/docs/_static/intro.png b/docs/source/_static/intro.png similarity index 100% rename from docs/_static/intro.png rename to docs/source/_static/intro.png diff --git a/docs/_static/logo.png b/docs/source/_static/logo.png similarity index 100% rename from docs/_static/logo.png rename to docs/source/_static/logo.png diff --git a/docs/_static/minigifs/add_feature.gif b/docs/source/_static/minigifs/add_feature.gif similarity index 100% rename from docs/_static/minigifs/add_feature.gif rename to docs/source/_static/minigifs/add_feature.gif diff --git a/docs/_static/minigifs/add_gdf_pick.gif b/docs/source/_static/minigifs/add_gdf_pick.gif similarity index 100% rename from docs/_static/minigifs/add_gdf_pick.gif rename to docs/source/_static/minigifs/add_gdf_pick.gif diff --git a/docs/_static/minigifs/add_wms.png b/docs/source/_static/minigifs/add_wms.png similarity index 100% rename from docs/_static/minigifs/add_wms.png rename to docs/source/_static/minigifs/add_wms.png diff --git a/docs/_static/minigifs/advanced_wms.gif b/docs/source/_static/minigifs/advanced_wms.gif similarity index 100% rename from docs/_static/minigifs/advanced_wms.gif rename to docs/source/_static/minigifs/advanced_wms.gif diff --git a/docs/_static/minigifs/annotations.png b/docs/source/_static/minigifs/annotations.png similarity index 100% rename from docs/_static/minigifs/annotations.png rename to docs/source/_static/minigifs/annotations.png diff --git a/docs/_static/minigifs/basics_first_map.png b/docs/source/_static/minigifs/basics_first_map.png similarity index 100% rename from docs/_static/minigifs/basics_first_map.png rename to docs/source/_static/minigifs/basics_first_map.png diff --git a/docs/_static/minigifs/classify_data_01.png b/docs/source/_static/minigifs/classify_data_01.png similarity index 100% rename from docs/_static/minigifs/classify_data_01.png rename to docs/source/_static/minigifs/classify_data_01.png diff --git a/docs/_static/minigifs/colorbar.png b/docs/source/_static/minigifs/colorbar.png similarity index 100% rename from docs/_static/minigifs/colorbar.png rename to docs/source/_static/minigifs/colorbar.png diff --git a/docs/_static/minigifs/companion_widget.gif b/docs/source/_static/minigifs/companion_widget.gif similarity index 100% rename from docs/_static/minigifs/companion_widget.gif rename to docs/source/_static/minigifs/companion_widget.gif diff --git a/docs/_static/minigifs/companion_widget_feature_info.gif b/docs/source/_static/minigifs/companion_widget_feature_info.gif similarity index 100% rename from docs/_static/minigifs/companion_widget_feature_info.gif rename to docs/source/_static/minigifs/companion_widget_feature_info.gif diff --git a/docs/_static/minigifs/compass.gif b/docs/source/_static/minigifs/compass.gif similarity index 100% rename from docs/_static/minigifs/compass.gif rename to docs/source/_static/minigifs/compass.gif diff --git a/docs/_static/minigifs/draw_shapes.gif b/docs/source/_static/minigifs/draw_shapes.gif similarity index 100% rename from docs/_static/minigifs/draw_shapes.gif rename to docs/source/_static/minigifs/draw_shapes.gif diff --git a/docs/_static/minigifs/dynamic_axes_updates.gif b/docs/source/_static/minigifs/dynamic_axes_updates.gif similarity index 100% rename from docs/_static/minigifs/dynamic_axes_updates.gif rename to docs/source/_static/minigifs/dynamic_axes_updates.gif diff --git a/docs/_static/minigifs/grid_01.png b/docs/source/_static/minigifs/grid_01.png similarity index 100% rename from docs/_static/minigifs/grid_01.png rename to docs/source/_static/minigifs/grid_01.png diff --git a/docs/_static/minigifs/grid_labels_01.png b/docs/source/_static/minigifs/grid_labels_01.png similarity index 100% rename from docs/_static/minigifs/grid_labels_01.png rename to docs/source/_static/minigifs/grid_labels_01.png diff --git a/docs/_static/minigifs/indicate_extent.png b/docs/source/_static/minigifs/indicate_extent.png similarity index 100% rename from docs/_static/minigifs/indicate_extent.png rename to docs/source/_static/minigifs/indicate_extent.png diff --git a/docs/_static/minigifs/inset_maps.png b/docs/source/_static/minigifs/inset_maps.png similarity index 100% rename from docs/_static/minigifs/inset_maps.png rename to docs/source/_static/minigifs/inset_maps.png diff --git a/docs/_static/minigifs/layer_selector.gif b/docs/source/_static/minigifs/layer_selector.gif similarity index 100% rename from docs/_static/minigifs/layer_selector.gif rename to docs/source/_static/minigifs/layer_selector.gif diff --git a/docs/_static/minigifs/layout_editor.gif b/docs/source/_static/minigifs/layout_editor.gif similarity index 100% rename from docs/_static/minigifs/layout_editor.gif rename to docs/source/_static/minigifs/layout_editor.gif diff --git a/docs/_static/minigifs/lines.png b/docs/source/_static/minigifs/lines.png similarity index 100% rename from docs/_static/minigifs/lines.png rename to docs/source/_static/minigifs/lines.png diff --git a/docs/_static/minigifs/logos.png b/docs/source/_static/minigifs/logos.png similarity index 100% rename from docs/_static/minigifs/logos.png rename to docs/source/_static/minigifs/logos.png diff --git a/docs/_static/minigifs/markers.png b/docs/source/_static/minigifs/markers.png similarity index 100% rename from docs/_static/minigifs/markers.png rename to docs/source/_static/minigifs/markers.png diff --git a/docs/_static/minigifs/pick_multi.gif b/docs/source/_static/minigifs/pick_multi.gif similarity index 100% rename from docs/_static/minigifs/pick_multi.gif rename to docs/source/_static/minigifs/pick_multi.gif diff --git a/docs/_static/minigifs/plot_shapes.gif b/docs/source/_static/minigifs/plot_shapes.gif similarity index 100% rename from docs/_static/minigifs/plot_shapes.gif rename to docs/source/_static/minigifs/plot_shapes.gif diff --git a/docs/_static/minigifs/rearrange_layers.gif b/docs/source/_static/minigifs/rearrange_layers.gif similarity index 100% rename from docs/_static/minigifs/rearrange_layers.gif rename to docs/source/_static/minigifs/rearrange_layers.gif diff --git a/docs/_static/minigifs/scalebar.gif b/docs/source/_static/minigifs/scalebar.gif similarity index 100% rename from docs/_static/minigifs/scalebar.gif rename to docs/source/_static/minigifs/scalebar.gif diff --git a/docs/_static/minigifs/select_layers_dropdown.gif b/docs/source/_static/minigifs/select_layers_dropdown.gif similarity index 100% rename from docs/_static/minigifs/select_layers_dropdown.gif rename to docs/source/_static/minigifs/select_layers_dropdown.gif diff --git a/docs/_static/minigifs/simple_callbacks.gif b/docs/source/_static/minigifs/simple_callbacks.gif similarity index 100% rename from docs/_static/minigifs/simple_callbacks.gif rename to docs/source/_static/minigifs/simple_callbacks.gif diff --git a/docs/_static/pycharm_preferences.png b/docs/source/_static/pycharm_preferences.png similarity index 100% rename from docs/_static/pycharm_preferences.png rename to docs/source/_static/pycharm_preferences.png diff --git a/docs/_static/pycharm_preferences_2.png b/docs/source/_static/pycharm_preferences_2.png similarity index 100% rename from docs/_static/pycharm_preferences_2.png rename to docs/source/_static/pycharm_preferences_2.png diff --git a/docs/_static/shape_imgs/contour.png b/docs/source/_static/shape_imgs/contour.png similarity index 100% rename from docs/_static/shape_imgs/contour.png rename to docs/source/_static/shape_imgs/contour.png diff --git a/docs/_static/shape_imgs/contour_filled.png b/docs/source/_static/shape_imgs/contour_filled.png similarity index 100% rename from docs/_static/shape_imgs/contour_filled.png rename to docs/source/_static/shape_imgs/contour_filled.png diff --git a/docs/_static/shape_imgs/contour_unfilled_filled.png b/docs/source/_static/shape_imgs/contour_unfilled_filled.png similarity index 100% rename from docs/_static/shape_imgs/contour_unfilled_filled.png rename to docs/source/_static/shape_imgs/contour_unfilled_filled.png diff --git a/docs/_static/shape_imgs/delaunay_triangulation.png b/docs/source/_static/shape_imgs/delaunay_triangulation.png similarity index 100% rename from docs/_static/shape_imgs/delaunay_triangulation.png rename to docs/source/_static/shape_imgs/delaunay_triangulation.png diff --git a/docs/_static/shape_imgs/delaunay_triangulation_flat.png b/docs/source/_static/shape_imgs/delaunay_triangulation_flat.png similarity index 100% rename from docs/_static/shape_imgs/delaunay_triangulation_flat.png rename to docs/source/_static/shape_imgs/delaunay_triangulation_flat.png diff --git a/docs/_static/shape_imgs/ellipses.png b/docs/source/_static/shape_imgs/ellipses.png similarity index 100% rename from docs/_static/shape_imgs/ellipses.png rename to docs/source/_static/shape_imgs/ellipses.png diff --git a/docs/_static/shape_imgs/geod_circles.png b/docs/source/_static/shape_imgs/geod_circles.png similarity index 100% rename from docs/_static/shape_imgs/geod_circles.png rename to docs/source/_static/shape_imgs/geod_circles.png diff --git a/docs/_static/shape_imgs/hexbin.png b/docs/source/_static/shape_imgs/hexbin.png similarity index 100% rename from docs/_static/shape_imgs/hexbin.png rename to docs/source/_static/shape_imgs/hexbin.png diff --git a/docs/_static/shape_imgs/raster.png b/docs/source/_static/shape_imgs/raster.png similarity index 100% rename from docs/_static/shape_imgs/raster.png rename to docs/source/_static/shape_imgs/raster.png diff --git a/docs/_static/shape_imgs/rectangles.png b/docs/source/_static/shape_imgs/rectangles.png similarity index 100% rename from docs/_static/shape_imgs/rectangles.png rename to docs/source/_static/shape_imgs/rectangles.png diff --git a/docs/_static/shape_imgs/scatter_points.png b/docs/source/_static/shape_imgs/scatter_points.png similarity index 100% rename from docs/_static/shape_imgs/scatter_points.png rename to docs/source/_static/shape_imgs/scatter_points.png diff --git a/docs/_static/shape_imgs/shade_points.png b/docs/source/_static/shape_imgs/shade_points.png similarity index 100% rename from docs/_static/shape_imgs/shade_points.png rename to docs/source/_static/shape_imgs/shade_points.png diff --git a/docs/_static/shape_imgs/shade_raster.png b/docs/source/_static/shape_imgs/shade_raster.png similarity index 100% rename from docs/_static/shape_imgs/shade_raster.png rename to docs/source/_static/shape_imgs/shade_raster.png diff --git a/docs/_static/shape_imgs/voronoi_diagram.png b/docs/source/_static/shape_imgs/voronoi_diagram.png similarity index 100% rename from docs/_static/shape_imgs/voronoi_diagram.png rename to docs/source/_static/shape_imgs/voronoi_diagram.png diff --git a/docs/_static/shapes_decision_tree.png b/docs/source/_static/shapes_decision_tree.png similarity index 100% rename from docs/_static/shapes_decision_tree.png rename to docs/source/_static/shapes_decision_tree.png diff --git a/docs/_static/spyder_preferences.png b/docs/source/_static/spyder_preferences.png similarity index 100% rename from docs/_static/spyder_preferences.png rename to docs/source/_static/spyder_preferences.png diff --git a/docs/_static/widget_annotations.png b/docs/source/_static/widget_annotations.png similarity index 100% rename from docs/_static/widget_annotations.png rename to docs/source/_static/widget_annotations.png diff --git a/docs/_static/widget_callbacks.png b/docs/source/_static/widget_callbacks.png similarity index 100% rename from docs/_static/widget_callbacks.png rename to docs/source/_static/widget_callbacks.png diff --git a/docs/_templates/custom-class-template.rst b/docs/source/_templates/custom-class-template.rst similarity index 100% rename from docs/_templates/custom-class-template.rst rename to docs/source/_templates/custom-class-template.rst diff --git a/docs/_templates/custom-module-template.rst b/docs/source/_templates/custom-module-template.rst similarity index 100% rename from docs/_templates/custom-module-template.rst rename to docs/source/_templates/custom-module-template.rst diff --git a/docs/_templates/obj_with_attributes_no_toc.rst b/docs/source/_templates/obj_with_attributes_no_toc.rst similarity index 91% rename from docs/_templates/obj_with_attributes_no_toc.rst rename to docs/source/_templates/obj_with_attributes_no_toc.rst index 150a7262c..a3bfe2c93 100644 --- a/docs/_templates/obj_with_attributes_no_toc.rst +++ b/docs/source/_templates/obj_with_attributes_no_toc.rst @@ -1,4 +1,4 @@ -{{ name | escape | underline}} +{{ objname | escape | underline}} .. currentmodule:: {{ module }} diff --git a/docs/_templates/only_names_in_toc.rst b/docs/source/_templates/only_names_in_toc.rst similarity index 100% rename from docs/_templates/only_names_in_toc.rst rename to docs/source/_templates/only_names_in_toc.rst diff --git a/docs/api/eomaps.eomaps.Maps.rst b/docs/source/api/eomaps.eomaps.Maps.rst old mode 100644 new mode 100755 similarity index 100% rename from docs/api/eomaps.eomaps.Maps.rst rename to docs/source/api/eomaps.eomaps.Maps.rst diff --git a/docs/api/eomaps.inset_maps.InsetMaps.rst b/docs/source/api/eomaps.inset_maps.InsetMaps.rst old mode 100644 new mode 100755 similarity index 100% rename from docs/api/eomaps.inset_maps.InsetMaps.rst rename to docs/source/api/eomaps.inset_maps.InsetMaps.rst diff --git a/docs/api/reference.rst b/docs/source/api/reference.rst old mode 100644 new mode 100755 similarity index 79% rename from docs/api/reference.rst rename to docs/source/api/reference.rst index 2824254fe..4d1ca93ff --- a/docs/api/reference.rst +++ b/docs/source/api/reference.rst @@ -1,10 +1,13 @@ -====== -eomaps -====== +============= +API Reference +============= .. currentmodule:: eomaps.eomaps -.. card:: The Maps object +The Maps object +=============== + +.. card:: :link: ../generated/eomaps.eomaps.Maps :link-type: doc @@ -22,14 +25,20 @@ Feature objects =============== +InsetMaps +--------- + .. card:: :shadow: none - .. card:: InsetMaps + .. card:: :link: ../generated/eomaps.inset_maps.InsetMaps :link-type: doc :margin: 0 + :py:class:`InsetMaps` objects highlight specific areas on a map. + + .. currentmodule:: eomaps.inset_maps .. autosummary:: @@ -46,14 +55,19 @@ Feature objects See :py:meth:`Maps.new_inset_map` on how to create a new InsetMap! +ColorBar +-------- + .. card:: :shadow: none - .. card:: ColorBar + .. card:: :link: ../generated/eomaps.colorbar.ColorBar :link-type: doc :margin: 0 + :py:class:`ColorBar` objects visualize the color- and value-distribution of plotted datasets. + .. currentmodule:: eomaps.colorbar .. autosummary:: @@ -69,16 +83,23 @@ Feature objects See :py:meth:`Maps.add_colorbar` on how to add a colorbar to a map! + +GridLines +--------- + .. card:: :shadow: none - .. card:: GridLines + .. card:: :link: ../generated/eomaps.grid.GridLines :link-type: doc :margin: 0 .. currentmodule:: eomaps.grid + :py:class:`GridLines` objects add lon/lat grids to a map. + + .. autosummary:: :toctree: ../generated :template: custom-class-template.rst @@ -93,16 +114,22 @@ Feature objects See :py:meth:`Maps.add_gridlines` on how to add grid-lines to a map! +Compass +------- + .. card:: :shadow: none - .. card:: Compass + .. card:: :link: ../generated/eomaps.compass.Compass :link-type: doc :margin: 0 .. currentmodule:: eomaps.compass + :py:class:`Compass` objects indicate the cardinal directions for a point on a map. + + .. autosummary:: :toctree: ../generated :template: custom-class-template.rst @@ -116,16 +143,22 @@ Feature objects See :py:meth:`Maps.add_compass` on how to add a compass (or North-arrow) to a map! +Scalebar +-------- + .. card:: :shadow: none - .. card:: ScaleBar + .. card:: :link: ../generated/eomaps.scalebar.ScaleBar :link-type: doc :margin: 0 .. currentmodule:: eomaps.scalebar + :py:class:`ScaleBar` objects measure distances on a map. + + .. autosummary:: :toctree: ../generated :template: custom-class-template.rst diff --git a/docs/conf.py b/docs/source/conf.py old mode 100644 new mode 100755 similarity index 71% rename from docs/conf.py rename to docs/source/conf.py index 712aab14d..6bcca147d --- a/docs/conf.py +++ b/docs/source/conf.py @@ -1,5 +1,7 @@ # Configuration file for the Sphinx documentation builder. -import sys, os +import os +import sys + from docutils.nodes import reference from eomaps import Maps @@ -8,10 +10,39 @@ sys.path.insert(0, os.path.abspath("..")) sys.path.insert(0, os.path.abspath(".")) -from gen_autodoc_file import make_feature_toctree_file +examples_dir = "../../examples" +gallery_dir = "./auto_examples" + + +from docs.source.gen_autodoc_file import make_feature_toctree_file # noqa: E402 make_feature_toctree_file() +####### Generate gallery ####### +from pathlib import Path # noqa: E402 + +from myst_sphinx_gallery import ( # noqa: E402 + GalleryConfig, + GridItemCard, + ThumbnailConfig, +) + +myst_gallery_grid_item = GridItemCard() +myst_gallery_grid_item.add_option("class-item", "myst-gallery-grid-item") + + +myst_sphinx_gallery_config = GalleryConfig( + examples_dirs="../../examples", + gallery_dirs="auto_examples", + root_dir=Path(__file__).parent, + notebook_thumbnail_strategy="code", + thumbnail_config=ThumbnailConfig( + max_animation_frames=80, + operation_kwargs={"color": "white"}, + ), + grid_item_card=myst_gallery_grid_item, +) + def mpl_rc_role_subst(name, rawtext, text, lineno, inliner, options={}, content=[]): """ @@ -87,9 +118,10 @@ def setup(app): "sphinx.ext.napoleon", "sphinx.ext.intersphinx", "sphinx_copybutton", - "sphinx_rtd_theme", - "myst_nb", + "pydata_sphinx_theme", "sphinx_design", + "myst_nb", + "myst_sphinx_gallery", ] @@ -104,10 +136,40 @@ def setup(app): templates_path = ["_templates"] html_static_path = ["_static"] -html_css_files = ["custom_css.css"] - -html_theme = "sphinx_rtd_theme" +html_css_files = ["css/custom.css", "css/gallery.css"] + +# PyData theme options +html_theme = "pydata_sphinx_theme" +html_logo = "../../logos/EO_Maps_Logo_V6.png" + +# hide left sidebar for orphan pages +html_sidebars = { + "contribute/contribute": [], + "installation": [], + "FAQ": [], + "api/reference": [], +} +html_theme_options = { + "collapse_navigation": False, + "show_nav_level": 2, + "show_toc_level": 2, + "header_links_before_dropdown": 10, + "icon_links": [ + { + "name": "GitHub", + "url": "https://github.com/raphaelquast/EOmaps", + "icon": "fa-brands fa-square-github", + "type": "fontawesome", + }, + { + "name": "PyPI", + "url": "https://pypi.org/project/eomaps/", + "icon": "fa-brands fa-python", + "type": "fontawesome", + }, + ], +} autosummary_generate = ["api/autodoc_additional_props.rst"] # "full_reference.rst", @@ -138,6 +200,14 @@ def autodoc_skip_member(app, what, name, obj, skip, options): napoleon_type_aliases = None napoleon_attr_annotations = True +# Support for notebook formats other than .ipynb + +source_suffix = { + ".rst": "restructuredtext", + ".md": "myst-nb", + ".myst": "myst-nb", +} + myst_update_mathjax = False # to use single $x^2$ for equations myst_render_markdown_format = "myst" # to parse markdown output with MyST parser myst_enable_extensions = ["dollarmath", "colon_fence"] @@ -157,9 +227,3 @@ def autodoc_skip_member(app, what, name, obj, skip, options): # (check "make_feature_toctree_file()" for more details. "api/eomaps.*", ] - -source_suffix = { - ".rst": "restructuredtext", - ".ipynb": "myst-nb", - ".myst": "myst-nb", -} diff --git a/docs/contribute.rst b/docs/source/contribute/contribute.rst old mode 100644 new mode 100755 similarity index 99% rename from docs/contribute.rst rename to docs/source/contribute/contribute.rst index 3eaa12248..3c7194803 --- a/docs/contribute.rst +++ b/docs/source/contribute/contribute.rst @@ -70,7 +70,7 @@ You can set up ``python`` with the following steps: Content of ``environment.yml``: -.. literalinclude:: ../environment.yml +.. literalinclude:: ../../../environment.yml diff --git a/docs/gen_autodoc_file.py b/docs/source/gen_autodoc_file.py old mode 100644 new mode 100755 similarity index 96% rename from docs/gen_autodoc_file.py rename to docs/source/gen_autodoc_file.py index dd680900c..3d5e79f20 --- a/docs/gen_autodoc_file.py +++ b/docs/source/gen_autodoc_file.py @@ -71,6 +71,7 @@ def make_feature_toctree_file(): "set_shape", "draw", "from_file", + "new_layer_from_file", "read_file", "util", "add_wms", @@ -122,7 +123,11 @@ def make_feature_toctree_file(): s += get_autosummary( "eomaps.widgets", - [i for i in get_members(widgets) if not i.rsplit(".", 1)[-1][0].islower()], + [ + i.rsplit(".", 1)[1] + for i in get_members(widgets) + if not i.rsplit(".", 1)[-1][0].islower() + ], ) basepath = Path(__file__).parent diff --git a/docs/source/index.rst b/docs/source/index.rst new file mode 100755 index 000000000..cd4b838af --- /dev/null +++ b/docs/source/index.rst @@ -0,0 +1,242 @@ +.. include:: substitutions.rst + +============================================ +Welcome to the documentation for **EOmaps**! +============================================ + +Here you can find detailed explanations on all the features and functionalities of EOmaps. + + +.. card:: Found a bug or got an idea for an interesting feature? + + .. grid:: 1 1 2 2 + :gutter: 1 + :margin: 0 + + .. grid-item-card:: :doc:`contribute/contribute` + :link: contribute/contribute + :link-type: doc + + Get all information you need to start contributing to EOmaps! + + .. grid-item-card:: GitHub + :link: https://github.com/raphaelquast/EOmaps/ + :link-type: url + + Open an issue or start a discussion on GitHub! + + + +Getting Started +--------------- + +To get started, have a look at the :doc:`installation` instructions and the :doc:`user_guide/how_to_use/api_basics` section to learn how to use EOmaps! + +In addition, there is also the :doc:`user_guide/interactivity/api_companion_widget` GUI that can be used to interactively edit/compare/overlay maps and explore the features and functionalities. + + +Data Visualization +------------------ + +Want to visualize some data? Have a look at the :doc:`user_guide/how_to_use/api_data_visualization` section to learn how to create beautiful maps with your datasets! + +EOmaps provides a variety of plot-shapes so you can select a shape that suits the structure, size and spatial representativeness of your data: + +.. include:: _shape_table.rst + +Map Features +------------ + +EOmaps provides many useful tools to customize your maps. + +.. grid:: 1 1 1 2 + :gutter: 1 + + .. grid-item-card:: :doc:`user_guide/map_features/naturalearth_features` + :link: user_guide/map_features/naturalearth_features + :link-type: doc + :shadow: none + + Add basic map features (coastlines, ocean-coloring etc. ) to the map. + + .. grid-item-card:: :doc:`user_guide/map_features/api_webmaps` + :link: user_guide/map_features/api_webmaps + :link-type: doc + :shadow: none + + Add imagery provided by WebMap services to the map. + + .. grid-item-card:: :doc:`user_guide/map_features/inset_maps` + :link: user_guide/map_features/inset_maps + :link-type: doc + :shadow: none + + Create zoomed-in views on specific regions of a map. + + .. grid-item-card:: :doc:`user_guide/map_features/api_vector_data` + :link: user_guide/map_features/api_vector_data + :link-type: doc + :shadow: none + + Add vector geometries to the map. + + .. grid-item-card:: :doc:`user_guide/map_features/api_annotations_markers_etc` + :link: user_guide/map_features/api_annotations_markers_etc + :link-type: doc + :shadow: none + + Add markers, annotations, lines, logos etc. to the map. + + .. grid-item-card:: :doc:`user_guide/map_features/api_scalebar` + :link: user_guide/map_features/api_scalebar + :link-type: doc + :shadow: none + + Add a scalebar to the map. + + .. grid-item-card:: :doc:`user_guide/map_features/api_compass` + :link: user_guide/map_features/api_compass + :link-type: doc + :shadow: none + + Add a compass (or North Arrow) to the map. + + .. grid-item-card:: :doc:`user_guide/map_features/api_gridlines` + :link: user_guide/map_features/api_gridlines + :link-type: doc + :shadow: none + + Add grid-lines (and optionally grid-labels) to the map. + + +Interactivity +------------- + +With a few lines of code, you can turn your maps into interactive data-analysis widgets! + +.. grid:: 1 1 1 2 + :gutter: 1 + + .. grid-item-card:: :doc:`user_guide/interactivity/api_companion_widget` + :link: user_guide/interactivity/api_companion_widget + :link-type: doc + :shadow: none + + A graphical user-interface to interact with the map. + + .. grid-item-card:: :doc:`user_guide/interactivity/api_callbacks` + :link: user_guide/interactivity/api_callbacks + :link-type: doc + :shadow: none + + Turn your maps into interactive data-analysis widgets. + + .. grid-item-card:: :doc:`user_guide/interactivity/api_layout_editor` + :link: user_guide/interactivity/api_layout_editor + :link-type: doc + :shadow: none + + Interactively re-arrange and re-size axes of a figure. + + .. grid-item-card:: :doc:`user_guide/interactivity/api_draw` + :link: user_guide/interactivity/api_draw + :link-type: doc + :shadow: none + + Interactively draw geometries on a map and export them as shapefiles. + + .. grid-item-card:: :doc:`user_guide/interactivity/api_utils` + :link: user_guide/interactivity/api_utils + :link-type: doc + :shadow: none + + A collection of utility widgets (layer-sliders, layer-selectors) + + .. grid-item-card:: :doc:`user_guide/interactivity/widgets` + :link: user_guide/interactivity/widgets + :link-type: doc + :shadow: none + + A collection of Jupyter Widgets (for Jupyter Notebooks) + + + + + +Miscellaneous +------------- + +.. grid:: 1 1 1 2 + :gutter: 1 + + .. grid-item-card:: :doc:`user_guide/miscellaneous/api_logging` + :link: user_guide/miscellaneous/api_logging + :link-type: doc + :shadow: none + + Details on logging. + + .. grid-item-card:: :doc:`user_guide/miscellaneous/api_command_line_interface` + :link: user_guide/miscellaneous/api_command_line_interface + :link-type: doc + :shadow: none + + How to use the ``eomaps`` command-line interface. + + .. grid-item-card:: :doc:`user_guide/miscellaneous/api_read_data` + :link: user_guide/miscellaneous/api_read_data + :link-type: doc + :shadow: none + + Read data from NetCDF, GeoTIFF or CSV files. + + .. grid-item-card:: :doc:`user_guide/miscellaneous/api_misc` + :link: user_guide/miscellaneous/api_misc + :link-type: doc + :shadow: none + + Additional functions and properties that might come in handy. + + +API Reference +------------- + +.. grid:: 1 1 1 2 + :gutter: 1 + + .. grid-item-card:: EOmaps API reference + :link: api/reference + :link-type: doc + :shadow: none + + Detailed information on the API of EOmaps. + + +Examples +-------- + +Make sure to check out the :doc:`Examples ` for an overview of the capabilities (incl. source code)! + +.. grid:: 1 1 1 2 + :gutter: 1 + + .. grid-item-card:: EOmaps Examples + :link: auto_examples/index + :link-type: doc + :shadow: none + + Detailed examples of EOmaps. + + + +.. toctree:: + :hidden: + :maxdepth: 1 + :caption: General + + Installation + user_guide/index + Examples + api/reference + Contribution Guide + FAQ diff --git a/docs/source/installation.rst b/docs/source/installation.rst new file mode 100644 index 000000000..784b95954 --- /dev/null +++ b/docs/source/installation.rst @@ -0,0 +1,78 @@ +.. _installation: + +=============== +🐛 Installation +=============== + +.. contents:: Contents: + :local: + :depth: 1 + +The following sections provide information how to install **EOmaps**. + +- A quick tutorial on how to **get started from scratch** is available here: :ref:`quickstart_guide` +- More details on how to **configure your favorite IDE** to get the most out of **EOmaps** can be found in the :ref:`faq` section :ref:`configuring_the_editor`. +- If you want to know how to setup **EOmaps** for development, have a look at the :ref:`contribute` + +Installation with ``conda`` or ``mamba`` +---------------------------------------- + +EOmaps is available on `conda-forge `_ and can be installed via: + +.. code-block:: bash + + conda install -c conda-forge eomaps + +This will install all required dependencies as well as the optional dependencies ``pandas``, ``geopandas``, ``mapclassify``, ``datashader``, ``owslib``, ``requests`` and ``qtpy`` + +.. admonition:: Greatly speed up the installation! + :class: dropdown + + Since the dependencies of EOmaps can be demanding to solve for ``conda``, it is **highly recommended** + that you use `mamba `_ to install EOmaps in a conda-environment! + + `mamba `_ is a reimplementation of the conda package manager in C++, capable of solving environments a lot faster. + + The recommended way to get started is to use `miniforge `_, a minimalistic installer + that provides both ``conda`` and ``mamba``, pre-configured to use the ``conda-forge`` channel by default. + For other options, checkout the `mamba-docs `_ + + Once ``mamba`` is installed, you just need to replace the term ``conda`` with ``mamba`` and you're good to go! + + .. code-block:: bash + + mamba install -c conda-forge eomaps + +Installation with ``pip`` +------------------------- + +EOmaps is also available on `pypi `_. + +To install EOmaps with a **minimal set of dependencies**, use: + +.. code-block:: bash + + pip install eomaps + +Optional dependencies +^^^^^^^^^^^^^^^^^^^^^ + +Some features (:ref:`webmap_layers`, :ref:`companion_widget`, etc.) require additional dependencies. +To use them you have to install the required dependency-groups: + +To get **all features of EOmaps**, you can use one of: + +- ``pip install eomaps[all]`` Install **ALL** required and optional dependencies +- ``pip install eomaps[all_nogui]`` Same as ``all`` but without installing the ``Qt`` GUI framework + +In addition, you can use the following dependency-groups to activate only selected features: + +- ``pip install eomaps[wms]`` Add dependencies required to use :ref:`WebMap services ` +- ``pip install eomaps[gui]`` Add dependencies for ``Qt`` GUI framework (and the :ref:`CompanionWidget `) +- ``pip install eomaps[io]`` Add support for ``pandas``, ``xarray``, ``geopandas`` and ``rioxarray`` +- ``pip install eomaps[shade]`` Add capabilities to visualize extremely large datasets (via ``datashader``) +- ``pip install eomaps[classify]`` Add support for ``mapclassify`` to classify datasets + +It is also possible to combine dependency-groups, e.g.: ``pip install eomaps[wms, gui]``. + +A full list of all associated packages can be found in :ref:`setup_a_dev_env` or in the ``pyproject.toml`` file. diff --git a/docs/quickstart_quide.rst b/docs/source/quickstart_quide.rst old mode 100644 new mode 100755 similarity index 100% rename from docs/quickstart_quide.rst rename to docs/source/quickstart_quide.rst diff --git a/docs/substitutions.rst b/docs/source/substitutions.rst old mode 100644 new mode 100755 similarity index 100% rename from docs/substitutions.rst rename to docs/source/substitutions.rst diff --git a/docs/api_basics.rst b/docs/source/user_guide/how_to_use/api_basics.rst old mode 100644 new mode 100755 similarity index 95% rename from docs/api_basics.rst rename to docs/source/user_guide/how_to_use/api_basics.rst index 5005cf936..ab5574710 --- a/docs/api_basics.rst +++ b/docs/source/user_guide/how_to_use/api_basics.rst @@ -45,7 +45,7 @@ for a map. .. grid-item:: :columns: 8 8 8 4 - .. image:: _static/minigifs/basics_first_map.png + .. image:: /_static/minigifs/basics_first_map.png - ``crs`` represents the projection used for plotting @@ -90,7 +90,7 @@ A :py:class:`Maps` object represents one (or more) of the following things **on You can create as many layers as you need! The following image explains how it works in general: -.. image:: _static/intro.png +.. image:: /_static/intro.png :width: 70% .. dropdown:: Creating new layers @@ -201,8 +201,8 @@ You can create as many layers as you need! The following image explains how it w .. _combine_layers: -🗗 Combine & compare multiple layers -************************************ +🗗 Combine & Compare +******************** All maps of a figure always show **the same visible layer**. @@ -219,7 +219,7 @@ The visible layer can be a **single layer-name**, or a **combination of multiple - Click on a single layer to make it the visible layer. - Hold down ``control`` or ``shift`` to overlay multiple layers. - .. image:: _static/minigifs/select_layers_dropdown.gif + .. image:: /_static/minigifs/select_layers_dropdown.gif | @@ -229,7 +229,7 @@ The visible layer can be a **single layer-name**, or a **combination of multiple - Hold down ``shift`` while clicking on a tab to overlay multiple layers. - Re-arrange the tabs to change the stacking-order of the layers. - .. image:: _static/minigifs/rearrange_layers.gif + .. image:: /_static/minigifs/rearrange_layers.gif .. dropdown:: Programmatically switch/overlay layers :icon: info @@ -323,8 +323,8 @@ The visible layer can be a **single layer-name**, or a **combination of multiple Maps.fetch_layers -Image export (jpeg, png, svg, etc.) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Image export (jpeg, png, svg ...) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Once the map is ready, an image of the map can be saved at any time by using :py:meth:`Maps.savefig` @@ -389,8 +389,9 @@ To adjust the margins of the subplots, use :py:meth:`m.subplots_adjust`, or have .. _multiple_maps: -Multiple Maps (and/or plots) in one figure -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Multiple Maps/Plots in one Figure +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ It is possible to combine multiple ``EOmaps`` maps and/or ordinary ``matpltolib`` plots in one figure. @@ -456,7 +457,7 @@ To position the map in a (virtual) grid, one of the following options are possib .. grid-item-card:: :columns: 6 6 6 4 - :img-background: _static/grids/grid1.png + :img-background: /_static/grids/grid1.png .. grid:: 1 1 1 2 @@ -480,7 +481,7 @@ To position the map in a (virtual) grid, one of the following options are possib .. grid-item-card:: :columns: 6 6 6 4 - :img-background: _static/grids/grid2.png + :img-background: /_static/grids/grid2.png .. grid:: 1 1 1 2 @@ -504,7 +505,7 @@ To position the map in a (virtual) grid, one of the following options are possib .. grid-item-card:: :columns: 6 6 6 4 - :img-background: _static/grids/grid3.png + :img-background: /_static/grids/grid3.png @@ -531,7 +532,7 @@ To position the map in a (virtual) grid, one of the following options are possib .. grid-item-card:: :columns: 6 6 6 4 - :img-background: _static/grids/grid4.png + :img-background: /_static/grids/grid4.png .. grid:: 1 1 1 2 @@ -553,7 +554,7 @@ To position the map in a (virtual) grid, one of the following options are possib .. grid-item-card:: :columns: 6 6 6 4 - :img-background: _static/grids/grid5.png + :img-background: /_static/grids/grid5.png @@ -577,7 +578,7 @@ To position the map in a (virtual) grid, one of the following options are possib .. grid-item-card:: :columns: 6 6 6 4 - :img-background: _static/grids/grid6.png + :img-background: /_static/grids/grid6.png Absolute positioning @@ -611,11 +612,11 @@ To set the absolute position of the map, provide a list of 4 floats representing .. grid-item-card:: :columns: 6 6 6 4 - :img-background: _static/grids/grid7.png + :img-background: /_static/grids/grid7.png -Using already existing figures / axes -************************************* +Using existing figures / axes +***************************** It is also possible to insert an EOmaps map into an existing figure or reuse an existing axes. @@ -637,8 +638,8 @@ It is also possible to insert an EOmaps map into an existing figure or reuse an m = Maps(f=f, ax=ax) -Dynamic updates of plots in the same figure -******************************************* +Dynamic updates of figures +************************** As soon as a :py:class:`Maps`-object is attached to a figure, EOmaps will handle re-drawing of the figure! Therefore **dynamically updated** artists must be added to the "blit-manager" (``m.BM``) to ensure @@ -698,7 +699,7 @@ Here's an example to show how it works: .. grid-item:: - .. image:: _static/minigifs/dynamic_axes_updates.gif + .. image:: /_static/minigifs/dynamic_axes_updates.gif MapsGrid objects @@ -837,8 +838,10 @@ The individual :py:class:`Maps` objects and ``matpltolib-Axes`` are then accessi MapsGrid.add_gdf -Naming conventions and autocompletion -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Syntax and Autocompletion +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. currentmodule:: eomaps.eomaps The goal of EOmaps is to provide a comprehensive, yet easy-to-use interface. @@ -867,8 +870,9 @@ Once a few basics keywords have been remembered, finding the right functions and The following list provides an overview of the naming-conventions used within EOmaps: -Add features to a map - "m.add\_" -********************************* + +Add features - "m.add\_" +************************ All functions that add features to a map start with ``add_``, e.g.: - :py:class:`Maps.add_feature`, :py:class:`Maps.add_wms`, :py:meth:`Maps.add_annotation`, :py:meth:`Maps.add_marker`, :py:meth:`Maps.add_gdf`, ... @@ -884,17 +888,17 @@ The used convention is the following: - ``m.add_wms.OpenStreetMap.add_layer.default()`` - ``m.add_wms.OpenStreetMap.OSM_mundialis.add_layer.OSM_WMS()`` -Set data specifications - "m.set\_" -*********************************** +Set data spces - "m.set\_" +************************** All functions that set properties of the associated dataset start with ``set_``, e.g.: - :py:meth:`Maps.set_data`, :py:class:`Maps.set_classify`, :py:class:`Maps.set_shape`, ... -Create new Maps-objects - "m.new\_" -*********************************** +Create new Maps - "m.new\_" +*************************** Actions that result in a new :py:class:`Maps` objects start with ``new_``. -- :py:meth:`Maps.new_layer`, :py:meth:`Maps.new_inset_map`, ... +- :py:meth:`Maps.new_layer`, :py:meth:`Maps.new_map`, :py:meth:`Maps.new_inset_map`, ... Callbacks - "m.cb." ******************* diff --git a/docs/api_data_visualization.rst b/docs/source/user_guide/how_to_use/api_data_visualization.rst old mode 100644 new mode 100755 similarity index 96% rename from docs/api_data_visualization.rst rename to docs/source/user_guide/how_to_use/api_data_visualization.rst index 4fdabce5f..3cc225630 --- a/docs/api_data_visualization.rst +++ b/docs/source/user_guide/how_to_use/api_data_visualization.rst @@ -310,7 +310,7 @@ Available shapes (see bleow for details on each plot-shape!): - .. include:: _shape_table.rst +.. include:: ../../_shape_table.rst @@ -370,7 +370,7 @@ Ellipses * - up to ~500k datapoints - 1D, 2D or mixed -.. image:: _static/shape_imgs/ellipses.png +.. image:: /_static/shape_imgs/ellipses.png :width: 50% .. code-block:: python @@ -398,7 +398,7 @@ Rectangles * - up to ~500k datapoints - 1D, 2D or mixed -.. image:: _static/shape_imgs/rectangles.png +.. image:: /_static/shape_imgs/rectangles.png :width: 50% .. code-block:: python @@ -427,7 +427,7 @@ Geodesic Circles * - up to ~500k - 1D, 2D or mixed -.. image:: _static/shape_imgs/geod_circles.png +.. image:: /_static/shape_imgs/geod_circles.png :width: 50% .. code-block:: python @@ -454,7 +454,7 @@ Voronoi Diagram * - up to ~500k datapoints - 1D, 2D or mixed -.. image:: _static/shape_imgs/voronoi_diagram.png +.. image:: /_static/shape_imgs/voronoi_diagram.png :width: 50% .. code-block:: python @@ -482,10 +482,10 @@ Delaunay Triangulation * - up to ~500k datapoints - 1D, 2D or mixed -.. image:: _static/shape_imgs/delaunay_triangulation.png +.. image:: /_static/shape_imgs/delaunay_triangulation.png :width: 50% -.. image:: _static/shape_imgs/delaunay_triangulation_flat.png +.. image:: /_static/shape_imgs/delaunay_triangulation_flat.png :width: 50% .. code-block:: python @@ -514,10 +514,10 @@ Contour plots * - up to a few million datapoints - 1D, 2D or mixed -.. image:: _static/shape_imgs/contour_filled.png +.. image:: /_static/shape_imgs/contour_filled.png :width: 50% -.. image:: _static/shape_imgs/contour.png +.. image:: /_static/shape_imgs/contour.png :width: 50% .. code-block:: python @@ -542,7 +542,7 @@ Hexbin plots * - up to a few million datapoints - 1D, 2D or mixed -.. image:: _static/shape_imgs/hexbin.png +.. image:: /_static/shape_imgs/hexbin.png :width: 50% @@ -573,7 +573,7 @@ Scatter Points * - ~500k datapoints - 1D, 2D or mixed -.. image:: _static/shape_imgs/scatter_points.png +.. image:: /_static/shape_imgs/scatter_points.png :width: 50% .. code-block:: python @@ -600,7 +600,7 @@ Raster * - billions of datapoints (large datasets are pre-aggregated) - 2D or 1D coordinates + 2D data -.. image:: _static/shape_imgs/raster.png +.. image:: /_static/shape_imgs/raster.png :width: 50% .. code-block:: python @@ -631,7 +631,7 @@ Shade Raster - 2D or 1D coordinates + 2D data - `datashader `_ -.. image:: _static/shape_imgs/shade_raster.png +.. image:: /_static/shape_imgs/shade_raster.png :width: 50% .. code-block:: python @@ -663,7 +663,7 @@ Shade Points - 1D, 2D or mixed - `datashader `_ -.. image:: _static/shape_imgs/shade_points.png +.. image:: /_static/shape_imgs/shade_points.png :width: 50% .. code-block:: python @@ -720,7 +720,7 @@ To assign a classification scheme to a :py:class:`Maps` object, use ``m.set_clas .. grid-item:: - .. image:: _static/minigifs/classify_data_01.png + .. image:: /_static/minigifs/classify_data_01.png :width: 75% @@ -791,7 +791,7 @@ To always keep the extent as-is, use ``m.plot_map(set_extent=False)``. .. grid-item:: - .. image:: _static/data_visualization/plot_data.png + .. image:: /_static/data_visualization/plot_data.png :width: 75% @@ -856,7 +856,7 @@ In general, the colors assigned to the shapes are specified by .. grid-item:: - .. image:: _static/data_visualization/customize_plot.png + .. image:: /_static/data_visualization/customize_plot.png :width: 75% @@ -923,7 +923,7 @@ To apply a uniform color to all datapoints, you can use `matpltolib's named colo .. grid-item:: - .. image:: _static/data_visualization/uniform_colors.png + .. image:: /_static/data_visualization/uniform_colors.png :width: 75% @@ -972,7 +972,7 @@ To explicitly color each datapoint with a pre-defined color, simply provide a li .. grid-item:: - .. image:: _static/data_visualization/explicit_colors.png + .. image:: /_static/data_visualization/explicit_colors.png :width: 75% @@ -1022,7 +1022,7 @@ You can fix individual color channels by passing a list with 1 element, e.g.: .. grid-item:: - .. image:: _static/data_visualization/rgba_composite.png + .. image:: /_static/data_visualization/rgba_composite.png :width: 75% @@ -1090,7 +1090,7 @@ Once a dataset has been plotted, a colorbar with a colored histogram on top can .. grid-item:: - .. image:: _static/data_visualization/colorbar.gif + .. image:: /_static/data_visualization/colorbar.gif .. autosummary:: @@ -1112,8 +1112,8 @@ The returned ``ColorBar``-object has the following useful methods defined: ColorBar.tick_params ColorBar.set_visible -Set colorbar tick labels based on bins -************************************** +Label colorbar bins +******************* .. currentmodule:: eomaps.colorbar @@ -1149,7 +1149,7 @@ To label the colorbar with custom names for a given set of bins, use :py:meth:`C .. grid-item:: - .. image:: _static/data_visualization/colorbar_ticks.png + .. image:: /_static/data_visualization/colorbar_ticks.png @@ -1159,8 +1159,9 @@ To label the colorbar with custom names for a given set of bins, use :py:meth:`C ColorBar.set_bin_labels -Using the colorbar as a "dynamic shade indicator" -************************************************* + +Dynamically updated Colorbars +***************************** .. note:: @@ -1192,4 +1193,4 @@ pixels within the current field of view by setting ``dynamic_shade_indicator=Tru .. grid-item:: - .. image:: _static/data_visualization/dynamic_colorbar.gif + .. image:: /_static/data_visualization/dynamic_colorbar.gif diff --git a/docs/source/user_guide/index.rst b/docs/source/user_guide/index.rst new file mode 100755 index 000000000..ef9850c82 --- /dev/null +++ b/docs/source/user_guide/index.rst @@ -0,0 +1,189 @@ +:html_theme.sidebar_secondary.remove: true + +========== +User guide +========== + + +Here you can find detailed information on all features and functionalities of EOmaps +with a lot of code-examples! + + +.. toctree:: + :maxdepth: 1 + :caption: How to use + :hidden: + :glob: + + how_to_use/* + +.. toctree:: + :maxdepth: 1 + :caption: Map Features + :hidden: + :glob: + + map_features/* + + +.. toctree:: + :maxdepth: 1 + :caption: Interactivity + :hidden: + :glob: + + interactivity/* + +.. toctree:: + :maxdepth: 1 + :caption: Miscellaneous + :hidden: + :glob: + + miscellaneous/* + +How to use +---------- + +.. grid:: 2 2 3 3 + :gutter: 1 + + .. grid-item-card:: + :link: how_to_use/api_basics + :link-type: doc + + :doc:`how_to_use/api_basics` + + .. grid-item-card:: + :link: how_to_use/api_data_visualization + :link-type: doc + + :doc:`how_to_use/api_data_visualization` + +Map Features +------------ + +.. grid:: 2 2 3 3 + :gutter: 1 + + .. grid-item-card:: + :link: map_features/api_annotations_markers_etc + :link-type: doc + + :doc:`map_features/api_annotations_markers_etc` + + .. grid-item-card:: + :link: map_features/api_compass + :link-type: doc + + :doc:`map_features/api_compass` + + .. grid-item-card:: + :link: map_features/api_gridlines + :link-type: doc + + :doc:`map_features/api_gridlines` + + .. grid-item-card:: + :link: map_features/api_scalebar + :link-type: doc + + :doc:`map_features/api_scalebar` + + .. grid-item-card:: + :link: map_features/api_vector_data + :link-type: doc + + :doc:`map_features/api_vector_data` + + .. grid-item-card:: + :link: map_features/api_webmaps + :link-type: doc + + :doc:`map_features/api_webmaps` + + .. grid-item-card:: + :link: map_features/inset_maps + :link-type: doc + + :doc:`map_features/inset_maps` + + .. grid-item-card:: + :link: map_features/naturalearth_features + :link-type: doc + + :doc:`map_features/naturalearth_features` + + +Interactivity +------------- + +.. grid:: 2 2 3 3 + :gutter: 1 + + .. grid-item-card:: + :link: interactivity/api_callbacks + :link-type: doc + + :doc:`interactivity/api_callbacks` + + .. grid-item-card:: + :link: interactivity/api_companion_widget + :link-type: doc + + :doc:`interactivity/api_companion_widget` + + .. grid-item-card:: + :link: interactivity/api_draw + :link-type: doc + + :doc:`interactivity/api_draw` + + .. grid-item-card:: + :link: interactivity/api_layout_editor + :link-type: doc + + :doc:`interactivity/api_layout_editor` + + .. grid-item-card:: + :link: interactivity/api_utils + :link-type: doc + + :doc:`interactivity/api_utils` + + .. grid-item-card:: + :link: interactivity/widgets + :link-type: doc + + :doc:`interactivity/widgets` + + +Miscellaneous +------------- + +.. grid:: 2 2 3 3 + :gutter: 1 + + .. grid-item-card:: + :link: miscellaneous/api_command_line_interface + :link-type: doc + + :doc:`miscellaneous/api_command_line_interface` + + .. grid-item-card:: + :link: miscellaneous/api_logging + :link-type: doc + + :doc:`miscellaneous/api_logging` + + .. grid-item-card:: + :link: miscellaneous/api_read_data + :link-type: doc + + :doc:`miscellaneous/api_read_data` + + .. grid-item-card:: + :link: miscellaneous/api_misc + :link-type: doc + + :doc:`miscellaneous/api_misc` diff --git a/docs/api_callbacks.rst b/docs/source/user_guide/interactivity/api_callbacks.rst old mode 100644 new mode 100755 similarity index 96% rename from docs/api_callbacks.rst rename to docs/source/user_guide/interactivity/api_callbacks.rst index e106849c2..3a134869a --- a/docs/api_callbacks.rst +++ b/docs/source/user_guide/interactivity/api_callbacks.rst @@ -8,8 +8,9 @@ :local: :depth: 1 + How to attach callbacks to a map -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. currentmodule:: eomaps.eomaps @@ -101,7 +102,7 @@ The ``< EVENT CATEGORY >`` hereby specifies the event that will trigger the call .. grid-item:: - .. image:: _static/minigifs/simple_callbacks.gif + .. image:: /_static/minigifs/simple_callbacks.gif In addition, each callback-container supports the following useful methods: @@ -131,8 +132,8 @@ In addition, each callback-container supports the following useful methods: set_execute_on_all_layers -Using callbacks with the companion-widget -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Companion-widget callbacks +~~~~~~~~~~~~~~~~~~~~~~~~~~ Some of the most commonly used pre-defined callbacks are integrated in the :ref:`companion_widget`. @@ -140,7 +141,7 @@ Some of the most commonly used pre-defined callbacks are integrated in the :ref: - basic click/pick callbacks to get information on the clicked point -.. image:: _static/widget_callbacks.png +.. image:: /_static/widget_callbacks.png :width: 60% @@ -151,8 +152,8 @@ Some of the most commonly used pre-defined callbacks are integrated in the :ref: Pre-defined callbacks ~~~~~~~~~~~~~~~~~~~~~ -Pre-defined click, pick and move callbacks -****************************************** +Click, pick and move +******************** Callbacks that can be used with ``m.cb.click``, ``m.cb.pick`` and ``m.cb.move``: @@ -191,8 +192,8 @@ Callbacks that can be used only with ``m.cb.pick``: highlight_geometry -Pre-defined keypress callbacks -****************************** +Keypress +******** Callbacks that can be used with ``m.cb.keypress`` @@ -403,11 +404,11 @@ To customize the picking-behavior, use ``m.cb.pick.set_props()``. The following .. grid-item:: - .. image:: _static/minigifs/pick_multi.gif + .. image:: /_static/minigifs/pick_multi.gif -Picking a dataset without plotting it first -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Picking "invisible" datasets +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. currentmodule:: eomaps.eomaps diff --git a/docs/api_companion_widget.rst b/docs/source/user_guide/interactivity/api_companion_widget.rst old mode 100644 new mode 100755 similarity index 92% rename from docs/api_companion_widget.rst rename to docs/source/user_guide/interactivity/api_companion_widget.rst index 132ff59d8..d326368c4 --- a/docs/api_companion_widget.rst +++ b/docs/source/user_guide/interactivity/api_companion_widget.rst @@ -14,7 +14,7 @@ EOmaps comes with an awesome companion widget that provides many useful features -.. |question_symbol| image:: ../eomaps/qtcompanion/icons/info.png +.. |question_symbol| image:: ../../../../eomaps/qtcompanion/icons/info.png :height: 25px .. admonition:: What are all those buttons and sliders for?? @@ -24,7 +24,7 @@ EOmaps comes with an awesome companion widget that provides many useful features - This will activate **help tooltips** that explain the individual controls. -.. image:: _static/minigifs/companion_widget.gif +.. image:: /_static/minigifs/companion_widget.gif :align: center .. raw:: html @@ -68,5 +68,5 @@ next to the corresponding entry in the **Edit** tab that will open a popup windo - Links to **sources**, **references** and **licensing details** (without warranty for correctness!) - The **source code** to reproduce the current appearance of the feature -.. image:: _static/minigifs/companion_widget_feature_info.gif +.. image:: /_static/minigifs/companion_widget_feature_info.gif :width: 50% diff --git a/docs/api_draw.rst b/docs/source/user_guide/interactivity/api_draw.rst old mode 100644 new mode 100755 similarity index 95% rename from docs/api_draw.rst rename to docs/source/user_guide/interactivity/api_draw.rst index 72f88c456..53411c153 --- a/docs/api_draw.rst +++ b/docs/source/user_guide/interactivity/api_draw.rst @@ -29,7 +29,7 @@ Starting with EOmaps v5.0 it is possible to draw simple shapes on the map using .. grid-item:: - .. image:: _static/minigifs/draw_shapes.gif + .. image:: /_static/minigifs/draw_shapes.gif .. note:: diff --git a/docs/api_layout_editor.rst b/docs/source/user_guide/interactivity/api_layout_editor.rst old mode 100644 new mode 100755 similarity index 97% rename from docs/api_layout_editor.rst rename to docs/source/user_guide/interactivity/api_layout_editor.rst index 1bc628f87..37d13a17a --- a/docs/api_layout_editor.rst +++ b/docs/source/user_guide/interactivity/api_layout_editor.rst @@ -39,7 +39,7 @@ You can use it to simply drag the axes the mouse to the desired locations and ch .. grid-item:: - .. image:: _static/minigifs/layout_editor.gif + .. image:: /_static/minigifs/layout_editor.gif diff --git a/docs/api_utils.rst b/docs/source/user_guide/interactivity/api_utils.rst old mode 100644 new mode 100755 similarity index 96% rename from docs/api_utils.rst rename to docs/source/user_guide/interactivity/api_utils.rst index 895430371..d4022ed2c --- a/docs/api_utils.rst +++ b/docs/source/user_guide/interactivity/api_utils.rst @@ -59,5 +59,5 @@ By default, the widgets will show all available layers (except the "all" layer) .. grid-item:: - .. image:: _static/minigifs/layer_selector.gif + .. image:: /_static/minigifs/layer_selector.gif :width: 50% diff --git a/docs/notebooks/widgets.ipynb b/docs/source/user_guide/interactivity/widgets.ipynb similarity index 99% rename from docs/notebooks/widgets.ipynb rename to docs/source/user_guide/interactivity/widgets.ipynb index 2f506f3a4..08d29258c 100644 --- a/docs/notebooks/widgets.ipynb +++ b/docs/source/user_guide/interactivity/widgets.ipynb @@ -889,7 +889,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.7" + "version": "3.11.9" }, "widgets": { "application/vnd.jupyter.widget-state+json": { diff --git a/docs/api_annotations_markers_etc.rst b/docs/source/user_guide/map_features/api_annotations_markers_etc.rst old mode 100644 new mode 100755 similarity index 96% rename from docs/api_annotations_markers_etc.rst rename to docs/source/user_guide/map_features/api_annotations_markers_etc.rst index c2f564e62..f253b5879 --- a/docs/api_annotations_markers_etc.rst +++ b/docs/source/user_guide/map_features/api_annotations_markers_etc.rst @@ -1,8 +1,8 @@ .. _annotations_and_markers: -🏕 Annotations, Markers, Logos, etc. -------------------------------------- +🏕 Annotations, Markers, Logos ... +---------------------------------- .. contents:: Contents: :local: @@ -46,7 +46,7 @@ To dynamically add annotations if you click on the map, checkout the associated Starting with EOmaps v7.0 it is also possible to interactively edit existing annotations by activating the **Edit Annotations** button. - .. image:: _static/widget_annotations.png + .. image:: /_static/widget_annotations.png :width: 50% @@ -109,7 +109,7 @@ To dynamically add annotations if you click on the map, checkout the associated .. grid-item:: - .. image:: _static/minigifs/annotations.png + .. image:: /_static/minigifs/annotations.png Markers @@ -193,7 +193,7 @@ To dynamically add markers if you click on the map, checkout the associated :ref .. grid-item:: - .. image:: _static/minigifs/markers.png + .. image:: /_static/minigifs/markers.png @@ -270,7 +270,7 @@ Lines can be added to a map with :py:meth:`Maps.add_line`. .. grid-item:: - .. image:: _static/minigifs/lines.png + .. image:: /_static/minigifs/lines.png @@ -318,7 +318,7 @@ To indicate rectangular areas in any given crs, simply use :py:meth:`Maps.indica .. grid-item:: - .. image:: _static/minigifs/indicate_extent.png + .. image:: /_static/minigifs/indicate_extent.png Logos @@ -352,7 +352,7 @@ Logos can be re-positioned and re-sized with the :ref:`layout_editor`! .. grid-item:: - .. image:: _static/minigifs/logos.png + .. image:: /_static/minigifs/logos.png diff --git a/docs/api_compass.rst b/docs/source/user_guide/map_features/api_compass.rst old mode 100644 new mode 100755 similarity index 96% rename from docs/api_compass.rst rename to docs/source/user_guide/map_features/api_compass.rst index 6cbd04db8..91e1dd8b2 --- a/docs/api_compass.rst +++ b/docs/source/user_guide/map_features/api_compass.rst @@ -43,7 +43,7 @@ A compass can be added to the map via :py:meth:`Maps.add_compass`: .. grid-item:: - .. image:: _static/minigifs/compass.gif + .. image:: /_static/minigifs/compass.gif :width: 50% diff --git a/docs/api_gridlines.rst b/docs/source/user_guide/map_features/api_gridlines.rst old mode 100644 new mode 100755 similarity index 97% rename from docs/api_gridlines.rst rename to docs/source/user_guide/map_features/api_gridlines.rst index 81ff39da2..76bfb6da3 --- a/docs/api_gridlines.rst +++ b/docs/source/user_guide/map_features/api_gridlines.rst @@ -71,7 +71,7 @@ If no explicit grid-spacing is provided (e.g. ``d=None``), the grid is dynamical .. grid-item:: - .. image:: _static/minigifs/grid_01.png + .. image:: /_static/minigifs/grid_01.png .. currentmodule:: eomaps.grid @@ -148,4 +148,4 @@ can be used (e.g. `color`, `fontsize`, `fontweight`, ...). .. grid-item:: - .. image:: _static/minigifs/grid_labels_01.png + .. image:: /_static/minigifs/grid_labels_01.png diff --git a/docs/api_scalebar.rst b/docs/source/user_guide/map_features/api_scalebar.rst old mode 100644 new mode 100755 similarity index 98% rename from docs/api_scalebar.rst rename to docs/source/user_guide/map_features/api_scalebar.rst index c8f54b552..b31c9452b --- a/docs/api_scalebar.rst +++ b/docs/source/user_guide/map_features/api_scalebar.rst @@ -39,7 +39,7 @@ In addition, many style properties of the scalebar can be adjusted to get the lo .. grid-item:: - .. image:: _static/minigifs/scalebar.gif + .. image:: /_static/minigifs/scalebar.gif :width: 50% diff --git a/docs/api_vector_data.rst b/docs/source/user_guide/map_features/api_vector_data.rst old mode 100644 new mode 100755 similarity index 98% rename from docs/api_vector_data.rst rename to docs/source/user_guide/map_features/api_vector_data.rst index 0a2756111..cf2e94863 --- a/docs/api_vector_data.rst +++ b/docs/source/user_guide/map_features/api_vector_data.rst @@ -80,5 +80,5 @@ For example, to highlight the clicked country, you could use: .. grid-item:: - .. image:: _static/minigifs/add_gdf_pick.gif + .. image:: /_static/minigifs/add_gdf_pick.gif :width: 75% diff --git a/docs/api_webmaps.rst b/docs/source/user_guide/map_features/api_webmaps.rst old mode 100644 new mode 100755 similarity index 95% rename from docs/api_webmaps.rst rename to docs/source/user_guide/map_features/api_webmaps.rst index be9dc9a16..41ee16525 --- a/docs/api_webmaps.rst +++ b/docs/source/user_guide/map_features/api_webmaps.rst @@ -8,8 +8,9 @@ :local: :depth: 1 -How to add WebMap services to a map -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Add WebMap services to a map +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. currentmodule:: eomaps.eomaps @@ -67,7 +68,7 @@ and ``< LAYER >`` indicates the actual layer-name. .. grid-item:: - .. image:: _static/minigifs/add_wms.png + .. image:: /_static/minigifs/add_wms.png Pre-defined WebMap services @@ -122,8 +123,8 @@ Pre-defined WebMap services :code:`m.add_wms.<...>. ... ..layers` -Using custom WebMap services -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Custom WebMap services +~~~~~~~~~~~~~~~~~~~~~~ .. currentmodule:: eomaps.eomaps.Maps.add_wms @@ -157,8 +158,8 @@ It is also possible to use custom WMS/WMTS/XYZ services. layer(...) -Setting date, style and other WebMap properties -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Setting WebMap properties +~~~~~~~~~~~~~~~~~~~~~~~~~ Some WebMap services allow passing additional arguments to set properties such as the **date** or the **style** of the map. To pass additional arguments to a WebMap service, simply provide them when when calling the layer, e.g.: @@ -217,4 +218,4 @@ To show an example, here's how to fetch multiple timestamps for the UV-index of .. grid-item:: - .. image:: _static/minigifs/advanced_wms.gif + .. image:: /_static/minigifs/advanced_wms.gif diff --git a/docs/notebooks/inset_maps.ipynb b/docs/source/user_guide/map_features/inset_maps.ipynb similarity index 100% rename from docs/notebooks/inset_maps.ipynb rename to docs/source/user_guide/map_features/inset_maps.ipynb diff --git a/docs/notebooks/naturalearth_features.ipynb b/docs/source/user_guide/map_features/naturalearth_features.ipynb similarity index 99% rename from docs/notebooks/naturalearth_features.ipynb rename to docs/source/user_guide/map_features/naturalearth_features.ipynb index 498d61255..15e490c38 100644 --- a/docs/notebooks/naturalearth_features.ipynb +++ b/docs/source/user_guide/map_features/naturalearth_features.ipynb @@ -295,7 +295,7 @@ "tags": [] }, "source": [ - "## Advanced usage - getting a hand on the data\n", + "## Getting a hand on the data\n", "\n", "For more advanced use-cases, it can be necessary to access the underlying datasets.\n", "\n", diff --git a/docs/api_command_line_interface.rst b/docs/source/user_guide/miscellaneous/api_command_line_interface.rst old mode 100644 new mode 100755 similarity index 100% rename from docs/api_command_line_interface.rst rename to docs/source/user_guide/miscellaneous/api_command_line_interface.rst diff --git a/docs/api_logging.rst b/docs/source/user_guide/miscellaneous/api_logging.rst old mode 100644 new mode 100755 similarity index 100% rename from docs/api_logging.rst rename to docs/source/user_guide/miscellaneous/api_logging.rst diff --git a/docs/api_misc.rst b/docs/source/user_guide/miscellaneous/api_misc.rst old mode 100644 new mode 100755 similarity index 100% rename from docs/api_misc.rst rename to docs/source/user_guide/miscellaneous/api_misc.rst diff --git a/docs/api_read_data.rst b/docs/source/user_guide/miscellaneous/api_read_data.rst old mode 100644 new mode 100755 similarity index 81% rename from docs/api_read_data.rst rename to docs/source/user_guide/miscellaneous/api_read_data.rst index c28d40691..2e21384d1 --- a/docs/api_read_data.rst +++ b/docs/source/user_guide/miscellaneous/api_read_data.rst @@ -26,6 +26,7 @@ EOmaps provides some basic capabilities to read and plot directly from commonly Read data from a file ~~~~~~~~~~~~~~~~~~~~~ + ``m.read_file.(...)`` can be used to read all relevant data (e.g. values, coordinates & crs) from a file. .. code-block:: python @@ -42,17 +43,19 @@ Read data from a file ... m.plot_map() -.. currentmodule:: eomaps.reader + + .. autosummary:: :nosignatures: - read_file.GeoTIFF - read_file.NetCDF - read_file.CSV + Maps.read_file.GeoTIFF + Maps.read_file.NetCDF + Maps.read_file.CSV + -Initialize a Maps-object from a file -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Create a new Map from a file +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``Maps.from_file.(...)`` can be used to directly initialize a :py:class:`Maps` object from a file. (This is particularly useful if you have a well-defined file-structure that you need to access regularly) @@ -67,20 +70,19 @@ Initialize a Maps-object from a file m.add_colorbar() m.cb.pick.attach.annotate() -.. currentmodule:: eomaps.reader .. autosummary:: :nosignatures: - from_file.GeoTIFF - from_file.NetCDF - from_file.CSV + Maps.from_file.GeoTIFF + Maps.from_file.NetCDF + Maps.from_file.CSV Create a new layer from a file ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Similar to :py:class:`Maps.from_file` , a new layer based on a file can be added to an existing :py:class:`Maps` object via ``Maps.new_layer_from_file.(...)``. +Similar to :py:class:`Maps.from_file` , a new layer based on a file can be added to an existing :py:class:`Maps` object via :py:class:Maps.new_layer_from_file`. .. code-block:: python @@ -97,12 +99,11 @@ Similar to :py:class:`Maps.from_file` , a new layer based on a file can be added cmap="RdBu" ) -.. currentmodule:: eomaps.reader .. autosummary:: :nosignatures: - new_layer_from_file.GeoTIFF - new_layer_from_file.NetCDF - new_layer_from_file.CSV + Maps.new_layer_from_file.GeoTIFF + Maps.new_layer_from_file.NetCDF + Maps.new_layer_from_file.CSV diff --git a/environment.yml b/environment.yml index b81bdc2f6..a9bbd256e 100644 --- a/environment.yml +++ b/environment.yml @@ -48,5 +48,6 @@ dependencies: - sphinx - sphinx-copybutton - sphinx-design - - sphinx_rtd_theme - myst-nb + - pydata-sphinx-theme + - myst-sphinx-gallery = 0.2.2 diff --git a/eomaps/eomaps.py b/eomaps/eomaps.py index 144bccd07..2f7c0309b 100644 --- a/eomaps/eomaps.py +++ b/eomaps/eomaps.py @@ -203,6 +203,7 @@ class Maps(MapsBase): __version__ = __version__ from_file = from_file + new_layer_from_file = new_layer_from_file read_file = read_file CRS = ccrs @@ -320,7 +321,7 @@ def __init__( if WebMapContainer is not None: self.add_wms = self.add_wms(weakref.proxy(self)) - self._new_layer_from_file = new_layer_from_file(weakref.proxy(self)) + self.new_layer_from_file = new_layer_from_file(weakref.proxy(self)) self.set_shape = self.set_shape(weakref.proxy(self)) self._shape = None @@ -465,12 +466,6 @@ def _edit_annotations(self): def edit_annotations(self, b=True, **kwargs): self._edit_annotations(b, **kwargs) - @property - @wraps(new_layer_from_file) - def new_layer_from_file(self): - """Create a new layer from a file.""" - return self._new_layer_from_file - def new_map( self, ax=None, diff --git a/examples/00-basics/GALLERY_HEADER.rst b/examples/00-basics/GALLERY_HEADER.rst new file mode 100644 index 000000000..304b14eeb --- /dev/null +++ b/examples/00-basics/GALLERY_HEADER.rst @@ -0,0 +1,3 @@ +======================== +Basic data visualization +======================== diff --git a/examples/00-basics/basics.rst b/examples/00-basics/basics.rst new file mode 100644 index 000000000..a291f5bd5 --- /dev/null +++ b/examples/00-basics/basics.rst @@ -0,0 +1,50 @@ +======================== +Basic data visualization +======================== + + +There are 3 basic steps required to visualize your data: + +1. Initialize a Maps-object with ``m = Maps()`` +2. Set the data and its specifications via ``m.set_data`` +3. Call ``m.plot_map()`` to generate the map! + + +.. image:: /_static/example_images/example_basics.gif + :align: center + +.. code-block:: python + + # EOmaps: A simple map + + from eomaps import Maps + import pandas as pd + import numpy as np + + # ----------- create some example-data + lon, lat = np.meshgrid(np.arange(-20, 40, 0.25), np.arange(30, 60, 0.25)) + data = pd.DataFrame( + dict(lon=lon.flat, lat=lat.flat, data_variable=np.sqrt(lon**2 + lat**2).flat) + ) + data = data.sample(15000) # take 15000 random datapoints from the dataset + # ------------------------------------ + + m = Maps(crs=4326) + m.add_title("Click on the map to pick datapoints!") + m.add_feature.preset.ocean() + m.add_feature.preset.coastline() + m.set_data( + data=data, # a pandas-DataFrame holding the data & coordinates + parameter="data_variable", # the DataFrame-column you want to plot + x="lon", # the name of the DataFrame-column representing the x-coordinates + y="lat", # the name of the DataFrame-column representing the y-coordinates + crs=4326, # the coordinate-system of the x- and y- coordinates + ) + m.plot_map() + m.add_colorbar(label="A dataset") + + c = m.add_compass((0.05, 0.86), scale=7, patch=None) + + m.cb.pick.attach.annotate() # attach a pick-annotation (on left-click) + m.add_logo() # add a logo + m.show() diff --git a/examples/01-Maps/GALLERY_HEADER.rst b/examples/01-Maps/GALLERY_HEADER.rst new file mode 100644 index 000000000..54db23de0 --- /dev/null +++ b/examples/01-Maps/GALLERY_HEADER.rst @@ -0,0 +1,3 @@ +=========== +Manage Maps +=========== diff --git a/examples/01-Maps/inset_maps.rst b/examples/01-Maps/inset_maps.rst new file mode 100644 index 000000000..45694b9c1 --- /dev/null +++ b/examples/01-Maps/inset_maps.rst @@ -0,0 +1,129 @@ +=================================================== +Inset-maps - get a zoomed-in view on selected areas +=================================================== + +Quickly create nice inset-maps to show details for specific regions. + +- the location and extent of the inset can be defined in any given crs + + - (or as a geodesic circle with a radius defined in meters) + +- the inset-map can have a different crs than the "parent" map + +(requires EOmaps >= v4.1) + + +.. image:: /_static/example_images/example_inset_maps.png + :width: 75% + :align: center + + +.. code-block:: python + + # EOmaps example: How to create inset-maps + + from eomaps import Maps + import numpy as np + + m = Maps(Maps.CRS.Orthographic()) + m.add_feature.preset.coastline() # add some coastlines + + # ---------- create a new inset-map + # showing a 15 degree rectangle around the xy-point + mi1 = m.new_inset_map( + xy=(5, 45), + xy_crs=4326, + shape="rectangles", + radius=15, + plot_position=(0.75, 0.4), + plot_size=0.5, + inset_crs=4326, + boundary=dict(ec="r", lw=1), + indicate_extent=dict(fc=(1, 0, 0, 0.25)), + ) + + mi1.add_indicator_line(m, marker="o") + + # populate the inset with some more detailed features + mi1.add_feature.preset("coastline", "ocean", "land", "countries", "urban_areas") + + # ---------- create another inset-map + # showing a 400km circle around the xy-point + mi2 = m.new_inset_map( + xy=(5, 45), + xy_crs=4326, + shape="geod_circles", + radius=400000, + plot_position=(0.25, 0.4), + plot_size=0.5, + inset_crs=3035, + boundary=dict(ec="g", lw=2), + indicate_extent=dict(fc=(0, 1, 0, 0.25)), + ) + mi2.add_indicator_line(m, marker="o") + + # populate the inset with some features + mi2.add_feature.preset("ocean", "land") + mi2.add_feature.preset.urban_areas(zorder=1) + + # print some data on all of the maps + + x, y = np.meshgrid(np.linspace(-50, 50, 100), np.linspace(-30, 70, 100)) + data = x + y + + m.set_data(data, x, y, crs=4326) + m.set_classify.Quantiles(k=4) + m.plot_map(alpha=0.5, ec="none", set_extent=False) + + # use the same data and classification for the inset-maps + for m_i in [mi1, mi2]: + m_i.inherit_data(m) + m_i.inherit_classification(m) + + mi1.set_shape.ellipses(np.mean(m.shape.radius) / 2) + mi1.plot_map(alpha=0.75, ec="k", lw=0.5, set_extent=False) + + mi2.set_shape.ellipses(np.mean(m.shape.radius) / 2) + mi2.plot_map(alpha=1, ec="k", lw=0.5, set_extent=False) + + + # add an annotation for the second datapoint to the inset-map + mi2.add_annotation(ID=1, xytext=(-120, 80)) + + # indicate the extent of the second inset on the first inset + mi2.add_extent_indicator(mi1, ec="g", lw=2, fc="g", alpha=0.5, zorder=0) + mi2.add_indicator_line(mi1, marker="o") + + # add some additional text to the inset-maps + for m_i, txt, color in zip([mi1, mi2], ["epsg: 4326", "epsg: 3035"], ["r", "g"]): + txt = m_i.ax.text( + 0.5, + 0, + txt, + transform=m_i.ax.transAxes, + horizontalalignment="center", + bbox=dict(facecolor=color), + ) + # add the text-objects as artists to the blit-manager + m_i.BM.add_artist(txt) + + mi2.add_colorbar(hist_bins=20, margin=dict(bottom=-0.2), label="some parameter") + # move the inset map (and the colorbar) to a different location + mi2.set_inset_position(x=0.3) + + # share pick events + for mi in [m, mi1, mi2]: + mi.cb.pick.attach.annotate(text=lambda ID, val, **kwargs: f"ID={ID}\nval={val:.2f}") + m.cb.pick.share_events(mi1, mi2) + + m.apply_layout( + { + "figsize": [6.4, 4.8], + "0_map": [0.1625, 0.09, 0.675, 0.9], + "1_inset_map": [0.5625, 0.15, 0.375, 0.5], + "2_inset_map": [0.0875, 0.33338, 0.325, 0.43225], + "3_cb": [0.0875, 0.12, 0.4375, 0.12987], + "3_cb_histogram_size": 0.8, + } + ) + m.show() diff --git a/examples/01-Maps/multiple_maps.rst b/examples/01-Maps/multiple_maps.rst new file mode 100644 index 000000000..5fb6fa266 --- /dev/null +++ b/examples/01-Maps/multiple_maps.rst @@ -0,0 +1,121 @@ +===================================== +Multiple Maps and Data-classification +===================================== + +- Create grids of maps via ``MapsGrid`` +- | Classify your data via ``m.set_classify_specs(scheme, **kwargs)`` + | (using classifiers provided by the ``mapclassify`` module) +- | Add individual callback functions to each subplot via + | ``m.cb.click.attach``, ``m.cb.pick.attach`` +- | Share events between Maps-objects of the MapsGrid via + | ``mg.share_click_events()`` and ``mg.share_pick_events()`` + +.. image:: /_static/example_images/example_multiple_maps.gif + :width: 75% + :align: center + +.. code-block:: python + + # EOmaps example: Data-classification and multiple Maps in one figure + + from eomaps import Maps + import pandas as pd + import numpy as np + + # ----------- create some example-data + lon, lat = np.meshgrid(np.arange(-20, 40, 0.5), np.arange(30, 60, 0.5)) + data = pd.DataFrame( + dict(lon=lon.flat, lat=lat.flat, data_variable=np.sqrt(lon**2 + lat**2).flat) + ) + data = data.sample(4000) # take 4000 random datapoints from the dataset + # ------------------------------------ + + # initialize a grid of Maps objects + m = Maps(ax=131, crs=4326, figsize=(11, 5)) + m2 = m.new_map(ax=132, crs=Maps.CRS.Stereographic()) + m3 = m.new_map(ax=133, crs=3035) + + # --------- set specs for the first map + m.text(0.5, 1.1, "epsg=4326", transform=m.ax.transAxes) + m.set_classify_specs(scheme="EqualInterval", k=10) + + # --------- set specs for the second map + m2.text(0.5, 1.1, "Stereographic", transform=m2.ax.transAxes) + m2.set_shape.rectangles() + m2.set_classify_specs(scheme="Quantiles", k=8) + + # --------- set specs for the third map + m3.text(0.5, 1.1, "epsg=3035", transform=m3.ax.transAxes) + m3.set_classify_specs( + scheme="StdMean", + multiples=[-1, -0.75, -0.5, -0.25, 0.25, 0.5, 0.75, 1], + ) + + # --------- plot all maps and add colorbars to all maps + # set the data on ALL maps-objects of the grid + for m_i in [m, m2, m3]: + m_i.set_data(data=data, x="lon", y="lat", crs=4326) + m_i.plot_map() + m_i.add_colorbar(extend="neither") + + m_i.add_feature.preset.ocean() + m_i.add_feature.preset.land() + # add the coastline to all layers of the maps + m_i.add_feature.preset.coastline(layer="all") + + + # --------- add a new layer for the second axis + # NOTE: this layer is not visible by default but it can be shown by clicking + # on the layer-switcher utility buttons (bottom center of the figure) + # or by using `m2.show()` or via `m.show_layer("layer 2")` + m21 = m2.new_layer(layer="layer 2") + m21.inherit_data(m2) + m21.set_shape.delaunay_triangulation(mask_radius=0.5) + m21.set_classify_specs(scheme="Quantiles", k=4) + m21.plot_map(cmap="RdYlBu") + m21.add_colorbar(extend="neither") + # add an annotation that is only executed if "layer 2" is active + m21.cb.click.attach.annotate(text="callbacks are layer-sensitive!") + + # --------- add some callbacks to indicate the clicked data-point to all maps + for m_i in [m, m2, m3]: + m_i.cb.pick.attach.mark(fc="r", ec="none", buffer=1, permanent=True) + m_i.cb.pick.attach.mark(fc="none", ec="r", lw=1, buffer=5, permanent=True) + m_i.cb.move.attach.mark(fc="none", ec="k", lw=2, buffer=10, permanent=False) + + for m_i in [m, m2, m21, m3]: + # --------- rotate the ticks of the colorbars + m_i.colorbar.ax_cb.tick_params(rotation=90, labelsize=8) + # add logos + m_i.add_logo(size=0.05) + + # add an annotation-callback to the second map + m2.cb.pick.attach.annotate(text="the closest point is here!", zorder=99) + + # share click & pick-events between all Maps-objects of the MapsGrid + m.cb.move.share_events(m2, m3) + m.cb.pick.share_events(m2, m3) + + # --------- add a layer-selector widget + m.util.layer_selector(ncol=2, loc="lower center", draggable=False) + + + m.apply_layout( + { + "figsize": [11.0, 5.0], + "0_map": [0.015, 0.44, 0.3125, 0.34375], + "1_map": [0.35151, 0.363, 0.32698, 0.50973], + "2_map": [0.705, 0.44, 0.2875, 0.37872], + "3_cb": [0.05522, 0.0825, 0.2625, 0.2805], + "3_cb_histogram_size": 0.8, + "4_cb": [0.33625, 0.11, 0.3525, 0.2], + "4_cb_histogram_size": 0.8, + "5_cb": [0.72022, 0.0825, 0.2625, 0.2805], + "5_cb_histogram_size": 0.8, + "6_logo": [0.2725, 0.451, 0.05, 0.04538], + "7_logo": [0.625, 0.3795, 0.05, 0.04538], + "8_logo": [0.625, 0.3795, 0.05, 0.04538], + "9_logo": [0.93864, 0.451, 0.05, 0.04538], + } + ) + m.show() diff --git a/examples/02-images/GALLERY_HEADER.rst b/examples/02-images/GALLERY_HEADER.rst new file mode 100644 index 000000000..753b77bf3 --- /dev/null +++ b/examples/02-images/GALLERY_HEADER.rst @@ -0,0 +1,3 @@ +======================== +Images, Contours, WebMap +======================== diff --git a/examples/02-images/contour.rst b/examples/02-images/contour.rst new file mode 100644 index 000000000..e648ebcc3 --- /dev/null +++ b/examples/02-images/contour.rst @@ -0,0 +1,62 @@ +================================ +Contour plots and Contour Levels +================================ + +Use the ``contour``-shape to draw contour-plots of regular (or irregular data) +or to indicate contour-levels on top of other plots. + +(requires EOmaps >= v7.1) + + +.. image:: /_static/example_images/example_contour.png + :width: 75% + :align: center + +.. code-block:: python + + # EOmaps example: Contour plots and contour levels + + from eomaps import Maps + import numpy as np + + # ------------- setup some random data + lon, lat = np.meshgrid(np.linspace(-180, 180, 200), np.linspace(-90, 90, 100)) + data = np.sqrt(lon**2 + lat**2) + name = "some parameter" + + # ------------- left map + m = Maps(ax=121) + m.add_title("Raster plot with contour-levels") + m.add_feature.preset.coastline() + # plot raster-data + m.set_data(data, lon, lat, parameter=name) + m.set_shape.raster() + m.plot_map() + + # layer to indicate contour-levels + m_cont = m.new_layer(inherit_data=True) + m_cont.set_shape.contour(filled=False) + m_cont.set_classify.EqualInterval(k=4) + m_cont.plot_map(colors=("r", "g", "b", "m")) + + # ------------- right map + m2 = m.new_map(ax=122, inherit_data=True) + m2.add_title("Filled contour plot") + m2.set_classify.EqualInterval(k=4) + m2.set_shape.contour(filled=True) + m2.plot_map(cmap="viridis") + + # add a colorbar and indicate contour-levels + cb = m.add_colorbar(label="Data histogram with indicated contour levels highlighted.") + cb.indicate_contours(m_cont) + + # apply a customized layout + layout = { + "figsize": [8.34, 3.9], + "0_map": [0.03652, 0.44167, 0.45, 0.48115], + "1_map": [0.51152, 0.44167, 0.45, 0.48115], + "2_cb": [-0.0125, 0.02673, 1.0125, 0.28055], + "2_cb_histogram_size": 0.76, + } + m.apply_layout(layout) + m.show() diff --git a/examples/02-images/rgb.rst b/examples/02-images/rgb.rst new file mode 100644 index 000000000..dc833399d --- /dev/null +++ b/examples/02-images/rgb.rst @@ -0,0 +1,46 @@ +======================== +Plot RGB images on a map +======================== + +To create an RGB or RGBA composite from 3 (or 4) datasets, pass the datasets as tuple: + +- the datasets must have the same size as the coordinate arrays! +- the datasets must be scaled between 0 and 1 + +If you pass a tuple of 3 or 4 arrays, they will be used to set the +RGB (or RGBA) colors of the shapes, e.g.: + +- ``m.plot_map(fc=(, , ))`` +- ``m.plot_map(fc=(, , , ))`` + +You can fix individual color channels by passing a list with 1 element, e.g.: + +- ``m.plot_map(fc=(, [0.12345], , ))`` + +.. image:: /_static/example_images/example_RGB.png + :width: 75% + :align: center + +.. code-block:: + + from eomaps import Maps + import numpy as np + + x, y = np.meshgrid(np.linspace(-30, 30, 100), np.linspace(-30, 0, 50)) + + # values must be between 0 and 1 + r = np.random.randint(0, 100, x.shape) / 100 + g = np.random.randint(0, 100, x.shape) / 100 + b = [0.4] + a = np.random.randint(0, 100, x.shape) / 100 + + m = Maps(figsize=(10, 5)) + + m.set_data(data=None, x=x, y=y) + m.set_shape.raster() + m.plot_map(fc=(r, g, b, a)) + ## add gridlines and ticklabels + g = m.add_gridlines(d=10, lw=1, linestyle='--', color='k') + g.add_labels(fontsize=10) + + m.show() diff --git a/examples/02-images/webmaps.rst b/examples/02-images/webmaps.rst new file mode 100644 index 000000000..369236b85 --- /dev/null +++ b/examples/02-images/webmaps.rst @@ -0,0 +1,100 @@ +=================================== +WebMap services and layer-switching +=================================== + +- add WebMap services using ``m.add_wms`` and ``m.add_wmts`` +- compare different data-layers and WebMap services using ``m.cb.click.peek_layer`` and ``m.cb.keypress.switch_layer`` + + +.. image:: /_static/example_images/example_webmaps.gif + :width: 75% + :align: center + +.. code-block:: python + + # EOmaps example: WebMap services and layer-switching + + from eomaps import Maps + import numpy as np + import pandas as pd + + # ------ create some data -------- + lon, lat = np.meshgrid(np.linspace(-50, 50, 150), np.linspace(30, 60, 150)) + data = pd.DataFrame( + dict(lon=lon.flat, lat=lat.flat, data=np.sqrt(lon**2 + lat**2).flat) + ) + # -------------------------------- + + m = Maps(Maps.CRS.GOOGLE_MERCATOR, layer="S1GBM_vv", figsize=(9, 4)) + # set the crs to GOOGLE_MERCATOR to avoid reprojecting the WebMap data + # (makes it a lot faster and it will also look much nicer!) + + # add S1GBM WebMap to the layer of this Maps-object + m.add_wms.S1GBM.add_layer.vv() + + # add OpenStreetMap on the currently invisible layer (OSM) + m.add_wms.OpenStreetMap.add_layer.default(layer="OSM") + + # create a new layer named "data" and plot some data + m2 = m.new_layer(layer="data") + m2.set_data(data=data.sample(5000), x="lon", y="lat", crs=4326) + m2.set_shape.geod_circles(radius=20000) + m2.plot_map() + + + # -------- CALLBACKS ---------- + # (use m.all to execute independent of the visible layer) + # on a left-click, show layers ("data", "OSM") in a rectangle + m.all.cb.click.attach.peek_layer(layer="OSM|data", how=0.2) + + # on a right-click, "swipe" the layers ("S1GBM_vv" and "data") from the left + m.all.cb.click.attach.peek_layer( + layer="S1GBM_vv|data", + how="left", + button=3, + ) + + # switch between the layers by pressing the keys 0, 1 and 2 + m.all.cb.keypress.attach.switch_layer(layer="S1GBM_vv", key="0") + m.all.cb.keypress.attach.switch_layer(layer="OSM", key="1") + m.all.cb.keypress.attach.switch_layer(layer="data", key="2") + + # add a pick callback that is only executed if the "data" layer is visible + m2.cb.pick.attach.annotate(zorder=100) # use a high zorder to put it on top + + # ------ UTILITY WIDGETS -------- + # add a clickable widget to switch between layers + m.util.layer_selector( + loc="upper left", + ncol=3, + bbox_to_anchor=(0.01, 0.99), + layers=["OSM", "S1GBM_vv", "data"], + ) + # add a slider to switch between layers + s = m.util.layer_slider( + pos=(0.5, 0.93, 0.38, 0.025), + color="r", + handle_style=dict(facecolor="r"), + txt_patch_props=dict(fc="w", ec="none", alpha=0.75, boxstyle="round, pad=.25"), + ) + + # explicitly set the layers you want to use in the slider + # (Note: you can also use combinations of multiple existing layers!) + s.set_layers(["OSM", "S1GBM_vv", "data", "OSM|data{0.5}"]) + + # ------------------------------ + + m.add_logo() + + m.apply_layout( + { + "figsize": [9.0, 4.0], + "0_map": [0.00625, 0.01038, 0.9875, 0.97924], + "1_slider": [0.45, 0.93, 0.38, 0.025], + "2_logo": [0.865, 0.02812, 0.12, 0.11138], + } + ) + + # fetch all layers before startup so that they are already cached + m.fetch_layers() + m.show() diff --git a/examples/03-geometry/GALLERY_HEADER.rst b/examples/03-geometry/GALLERY_HEADER.rst new file mode 100644 index 000000000..ce5f24e9f --- /dev/null +++ b/examples/03-geometry/GALLERY_HEADER.rst @@ -0,0 +1,3 @@ +======================= +Points, Lines, Polygons +======================= diff --git a/examples/03-geometry/lines.rst b/examples/03-geometry/lines.rst new file mode 100644 index 000000000..7acc9cf71 --- /dev/null +++ b/examples/03-geometry/lines.rst @@ -0,0 +1,127 @@ +Lines and Annotations +--------------------- + +Draw lines defined by a set of anchor-points and add some nice annotations. + +Connect the anchor-points via: + +- geodesic lines +- straight lines +- reprojected straight lines defined in a given projection + + +(requires EOmaps >= v4.3.1) + +.. image:: /_static/example_images/example_lines.png + :width: 75% + :align: center + +.. code-block:: python + + # EOmaps example: drawing lines on a map + + from eomaps import Maps + + m = Maps(Maps.CRS.Mollweide(), figsize=(8, 4)) + m.add_feature.preset.ocean() + m.add_feature.preset.land() + + # get a few points for some basic lines + l1 = [(-135, 50), (45, 45), (123, 76)] + l2 = [(-120, -24), (-160, 34), (-153, -60), (-128, -82), (-24, -25), (-16, 35)] + + # define annotation-styles + bbox_style1 = dict( + xy_crs=4326, + fontsize=8, + bbox=dict(boxstyle="circle,pad=0.25", ec="b", fc="b", alpha=0.25), + ) + bbox_style2 = dict( + xy_crs=4326, + fontsize=6, + bbox=dict(boxstyle="round,pad=0.25", ec="k", fc="r", alpha=0.5), + horizontalalignment="center", + ) + + # -------- draw a line with 100 intermediate points per line-segment, + # mark anchor-points with "o" and every 10th intermediate point with "x" + m.add_line(l1, c="b", lw=0.5, marker="x", ms=3, markevery=10, n=100, mark_points="bo") + + m.add_annotation(xy=l1[0], text="start", xytext=(10, -20), **bbox_style1) + m.add_annotation(xy=l1[-1], text="end", xytext=(10, -30), **bbox_style1) + + # -------- draw a line with ~1km spacing between intermediate points per line-segment, + # mark anchor-points with "*" and every 1000th intermediate point with "." + + d_inter, d_tot = m.add_line( + l2, + c="r", + lw=0.5, + marker=".", + del_s=1000, + markevery=1000, + mark_points=dict(marker="*", fc="darkred", ec="k", lw=0.25, s=60, zorder=99), + ) + + for i, (point, distance) in enumerate(zip(l2[:-1], d_tot)): + if i == 0: + t = "start" + else: + t = f"segment {i}" + + m.add_annotation( + xy=point, text=f"{t}\n{distance/1000:.0f}km", xytext=(10, 20), **bbox_style2 + ) + + m.add_annotation(xy=l2[-1], text="end", xytext=(10, 20), **bbox_style2) + + + # -------- show the effect of different connection-styles + + l3 = [(50, 20), (120, 20), (120, -30), (50, -30), (50, 20)] + l4 = [(55, 15), (115, 15), (115, -25), (55, -25), (55, 15)] + l5 = [(60, 10), (110, 10), (110, -20), (60, -20), (60, 10)] + + # -------- connect points via straight lines + m.add_line(l3, lw=0.75, ls="--", c="k", mark_points="k.") + m.add_annotation( + xy=l3[1], + fontsize=6, + xy_crs=4326, + text="geodesic lines", + xytext=(20, 10), + bbox=dict(ec="k", fc="w", ls="--"), + ) + + # -------- connect points via lines that are straight in a given projection + m.add_line( + l4, + connect="straight_crs", + xy_crs=4326, + lw=1, + c="purple", + mark_points=dict(fc="purple", marker="."), + ) + m.add_annotation( + xy=l4[1], + fontsize=6, + xy_crs=4326, + text="straight lines\nin epsg 4326", + xytext=(21, -10), + bbox=dict(ec="purple", fc="w"), + ) + + # -------- connect points via geodesic lines + m.add_line(l5, connect="straight", lw=0.5, c="r", mark_points="r.") + + m.add_annotation( + xy=l5[1], + fontsize=6, + xy_crs=4326, + text="straight lines", + xytext=(24, -20), + bbox=dict(ec="r", fc="w"), + ) + + m.add_logo() + m.show() diff --git a/examples/03-geometry/vector_data.rst b/examples/03-geometry/vector_data.rst new file mode 100644 index 000000000..26f1afaaf --- /dev/null +++ b/examples/03-geometry/vector_data.rst @@ -0,0 +1,91 @@ +================================== +Vector-data interactive geometries +================================== + +EOmaps can be used to assign callbacks to vector data (e.g. ``geopandas.GeoDataFrames``). + +- to make a GeoDataFrame pickable, first use ``m.add_gdf(picker_name="MyPicker")`` + +- now you can assign callbacks via ``m.cb.pick__MyPicker.attach...`` just as you + would do with the ordinary ``m.cb.click`` or ``m.cb.pick`` callbacks + +.. Note:: + For large datasets that are visualized as simple rectangles, ellipses etc. + it is recommended to use EOmaps to visualize the data with ``m.plot_map()`` + since the generation of the plot and the identification of the picked pixels + will be much faster! + + If the GeoDataFrame contains multiple different geometry types + (e.g. Lines, Patches, etc.) a unique pick-collection will be assigned + for each of the geometry types! + +.. image:: /_static/example_images/example_vector_data.gif + :width: 75% + :align: center + +.. code-block:: python + + # EOmaps example: Using geopandas - interactive shapes! + + from eomaps import Maps + import pandas as pd + import numpy as np + + # geopandas is used internally... the import is just here to show that! + import geopandas as gpd + + # ----------- create some example-data + lon, lat = np.meshgrid(np.linspace(-180, 180, 25), np.linspace(-90, 90, 25)) + data = pd.DataFrame( + dict(lon=lon.flat, lat=lat.flat, data=np.sqrt(lon**2 + lat**2).flat) + ) + + # setup 2 maps with different projections next to each other + m = Maps(ax=121, crs=4326, figsize=(10, 5)) + m2 = Maps(f=m.f, ax=122, crs=Maps.CRS.Orthographic(45, 45)) + + # assign data to the Maps objects + m.set_data(data=data.sample(100), x="lon", y="lat", crs=4326, parameter="data") + m2.set_data(data=data, x="lon", y="lat", crs=4326) + + # fetch data (incl. metadata) for the "admin_0_countries" NaturalEarth feature + countries = m.add_feature.cultural.admin_0_countries.get_gdf(scale=50) + + for m_i in [m, m2]: + m_i.add_feature.preset.ocean() + + m_i.add_gdf( + countries, + picker_name="countries", + pick_method="contains", + val_key="NAME", + fc="none", + ec="k", + lw=0.5, + ) + + m_i.set_shape.rectangles(radius=3, radius_crs=4326) + m_i.plot_map(alpha=0.75, ec=(1, 1, 1, 0.5)) + + # attach a callback to highlite the rectangles + m_i.cb.pick.attach.mark(shape="rectangles", fc="none", ec="b", lw=2) + + # attach a callback to highlite the countries and indicate the names + picker = m_i.cb.pick["countries"] + picker.attach.highlight_geometry(fc="r", ec="k", lw=0.5, alpha=0.5) + picker.attach.annotate(text=lambda val, **kwargs: str(val)) + + # share pick events between the maps-objects + m.cb.pick.share_events(m2) + m.cb.pick["countries"].share_events(m2) + + m.add_logo() + m.apply_layout( + { + "figsize": [10.0, 5.0], + "0_map": [0.005, 0.25114, 0.5, 0.5], + "1_map": [0.5125, 0.0375, 0.475, 0.95], + "2_logo": [0.875, 0.01, 0.12, 0.09901], + } + ) + m.show() diff --git a/examples/04-geomap_components/GALLERY_HEADER.rst b/examples/04-geomap_components/GALLERY_HEADER.rst new file mode 100644 index 000000000..13c545d5f --- /dev/null +++ b/examples/04-geomap_components/GALLERY_HEADER.rst @@ -0,0 +1,3 @@ +=========================== +Geographical Map Components +=========================== diff --git a/examples/04-geomap_components/gridlines.rst b/examples/04-geomap_components/gridlines.rst new file mode 100644 index 000000000..784c4199c --- /dev/null +++ b/examples/04-geomap_components/gridlines.rst @@ -0,0 +1,85 @@ +Gridlines and Grid Labels +------------------------- + +Draw custom grids and add grid labels. + +(requires EOmaps >= v6.5) + +.. image:: /_static/example_images/example_gridlines.png + :width: 75% + :align: center + +.. code-block:: python + + # EOmaps example: Customized gridlines + + from eomaps import Maps + + m = Maps(crs=Maps.CRS.Stereographic()) + m.subplots_adjust(left=0.1, right=0.9, bottom=0.1, top=0.9) + + m.add_feature.preset.ocean() + m.add_feature.preset.land() + + # draw a regular 5 degree grid + m.add_gridlines(5, lw=0.25, alpha=0.5) + # draw a grid with 20 degree longitude spacing and add labels + g = m.add_gridlines((20, None), c="b", n=500) + g.add_labels(offset=10, fontsize=8, c="b") + # draw a grid with 20 degree latitude spacing, add labels and exclude the 10° tick + g = m.add_gridlines((None, 20), c="g", n=500) + g.add_labels(where="l", offset=10, fontsize=8, c="g", exclude=[10]) + # explicitly highlight 10°N line and add a label on one side of the map + g = m.add_gridlines((None, [10]), c="indigo", n=500, lw=1.5) + g.add_labels(where="l", fontsize=12, fontweight="bold", c="indigo") + + + # ----------------- first inset-map + mi = m.new_inset_map(xy=(45, 45), radius=10, inset_crs=m.crs_plot) + mi.add_feature.preset.ocean() + mi.add_feature.preset.land() + + # draw a regular 1 degree grid + g = mi.add_gridlines((None, 1), c="g", lw=0.6) + # add some specific latitude gridlines and add labels + g = mi.add_gridlines((None, [40, 45, 50]), c="g", lw=2) + g.add_labels(where="lr", offset=7, fontsize=6, c="g") + # add some specific longitude gridlines and add labels + g = mi.add_gridlines(([40, 45, 50], None), c="b", lw=2) + g.add_labels(where="tb", offset=7, fontsize=6, c="b") + + mi.add_extent_indicator(m, fc="darkred", ec="none", alpha=0.5) + mi.add_indicator_line() + + # ----------------- second inset-map + mi = m.new_inset_map( + inset_crs=m.crs_plot, + xy=(-10, 10), + radius=20, + shape="rectangles", + boundary=dict(ec="k"), + ) + mi.add_feature.preset.ocean() + mi.add_feature.preset.land() + + mi.add_extent_indicator(m, fc=".5", ec="none", alpha=0.5) + mi.add_indicator_line(c="k") + + # draw a regular 1 degree grid + g = mi.add_gridlines(5, lw=0.25) + # add some specific latitude gridlines and add labels + g = mi.add_gridlines((None, [0, 10, 25]), c="g", lw=2) + g.add_labels(where="l", fontsize=10, c="g") + # add some specific longitude gridlines and add labels + g = mi.add_gridlines(([-25, -10, 0], None), c="b", lw=2) + g.add_labels(where="t", fontsize=10, c="b") + + m.apply_layout( + { + "figsize": [7.39, 4.8], + "0_map": [0.025, 0.07698, 0.5625, 0.86602], + "1_inset_map": [0.7, 0.53885, 0.225, 0.41681], + "2_inset_map": [0.6625, 0.03849, 0.275, 0.42339], + } + ) + m.show() diff --git a/examples/04-geomap_components/scalebars.rst b/examples/04-geomap_components/scalebars.rst new file mode 100644 index 000000000..3971d07d1 --- /dev/null +++ b/examples/04-geomap_components/scalebars.rst @@ -0,0 +1,79 @@ +=============== +Using Scalebars +=============== + +EOmaps has a nice customizable scalebar feature! + - use ``s = m.add_scalebar(lon, lat, azim)`` to attach a scalebar to the plot + - once the scalebar is there, you can drag it around and change its + properties via ``s.set_position``, ``s.set_scale_props()``, + ``s.set_label_props()`` and ``s.set_patch_props()`` + +.. Note:: + You can also simply drag the scalebar with the mouse! + + - LEFT-click on it to make it interactive! + - RIGHT-click anywhere on the map to make it fixed again + + There are also some useful keyboard shortcuts you can use while the + scalebar is interactive + + - use ``+``/``-`` to rotate the scalebar + - use ``alt`` + ``+``/``-`` to set the text-offset + - use the ``arrow-keys`` to increase the frame-widths + - use ``alt`` + ``arrow-keys`` to decrease the frame-widths + - use ``delete`` to remove the scalebar from the plot + +.. image:: /_static/example_images/example_scalebars.gif + :width: 75% + :align: center + +The data displayed in the above gif is taken from: + - NaturalEarth (https://www.naturalearthdata.com/) + + +.. code-block:: python + + # EOmaps example: Adding scalebars - what about distances? + from eomaps import Maps + + m = Maps(figsize=(9, 5)) + m.add_feature.preset.ocean(ec="k", scale="110m") + + s1 = m.add_scalebar((0, 45), 30, scale=10e5, n=8, preset="kr") + + s2 = m.add_scalebar( + (-11, -50), + -45, + scale=5e5, + n=10, + scale_props=dict(width=5, colors=("k", ".25", ".5", ".75", ".95")), + patch_props=dict(offsets=(1, 1.4, 1, 1), fc=(0.7, 0.8, 0.3, 1)), + label_props=dict( + offset=0.5, scale=1.4, every=5, weight="bold" # , family="Calibri" + ), + ) + + s3 = m.add_scalebar( + (-120, -20), + 0, + scale=5e5, + n=10, + scale_props=dict(width=3, colors=(*["w", "darkred"] * 2, *["w"] * 5, "darkred")), + patch_props=dict(fc=(0.25, 0.25, 0.25, 0.8), ec="k", lw=0.5, offsets=(1, 1, 1, 2)), + label_props=dict( + every=(1, 4, 10), color="w", rotation=45, weight="bold" # , family="Impact" + ), + line_props=dict(color="w"), + ) + + # it's also possible to update the properties of an existing scalebar + # via the setter-functions! + s4 = m.add_scalebar(n=10, preset="bw") + s4.set_scale_props(width=3, colors=[(1, 0.6, 0), (0, 0.5, 0.5)]) + s4.set_label_props(every=2) + + # NOTE that the last scalebar (s4) is automatically re-scaled and re-positioned + # on zoom events (the default if you don't provide an explicit scale & position)! + + m.add_logo() + m.show() diff --git a/examples/05-custom/GALLERY_HEADER.rst b/examples/05-custom/GALLERY_HEADER.rst new file mode 100644 index 000000000..3c8e6b492 --- /dev/null +++ b/examples/05-custom/GALLERY_HEADER.rst @@ -0,0 +1,3 @@ +================================ +Customize the appearance of plot +================================ diff --git a/examples/05-custom/customization.rst b/examples/05-custom/customization.rst new file mode 100644 index 000000000..3af149721 --- /dev/null +++ b/examples/05-custom/customization.rst @@ -0,0 +1,78 @@ +==================================== +Customize the appearance of the plot +==================================== + +- use ``m.set_plot_specs()`` to set the general appearance of the plot +- after creating the plot, you can access individual objects via ``m.figure.<...>`` … most importantly: + + - ``f`` : the matplotlib figure + - ``ax``, ``ax_cb``, ``ax_cb_plot`` : the axes used for plotting the map, colorbar and histogram + - ``gridspec``, ``cb_gridspec`` : the matplotlib GridSpec instances for the plot and the colorbar + - ``coll`` : the collection representing the data on the map + +.. image:: /_static/example_images/example_customization.png + :width: 75% + :align: center + + +.. code-block:: + + # EOmaps example: Customize the appearance of the plot + + from eomaps import Maps + import pandas as pd + import numpy as np + + # ----------- create some example-data + lon, lat = np.meshgrid(np.arange(-30, 60, 0.25), np.arange(30, 60, 0.3)) + data = pd.DataFrame( + dict(lon=lon.flat, lat=lat.flat, data_variable=np.sqrt(lon**2 + lat**2).flat) + ) + data = data.sample(3000) # take 3000 random datapoints from the dataset + # ------------------------------------ + + m = Maps(crs=3857, figsize=(9, 5)) + m.set_frame(rounded=0.2, lw=1.5, ec="midnightblue", fc="ivory") + m.text(0.5, 0.97, "What a nice figure", fontsize=12) + + m.add_feature.preset.ocean(fc="lightsteelblue") + m.add_feature.preset.coastline(lw=0.25) + + m.set_data(data=data, x="lon", y="lat", crs=4326) + m.set_shape.geod_circles(radius=30000) # plot geodesic-circles with 30 km radius + m.set_classify_specs( + scheme="UserDefined", bins=[35, 36, 37, 38, 45, 46, 47, 48, 55, 56, 57, 58] + ) + m.plot_map( + edgecolor="k", # give shapes a black edgecolor + linewidth=0.5, # with a linewidth of 0.5 + cmap="RdYlBu", # use a red-yellow-blue colormap + vmin=35, # map colors to values between 35 and 60 + vmax=60, + alpha=0.75, # add some transparency + ) + + # add a colorbar + m.add_colorbar( + label="some parameter", + hist_bins="bins", + hist_size=1, + hist_kwargs=dict(density=True), + ) + + # add a y-label to the histogram + m.colorbar.ax_cb_plot.set_ylabel("The Y label") + + # add a logo to the plot + m.add_logo() + + m.apply_layout( + { + "figsize": [9.0, 5.0], + "0_map": [0.10154, 0.2475, 0.79692, 0.6975], + "1_cb": [0.20125, 0.0675, 0.6625, 0.135], + "1_cb_histogram_size": 1, + "2_logo": [0.87501, 0.09, 0.09999, 0.07425], + } + ) + m.show() diff --git a/examples/06-overlays/GALLERY_HEADER.rst b/examples/06-overlays/GALLERY_HEADER.rst new file mode 100644 index 000000000..a7b67e4e9 --- /dev/null +++ b/examples/06-overlays/GALLERY_HEADER.rst @@ -0,0 +1,3 @@ +================================= +Overlays, markers and annotations +================================= diff --git a/examples/06-overlays/overlays.rst b/examples/06-overlays/overlays.rst new file mode 100644 index 000000000..05befa6cb --- /dev/null +++ b/examples/06-overlays/overlays.rst @@ -0,0 +1,175 @@ +================================= +Overlays, markers and annotations +================================= + + +(… plot-generation might take a bit longer since overlays need to be downloaded first!) + +- add basic overlays with `m.add_overlay` +- add static annotations / markers with `m.add_annotation` and `m.add_marker` +- use “connected” Maps-objects to get multiple interactive data-layers! + +.. image:: /_static/example_images/example_overlays.gif + :align: center + :width: 75% + + +The data displayed in the above gif is taken from: + - NaturalEarth (https://www.naturalearthdata.com/) + +.. code-block:: python + + # EOmaps example: Add overlays and indicators + + from eomaps import Maps + import pandas as pd + import numpy as np + import matplotlib.pyplot as plt + from matplotlib.patches import Patch + + # create some data + lon, lat = np.meshgrid(np.linspace(-20, 40, 100), np.linspace(30, 60, 100)) + data = pd.DataFrame( + dict( + lon=lon.flat, + lat=lat.flat, + param=(((lon - lon.mean()) ** 2 - (lat - lat.mean()) ** 2)).flat, + ) + ) + data_OK = data[data.param >= 0] + data_OK.var = np.sqrt(data_OK.param) + data_mask = data[data.param < 0] + + # --------- initialize a Maps object and plot the data + m = Maps(Maps.CRS.Orthographic(), figsize=(10, 7)) + m.ax.set_title("Wooohoo, a flashy map-widget with static indicators!") + m.set_data(data=data_OK, x="lon", y="lat", crs=4326) + m.set_shape.rectangles(mesh=True) + m.set_classify_specs(scheme="Quantiles", k=10) + m.plot_map(cmap="Spectral_r") + + # ... add an "annotate" callback + cid = m.cb.click.attach.annotate(bbox=dict(alpha=0.75, color="w")) + + # - create a new layer and plot another dataset + m2 = m.new_layer() + m2.set_data(data=data_mask, x="lon", y="lat", crs=4326) + m2.set_shape.rectangles() + m2.plot_map(cmap="magma", set_extent=False) + + # create a new layer for some dynamically updated data + m3 = m.new_layer() + m3.set_data(data=data_OK.sample(1000), x="lon", y="lat", crs=4326) + m3.set_shape.ellipses(radius=25000, radius_crs=3857) + + # plot the map and set dynamic=True to allow continuous updates of the + # collection without re-drawing the background map + m3.plot_map( + cmap="gist_ncar", edgecolor="w", linewidth=0.25, dynamic=True, set_extent=False + ) + + + # define a callback that changes the values of the previously plotted dataset + # NOTE: this is not possible for the shapes: "shade_points" and "shade_raster"! + def callback(m, **kwargs): + # NOTE: Since we change the array of a dynamic collection, the changes will be + # reverted as soon as the background is re-drawn (e.g. on pan/zoom events) + selection = np.random.randint(0, len(m.data), 1000) + m.coll.set_array(data_OK.param.iloc[selection]) + + + # attach the callback (to update the dataset plotted on the Maps object "m3") + m.cb.click.attach(callback, m=m3, on_motion=True) + + # --------- add some basic overlays from NaturalEarth + m.add_feature.preset.coastline() + m.add_feature.preset.lakes() + m.add_feature.preset.rivers_lake_centerlines() + m.add_feature.preset.countries() + m.add_feature.preset.urban_areas() + + # add a customized legend + leg = m.ax.legend( + [ + Patch(fc="b"), + plt.Line2D([], [], c="b"), + Patch(fc="r"), + plt.Line2D([], [], c=".75"), + ], + ["lakes", "rivers", "urban areas", "countries"], + ncol=2, + loc="lower center", + facecolor="w", + framealpha=1, + ) + # add the legend as artist to keep it on top + m.BM.add_artist(leg) + + # --------- add some fancy (static) indicators for selected pixels + mark_id = 6060 + for buffer in np.linspace(1, 5, 10): + m.add_marker( + ID=mark_id, + shape="ellipses", + radius="pixel", + fc=(1, 0, 0, 0.1), + ec="r", + buffer=buffer * 5, + n=100, # use 100 points to represent the ellipses + ) + m.add_marker( + ID=mark_id, shape="rectangles", radius="pixel", fc="g", ec="y", buffer=3, alpha=0.5 + ) + m.add_marker( + ID=mark_id, shape="ellipses", radius="pixel", fc="k", ec="none", buffer=0.2 + ) + m.add_annotation( + ID=mark_id, + text=f"Here's Vienna!\n... the data-value is={m.data.param.loc[mark_id]:.2f}", + xytext=(80, 70), + textcoords="offset points", + bbox=dict(boxstyle="round", fc="w", ec="r"), + horizontalalignment="center", + arrowprops=dict(arrowstyle="fancy", facecolor="r", connectionstyle="arc3,rad=0.35"), + ) + + mark_id = 3324 + m.add_marker(ID=mark_id, shape="ellipses", radius=3, fc="none", ec="g", ls="--", lw=2) + m.add_annotation( + ID=mark_id, + text="", + xytext=(0, 98), + textcoords="offset points", + arrowprops=dict( + arrowstyle="fancy", facecolor="g", connectionstyle="arc3,rad=-0.25" + ), + ) + + m.add_marker( + ID=mark_id, + shape="geod_circles", + radius=500000, + radius_crs=3857, + fc="none", + ec="b", + ls="--", + lw=2, + ) + + m.add_annotation( + ID=mark_id, + text=( + "Here's the center of:\n" + + " $\\bullet$ a blue 'circle' with 50km radius\n" + + " $\\bullet$ a green 'circle' with 3deg radius" + ), + xytext=(-80, 100), + textcoords="offset points", + bbox=dict(boxstyle="round", fc="w", ec="k"), + horizontalalignment="left", + arrowprops=dict(arrowstyle="fancy", facecolor="w", connectionstyle="arc3,rad=0.35"), + ) + + cb = m.add_colorbar(label="The Data", tick_precision=1) + m.add_logo() + m.show() diff --git a/examples/GALLERY_HEADER.rst b/examples/GALLERY_HEADER.rst new file mode 100755 index 000000000..b469aa824 --- /dev/null +++ b/examples/GALLERY_HEADER.rst @@ -0,0 +1,10 @@ +.. include:: ../substitutions.rst + +:html_theme.sidebar_secondary.remove: true + +.. _EOmaps_examples: + +🗺 EOmaps examples +================== + +This page contains a collection of examples that show how to create beautiful maps. Click on any image to see the full image and source code. diff --git a/examples/callbacks/GALLERY_HEADER.rst b/examples/callbacks/GALLERY_HEADER.rst new file mode 100644 index 000000000..a6e640c6a --- /dev/null +++ b/examples/callbacks/GALLERY_HEADER.rst @@ -0,0 +1,3 @@ +=================================================== +Callbacks : turn your maps into interactive widgets +=================================================== diff --git a/examples/callbacks/callbacks.rst b/examples/callbacks/callbacks.rst new file mode 100644 index 000000000..eb2fbf8ab --- /dev/null +++ b/examples/callbacks/callbacks.rst @@ -0,0 +1,211 @@ +=================================================== +Callbacks : turn your maps into interactive widgets +=================================================== + +- **Callback functions** can easily be attached to the plot to turn it + into an interactive plot-widget! + + - | there’s a nice list of (customizable) pre-defined callbacks accessible via: + | ``m.cb.click``, ``m.cb.pick``, ``m.cb.keypress`` and ``m.cb.dynamic`` + + - use ``annotate`` (and ``clear_annotations``) to create text-annotations + - use ``mark`` (and ``clear_markers``) to add markers + - use ``peek_layer`` (and ``switch_layer``) to compare multiple layers of data + - ... and many more: ``plot``, ``print_to_console``, ``get_values``, ``load`` ... + + - | ... but you can also define a custom one and connect it via + | ``m.cb.click.attach()`` (works also with ``pick`` and ``keypress``)! + + +.. image:: /_static/example_images/example_callbacks.gif + :align: center + +.. code-block:: python + + # EOmaps example: Turn your maps into a powerful widgets + + from eomaps import Maps + import pandas as pd + import numpy as np + + # create some data + lon, lat = np.meshgrid(np.linspace(-20, 40, 50), np.linspace(30, 60, 50)) + + data = pd.DataFrame( + dict(lon=lon.flat, lat=lat.flat, data=np.sqrt(lon**2 + lat**2).flat) + ) + + # --------- initialize a Maps object and plot a basic map + m = Maps(crs=3035, figsize=(10, 8)) + m.set_data(data=data, x="lon", y="lat", crs=4326) + m.ax.set_title("A clickable widget!") + m.set_shape.rectangles() + + m.set_classify_specs(scheme="EqualInterval", k=5) + m.add_feature.preset.coastline() + m.add_feature.preset.ocean() + m.plot_map() + + # add some static text + m.text( + 0.66, + 0.92, + ( + "Left-click: temporary annotations\n" + "Right-click: permanent annotations\n" + "Middle-click: clear permanent annotations" + ), + fontsize=10, + horizontalalignment="left", + verticalalignment="top", + color="k", + fontweight="bold", + bbox=dict(facecolor="w", alpha=0.75), + ) + + + # --------- attach pre-defined CALLBACK functions --------- + + ### add a temporary annotation and a marker if you left-click on a pixel + m.cb.pick.attach.mark( + button=1, + permanent=False, + fc=[0, 0, 0, 0.5], + ec="w", + ls="--", + buffer=2.5, + shape="ellipses", + zorder=1, + ) + m.cb.pick.attach.annotate( + button=1, + permanent=False, + bbox=dict(boxstyle="round", fc="w", alpha=0.75), + zorder=999, + ) + ### save all picked values to a dict accessible via m.cb.get.picked_vals + m.cb.pick.attach.get_values(button=1) + + ### add a permanent marker if you right-click on a pixel + m.cb.pick.attach.mark( + button=3, + permanent=True, + facecolor=[1, 0, 0, 0.5], + edgecolor="k", + buffer=1, + shape="rectangles", + zorder=1, + ) + + + ### add a customized permanent annotation if you right-click on a pixel + def text(m, ID, val, pos, ind): + return f"ID={ID}" + + + m.cb.pick.attach.annotate( + button=3, + permanent=True, + bbox=dict(boxstyle="round", fc="r"), + text=text, + xytext=(10, 10), + zorder=2, # use zorder=2 to put the annotations on top of the markers + ) + + ### remove all permanent markers and annotations if you middle-click anywhere on the map + m.cb.pick.attach.clear_annotations(button=2) + m.cb.pick.attach.clear_markers(button=2) + + # --------- define a custom callback to update some text to the map + # (use a high zorder to draw the texts above all other things) + txt = m.text( + 0.5, + 0.35, + "You clicked on 0 pixels so far", + fontsize=15, + horizontalalignment="center", + verticalalignment="top", + color="w", + fontweight="bold", + animated=True, + zorder=99, + transform=m.ax.transAxes, + ) + txt2 = m.text( + 0.18, + 0.9, + " lon / lat " + "\n", + fontsize=12, + horizontalalignment="right", + verticalalignment="top", + fontweight="bold", + animated=True, + zorder=99, + transform=m.ax.transAxes, + ) + + + def cb1(m, pos, ID, val, **kwargs): + # update the text that indicates how many pixels we've clicked + nvals = len(m.cb.pick.get.picked_vals["ID"]) + txt.set_text( + f"You clicked on {nvals} pixel" + + ("s" if nvals > 1 else "") + + "!\n... and the " + + ("average " if nvals > 1 else "") + + f"value is {np.mean(m.cb.pick.get.picked_vals['val']):.3f}" + ) + + # update the list of lon/lat coordinates on the top left of the figure + d = m.data.loc[ID] + lonlat_list = txt2.get_text().splitlines() + if len(lonlat_list) > 10: + lonlat_txt = lonlat_list[0] + "\n" + "\n".join(lonlat_list[-10:]) + "\n" + else: + lonlat_txt = txt2.get_text() + txt2.set_text(lonlat_txt + f"{d['lon']:.2f} / {d['lat']:.2f}" + "\n") + + + m.cb.pick.attach(cb1, button=1, m=m) + + + def cb2(m, pos, val, **kwargs): + # plot a marker at the pixel-position + (l,) = m.ax.plot(*pos, marker="*", animated=True) + # add the custom marker to the blit-manager! + m.BM.add_artist(l) + + # print the value at the pixel-position + # use a low zorder so the text will be drawn below the temporary annotations + m.text( + pos[0], + pos[1] - 150000, + f"{val:.2f}", + horizontalalignment="center", + verticalalignment="bottom", + color=l.get_color(), + zorder=1, + transform=m.ax.transData, + ) + + + m.cb.pick.attach(cb2, button=3, m=m) + + # add a "target-indicator" on mouse-movement + m.cb.move.attach.mark(fc="r", ec="none", radius=10000, shape="geod_circles") + m.cb.move.attach.mark(fc="none", ec="r", radius=50000, shape="geod_circles") + + # add a colorbar + m.add_colorbar(hist_bins="bins", label="A classified dataset") + m.add_logo() + + m.apply_layout( + { + "figsize": [10.0, 8.0], + "0_map": [0.04375, 0.27717, 0.9125, 0.69566], + "1_cb": [0.01, 0.0, 0.98, 0.23377], + "1_cb_histogram_size": 0.8, + "2_logo": [0.825, 0.29688, 0.12, 0.06188], + } + ) + m.show() diff --git a/examples/widgets/GALLERY_HEADER.rst b/examples/widgets/GALLERY_HEADER.rst new file mode 100644 index 000000000..a636c0f09 --- /dev/null +++ b/examples/widgets/GALLERY_HEADER.rst @@ -0,0 +1,3 @@ +========================= +Widgets for data analysis +========================= diff --git a/examples/widgets/row_col_selector.rst b/examples/widgets/row_col_selector.rst new file mode 100644 index 000000000..1c0cda8bf --- /dev/null +++ b/examples/widgets/row_col_selector.rst @@ -0,0 +1,132 @@ +================================ +Select 1D slices of a 2D dataset +================================ + +Use custom callback functions to perform arbitrary tasks on the data when clicking on the map. + +- Identify clicked row/col in a 2D dataset +- Highlight the found row and column using a new layer + +(requires EOmaps >= v3.1.4) + +.. image:: /_static/example_images/example_row_col_selector.gif + :width: 75% + :align: center + +.. code-block:: python + + # EOmaps example: Select 1D slices of a 2D dataset + + from eomaps import Maps + import numpy as np + + # setup some random 2D data + lon, lat = np.meshgrid(np.linspace(-180, 180, 200), np.linspace(-90, 90, 100)) + data = np.sqrt(lon**2 + lat**2) + np.random.normal(size=lat.shape) ** 2 * 20 + name = "some parameter" + # ---------------------- + + # create a new map spanning the left row of a 2x2 grid + m = Maps(crs=Maps.CRS.InterruptedGoodeHomolosine(), ax=(2, 2, (1, 3)), figsize=(8, 5)) + m.add_feature.preset.coastline() + m.set_data(data, lon, lat, parameter=name) + m.set_classify_specs(Maps.CLASSIFIERS.NaturalBreaks, k=5) + m.plot_map() + + # create 2 ordinary matplotlib axes to show the selected data + ax_row = m.f.add_subplot(222) # 2x2 grid, top right + ax_row.set_xlabel("Longitude") + ax_row.set_ylabel(name) + ax_row.set_xlim(-185, 185) + ax_row.set_ylim(data.min(), data.max()) + + ax_col = m.f.add_subplot(224) # 2x2 grid, bottom right + ax_col.set_xlabel("Latitude") + ax_col.set_ylabel(name) + ax_col.set_xlim(-92.5, 92.5) + ax_col.set_ylim(data.min(), data.max()) + + # add a colorbar for the data + m.add_colorbar(label=name) + m.colorbar.ax_cb.tick_params(rotation=90) # rotate colorbar ticks 90° + + # add new layers to plot row- and column data + m2 = m.new_layer() + m2.set_shape.ellipses(m.shape.radius) + + m3 = m.new_layer() + m3.set_shape.ellipses(m.shape.radius) + + + # define a custom callback to indicate the clicked row/column + def cb(m, ind, ID, *args, **kwargs): + # get row and column from the data + # NOTE: "ind" always represents the index of the flattened array! + r, c = np.unravel_index(ind, m.data.shape) + + # ---- highlight the picked column + # use "dynamic=True" to avoid re-drawing the background on each pick + # use "set_extent=False" to avoid resetting the plot extent on each draw + m2.set_data(m.data_specs.data[:, c], m.data_specs.x[:, c], m.data_specs.y[:, c]) + m2.plot_map(fc="none", ec="b", set_extent=False, dynamic=True) + + # ---- highlight the picked row + m3.set_data(m.data_specs.data[r, :], m.data_specs.x[r, :], m.data_specs.y[r, :]) + m3.plot_map(fc="none", ec="r", set_extent=False, dynamic=True) + + # ---- plot the data for the selected column + (art0,) = ax_col.plot(m.data_specs.y[:, c], m.data_specs.data[:, c], c="b") + (art01,) = ax_col.plot( + m.data_specs.y[r, c], + m.data_specs.data[r, c], + c="k", + marker="o", + markerfacecolor="none", + ms=10, + ) + + # ---- plot the data for the selected row + (art1,) = ax_row.plot(m.data_specs.x[r, :], m.data_specs.data[r, :], c="r") + (art11,) = ax_row.plot( + m.data_specs.x[r, c], + m.data_specs.data[r, c], + c="k", + marker="o", + markerfacecolor="none", + ms=10, + ) + + # make all artists temporary (e.g. remove them on next pick) + # "m2.coll" represents the collection created by "m2.plot_map()" + for a in [art0, art01, art1, art11, m2.coll, m3.coll]: + m.cb.pick.add_temporary_artist(a) + + + # attach the custom callback + m.cb.pick.attach(cb, m=m) + + + # ---- add a pick-annotation with a custom text + def text(ind, val, **kwargs): + r, c = np.unravel_index(ind, m.data.shape) + return ( + f"row/col = {r}/{c}\n" + f"lon/lat = {m.data_specs.x[r, c]:.2f}/{m.data_specs.y[r, c]:.2f}\n" + f"val = {val:.2f}" + ) + + + m.cb.pick.attach.annotate(text=text, fontsize=7) + + # apply a previously arranged layout (e.g. check "layout-editor" in the docs!) + m.apply_layout( + { + "figsize": [8, 5], + "0_map": [0.015, 0.49253, 0.51, 0.35361], + "1_": [0.60375, 0.592, 0.38, 0.392], + "2_": [0.60375, 0.096, 0.38, 0.392], + "3_cb": [0.025, 0.144, 0.485, 0.28], + "3_cb_histogram_size": 0.8, + } + ) + m.show() diff --git a/examples/widgets/timeseries.rst b/examples/widgets/timeseries.rst new file mode 100644 index 000000000..7fe5e8721 --- /dev/null +++ b/examples/widgets/timeseries.rst @@ -0,0 +1,119 @@ +========================= +Timeseries and histograms +========================= + + +Callback-functions can be used to trigger updates on other plots. +This example shows how to use EOmaps to analyze a database that is associated with a map. + +- create a grid of ``Maps`` objects and ordinary matplotlib axes via ``MapsGrid`` +- define a custom callback to update the plots if you click on the map + + +.. image:: /_static/example_images/example_timeseries.gif + :width: 75% + :align: center + +.. code-block:: python + + # EOmaps example: Data analysis widgets - Interacting with a database + + from eomaps import Maps + import pandas as pd + import numpy as np + + # ============== create a random database ============= + length, Nlon, Nlat = 1000, 100, 50 + lonmin, lonmax, latmin, latmax = -70, 175, 0, 75 + + database = np.full((Nlon * Nlat, length), np.nan) + for i in range(Nlon * Nlat): + size = np.random.randint(1, length) + x = np.random.normal(loc=np.random.rand(), scale=np.random.rand(), size=size) + np.put(database, range(i * length, i * length + size), x) + lon, lat = np.meshgrid( + np.linspace(lonmin, lonmax, Nlon), np.linspace(latmin, latmax, Nlat) + ) + + IDs = [f"point_{i}" for i in range(Nlon * Nlat)] + database = pd.DataFrame(database, index=IDs) + coords = pd.DataFrame(dict(lon=lon.flat, lat=lat.flat), index=IDs) + + # -------- calculate the number of values in each dataset + # (e.g. the data actually shown on the map) + data = pd.DataFrame(dict(count=database.count(axis=1), **coords)) + # ===================================================== + + + # initialize a map on top + m = Maps(ax=211) + m.add_feature.preset.ocean() + m.add_feature.preset.coastline() + + # initialize 2 matplotlib plot-axes below the map + ax_left = m.f.add_subplot(223) + ax_left.set_ylabel("data-values") + ax_left.set_xlabel("data-index") + + ax_right = m.f.add_subplot(224) + ax_right.set_ylabel("data-values") + ax_right.set_xlabel("histogram count") + + ax_left.sharey(ax_right) + + # -------- assign data to the map and plot it + m.set_data(data=data, x="lon", y="lat", crs=4326) + m.set_classify_specs( + scheme=Maps.CLASSIFIERS.UserDefined, + bins=[50, 100, 200, 400, 800], + ) + m.set_shape.ellipses(radius=0.5) + m.plot_map() + + + # -------- define a custom callback function to update the plots + def update_plots(ID, **kwargs): + # get the data + x = database.loc[ID].dropna() + + # plot the lines and histograms + (l,) = ax_left.plot(x, lw=0.5, marker=".", c="C0") + cnt, val, art = ax_right.hist(x.values, bins=50, orientation="horizontal", fc="C0") + + # re-compute axis limits based on the new artists + ax_left.relim() + ax_right.relim() + ax_left.autoscale() + ax_right.autoscale() + + # add all artists as "temporary pick artists" so that they + # are removed when the next datapoint is selected + for a in [l, *art]: + m.cb.pick.add_temporary_artist(a) + + + # attach the custom callback (and some pre-defined callbacks) + m.cb.pick.attach(update_plots) + m.cb.pick.attach.annotate() + m.cb.pick.attach.mark(permanent=False, buffer=1, fc="none", ec="r") + m.cb.pick.attach.mark(permanent=False, buffer=2, fc="none", ec="r", ls=":") + + # add a colorbar + m.add_colorbar(0.25, label="Number of observations") + m.colorbar.ax_cb_plot.tick_params(labelsize=6) + + # add a logo + m.add_logo() + + m.apply_layout( + { + "figsize": [6.4, 4.8], + "0_map": [0.05625, 0.60894, 0.8875, 0.36594], + "1_": [0.12326, 0.11123, 0.35, 0.31667], + "2_": [0.58674, 0.11123, 0.35, 0.31667], + "3_cb": [0.12, 0.51667, 0.82, 0.06166], + "3_cb_histogram_size": 0.8, + "4_logo": [0.8125, 0.62333, 0.1212, 0.06667], + } + ) + m.show() diff --git a/tests/test_basic_functions.py b/tests/test_basic_functions.py index 166b917a1..043612292 100644 --- a/tests/test_basic_functions.py +++ b/tests/test_basic_functions.py @@ -3,11 +3,11 @@ import matplotlib as mpl import matplotlib.pyplot as plt -from matplotlib.gridspec import GridSpec -from matplotlib.backend_bases import MouseEvent, KeyEvent - -import pandas as pd import numpy as np +import pandas as pd +from matplotlib.backend_bases import KeyEvent, MouseEvent +from matplotlib.gridspec import GridSpec +from pytest import mark from eomaps import Maps, MapsGrid @@ -938,6 +938,7 @@ def test_utility_widgets(self): s3.remove() s4.remove() + @mark.skip("nominatim.openstreetmap.org is not available now") def test_set_extent_to_location(self): import requests diff --git a/tests/test_doc_codeblocks.py b/tests/test_doc_codeblocks.py index 22dd683fe..191843fea 100644 --- a/tests/test_doc_codeblocks.py +++ b/tests/test_doc_codeblocks.py @@ -20,7 +20,7 @@ def test(*args, **kwargs): return test -def is_code_block(node): +def is_test_code_block(node): return ( node.tagname == "literal_block" and "code" in node.attributes["classes"] @@ -29,42 +29,83 @@ def is_code_block(node): ) -class _TestSequenceMeta(type): +def is_any_code_block(node): + return node.tagname == "literal_block" and "code" in node.attributes["classes"] + + +def _parse_codeblocks_as_test(p, tests, condition=is_test_code_block): + with open(p, "r", encoding="utf8") as file: + data = file.read() + + # initialize a "Publisher" to parse the file + doctree = publish_doctree( + data, settings_overrides={"report_level": Reporter.SEVERE_LEVEL} + ) + + # get a list of all code-blocks in the file + code_blocks = list(doctree.findall(condition=condition)) + + # generate unique tests for each code snippet + for i, node in enumerate(code_blocks): + source_code = node.astext() + names = node.attributes.get("names") + if len(names) > 0: + name = names[0] + else: + name = p.name + + test_name = f"test_{p.stem}_{i}" + tests[test_name] = gen_test(i, name, source_code) + + +# the path to the re-structured text file that should be analyzed +docs_path = Path(__file__).parent.parent / "docs" / "source" +examples_path = Path(__file__).parent.parent / "examples" + + +class _TestDocsSequenceMeta(type): + """ + Metaclass to create tests from each code-block in the documentation + whose ID starts with "test_". + """ + def __new__(mcs, name, bases, tests): - # the path to the re-structured text file that should be analyzed - parent_path = Path(__file__).parent.parent / "docs" - # set cwd to doc parent path to avoid issues with 'include' statements + # remember current working directory cwd = os.getcwd() - os.chdir(parent_path) - for p in filter(lambda x: x.suffix == ".rst", parent_path.iterdir()): - with open(p, "r", encoding="utf8") as file: - data = file.read() + # set cwd to doc parent path to avoid issues with 'include' statements + for p in docs_path.rglob("*.rst"): + os.chdir(p.parent) + _parse_codeblocks_as_test(p, tests, condition=is_test_code_block) + + os.chdir(cwd) + + return type.__new__(mcs, name, bases, tests) + - # initialize a "Publisher" to parse the file - doctree = publish_doctree( - data, settings_overrides={"report_level": Reporter.SEVERE_LEVEL} - ) +class _TestExamplesSequenceMeta(type): + """ + Metaclass to create tests from each code-block found in the examples directory. + """ - # get a list of all code-blocks in the file - # replace .traverse with .findall once docutils > 18.1 is used! - if hasattr(doctree, "findall"): - code_blocks = list(doctree.findall(condition=is_code_block)) - else: - code_blocks = list(doctree.traverse(condition=is_code_block)) + def __new__(mcs, name, bases, tests): + # remember current working directory + cwd = os.getcwd() - # generate unique tests for each code snippet - for i, node in enumerate(code_blocks): - source_code = node.astext() - name = node.attributes["names"][0] + # set cwd to doc user_guide path to avoid issues with 'include' statements + os.chdir(docs_path / "user_guide") - test_name = f"test_{p.stem}_{i}" - tests[test_name] = gen_test(i, name, source_code) + for p in examples_path.rglob("*.rst"): + _parse_codeblocks_as_test(p, tests, condition=is_any_code_block) os.chdir(cwd) return type.__new__(mcs, name, bases, tests) -class TestDocumentationCodeblocks(unittest.TestCase, metaclass=_TestSequenceMeta): +class TestDocumentationCodeblocks(unittest.TestCase, metaclass=_TestDocsSequenceMeta): + pass + + +class TestExamplesCodeblocks(unittest.TestCase, metaclass=_TestExamplesSequenceMeta): pass diff --git a/tests/test_doc_notebooks.py b/tests/test_doc_notebooks.py index 876846096..d89b2f192 100644 --- a/tests/test_doc_notebooks.py +++ b/tests/test_doc_notebooks.py @@ -20,7 +20,8 @@ plt.ion() # use interactive mode to avoid blocking images -basepath = Path(__file__).parent.parent / "docs" / "notebooks" +basepath = Path(__file__).parent.parent / "docs" +note_files = list(basepath.rglob("*.ipynb")) class TestDocNotebooks: @@ -38,7 +39,7 @@ def _use_cell(self, cell): @pytest.mark.parametrize( "notebook", - filter(lambda x: x.suffix == ".ipynb", basepath.iterdir()), + note_files, ids=lambda x: x.stem, ) def test_doc_notebook(self, notebook): diff --git a/tests/test_examples.py b/tests/test_examples.py deleted file mode 100644 index f98dc6a5c..000000000 --- a/tests/test_examples.py +++ /dev/null @@ -1,44 +0,0 @@ -"""Test running all python-files in docs/examples that start with 'example_' """ -from pathlib import Path -import matplotlib.pyplot as plt -import unittest -import pytest -import numpy as np - - -def gen_test(name, code): - @pytest.mark.mpl_image_compare() - def test(*args, **kwargs): - try: - np.random.seed(0) # make tests descriptive - exec(code) - - return locals()["m"] - except Exception as ex: - raise AssertionError(f"Example '{name}' failed.") from ex - finally: - plt.close("all") - - return test - - -class _TestSequenceMeta(type): - def __new__(mcs, name, bases, tests): - # the path to the folder containing the example scripts - parent_path = Path(__file__).parent.parent / "docs" / "examples" - - examples = filter( - lambda x: x.stem.startswith("example_") and x.suffix == ".py", - parent_path.iterdir(), - ) - - # generate unique tests for each example - for f in examples: - test_name = f"test_{f.stem}" - tests[test_name] = gen_test(name, f.read_text()) - - return type.__new__(mcs, name, bases, tests) - - -class TestExamples(unittest.TestCase, metaclass=_TestSequenceMeta): - pass