{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import geoviews as gv\n", "import geoviews.feature as gf\n", "\n", "from geoviews import opts\n", "from cartopy import crs\n", "\n", "gv.extension('bokeh', 'matplotlib')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The GeoViews package provides a library of [HoloViews](https://holoviews.org) Element types which make it very easy to plot data on various geographic projections and other utilities to plot in geographic coordinate systems.\n", "\n", "Elements are very simple wrappers around the data along with some declaration about its dimensions. The only thing that distinguishes a *GeoViews* element from a *HoloViews* one is the addition of a ``crs`` parameter. The ``crs`` parameter defines a cartopy coordinate reference system declaring the coordinate system of the data. This allows GeoViews to automatically project the data to the displayed ``projection``. The ``crs`` therefore serves a dual purpose:\n", "\n", "1. The ``Element.crs`` defines which coordinate system is defined in.\n", "2. The plot ``projection`` defined as a plot option defines what coordinate system to display the data in.\n", "\n", "By default all elements assume a ``PlateCarree`` projection (also sometimes known as the equirectangular projection), and therefore expects longitudes and latitudes. \n", "\n", "To start with let's declare two ``Points`` objects, one using the default ``PlateCarree`` ``crs``, the other using the ``GOOGLE_MERCATOR`` coordinate system. Just looking at the difference in the coordinates for NYC or Beijing and London we can immediately tell they are very different units but by declaring the ``crs`` we can make it possible to automatically translate between them:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "nyc, beijing = (-74.0, 40.7, 'NYC'), (116.4, 39.9, 'Beijing')\n", "london = (14471.53, 6712008., 'London')\n", "\n", "cities_lonlat = gv.Points([nyc, beijing], vdims='City')\n", "cities_mercator = gv.Points([london], crs=crs.GOOGLE_MERCATOR, vdims='City')\n", "\n", "(gv.tile_sources.OSM * cities_lonlat * cities_mercator).opts(\n", " opts.Points(global_extent=True, width=500, height=475, size=12, color='black'))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As we can see we overlaid the above plot on a WMTS tile source, letting us interactively zoom in and out of the plot. This functionality is only available when using a Web Mercator ``projection``, which is what the bokeh backend uses by default. When using matplotlib on the other hand the plot will automatically use the ``crs`` declared on the plotted element (in this case ``PlateCarree``):" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "features = gv.Overlay([gf.ocean, gf.land, gf.rivers, gf.lakes, gf.borders, gf.coastline])\n", "\n", "gv.output(features, backend='matplotlib', fig='svg', size=300)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When using bokeh a custom plot ``projection`` may also be used. The same behavior as matplotlib can be enabled with the ``infer_projection`` parameter or an explicit projection can be set:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "(features * cities_lonlat * cities_mercator).options(\n", " opts.Points(projection=crs.Mollweide(), width=800, height=400, size=12, color='black'))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Supported projections" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "GeoViews supports all projections defined in the ``cartopy.crs`` module, which are laid out in detail in the [cartopy documentation](http://scitools.org.uk/cartopy/docs/v0.15/crs/projections.html). Here we list a representative collection of these coordinate systems (skipping ``PlateCarree`` and ``Mercator`` which we have already seen):" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "projections = [crs.RotatedPole, crs.LambertCylindrical, crs.Geostationary, \n", " crs.AzimuthalEquidistant, crs.OSGB, crs.EuroPP, crs.Gnomonic,\n", " crs.Mollweide, crs.OSNI, crs.Miller, crs.InterruptedGoodeHomolosine,\n", " crs.SouthPolarStereo, crs.Orthographic, crs.NorthPolarStereo, crs.Robinson,\n", " crs.LambertConformal, crs.AlbersEqualArea]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we want to display the plot in a different coordinate system than the data is defined in we can declare the ``projection`` as a plot option:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "proj_layout = gv.Layout([gf.coastline.relabel(group=p.__name__).opts(projection=p(), backend='matplotlib')\n", " for p in projections])\n", "\n", "gv.output(proj_layout, backend='matplotlib')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Projecting with bokeh" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When working with bokeh, as we saw above, it defaults to a web Mercator projection. So if we want to override the plot projection we have to explicitly declare it as a plot option:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "opts = dict(width=200, height=225, global_extent=True, axiswise=True)\n", "gv.Layout([gf.coastline.relabel(group=p.__name__).opts(projection=p(), **opts) for p in projections]).cols(4)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Customizing projections" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Many coordinate reference systems provide various parameters to customize certain aspects of the projection, e.g. the Orthographic CRS allows overriding the ``central_longitude`` and ``central_latitude`` to change the view of the globe:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "features.opts(projection=crs.Orthographic(central_longitude=-90, central_latitude=30), global_extent=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This is particularly important when the data is specified in the longitudes of the data are defined in the range 0 to 360 rather than the default -180 to 180. In this case ensure that the ``crs`` of the element declares the actual ``central_longitude``, e.g. 180 degrees rather than the default 0 degrees. Let us draw the same polygon with the default ``crs`` (centered on 0) and a custom ``crs`` centered on 180 degrees:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "poly_data = [(180, 0), (270, 45), (360, 0)]\n", "features * gv.Polygons(poly_data) * gv.Polygons(poly_data, crs=crs.PlateCarree(central_longitude=180)).opts(global_extent=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As we can see the default projection wraps the values above 180, while the custom projection automatically translates the polygon by the ``central_longitude``." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Explicitly projecting" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As we have discovered the plotting system will project data to the desired coordinate system automatically. However in certain cases this can be quite expensive especially if it occurs multiple times. Therefore GeoViews makes a high-level operation available to project most types of data from their native ``crs`` to the declared projection. Here we will declare a ``Points`` element containing the longitude/latitude locations of a number of cities. Using the ``gv.project`` operation we can easily project the data to Mercator coordinates:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "cities = gv.Points([(-74.01, 40.71, 'New York'), (0.13, 51.51, 'London'), (116.40, 39.9, 'Beijing')], vdims='City')\n", "projected = gv.operation.project(cities, projection=crs.GOOGLE_MERCATOR)\n", "print(projected.crs)\n", "projected.dframe()" ] } ], "metadata": { "language_info": { "name": "python", "pygments_lexer": "ipython3" } }, "nbformat": 4, "nbformat_minor": 2 }