I will summarize a package called folium that is convenient for handling geographic information in Python.
folium is a library that wraps a JavaScript library called Leaflet in Python.
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).
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
Define the constants used below.
#Latitude and longitude of Toranomon Hills
office_lat = 35.66687568
office_lng = 139.74947495
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")
tiles
is
You can choose from.
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
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
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
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 the geojson library to work with GeoJSON.
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
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.
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
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
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
[^ 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