[PYTHON] How to use folium (visualization of location information)

Overview

I will summarize a package called folium that is convenient for handling geographic information in Python.

About folium

folium is a library that wraps a JavaScript library called Leaflet in Python.

  1. Create a Map object
  2. Add various shapes to the Map object
  3. Save the Map object as html

You can easily create an HTML file containing an interactive map by following the procedure. On JupyterLab, the map is displayed on the screen simply by displaying the Map object.

This entry is written on the Jupyter Lab. If you run the code in this entry in order in JupyterLab, you will get the same result (should).

version information

import sys
import folium
print(f"""Python
{sys.version}

folium
{folium.__version__}""")
Python
3.7.3 (default, Mar 27 2019, 16:54:48) 
[Clang 4.0.1 (tags/RELEASE_401/final)]

folium
0.10.1

Preparation

Define the constants used below.

#Latitude and longitude of Toranomon Hills
office_lat = 35.66687568
office_lng = 139.74947495

Basic

Display the map for the time being

To display a map on Jupyter, simply evaluate folium.Map. Alternatively, you can spit it out into an HTML file with Map.save.

fmap1 = folium.Map(
    location=[office_lat, office_lng],
    tiles = "OpenStreetMap",
    zoom_start = 20, #Magnification when drawing 1 to 20
    width = 800, height = 800 #Map size
) 
fmap1 #Or fmap1.save("1.html")

スクリーンショット 2020-02-03 16.08.01.png

tiles is

You can choose from.

Put a marker

Place a marker. You can register a pop-up as a marker.

fmap2 = folium.Map(
    location=[office_lat, office_lng], 
    zoom_start=20
)
folium.Marker([office_lat, office_lng], popup="Datawise is here").add_to(fmap2)
fmap2

スクリーンショット 2020-02-03 16.09.19.png

Try to draw a line

Pass a list of (lat, lng) to PolyLine and draw a polygonal line

import itertools as it

#The apex of the quadrangle centered on Toranomon Hills
sq = [
    (office_lat + dy * pow(10, -3), office_lng + dx * pow(10, -3))
    for dx, dy in it.product([-1, 1], [-1, 1])
]
fmap3 = folium.Map(location=[office_lat, office_lng], zoom_start=20)
folium.PolyLine(locations=sq).add_to(fmap3)
fmap3

スクリーンショット 2020-02-03 16.10.25.png

Try painting the surface

You can draw polygons with Polygon.

sw, nw, se, ne = sq
fmap4 = folium.Map(location=[office_lat, office_lng], zoom_start=20)
folium.Polygon(
    locations=[sw, se, ne, nw], #Polygon vertices
    color="red", #Line color
    weight=10, #Line thickness
    fill=True, #Fill
    fill_opacity=0.5 #Transparency (1=Opacity)
).add_to(fmap4)

fmap4

スクリーンショット 2020-02-03 16.11.31.png

In principle, if you have such a tool, you can visualize the information of appropriate data on the map ... However, in practice, managing polygons with a list of vertices is dull (maybe). ..

GeoJSON is defined as a standard for expressing shapes on maps, and it can also be handled by folium.

Use GeoJSON objects

Use the geojson library to work with GeoJSON.

GeoJSON notes

GeoJSON will die if you don't read the spec carefully. I spent several hours noticing the following points ... It is no exaggeration to say that I wrote this entry because I wanted to convey these to my colleagues.

--In GeoJSON, coordinates are represented by (longitude, latitude) instead of (latitude, longitude) (opposite to folium). --The list passed to geojson.Polygon must start and end with the same value --geojson.Polygon coordinates receive "list of (longitude, latitude)" list "" instead of "list of (longitude, latitude)" [^ 1]

For details, use the code below ...

import geojson as gj

# (lat、lng)List of
#Point 1.First and last elements have the same value
lat_lng = [sw, se, ne, nw, sw] 

#Point 2. (lng, lat)Convert to
def swap(p):
    return p[1], p[0]
lng_lat = list(map(swap, lat_lng))

#Point 3.Make a list of (lng, lat) lists
lng_lat2 = [lng_lat]

poly5 = gj.Polygon(lng_lat2)
fmap5 = folium.Map(location=[office_lat, office_lng], zoom_start=20)
folium.GeoJson(poly5).add_to(fmap5)
fmap5

スクリーンショット 2020-02-03 16.14.29.png

I haven't investigated it in detail, but if it doesn't work, save it as HTML → open it in a browser and look at JS errors, etc. There may be some information.

Draw multiple polygons

In GeoJSON, a collection of multiple objects is represented by a FeatureCollection. With FeatureCollection, you can draw many at once. (Actually, folium.GeoJson expects a FeatureCollection as the GeoJSON to be passed, but when anything else is passed, it is internally converted to a FeatureCollection)

def slide(poly, i): 
    """
A function that slightly shifts polygons
    """
    vtx = poly["coordinates"][0] # gj.Polygon's coordinates are a list of vertices"List of"
    vtx2 = [
        (lng + i * pow(10, -3), lat + i * pow(10, -3))
        for lng, lat in vtx
    ]
    return gj.Polygon([vtx2]) # gj.Polygon coodinate is (omitted)





fmap6 = folium.Map(location=[office_lat, office_lng], zoom_start=16)
polys6 = [slide(poly5, i) for i in range(-2, 3)]
fc6 = gj.FeatureCollection(polys6)
folium.GeoJson(fc6).add_to(fmap6)
fmap6

スクリーンショット 2020-02-03 16.24.42.png

It looks like it's working fine above, but it's actually doing something wrong with the GeoJSON spec. FeatureCollection Specifications Above, FeatureCollection features must be a list of objects with type = "feature". .. On the other hand, if you look at "features" of fc6

fc6["features"]
[{"coordinates": [[[139.746475, 35.663876], [139.748475, 35.663876], [139.748475, 35.665876], [139.746475, 35.665876], [139.746475, 35.663876]]], "type": "Polygon"},
 {"coordinates": [[[139.747475, 35.664876], [139.749475, 35.664876], [139.749475, 35.666876], [139.747475, 35.666876], [139.747475, 35.664876]]], "type": "Polygon"},
 {"coordinates": [[[139.748475, 35.665876], [139.750475, 35.665876], [139.750475, 35.667876], [139.748475, 35.667876], [139.748475, 35.665876]]], "type": "Polygon"},
 {"coordinates": [[[139.749475, 35.666876], [139.751475, 35.666876], [139.751475, 35.668876], [139.749475, 35.668876], [139.749475, 35.666876]]], "type": "Polygon"},
 {"coordinates": [[[139.750475, 35.667876], [139.752475, 35.667876], [139.752475, 35.669876], [139.750475, 35.669876], [139.750475, 35.667876]]], "type": "Polygon"}]

It's hard to see, but fc6's "features" is an array of objects with type = "Polygon". (I think the above example worked because folium did a good job, I'm sure)

You can get the correct GeoJSON object by writing:

fmap7 = folium.Map(location=[office_lat, office_lng], zoom_start=16)
fc7 = gj.FeatureCollection(
    features=[
        gj.Feature(
            geometry=p,
            id=i
        ) for i, p in enumerate(polys6)
    ]
)
folium.GeoJson(fc7).add_to(fmap7)
fmap7

スクリーンショット 2020-02-03 16.16.20.png

Change the format of Polygon

To change the format of Polygon, pass style_function to folium.GeoJson. The result of applying style_function to each Feature is used as the format when drawing that Feature.

fmap8 = folium.Map(location=[office_lat, office_lng], zoom_start=16)
folium.GeoJson(
    fc7,
    style_function=lambda feature: {
        "fillColor": "red",
        "color": "black",
        "weight": 10 / (feature["id"] + 1),
        "fillOpacity": feature["id"] * 0.2
    }).add_to(fmap8)
fmap8

スクリーンショット 2020-02-03 16.17.16.png

[^ 1]: This Japanese is difficult to understand, so I will supplement it. In GeoJSON, points on the map are represented by a list of length 2 [longitude, latitude]. Since a polygon is represented by multiple points (= each vertex), the outer circumference of the polygon is represented by a list containing multiple [longitude, latitude]. I thought that this could represent a polygon, but GeoJSON's Polygon can also represent a polygon with holes. A perforated polygon is represented by a list in which the first element is a list that represents the outer circumference, and the second and subsequent elements are a list that represents the circumference of the inner hole. Due to GeoJSON specifications, it is necessary to prepare a list that contains only one list that represents the outer circumference (this is a list that contains multiple lists of length 2 called [longitude, latitude]) even if there are no holes. For example, to define a quadrangle with four points [0,0], [0,1], [1,1], [1,0] as vertices, [[0,0], [0] , 1], [1,1], [1,0]] not a list of vertices,[[[0,0], [0,1], [1,1], [1,0 You need to have a list of lists of vertices called]]].

Recommended Posts

How to use folium (visualization of location information)
Summary of how to use pandas.DataFrame.loc
Summary of how to use pyenv-virtualenv
Summary of how to use csvkit
[Python] Summary of how to use pandas
How to calculate Use% of df command
[Python2.7] Summary of how to use unittest
Jupyter Notebook Basics of how to use
Basics of PyTorch (1) -How to use Tensor-
Summary of how to use Python list
[Python2.7] Summary of how to use subprocess
[Question] How to use plot_surface of python
How to use xml.etree.ElementTree
How to use Python-shell
How to use tf.data
A simple example of how to use ArgumentParser
How to use virtualenv
How to use Seaboan
[Python] How to use two types of type ()
How to use image-match
How to use shogun
How to use Pandas 2
How to use Virtualenv
How to use numpy.vectorize
How to use pytest_report_header
How to use partial
How to use Bio.Phylo
Not much mention of how to use Pickle
Summary of how to use MNIST in Python
How to use SymPy
How to use x-means
How to use WikiExtractor.py
How to use IPython
How to use virtualenv
How to use Matplotlib
How to use iptables
How to use numpy
How to use TokyoTechFes2015
How to use venv
How to use dictionary {}
How to use Pyenv
How to use list []
How to use python-kabusapi
How to use OptParse
How to use return
How to use dotenv
How to use pyenv-virtualenv
How to use Go.mod
How to use imutils
How to use import
[Rails] How to get location information using Geolocation API
A memo of how to use AIST supercomputer ABCI
I tried to summarize how to use matplotlib of python
Memo of how to use properly when combining pandas.DataFrame
How to use Python Kivy ① ~ Basics of Kv Language ~
How to use Qt Designer
How to use search sorted
[gensim] How to use Doc2Vec
python3: How to use bottle (2)
Understand how to use django-filter
How to use the generator