Polygon overlay in leaflet map - python

I am creating an interactive map using ipyleaflet using the following code:
from ipyleaflet import Map, Polygon
polygon = Polygon(
locations=[[(38.844185,-4.804621),(39.241299,-1.899833),(40.74308,-2.205491),(40.34742,-5.17429),(38.844185,-4.804621)],[(39.365192,-1.941078),(40.867912,-1.567062),(41.276688,-4.670904),(39.775406,-4.976737),(39.365192,-1.941078)],[(39.706161,-1.849863),(41.207623,-1.465817),(41.617561,-4.594476),(40.117233,-4.908839),(39.706161,-1.849863)],[(39.702591,-5.033657),(40.101254,-2.077048),(41.602196,-2.389729),(41.204681,-5.413605),(39.702591,-5.033657)]],
color="green",
fill_opacity= 0.5,
fill_color="green"
)
m = Map(center=(38.5531, -4.6914), zoom=6)
m.add_layer(polygon);
m
The ouptut looks like this:
I am wondering how can I make the intersection of the polygons not be fully transparent. Looking at the attributes https://ipyleaflet.readthedocs.io/en/latest/api_reference/polygon.html in the documentation, I don't see any option?
An example of the desired output can be seen in the image below:

You've got bad results because ipyleaflet subtracts ovellaped polygons (you can see it in second example in the documentation link you posted, "Polygon with hole")
You need to add each polygon separately, I changed your code a bit, now it creates and applies polygons in loop:
from ipyleaflet import Map, Polygon
poligons = [[(38.844185,-4.804621),(39.241299,-1.899833),(40.74308,-2.205491),(40.34742,-5.17429),(38.844185,-4.804621)],
[(39.365192,-1.941078),(40.867912,-1.567062),(41.276688,-4.670904),(39.775406,-4.976737),(39.365192,-1.941078)],
[(39.706161,-1.849863),(41.207623,-1.465817),(41.617561,-4.594476),(40.117233,-4.908839),(39.706161,-1.849863)],
[(39.702591,-5.033657),(40.101254,-2.077048),(41.602196,-2.389729),(41.204681,-5.413605),(39.702591,-5.033657)]]
m = Map(center=(38.5531, -4.6914), zoom=6)
for poly in poligons:
polygon = Polygon(
locations= poly,
color="green",
fill_opacity= 0.5,
fill_color="green"
)
m.add_layer(polygon);
m
Result:

Related

ipyleaflet layer not scaled/positioned correctly

I'm trying to overlay an esri bathymetry map on my south polar stereographic map using the 'url' function in ipyleaflet. Doing this results with {z}/{y}/{x} in the url inserts the layer, but at a smaller size in the right-hand corner of the basemap. I tried to give the tilelayer the same crs, but that did not change the output. My code is as such:
from ipyleaflet import (
Map,
basemaps,
TileLayer,
basemap_to_tiles,
projections)
from ipywidgets import (Layout,
widgets as w
tile_layer = TileLayer(url='https://tiles.arcgis.com/tiles/C8EMgrsFcRFL6LrL/arcgis/rest/services/Antarctic_Basemap/MapServer/tile/{z}/{y}/{x}')
spsLayout = Layout(width='800px', height='800px')
m = Map(center=(-90, 0),
zoom=0,
layout=spsLayout,
basemap= basemaps.NASAGIBS.BlueMarble3031,
crs=projections.EPSG3031)
m.add_layer(tile_layer)
m
Here's where I got the url for the tiles In the bottom right. Thanks for your help!
I think the issue here is that this bathymetry base map use a different tilegrid(metatiles 3x3) from the one used with ipyleaflet(4x4). Unfortunately I don't think there is currently a way for Leaflet(hence ipyleaflet) to understand multiple tilegrids in the same map. A workaround would be to use ArcGIS in your notebook instead of ipyleaflet but that would require a paid subscription.
If you don't mind defining your own projection and resolutions you can use the ArcGIS layers and avoid completely thedefault polar projection and basemaps from ipyleaflet. i.e.
from ipyleaflet import (
Map,
basemaps,
TileLayer,
basemap_to_tiles,
projections)
ESRI_3031 = dict(
name='EPSG:3031',
custom=True,
proj4def="""+proj=stere +lat_0=-90 +lat_ts=-71 +lon_0=0 +k=1
+x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs""",
origin=[-3.06361E7, 3.0636099999999993E7],
resolutions=[
67733.46880027094,
33866.73440013547,
16933.367200067736,
8466.683600033868,
4233.341800016934,
2116.670900008467,
1058.3354500042335,
529.1677250021168,
264.5838625010584,
],
bounds=[
[-4524583.19363305,-4524449.487765655],
[4524449.4877656475,4524583.193633042]
]
)
tile_layer = TileLayer(url='https://tiles.arcgis.com/tiles/C8EMgrsFcRFL6LrL/arcgis/rest/services/Antarctic_Basemap/MapServer/tile/{z}/{y}/{x}')
spsLayout = Layout(width='800px', height='800px')
m = Map(center=(-90, 0),
zoom=0,
layout=spsLayout,
basemap= tile_layer,
crs=ESRI_3031)
m

Mapping Antarctic Polar Stereographic data in ipyleaflet

I am trying to plot Antarctic Sea-Ice EPSG:3031 data using ipyleaflet like so:
from ipyleaflet import Map, WMSLayer, basemaps
wms = WMSLayer(
url='http://geos.polarview.aq/geoserver/wms',
layers='polarview:iceedgeS15',
format='image/png',
transparent=True,
attribution='Polarview'
)
m = Map(basemap=basemaps.NASAGIBS.BlueMarble3031, center=(-90, 0), zoom=1, crs=projections.EPSG3031)
m.add_layer(wms)
m
The photos in the following links illustrate my issues clearly:
The data and the basemap do not align
If I omit the basemap and projection information it looks reasonable in terms of alignment but doesn't have the perspective and projection I desire. I have been also been using Leafmap to add local geotiffs and run into similar issues.
I have read through a few relevant PRs and checked out xarray-leaflet but haven't had any luck. This sea-ice concentration data is an example of a non-wms data source that I encounter the same problem with which could be helpful for testing purposes.
Great question.
You'll need to add coordinate reference system (CRS) information to your WMSLayer. See the custom projections notebook within the ipyleaflet examples. For spatial projections that are not Web Mercator (EPSG:3857), you'll likely need to define the bounds of your imagery.
from ipyleaflet import Map, WMSLayer, basemaps
# create map with NASA Blue Marble as background
m2 = Map(basemap=basemaps.NASAGIBS.BlueMarble3031,
center=(-90, 0), zoom=1, crs=projections.EPSG3031)
# projection for Polarview sea ice edge
POLAR3031 = dict(
name='EPSG:3031',
custom=True,
proj4def="""+proj=stere +lat_0=-90 +lat_ts=-71 +lon_0=0 +k=1
+x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs""",
bounds=[[-2822131.5,-3057369.25],[3744213.75,3822194.25]]
)
wms = WMSLayer(
url='http://geos.polarview.aq/geoserver/wms',
layers='polarview:iceedgeS15',
format='image/png',
transparent=True,
attribution='Polarview',
crs=POLAR3031
)
m2.add(wms)
# show map
m2

is it possible to colour a rectangle in folium python

I have a map:
map_osm = folium.Map(location=[51.366975, 0.039039],zoom_start=12)
map_osm
I want to be able to add a rectangle marker that I can colour according to some statistic. I found a polygon_marker (http://bl.ocks.org/wrobstory/5609786) but receive the error 'Map' object has no attribute 'polygon_marker' when I try:
map_osm.polygon_marker(location=[45.5132, -122.6708], popup='Hawthorne Bridge',
fill_color='#45647d', num_sides=4, radius=10)
In the final product, I would like to have lots of rectangles colour coded.
Any suggestions
After little digging into the the docs and source code and quick experimentation, you can just input parameter fill_color='blue' when you initialize Rectangle object.
Example code:
m = folium.Map(
location=[-6.237933179178703, 106.81783770824106],
zoom_start=13,
tiles='Stamen Toner'
)
folium.Rectangle(bounds=points, color='#ff7800', fill=True, fill_color='#ffff00', fill_opacity=0.2).add_to(m)
Result
A little more research, I found:
grid_pt=(51.4,0.05)
W=grid_pt[1]-0.005
E=grid_pt[1]+0.005
N=grid_pt[0]+0.005
S=grid_pt[0]-0.005
upper_left=(N,W)
upper_right=(N,E)
lower_right=(S,E)
lower_left=(S,W)
line_color='red'
fill_color='red'
weight=2
text='text'
edges = [upper_left, upper_right, lower_right, lower_left]
map_osm = folium.Map(location=[latty, longy],zoom_start=14)
map_osm.add_child(folium.vector_layers.Polygon(locations=edges, color=line_color, fill_color=fill_color,
weight=weight, popup=(folium.Popup(text))))
this works to add a single rectangle, then loop to get more

Folium PolyLine not showing up on map - folium 0.7.0 with python 3.6 (anaconda) in Jupyter notebook

I have a folium map of a neighborhood in New York City generated using the following code:
m = folium.Map(location=[40.7035, -73.990],
zoom_start=16.5,
tiles='cartodbpositron')
I then try to add lines connecting points on the map using folium.PolyLine(), but even though I see them listed when I call m._children, they don't show up on the map.
Here's the code to create the lines, where G is a networkx graph:
for x, y in G.edges():
points = [nx.get_node_attributes(G, 'loc')[x], nx.get_node_attributes(G, 'loc')[y]]
egde = folium.PolyLine(locations=points, weight=5, color='red')
edge.add_to(m)
A sample point:
[(-73.986635, 40.703988), (-73.988683, 40.702674)]
Output of m.children (first few lines):
OrderedDict([('cartodbpositron',
<folium.raster_layers.TileLayer at 0x12279feb8>),
('poly_line_ae5785771a2148c5a8559cb0085b10a4',
<folium.vector_layers.PolyLine at 0x122892128>),
('poly_line_ee73b495559940d484064e8c8492eda5',
<folium.vector_layers.PolyLine at 0x1229734a8>),
('poly_line_415a7ed70a2a425e876c8a6711408a6a', ...
Any idea what I might be doing wrong?
This is kind of odd that folium polyline expects coordinates in [latitude, longitude] format, whereas in general accepted format is [longitude, latitude].
Let's take an example:
I'm assuming you are using OSRM to get geometries.
OSRM geometries returns coordinates in the form of [[longitude,latitude],...].
If you'll directly use them with folium, it'll not show the polyline.
Reverse the geometry coordinates using following function:
def reverse_lon_lat(x):
a = [[p[1], p[0]] for p in x]
return a
and draw the polylines you intend to.

shapely parallel_offset sometimes does not generate closed ring

I am using the parallel_offset function of the shapely package to get offset structures to some polygons that are closed rings. I have several polygons at once, many with similar shapes. Around 10-25% of them, however, do not generate a closed ring from the parallel_offset. Here is a MWE of a shape that does not work:
import matplotlib.pyplot as plt
from shapely.geometry.polygon import LinearRing
def plot_line(ax, ob, color):
x, y = ob.xy
ax.plot(x, y, color=color, alpha=0.7, linewidth=3,
solid_capstyle='round', zorder=2)
polygon = [[-29.675, -30.675],
[-28.4094, -29.4094],
[-28.325, -29.325],
[-28.325, -29.764],
[-28.325, -29.7933],
[-28.4587, -29.8274],
[-28.4676, -29.8297],
[-28.5956, -29.8814],
[-28.6041, -29.8848],
[-28.724, -29.953],
[-28.732, -29.9576],
[-28.8417, -30.0413],
[-28.849, -30.0469],
[-28.9466, -30.1445],
[-28.9531, -30.151],
[-29.0368, -30.2607],
[-29.0424, -30.268],
[-29.1106, -30.3879],
[-29.1152, -30.3959],
[-29.1669, -30.5239],
[-29.1703, -30.5324],
[-29.2044, -30.6661],
[-29.2067, -30.675],
[-29.6457, -30.675],
[-29.675, -30.675]]
poly_line = LinearRing(polygon)
poly_line_offset = poly_line.parallel_offset(0.05, side="left", resolution=16,
join_style=2, mitre_limit=1)
fig = plt.figure()
ax = fig.add_subplot(111)
plot_line(ax, poly_line, "blue")
plot_line(ax, poly_line_offset, "green")
plt.show()
As you can see, the green offset polygon does not close at the point that is first/last in the list of vertices. Other very similar shapes, however, do work as intended. They have the same data structure and also have the same start/end point, as does my example above. The join_style attribute does not change the outcome to what I want. Changing the resolution or distance does not help either. The documentation also does not address this issue.
Do you have any guidance? I am using shapely 1.6.3.
not completely sure why this happens, nevertheless you might use a workaround based on the buffer method:
poly_line = LinearRing(polygon)
poly_line_offset = poly_line.buffer(0.05,
resolution=16, join_style=2, mitre_limit=1).exterior
With your data, this produces the (probably) desired result:
Here's a work around I did in my code.
I basically rolled the LinearRing (shifting the start point along the ring),
applied two offsets, and then added them back together.
It's probably not an ideal solution, but hopefully can work as a starting point:
from shapely import ops, geometry
import numpy as np
# test geo:
ring_coords = [(0,0.1),(0,2),(4,2),(4,0)]
ring = geometry.LinearRing(ring_coords)
# shifts the ring by one point
rolled = LinearRing(np.roll(ring.coords[:-1], 2))
# apply the offsets
offset_ring = ring.parallel_offset(-0.2, side='right', resolution=3, join_style=2, mitre_limit=3)
offset_rolled = rolled.parallel_offset(-0.2, side='right', resolution=3, join_style=2, mitre_limit=3)
# combine the points
# assuming you started with two rings, backward should be empty
forward, backward = ops.shared_paths(offset_rolled, offset_ring)
combined = geometry.LinearRing(ops.linemerge(forward))

Categories

Resources