|
6 | 6 | "source": [ |
7 | 7 | "# Roads as Vectors\n", |
8 | 8 | "\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)" |
10 | 15 | ] |
11 | 16 | }, |
12 | 17 | { |
|
23 | 28 | "import rasterio\n", |
24 | 29 | "from rasterio import features as rfeatures\n", |
25 | 30 | "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" |
27 | 34 | ] |
28 | 35 | }, |
29 | 36 | { |
|
42 | 49 | "cell_type": "markdown", |
43 | 50 | "metadata": {}, |
44 | 51 | "source": [ |
45 | | - "## Identify road feed feature for download\n", |
| 52 | + "## Obtain Analytics Raster\n", |
| 53 | + "\n", |
| 54 | + "### Identify road feed feature for download\n", |
46 | 55 | "\n", |
47 | 56 | "We want to download the most recent feature from the feed for road detection in Kirazli, Turkey." |
48 | 57 | ] |
|
100 | 109 | "cell_type": "markdown", |
101 | 110 | "metadata": {}, |
102 | 111 | "source": [ |
103 | | - "## Download Quad Raster" |
| 112 | + "### Download Quad Raster" |
104 | 113 | ] |
105 | 114 | }, |
106 | 115 | { |
|
212 | 221 | "cell_type": "markdown", |
213 | 222 | "metadata": {}, |
214 | 223 | "source": [ |
215 | | - "## Visualize Roads Image\n", |
| 224 | + "### Visualize Roads Image\n", |
216 | 225 | "\n", |
217 | 226 | "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." |
218 | 227 | ] |
|
237 | 246 | { |
238 | 247 | "data": { |
239 | 248 | "text/plain": [ |
240 | | - "<matplotlib.axes._subplots.AxesSubplot at 0x7fd86fae4690>" |
| 249 | + "<matplotlib.axes._subplots.AxesSubplot at 0x7f64d938b550>" |
241 | 250 | ] |
242 | 251 | }, |
243 | 252 | "execution_count": 10, |
|
276 | 285 | "cell_type": "markdown", |
277 | 286 | "metadata": {}, |
278 | 287 | "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", |
280 | 327 | "\n", |
281 | 328 | "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", |
282 | 329 | "\n", |
|
285 | 332 | }, |
286 | 333 | { |
287 | 334 | "cell_type": "code", |
288 | | - "execution_count": 11, |
| 335 | + "execution_count": 13, |
289 | 336 | "metadata": {}, |
290 | 337 | "outputs": [ |
291 | 338 | { |
|
329 | 376 | "cell_type": "markdown", |
330 | 377 | "metadata": {}, |
331 | 378 | "source": [ |
332 | | - "# Convert to Vectors with GDAL CLI\n", |
| 379 | + "### Rasterio - Filtering and Simplifying\n", |
333 | 380 | "\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." |
335 | 382 | ] |
336 | 383 | }, |
337 | 384 | { |
338 | 385 | "cell_type": "code", |
339 | | - "execution_count": 27, |
| 386 | + "execution_count": 14, |
340 | 387 | "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": [], |
353 | 389 | "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)" |
355 | 420 | ] |
356 | 421 | }, |
357 | 422 | { |
358 | 423 | "cell_type": "code", |
359 | | - "execution_count": 30, |
| 424 | + "execution_count": 15, |
360 | 425 | "metadata": {}, |
361 | 426 | "outputs": [ |
362 | 427 | { |
363 | 428 | "name": "stdout", |
364 | 429 | "output_type": "stream", |
365 | 430 | "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" |
370 | 432 | ] |
371 | 433 | } |
372 | 434 | ], |
373 | 435 | "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)" |
375 | 439 | ] |
376 | | - }, |
377 | | - { |
378 | | - "cell_type": "code", |
379 | | - "execution_count": null, |
380 | | - "metadata": {}, |
381 | | - "outputs": [], |
382 | | - "source": [] |
383 | 440 | } |
384 | 441 | ], |
385 | 442 | "metadata": { |
|
0 commit comments