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 | |
| } |