Skip to content

Commit 084fc89

Browse files
committed
add rasterio filtering and simplifying example
1 parent feb954b commit 084fc89

File tree

1 file changed

+94
-37
lines changed

1 file changed

+94
-37
lines changed

jupyter-notebooks/analytics-snippets/roads_as_vector.ipynb

Lines changed: 94 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@
66
"source": [
77
"# Roads as Vectors\n",
88
"\n",
9-
"This notebook demonstrates converting the roads raster that is the output of the Analaytics feed into a vector dataset."
9+
"This notebook demonstrates converting the roads raster that is the output of the Analaytics feed into a vector dataset.\n",
10+
"\n",
11+
"It demonstrates the following techniques for converting to vector:\n",
12+
"1. GDAL CLI\n",
13+
"2. Rasterio (no processing)\n",
14+
"3. Rasterio (with filtering and simplification)"
1015
]
1116
},
1217
{
@@ -23,7 +28,9 @@
2328
"import rasterio\n",
2429
"from rasterio import features as rfeatures\n",
2530
"from rasterio.enums import Resampling\n",
26-
"from rasterio.plot import show"
31+
"from rasterio.plot import show\n",
32+
"import shapely\n",
33+
"from shapely.geometry import shape as sshape"
2734
]
2835
},
2936
{
@@ -42,7 +49,9 @@
4249
"cell_type": "markdown",
4350
"metadata": {},
4451
"source": [
45-
"## Identify road feed feature for download\n",
52+
"## Obtain Analytics Raster\n",
53+
"\n",
54+
"### Identify road feed feature for download\n",
4655
"\n",
4756
"We want to download the most recent feature from the feed for road detection in Kirazli, Turkey."
4857
]
@@ -100,7 +109,7 @@
100109
"cell_type": "markdown",
101110
"metadata": {},
102111
"source": [
103-
"## Download Quad Raster"
112+
"### Download Quad Raster"
104113
]
105114
},
106115
{
@@ -212,7 +221,7 @@
212221
"cell_type": "markdown",
213222
"metadata": {},
214223
"source": [
215-
"## Visualize Roads Image\n",
224+
"### Visualize Roads Image\n",
216225
"\n",
217226
"The output of the analytics road detection is a boolean image where road pixels are given a value of True and non-road pixels are given a value of False."
218227
]
@@ -237,7 +246,7 @@
237246
{
238247
"data": {
239248
"text/plain": [
240-
"<matplotlib.axes._subplots.AxesSubplot at 0x7fd86fae4690>"
249+
"<matplotlib.axes._subplots.AxesSubplot at 0x7f64d938b550>"
241250
]
242251
},
243252
"execution_count": 10,
@@ -276,7 +285,45 @@
276285
"cell_type": "markdown",
277286
"metadata": {},
278287
"source": [
279-
"## Convert to Vector using Rasterio (no filtering)\n",
288+
"## Convert Roads to Vector Features\n",
289+
"\n",
290+
"### GDAL Command-Line Interface (CLI)\n",
291+
"\n",
292+
"GDAL provides a python script that can be run via the CLI. It is quite easy to run and fast, though it doesn't allow for some of the convenient pixel-space filtering and processing that rasterio provides and we will use later on."
293+
]
294+
},
295+
{
296+
"cell_type": "code",
297+
"execution_count": 11,
298+
"metadata": {},
299+
"outputs": [],
300+
"source": [
301+
"gdal_output_filename = os.path.join('data', 'test_gdal.shp')"
302+
]
303+
},
304+
{
305+
"cell_type": "code",
306+
"execution_count": 12,
307+
"metadata": {},
308+
"outputs": [
309+
{
310+
"name": "stdout",
311+
"output_type": "stream",
312+
"text": [
313+
"Creating output data/test_gdal.shp of format ESRI Shapefile.\n",
314+
"0...10...20...30...40...50...60...70...80...90...100 - done.\n"
315+
]
316+
}
317+
],
318+
"source": [
319+
"!gdal_polygonize.py $filename $gdal_output_filename"
320+
]
321+
},
322+
{
323+
"cell_type": "markdown",
324+
"metadata": {},
325+
"source": [
326+
"### Rasterio - no filtering\n",
280327
"\n",
281328
"In this section we use rasterio to convert the binary roads raster into a vector dataset. The vectors are written to disk as a shapefile. The shapefile can be imported into geospatial programs such as QGIS or ArcGIS for visualization and further processing.\n",
282329
"\n",
@@ -285,7 +332,7 @@
285332
},
286333
{
287334
"cell_type": "code",
288-
"execution_count": 11,
335+
"execution_count": 13,
289336
"metadata": {},
290337
"outputs": [
291338
{
@@ -329,57 +376,67 @@
329376
"cell_type": "markdown",
330377
"metadata": {},
331378
"source": [
332-
"# Convert to Vectors with GDAL CLI\n",
379+
"### Rasterio - Filtering and Simplifying\n",
333380
"\n",
334-
"GDAL provides a python script that can be run via the CLI. It is quite easy to run and fast, though it doesn't allow for some of the convenient pixel-space filtering and processing that rasterio provides and we will use later on."
381+
"In this section, we use `shapely` to filter the road vectors by size and simplify them so we don't have a million pixel edges."
335382
]
336383
},
337384
{
338385
"cell_type": "code",
339-
"execution_count": 27,
386+
"execution_count": 14,
340387
"metadata": {},
341-
"outputs": [
342-
{
343-
"data": {
344-
"text/plain": [
345-
"'data/1176-1272_2019-07-01.tif'"
346-
]
347-
},
348-
"execution_count": 27,
349-
"metadata": {},
350-
"output_type": "execute_result"
351-
}
352-
],
388+
"outputs": [],
353389
"source": [
354-
"gdal_output_filename = os.path.join('data', 'test_gdal.shp')"
390+
"def roads_as_vectors_with_filtering(filename, min_pixel_size=5): \n",
391+
" with rasterio.open(filename) as dataset:\n",
392+
" roads = dataset.read(1)\n",
393+
" road_mask = roads == 255 # mask non-road pixels\n",
394+
"\n",
395+
" # we skip transform on vectorization so we can perform filtering in pixel space\n",
396+
" road_shapes = rfeatures.shapes(roads, mask=road_mask, connectivity=8)\n",
397+
" road_geometries = (r for r, _ in road_shapes)\n",
398+
" geo_shapes = (sshape(g) for g in road_geometries)\n",
399+
"\n",
400+
" # filter to shapes bigger than min_pixel_size\n",
401+
" geo_shapes = (s for s in geo_shapes if s.area > min_pixel_size)\n",
402+
" \n",
403+
" # simplify so we don't have a million pixel edge points\n",
404+
" tolerance = 1 #1.5\n",
405+
" geo_shapes = (g.simplify(tolerance, preserve_topology=False)\n",
406+
" for g in geo_shapes)\n",
407+
"\n",
408+
" # apply image transform \n",
409+
" # rasterio transform: (a, b, c, d, e, f, 0, 0, 1), c and f are offsets\n",
410+
" # shapely: a b d e c/xoff f/yoff\n",
411+
" d = dataset.transform\n",
412+
" shapely_transform = [d[0], d[1], d[3], d[4], d[2], d[5]]\n",
413+
" proj_shapes = (shapely.affinity.affine_transform(g, shapely_transform)\n",
414+
" for g in geo_shapes)\n",
415+
" \n",
416+
" road_geometries = (shapely.geometry.mapping(s) for s in proj_shapes)\n",
417+
" \n",
418+
" crs = dataset.crs\n",
419+
" return (road_geometries, crs)"
355420
]
356421
},
357422
{
358423
"cell_type": "code",
359-
"execution_count": 30,
424+
"execution_count": 15,
360425
"metadata": {},
361426
"outputs": [
362427
{
363428
"name": "stdout",
364429
"output_type": "stream",
365430
"text": [
366-
"\r\n",
367-
"gdal_polygonize [-8] [-nomask] [-mask filename] raster_file [-b band|mask]\r\n",
368-
" [-q] [-f ogr_format] out_file [layer] [fieldname]\r\n",
369-
"\r\n"
431+
"wrote 838 geometries to data/test_filt.shp\n"
370432
]
371433
}
372434
],
373435
"source": [
374-
"!gdal_polygonize.py $filename $gdal_output_filename"
436+
"road_geometries_filt, crs = roads_as_vectors_with_filtering(filename)\n",
437+
"output_filename = os.path.join('data', 'test_filt.shp')\n",
438+
"save_as_shapefile(output_filename, road_geometries_filt, crs)"
375439
]
376-
},
377-
{
378-
"cell_type": "code",
379-
"execution_count": null,
380-
"metadata": {},
381-
"outputs": [],
382-
"source": []
383440
}
384441
],
385442
"metadata": {

0 commit comments

Comments
 (0)