Run live tile processing and display on Jupyter Notbook
pip install numpy
pip install rasterio[s3]
pip install rio-glui mapboxgl opencv-python scipy
{ | |
"cells": [ | |
{ | |
"cell_type": "code", | |
"execution_count": 1, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"import os\n", | |
"\n", | |
"from rio_glui.server import TileServer\n", | |
"from rio_glui.raster import RasterTiles\n", | |
"\n", | |
"from mapboxgl.utils import *\n", | |
"from mapboxgl.viz import *" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 2, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"token = os.getenv('MAPBOX_ACCESS_TOKEN')" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 3, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"file = 'https://oin-hotosm.s3.amazonaws.com/5ac626e091b5310010e0d482/0/5ac626e091b5310010e0d483.tif'" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 4, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"import mercantile\n", | |
"import numpy\n", | |
"\n", | |
"import cv2\n", | |
"from scipy.ndimage.morphology import binary_dilation\n", | |
"from rasterio.plot import reshape_as_image\n", | |
"from rio_tiler.utils import tile_read\n", | |
"\n", | |
"\n", | |
"# https://github.com/mapbox/rio-glui/blob/master/rio_glui/raster.py#L148-L158\n", | |
"class customRaster(RasterTiles):\n", | |
" def read_tile(self, z, x, y):\n", | |
" \"\"\"Read raster tile data and mask.\"\"\"\n", | |
" mercator_tile = mercantile.Tile(x=x, y=y, z=z)\n", | |
" tile_bounds = mercantile.xy_bounds(mercator_tile)\n", | |
"\n", | |
" data, mask = tile_read(\n", | |
" self.path,\n", | |
" tile_bounds,\n", | |
" self.tiles_size,\n", | |
" indexes=self.indexes,\n", | |
" nodata=self.nodata,\n", | |
" )\n", | |
"\n", | |
" #################################\n", | |
" # apply your custom function here\n", | |
" # Extract Linear Features\n", | |
" # https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_houghlines/py_houghlines.html\n", | |
" img = reshape_as_image(data)\n", | |
" gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\n", | |
" edges = cv2.Canny(gray, 50, 100)\n", | |
"\n", | |
" # Edges dilation for visual\n", | |
" edges = binary_dilation(edges).astype(numpy.uint8) * 255\n", | |
" \n", | |
" data[0, edges > 0] = 255\n", | |
" \n", | |
" return data, mask" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 5, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"raster = customRaster(file, indexes=(1,2,3))\n", | |
"ts = TileServer(raster)\n", | |
"ts.start()" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 6, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/html": [ | |
"<iframe id=\"map\", srcdoc=\"<!DOCTYPE html>\n", | |
"<html>\n", | |
"<head>\n", | |
"<title>mapboxgl-jupyter viz</title>\n", | |
"<meta charset='UTF-8' />\n", | |
"<meta name='viewport'\n", | |
" content='initial-scale=1,maximum-scale=1,user-scalable=no' />\n", | |
"<script type='text/javascript'\n", | |
" src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.47.0/mapbox-gl.js'></script>\n", | |
"<link type='text/css'\n", | |
" href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.47.0/mapbox-gl.css' \n", | |
" rel='stylesheet' />\n", | |
"\n", | |
"<style type='text/css'>\n", | |
" body { margin:0; padding:0; }\n", | |
" .map { position: absolute; top:0; bottom:0; width:100%; }\n", | |
" .legend {\n", | |
" background-color: white;\n", | |
" color: #6e6e6e;\n", | |
" border-radius: 3px;\n", | |
" bottom: 10px;\n", | |
" box-shadow: 0 1px 2px rgba(0, 0, 0, 0.10);\n", | |
" font: 12px/20px 'Helvetica Neue', Arial, Helvetica, sans-serif;\n", | |
" padding: 0;\n", | |
" position: absolute;\n", | |
" right: 10px;\n", | |
" z-index: 1;\n", | |
" min-width: 100px;\n", | |
" }\n", | |
" .legend.horizontal {bottom: 10px; text-align: left;}\n", | |
"\n", | |
" /* legend header */\n", | |
" .legend .legend-header { border-radius: 3px 3px 0 0; background: white; }\n", | |
" .legend .legend-title {\n", | |
" padding: 6px 12px 6px 12px;\n", | |
" text-shadow: 0 0 2px white;\n", | |
" text-transform: capitalize;\n", | |
" text-align: center;\n", | |
" font-weight: bold !important;\n", | |
" font-size: 14px;\n", | |
" font: 12px/20px 'Helvetica Neue', Arial, Helvetica, sans-serif;\n", | |
" max-width: 160px;\n", | |
" }\n", | |
" .legend-title {padding: 6px 12px 6px 12px; text-shadow: 0 0 2px #FFF; text-transform: capitalize; text-align: center; max-width: 160px; font-size: 0.9em; font-weight: bold;}\n", | |
" .legend.horizontal .legend-title {text-align: left;}\n", | |
"\n", | |
" /* legend items */\n", | |
" .legend-content {margin: 6px 12px 6px 12px; overflow: hidden; padding: 0; float: left; list-style: none; font-size: 0.8em;}\n", | |
" .legend.vertical .legend-item {white-space: nowrap;}\n", | |
" .legend-value {display: inline-block; line-height: 18px; vertical-align: top;}\n", | |
" .legend.horizontal ul.legend-content li.legend-item .legend-value,\n", | |
" .legend.horizontal ul.legend-content li.legend-item {display: inline-block; float: left; width: 30px; margin-bottom: 0; text-align: center; height: 30px;}\n", | |
"\n", | |
" /* legend key styles */\n", | |
" .legend-key {display: inline-block; height: 10px;}\n", | |
" .legend-key.default, .legend-key.square {border-radius: 0;}\n", | |
" .legend-key.circle {border-radius: 50%;}\n", | |
" .legend-key.rounded-square {border-radius: 20%;}\n", | |
" .legend.vertical .legend-key {width: 10px; margin-right: 5px; margin-left: 1px;}\n", | |
" .legend.horizontal .legend-key {width: 30px; margin-right: 0; margin-top: 1px; float: left;}\n", | |
" .legend.horizontal .legend-key.square, .legend.horizontal .legend-key.rounded-square, .legend.horizontal .legend-key.circle {margin-left: 10px; width: 10px;}\n", | |
" .legend.horizontal .legend-key.line {margin-left: 5px;}\n", | |
" .legend.horizontal .legend-key.line, .legend.vertical .legend-key.line {border-radius: 10%; width: 20px; height: 3px; margin-bottom: 2px;}\n", | |
"\n", | |
" /* gradient bar alignment */\n", | |
" .gradient-bar {margin: 6px 12px 6px 12px;}\n", | |
" .legend.horizontal .gradient-bar {width: 88%; height: 10px;}\n", | |
" .legend.vertical .gradient-bar {width: 10px; min-height: 50px; position: absolute; bottom: 4px;}\n", | |
"\n", | |
" /* contiguous vertical bars (discrete) */\n", | |
" .legend.vertical.contig .legend-key {height: 15px; width: 10px;}\n", | |
" .legend.vertical.contig li.legend-item {height: 15px;}\n", | |
" .legend.vertical.contig {padding-bottom: 6px;}\n", | |
"\n", | |
"</style>\n", | |
"\n", | |
"</head>\n", | |
"<body>\n", | |
"\n", | |
"<div id='map' class='map'></div>\n", | |
"\n", | |
"<script type='text/javascript'>\n", | |
"\n", | |
"var legendHeader;\n", | |
"\n", | |
"function calcColorLegend(myColorStops, title) {\n", | |
"\n", | |
" // create legend\n", | |
" var legend = document.createElement('div');\n", | |
" if ('square' === 'contiguous-bar') {\n", | |
" legend.className = 'legend vertical contig';\n", | |
" }\n", | |
" else {\n", | |
" legend.className = 'legend vertical';\n", | |
" }\n", | |
"\n", | |
" legend.id = 'legend';\n", | |
" document.body.appendChild(legend);\n", | |
"\n", | |
" // add legend header and content elements\n", | |
" var mytitle = document.createElement('div'),\n", | |
" legendContent = document.createElement('ul');\n", | |
" legendHeader = document.createElement('div');\n", | |
" mytitle.textContent = title;\n", | |
" mytitle.className = 'legend-title'\n", | |
" legendHeader.className = 'legend-header'\n", | |
" legendContent.className = 'legend-content'\n", | |
" legendHeader.appendChild(mytitle);\n", | |
" legend.appendChild(legendHeader);\n", | |
" legend.appendChild(legendContent);\n", | |
"\n", | |
" if (false === true) {\n", | |
" var gradientText = 'linear-gradient(to right, ';\n", | |
" var gradient = document.createElement('div');\n", | |
" gradient.className = 'gradient-bar';\n", | |
" legend.appendChild(gradient);\n", | |
" }\n", | |
"\n", | |
" // calculate a legend entries on a Mapbox GL Style Spec property function stops array\n", | |
" for (p = 0; p < myColorStops.length; p++) {\n", | |
" if (!!document.getElementById('legend-points-value-' + p)) {\n", | |
" //update the legend if it already exists\n", | |
" document.getElementById('legend-points-value-' + p).textContent = myColorStops[p][0];\n", | |
" document.getElementById('legend-points-id-' + p).style.backgroundColor = myColorStops[p][1];\n", | |
" }\n", | |
" else {\n", | |
" // create the legend if it doesn't yet exist\n", | |
" var item = document.createElement('li');\n", | |
" item.className = 'legend-item';\n", | |
"\n", | |
" var key = document.createElement('span');\n", | |
" key.className = 'legend-key square';\n", | |
" key.id = 'legend-points-id-' + p;\n", | |
" key.style.backgroundColor = myColorStops[p][1]; \n", | |
"\n", | |
" var value = document.createElement('span');\n", | |
" value.className = 'legend-value';\n", | |
" value.id = 'legend-points-value-' + p;\n", | |
"\n", | |
" item.appendChild(key);\n", | |
" item.appendChild(value);\n", | |
" legendContent.appendChild(item);\n", | |
" \n", | |
" data = document.getElementById('legend-points-value-' + p)\n", | |
"\n", | |
" // round number values in legend if precision defined\n", | |
" if ((typeof(myColorStops[p][0]) == 'number') && (typeof(null) == 'number')) {\n", | |
" data.textContent = myColorStops[p][0].toFixed(null);\n", | |
" }\n", | |
" else {\n", | |
" data.textContent = myColorStops[p][0];\n", | |
" }\n", | |
"\n", | |
" // add color stop to gradient list\n", | |
" if (false === true) {\n", | |
" if (p < myColorStops.length - 1) {\n", | |
" gradientText = gradientText + myColorStops[p][1] + ', ';\n", | |
" }\n", | |
" else {\n", | |
" gradientText = gradientText + myColorStops[p][1] + ')';\n", | |
" }\n", | |
" if ('vertical' === 'vertical') {\n", | |
" gradientText = gradientText.replace('to right', 'to bottom');\n", | |
" }\n", | |
" }\n", | |
" }\n", | |
" }\n", | |
"\n", | |
" if (false === true) {\n", | |
" // convert to gradient scale appearance\n", | |
" gradient.style.background = gradientText;\n", | |
"\n", | |
" // hide legend keys generated above\n", | |
" var keys = document.getElementsByClassName('legend-key');\n", | |
" for (var i=0; i < keys.length; i++) {\n", | |
" keys[i].style.visibility = 'hidden';\n", | |
" }\n", | |
"\n", | |
" if ('vertical' === 'vertical') {\n", | |
" gradient.style.height = (legendContent.offsetHeight - 6) + 'px';\n", | |
" }\n", | |
" }\n", | |
"\n", | |
" // add class for styling bordered legend keys\n", | |
" if (true) {\n", | |
" var keys = document.getElementsByClassName('legend-key');\n", | |
" for (var i=0; i < keys.length; i++) {\n", | |
" if (keys[i]) {\n", | |
" keys[i].classList.add('bordered');\n", | |
" }\n", | |
" }\n", | |
" var gradientBars = document.getElementsByClassName('gradient-bar');\n", | |
" for (var i=0; i < keys.length; i++) {\n", | |
" if (gradientBars[i]) {\n", | |
" gradientBars[i].classList.add('bordered');\n", | |
" }\n", | |
" }\n", | |
" }\n", | |
"\n", | |
" // update right-margin for compact Mapbox attribution based on calculated legend width\n", | |
" var attribMargin = legend.offsetWidth + 15;\n", | |
" document.getElementsByClassName('mapboxgl-ctrl-attrib')[0].style.marginRight = attribMargin.toString() + 'px';\n", | |
"\n", | |
"}\n", | |
"\n", | |
"\n", | |
"function generateInterpolateExpression(propertyValue, stops) {\n", | |
" var expression;\n", | |
" if (propertyValue == 'zoom') {\n", | |
" expression = ['interpolate', ['exponential', 1.2], ['zoom']]\n", | |
" }\n", | |
" else if (propertyValue == 'heatmap-density') {\n", | |
" expression = ['interpolate', ['linear'], ['heatmap-density']]\n", | |
" }\n", | |
" else {\n", | |
" expression = ['interpolate', ['linear'], ['get', propertyValue]]\n", | |
" }\n", | |
"\n", | |
" for (var i=0; i<stops.length; i++) {\n", | |
" expression.push(stops[i][0], stops[i][1])\n", | |
" }\n", | |
" return expression\n", | |
"}\n", | |
"\n", | |
"\n", | |
"function generateMatchExpression(propertyValue, stops, defaultValue) {\n", | |
" var expression;\n", | |
" expression = ['match', ['get', propertyValue]]\n", | |
" for (var i=0; i<stops.length; i++) {\n", | |
" expression.push(stops[i][0], stops[i][1])\n", | |
" }\n", | |
" expression.push(defaultValue)\n", | |
" \n", | |
" return expression\n", | |
"}\n", | |
"\n", | |
"\n", | |
"function generatePropertyExpression(expressionType, propertyValue, stops, defaultValue) {\n", | |
" var expression;\n", | |
" if (expressionType == 'match') {\n", | |
" expression = generateMatchExpression(propertyValue, stops, defaultValue)\n", | |
" }\n", | |
" else {\n", | |
" expression = generateInterpolateExpression(propertyValue, stops)\n", | |
" }\n", | |
"\n", | |
" return expression\n", | |
"}\n", | |
"\n", | |
"</script>\n", | |
"\n", | |
"<!-- main map creation code, extended by mapboxgl/templates/raster.html -->\n", | |
"<script type='text/javascript'>\n", | |
"\n", | |
" mapboxgl.accessToken = 'pk.eyJ1IjoidmluY2VudHNhcmFnbyIsImEiOiJjaXZ2MjlxbWUwMG41MnlwNzNnaGN0a2NmIn0.HxKPljj1_AK5333eBz-YxQ';\n", | |
"\n", | |
" var map = new mapboxgl.Map({\n", | |
" container: 'map',\n", | |
" attributionControl: false,\n", | |
" style: 'mapbox://styles/mapbox/light-v9?optimize=true',\n", | |
" center: [39.3000632136319, -5.756631921716197],\n", | |
" zoom: 13,\n", | |
" pitch: 0,\n", | |
" bearing: 0,\n", | |
" transformRequest: (url, resourceType) => {\n", | |
" if ( url.slice(0,22) == 'https://api.mapbox.com' || \n", | |
" url.slice(0,26) == 'https://a.tiles.mapbox.com' || \n", | |
" url.slice(0,26) == 'https://b.tiles.mapbox.com' ||\n", | |
" url.slice(0,26) == 'https://c.tiles.mapbox.com' ||\n", | |
" url.slice(0,26) == 'https://d.tiles.mapbox.com') {\n", | |
" //Add Mapboxgl-Jupyter Plugin identifier for Mapbox API traffic\n", | |
" return {\n", | |
" url: [url.slice(0, url.indexOf('?')+1), 'pluginName=PythonMapboxgl&', url.slice(url.indexOf('?')+1)].join('')\n", | |
" }\n", | |
" }\n", | |
" else {\n", | |
" //Do not transform URL for non Mapbox GET requests\n", | |
" return {url: url}\n", | |
" }\n", | |
" },\n", | |
" });\n", | |
"\n", | |
" \n", | |
" \n", | |
" map.addControl(new mapboxgl.AttributionControl({ compact: true }));\n", | |
"\n", | |
" \n", | |
"\n", | |
" \n", | |
"\n", | |
" map.addControl(new mapboxgl.NavigationControl());\n", | |
"\n", | |
" \n", | |
"\n", | |
" \n", | |
"\n", | |
" \n", | |
"\n", | |
" map.on('style.load', function() {\n", | |
" console.log('Yoooooo');\n", | |
" let params = {\n", | |
" 'type': 'raster',\n", | |
" 'tiles': [\n", | |
" 'http://127.0.0.1:8080/tiles/{z}/{x}/{y}.png'\n", | |
" ],\n", | |
" 'tileSize': 256,\n", | |
" 'minzoom': 0,\n", | |
" 'maxzoom': 22\n", | |
" }\n", | |
" if ([39.28650720617372, -5.770217424643658, 39.313619221090086, -5.743046418788738] !== undefined) params.bounds = [39.28650720617372, -5.770217424643658, 39.313619221090086, -5.743046418788738];\n", | |
"\n", | |
" map.addSource('raster-tiles', params);\n", | |
"\n", | |
" map.addLayer({\n", | |
" 'id': 'raster-tiles',\n", | |
" 'type': 'raster',\n", | |
" 'source': 'raster-tiles'\n", | |
" }, '' );\n", | |
"\n", | |
" });\n", | |
"\n", | |
"\n", | |
"\n", | |
"</script>\n", | |
"\n", | |
"</body>\n", | |
"</html>\" style=\"width: 100%; height: 1000px;\"></iframe>" | |
], | |
"text/plain": [ | |
"<IPython.core.display.HTML object>" | |
] | |
}, | |
"metadata": {}, | |
"output_type": "display_data" | |
} | |
], | |
"source": [ | |
"viz = RasterTilesViz(ts.get_tiles_url(), \n", | |
" tiles_bounds=ts.get_bounds(),\n", | |
" center=ts.get_center(),\n", | |
" access_token=token, \n", | |
" height='1000px', \n", | |
" zoom=13)\n", | |
"viz.show()" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 8, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"ts.stop()" | |
] | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"display_name": "Python 3", | |
"language": "python", | |
"name": "python3" | |
}, | |
"language_info": { | |
"codemirror_mode": { | |
"name": "ipython", | |
"version": 3 | |
}, | |
"file_extension": ".py", | |
"mimetype": "text/x-python", | |
"name": "python", | |
"nbconvert_exporter": "python", | |
"pygments_lexer": "ipython3", | |
"version": "3.7.0" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 2 | |
} |