New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add contour map functionality #958

Open
tjansson60 opened this Issue Sep 10, 2018 · 10 comments

Comments

Projects
None yet
5 participants
@tjansson60

tjansson60 commented Sep 10, 2018

Dear all

I would like to make a map of weather-like data as temperature or pressure on a folium map similar to the plot below (from https://cdoovision.com/us-temperature-contour-map/):
us-temperature-contour-map-sfc-con-temp

As it can be seen it is not a choropleth map as the contours go through the states and it is not a heatmap as I do not care how many measurements underlies this. The data I have is not gridded, but I have many individual measurements semi-randomly scattered over a large area.

Is this possible in folium currently in any way?

@jtbaker

This comment has been minimized.

Contributor

jtbaker commented Sep 10, 2018

This is a pretty broad question.

I think it needs to be reframed in terms of, what does your data look like? There are two methods of data classification for pretty much all GIS - Vector and Raster data.

  • Vector
    A collection of one or more vertices to form some sort of geometric shape "feature" we can display on a two dimensional surface like a map on our screens - these usually take the form of points, lines, polygons, or multipolygons. This format is suited to data that is either continous or discrete in nature.

  • Raster
    A grid or matrix array of values on a coordinate plane. You can think of these as "pixels" on a screen, with each pixel having a quantitative "value". They can vary in terms of their resolution, and the area that they cover. This format is better suited to data which is continuous in nature.

They're fundamentally different types of data, each with their own strengths and weaknesses. here's some more reading on this subject.

The map you posted could easily be plotted as a choropleth map if each of the bands was classified as a polygon with some sort of data classification based on which to color it. But I think in general natural science/physical geography mapping tends to use rasters more commonly.

folium is capable of plotting both types of data - once you figure out what sorts of data that you have access to, you probably want to start with either the folium.features.GeoJson class for vector, or folium.raster_layers.ImageOverlay class for raster. There are examples of each in the example notebook section.

Hope this helps!

@tjansson60

This comment has been minimized.

tjansson60 commented Sep 11, 2018

Hi Jason

Thank for the detailed answer for my perhaps imprecise question.

I have collection of semi-randomly distributed vector points for a selection of vehicles where I have (speed, latitude, longitude). I wish to make a contour map of speeds over a folium interactive map.

I am not interested in any sectioning of the geografic map in municipalities, zip number or parishes, so a choropleth map is not the solution. Individual points can be also be noisy and they are not interesting by themselves. So what I was looking for was a simple way to superimpose a contour plot on a folium map.

In this example a contour map is superimposed on a basemap and the underlying scattered individual points can also be seen:
qv0xd
(from: https://stackoverflow.com/questions/26872337/how-can-i-get-my-contour-plot-superimposed-on-a-basemap)

So I think I understand I could make the gridded contour my self and plot on top of the folium map as a raster layer, but what I was really hopping for what that there was a builtin method that just took the data and some parameters and did it automatically. :)

Kind regards
Thomas Jansson

@Conengmo

This comment has been minimized.

Collaborator

Conengmo commented Sep 11, 2018

Hi Thomas,

Interesting question, but as far as I know this is currently not included in folium. Would be cool to have it though! We use Leaflet under the hood, so maybe you can check if there is any Leaflet plugin that does what you want. We can than implement that plugin in folium.

@tjansson60

This comment has been minimized.

tjansson60 commented Sep 11, 2018

Hi Frank

That's the thing. I have grown so accustomed to folium more less being able to do everything I want with very few lines of code, so I think it would make a great addition. :)

I didn't find anything in the official leaflet docs, but I did find this interesting article on creating just this kind of plot in R using their leaflet wrapper. So I am guessing their code could be wrapped in a helper function in folium:

image
From: http://technocyclist.blogspot.com/2014/10/plot-contour-polygons-in-leaflet-using-r.html

@ocefpaf

This comment has been minimized.

Member

ocefpaf commented Sep 11, 2018

@tjansson60 here is what folium can do so far:

http://nbviewer.jupyter.org/gist/ocefpaf/78095b6e5d22723aae3bf05e5600e165

You'll note that we are lacking:

  • An easy way to add the legend for the color values. We can generate a raster image in the raster example and add as a float image but that is a "hack."
  • A nice way to do the vector option directly from the GeoJSON created by mplleaflet, at the moment the color info in there but it is ignored.
@tjansson60

This comment has been minimized.

tjansson60 commented Sep 11, 2018

@ocefpaf Thanks for the example.

@Conengmo

This comment has been minimized.

Collaborator

Conengmo commented Sep 17, 2018

Cool example @ocefpaf. Would be interesting to find a way to implement this in folium in a sensible way. I couldn't find any Leaflet code that does contour maps, so we're on our own.

@ocefpaf

This comment has been minimized.

Member

ocefpaf commented Sep 17, 2018

I couldn't find any Leaflet code that does contour maps, so we're on our own.

Yep 😄

Would be interesting to find a way to implement this in folium in a sensible way

The idea if using mplleaflet to plot matplotlib GeoJson is one that I wanted to do for a while. That would cover the vector version of the problem

Regarding raster, I guess that is relatively easy to do and I'm not inclined to add a built-in support for it beyond what we already have.

The issue with the legend remains though. We need to investigate how to do that properly.

@Conengmo Conengmo changed the title from question: contour map of values on map to Add contour map functionality Sep 29, 2018

@tjansson60

This comment has been minimized.

tjansson60 commented Oct 4, 2018

A little update from my side: I now have something working and I have written an example below to illustrate this. I hope others perhaps can use this until a real feature is available in folium. As I have written before I have grown accustomed to everything being very simple in folium, so I think a built-in contour map function would be a great addition. :)

image

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import numpy as np
import pandas as pd
import folium
import branca
from folium import plugins
import matplotlib.pyplot as plt
from scipy.interpolate import griddata
import geojsoncontour
import scipy as sp
import scipy.ndimage

# Setup
temp_mean = 12
temp_std  = 2
debug     = False

# Setup colormap
colors = ['#d7191c',  '#fdae61',  '#ffffbf',  '#abdda4',  '#2b83ba']
vmin   = temp_mean - 2 * temp_std
vmax   = temp_mean + 2 * temp_std
levels = len(colors)
cm     = branca.colormap.LinearColormap(colors, vmin=vmin, vmax=vmax).to_step(levels)

# Create a dataframe with fake data
df = pd.DataFrame({
    'longitude':   np.random.normal(11.84,     0.15,     1000),
    'latitude':    np.random.normal(55.55,     0.15,     1000),
    'temperature': np.random.normal(temp_mean, temp_std, 1000)})

# The original data
x_orig = np.asarray(df.longitude.tolist())
y_orig = np.asarray(df.latitude.tolist())
z_orig = np.asarray(df.temperature.tolist())

# Make a grid
x_arr          = np.linspace(np.min(x_orig), np.max(x_orig), 500)
y_arr          = np.linspace(np.min(y_orig), np.max(y_orig), 500)
x_mesh, y_mesh = np.meshgrid(x_arr, y_arr)

# Grid the values
z_mesh = griddata((x_orig, y_orig), z_orig, (x_mesh, y_mesh), method='linear')

# Gaussian filter the grid to make it smoother
sigma = [5, 5]
z_mesh = sp.ndimage.filters.gaussian_filter(z_mesh, sigma, mode='constant')

# Create the contour
contourf = plt.contourf(x_mesh, y_mesh, z_mesh, levels, alpha=0.5, colors=colors, linestyles='None', vmin=vmin, vmax=vmax)

# Convert matplotlib contourf to geojson
geojson = geojsoncontour.contourf_to_geojson(
    contourf=contourf,
    min_angle_deg=3.0,
    ndigits=5,
    stroke_width=1,
    fill_opacity=0.5)

# Set up the folium plot
geomap = folium.Map([df.latitude.mean(), df.longitude.mean()], zoom_start=10, tiles="cartodbpositron")

# Plot the contour plot on folium
folium.GeoJson(
    geojson,
    style_function=lambda x: {
        'color':     x['properties']['stroke'],
        'weight':    x['properties']['stroke-width'],
        'fillColor': x['properties']['fill'],
        'opacity':   0.6,
    }).add_to(geomap)

# Add the colormap to the folium map
cm.caption = 'Temperature'
geomap.add_child(cm)

# Fullscreen mode
plugins.Fullscreen(position='topright', force_separate_button=True).add_to(geomap)

# Plot the data
geomap.save(f'data/folium_contour_temperature_map.html')
@meteoDaniel

This comment has been minimized.

meteoDaniel commented Oct 10, 2018

I suggest to transform your 2d grib data to tiles. Than you could load the data as a tile layer to your map. This is much faster than plotting geojson data or similar to the map.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment