File: polygons_from_list_of_points.md

package info (click to toggle)
folium 0.20.0%2Bdfsg-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,168 kB
  • sloc: python: 4,489; makefile: 134; sh: 26
file content (268 lines) | stat: -rw-r--r-- 7,981 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
# Creating a polygon from a list of points

For many of those working with geo data it is a common task being asked to create a polygon from a list of points. More specific, to create a polygon that wraps around those points in a meaningful manner. So, there are several sources in the web explaining how to create the shape (see sources at end of document). This example notebook is the application of those solutions to folium maps.

## Helpers

```{code-cell} ipython3
# Imports
import random

import folium
from scipy.spatial import ConvexHull


# Function to create a list of some random points
def randome_points(amount, LON_min, LON_max, LAT_min, LAT_max):

    points = []
    for _ in range(amount):
        points.append(
            (random.uniform(LON_min, LON_max), random.uniform(LAT_min, LAT_max))
        )

    return points


# Function to draw points in the map
def draw_points(map_object, list_of_points, layer_name, line_color, fill_color, text):

    fg = folium.FeatureGroup(name=layer_name)

    for point in list_of_points:
        fg.add_child(
            folium.CircleMarker(
                point,
                radius=1,
                color=line_color,
                fill_color=fill_color,
                popup=(folium.Popup(text)),
            )
        )

    map_object.add_child(fg)
```

## Convex hull

The convex hull is probably the most common approach - its goal is to create the smallest polygon that contains all points from a given list. The scipy.spatial package provides this algorithm (https://docs.scipy.org/doc/scipy-0.19.0/reference/generated/scipy.spatial.ConvexHull.html, accessed 29.12.2018).

```{code-cell} ipython3
# Function that takes a map and a list of points (LON,LAT tuples) and
# returns a map with the convex hull polygon from the points as a new layer


def create_convexhull_polygon(
    map_object, list_of_points, layer_name, line_color, fill_color, weight, text
):

    # Since it is pointless to draw a convex hull polygon around less than 3 points check len of input
    if len(list_of_points) < 3:
        return

    # Create the convex hull using scipy.spatial
    form = [list_of_points[i] for i in ConvexHull(list_of_points).vertices]

    # Create feature group, add the polygon and add the feature group to the map
    fg = folium.FeatureGroup(name=layer_name)
    fg.add_child(
        folium.vector_layers.Polygon(
            locations=form,
            color=line_color,
            fill_color=fill_color,
            weight=weight,
            popup=(folium.Popup(text)),
        )
    )
    map_object.add_child(fg)

    return map_object
```

```{code-cell} ipython3
# Initialize map
my_convexhull_map = folium.Map(location=[48.5, 9.5], zoom_start=8)

# Create a convex hull polygon that contains some points
list_of_points = randome_points(
    amount=10, LON_min=48, LON_max=49, LAT_min=9, LAT_max=10
)

create_convexhull_polygon(
    my_convexhull_map,
    list_of_points,
    layer_name="Example convex hull",
    line_color="lightblue",
    fill_color="lightskyblue",
    weight=5,
    text="Example convex hull",
)

draw_points(
    my_convexhull_map,
    list_of_points,
    layer_name="Example points for convex hull",
    line_color="royalblue",
    fill_color="royalblue",
    text="Example point for convex hull",
)

# Add layer control and show map
folium.LayerControl(collapsed=False).add_to(my_convexhull_map)
my_convexhull_map
```

## Envelope

The envelope is another interesting approach - its goal is to create a box that contains all points from a given list.

```{code-cell} ipython3
def create_envelope_polygon(
    map_object, list_of_points, layer_name, line_color, fill_color, weight, text
):

    # Since it is pointless to draw a box around less than 2 points check len of input
    if len(list_of_points) < 2:
        return

    # Find the edges of box
    from operator import itemgetter

    list_of_points = sorted(list_of_points, key=itemgetter(0))
    x_min = list_of_points[0]
    x_max = list_of_points[len(list_of_points) - 1]

    list_of_points = sorted(list_of_points, key=itemgetter(1))
    y_min = list_of_points[0]
    y_max = list_of_points[len(list_of_points) - 1]

    upper_left = (x_min[0], y_max[1])
    upper_right = (x_max[0], y_max[1])
    lower_right = (x_max[0], y_min[1])
    lower_left = (x_min[0], y_min[1])

    edges = [upper_left, upper_right, lower_right, lower_left]

    # Create feature group, add the polygon and add the feature group to the map
    fg = folium.FeatureGroup(name=layer_name)
    fg.add_child(
        folium.vector_layers.Polygon(
            locations=edges,
            color=line_color,
            fill_color=fill_color,
            weight=weight,
            popup=(folium.Popup(text)),
        )
    )
    map_object.add_child(fg)

    return map_object
```

```{code-cell} ipython3
# Initialize map
my_envelope_map = folium.Map(location=[49.5, 8.5], zoom_start=8)

# Create an envelope polygon that contains some points
list_of_points = randome_points(
    amount=10, LON_min=49.1, LON_max=50, LAT_min=8, LAT_max=9
)

create_envelope_polygon(
    my_envelope_map,
    list_of_points,
    layer_name="Example envelope",
    line_color="indianred",
    fill_color="red",
    weight=5,
    text="Example envelope",
)

draw_points(
    my_envelope_map,
    list_of_points,
    layer_name="Example points for envelope",
    line_color="darkred",
    fill_color="darkred",
    text="Example point for envelope",
)

# Add layer control and show map
folium.LayerControl(collapsed=False).add_to(my_envelope_map)
my_envelope_map
```

## Concave hull (alpha shape)
In some cases the convex hull does not yield good results - this is when the shape of the polygon should be concave instead of convex. The solution is a concave hull that is also called alpha shape. Yet, there is no ready to go, off the shelve solution for this but there are great resources (see: https://web.archive.org/web/20191207074940/http://blog.thehumangeo.com/2014/05/12/drawing-boundaries-in-python/, accessed 04.01.2019.


## Putting it together

Just putting it all together...

```{code-cell} ipython3
# Initialize map
my_map_global = folium.Map(location=[48.2460683, 9.26764125], zoom_start=7)

# Create a convex hull polygon that contains some points
list_of_points = randome_points(
    amount=10, LON_min=48, LON_max=49, LAT_min=9, LAT_max=10
)

create_convexhull_polygon(
    my_map_global,
    list_of_points,
    layer_name="Example convex hull",
    line_color="lightblue",
    fill_color="lightskyblue",
    weight=5,
    text="Example convex hull",
)

draw_points(
    my_map_global,
    list_of_points,
    layer_name="Example points for convex hull",
    line_color="royalblue",
    fill_color="royalblue",
    text="Example point for convex hull",
)

# Create an envelope polygon that contains some points
list_of_points = randome_points(
    amount=10, LON_min=49.1, LON_max=50, LAT_min=8, LAT_max=9
)

create_envelope_polygon(
    my_map_global,
    list_of_points,
    layer_name="Example envelope",
    line_color="indianred",
    fill_color="red",
    weight=5,
    text="Example envelope",
)

draw_points(
    my_map_global,
    list_of_points,
    layer_name="Example points for envelope",
    line_color="darkred",
    fill_color="darkred",
    text="Example point for envelope",
)

# Add layer control and show map
folium.LayerControl(collapsed=False).add_to(my_map_global)
my_map_global
```

## Sources:

* https://web.archive.org/web/20200222150431/http://blog.yhat.com/posts/interactive-geospatial-analysis.html, accessed 28.12.2018

* https://docs.scipy.org/doc/scipy-0.19.0/reference/generated/scipy.spatial.ConvexHull.html, accessed 29.12.2018

* https://medium.com/@vworri/simple-geospacial-mapping-with-geopandas-and-the-usual-suspects-77f46d40e807, accessed 29.12.2018

* https://web.archive.org/web/20191207074940/http://blog.thehumangeo.com/2014/05/12/drawing-boundaries-in-python/, accessed 04.01.2019