├── .gitignore ├── README.md ├── example └── React_Mapbox_GL.ipynb ├── js ├── package.json ├── src │ ├── components │ │ ├── dispatcher.js │ │ ├── footprint_overlay.js │ │ ├── glmap.js │ │ └── index.js │ └── index.js └── webpack.config.js ├── jupyter_map_gl ├── __init__.py ├── glmap.py └── static │ └── index.js ├── resources ├── React_Mapbox_GL.png └── map.gif └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | js/node_modules 2 | *.pyc 3 | **/*.pyc 4 | jupyter_map_gl.egg-info 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Jupyter Map GL 2 | 3 | A little ReactJS component wrapper around https://github.com/uber/react-map-gl for use within a jupyter notebook. 4 | 5 | Showcases the use of: https://github.com/timbr-io/jupyter-react-js and https://github.com/timbr-io/jupyter-react. 6 | 7 | 8 | 9 | ## Install 10 | 11 | ```bash 12 | pip install jupyter-map-gl 13 | jupyter nbextension enable --py jupyter_map_gl 14 | ``` 15 | 16 | ## Example usage 17 | 18 | ```python 19 | 20 | from jupyter_map_gl import GlMap 21 | from IPython.display import display 22 | 23 | glmap = GlMap(props={'width':900, 24 | 'height': 500, 25 | 'geojson': geojson, 26 | 'mapboxApiAccessToken': 'YOUR_MB_TOKEN' 27 | }) 28 | 29 | display(glmap) 30 | ``` 31 | -------------------------------------------------------------------------------- /example/React_Mapbox_GL.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 95, 6 | "metadata": { 7 | "collapsed": false 8 | }, 9 | "outputs": [], 10 | "source": [ 11 | "import json, os\n", 12 | "\n", 13 | "def maybe_download(path, url):\n", 14 | " if not os.path.exists(path):\n", 15 | " import urllib\n", 16 | " urllib.urlretrieve(url, path)\n", 17 | " with open(path, 'r') as data:\n", 18 | " geojson = json.loads(data.read())\n", 19 | " return geojson" 20 | ] 21 | }, 22 | { 23 | "cell_type": "code", 24 | "execution_count": 110, 25 | "metadata": { 26 | "collapsed": false 27 | }, 28 | "outputs": [], 29 | "source": [ 30 | "from jupyter_map_gl import GlMap\n", 31 | "from IPython.display import display\n", 32 | "\n", 33 | "url = 'https://raw.githubusercontent.com/chelm/geodata/master/2015-tornadoes.json'\n", 34 | "geojson = maybe_download('./data/2015-tornados.json', url)\n", 35 | "\n", 36 | "layer_props = {\n", 37 | " 'dotRadius': 4,\n", 38 | " 'globalOpacity': 1,\n", 39 | " 'compositeOperation': 'screen',\n", 40 | " 'dotFill': '#0FBA0F'\n", 41 | "}\n", 42 | "\n", 43 | "m = GlMap(props={'width':1000, \n", 44 | " 'height': 500,\n", 45 | " 'latitude': 39.9,\n", 46 | " 'longitude': -99,\n", 47 | " 'geojson': geojson,\n", 48 | " 'layerProps': layer_props,\n", 49 | " 'mapboxApiAccessToken': os.environ['MBTOKEN']\n", 50 | " })\n", 51 | "display(m)" 52 | ] 53 | }, 54 | { 55 | "cell_type": "code", 56 | "execution_count": 111, 57 | "metadata": { 58 | "collapsed": false 59 | }, 60 | "outputs": [ 61 | { 62 | "data": { 63 | "image/png": "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\nKyUQsYjAObeYc24lAO/9J8CTwAyUWpGEvo5DkQiJwa7dIHrUIiIiIiIiaoBakA5FMp6IRRXOuY1Q\nDtq9wAfAv4DPkRB+GfJODwN+5r3/ul79jGhdOOf2AbZA+bkPowiFaYBH6+0eiKjpuNYO1W0NREUt\nIiIiIiKihqiGdCiS8UQsqrBcz+uAPwLjgSGoyPWKqMD1GW0hLDOideGcG47qSK6M2HPv8N7/wjl3\nHMrbfRm436sAelTUIiIiIiIiIppGrUiHIhlPxKIE51xv4FpEnrMXyv9cEUVkvtfUbyPaLywkdh1g\nNGK//QiYmbA92nfapRIfFbWIiIiIiIg2ikjGE7Eowjk3FJgNJPUCP2/mJxHtDM65rVCpkxeAfsAv\nUI3KjYC+SIG/2Hv/cL362BqIilpEREREREQbR3u1FkdENAbzJo8FZgIHeu9vrXOXIloRzrl3gEGI\nJOQ2xPT4ArADygPeHNWTbNc5u1FRi4iIiIiIiIiIaJMwr3JP7/2f692XiNaDKeoTUb7vud77c4LP\nlgM+XxRIZaKiFhERERERERERERHR5uCcG4hIZgYCp3nvf1LnLrUqoqIWEREREREREREREdFm4Zzb\nCbgI6AEMBz5cFMLBo6IWEREREREREREREdHm4Zw7Bpjmvf93vfvSGoiKWkRERERERERERETEQoP2\nWDOtEqKiFhERERERERERERER0cbQod4diIiIiIiIiIiIiIiIiGiIqKhFRERERERERERERES0MURF\nLSIiIiIiIiIiIiIioo0hKmoRERERERERERERERFtDFFRi4iIiIiIiIiIiIiIaGOIilpERERERERE\nREREREQbw/8HWKzVJZJ2Mi4AAAAASUVORK5CYII=\n", 64 | "text/plain": [ 65 | "" 66 | ] 67 | }, 68 | "metadata": {}, 69 | "output_type": "display_data" 70 | } 71 | ], 72 | "source": [ 73 | "# Visualize tornadoes by state\n", 74 | "\n", 75 | "from collections import Counter\n", 76 | "%matplotlib inline\n", 77 | "import matplotlib.pyplot as plt\n", 78 | "import matplotlib.dates as mdates\n", 79 | "import datetime\n", 80 | "from dateutil import parser\n", 81 | "\n", 82 | "states_group = Counter([f['properties']['State1'] for f in geojson['features']]).items()\n", 83 | "states = zip(*list(reversed(sorted(states_group, key=lambda tup: tup[1]))))\n", 84 | "\n", 85 | "fig, ax = plt.subplots(1, figsize=(15,3))\n", 86 | "plt.xticks(range(len(states[0])), states[0], rotation=60)\n", 87 | "ax.set_title('2015 Tornadoes by State')\n", 88 | "ax.bar(range(len(states[0])), states[1], color='c', lw=.5)\n", 89 | "plt.show()\n", 90 | "\n" 91 | ] 92 | }, 93 | { 94 | "cell_type": "code", 95 | "execution_count": 114, 96 | "metadata": { 97 | "collapsed": true 98 | }, 99 | "outputs": [], 100 | "source": [ 101 | "# Aggregate to tornado counts to state geoms \n", 102 | "\n", 103 | "url = 'https://raw.githubusercontent.com/chelm/geodata/master/us-states.geojson'\n", 104 | "state_geojson = maybe_download('./data/us-states.geojson', url)\n", 105 | "\n", 106 | "for f in state_geojson['features']:\n", 107 | " try:\n", 108 | " idx = states[0].index(f['properties']['name'])\n", 109 | " f['properties']['value'] = states[1][idx]\n", 110 | " except:\n", 111 | " f['properties']['value'] = 0\n", 112 | "\n", 113 | "\n", 114 | "layer_props = {\n", 115 | " 'globalOpacity': 1,\n", 116 | " 'colorDomain': [0, 50, 100, 200, 300],\n", 117 | " 'colorRange': ['#ffdaa3','#ffb27c','#d3394a','#b3152f','#8b0000']\n", 118 | "}\n", 119 | "\n", 120 | "m2 = GlMap(props={'width':1000, \n", 121 | " 'height': 500,\n", 122 | " 'latitude': 39.9,\n", 123 | " 'longitude': -99,\n", 124 | " 'geojson': state_geojson, \n", 125 | " 'layerProps': layer_props,\n", 126 | " 'mapboxApiAccessToken': os.environ['MBTOKEN']\n", 127 | " })\n", 128 | "display(m2)" 129 | ] 130 | }, 131 | { 132 | "cell_type": "code", 133 | "execution_count": null, 134 | "metadata": { 135 | "collapsed": true 136 | }, 137 | "outputs": [], 138 | "source": [] 139 | } 140 | ], 141 | "metadata": { 142 | "kernelspec": { 143 | "display_name": "Python 2", 144 | "language": "python", 145 | "name": "python2" 146 | }, 147 | "language_info": { 148 | "codemirror_mode": { 149 | "name": "ipython", 150 | "version": 2 151 | }, 152 | "file_extension": ".py", 153 | "mimetype": "text/x-python", 154 | "name": "python", 155 | "nbconvert_exporter": "python", 156 | "pygments_lexer": "ipython2", 157 | "version": "2.7.11" 158 | } 159 | }, 160 | "nbformat": 4, 161 | "nbformat_minor": 0 162 | } 163 | -------------------------------------------------------------------------------- /js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jupyter_map_gl", 3 | "version": "0.1.0", 4 | "description": "", 5 | "author": "Chris Helm", 6 | "main": "src/index.js", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/timrb.io/jupyter_map_gl.git" 10 | }, 11 | "keywords": [ 12 | "jupyter", 13 | "ipython" 14 | ], 15 | "scripts": { 16 | "prepublish": "webpack -p", 17 | "test": "echo \"Error: no test specified\" && exit 1" 18 | }, 19 | "devDependencies": { 20 | "css-loader": "^0.23.1", 21 | "json-loader": "^0.5.4", 22 | "jsx-loader": "^0.13.2", 23 | "style-loader": "^0.13.1", 24 | "webpack": "^1.12.14", 25 | "react-hot-loader": "^1.3.0", 26 | "transform-loader": "^0.2.3" 27 | }, 28 | "dependencies": { 29 | "autobind-decorator": "^1.3.3", 30 | "babel-cli": "^6.10.1", 31 | "babel-loader": "^6.2.4", 32 | "babel-plugin-add-module-exports": "^0.2.1", 33 | "babel-plugin-transform-decorators-legacy": "^1.3.4", 34 | "babel-preset-es2015": "^6.9.0", 35 | "babel-preset-react": "^6.11.1", 36 | "babel-preset-stage-1": "^6.5.0", 37 | "flux": "^2.1.1", 38 | "jupyter-react-js": "^0.4.0", 39 | "react": "^15.3.2", 40 | "react-dom": "^15.3.2", 41 | "jupyter-glmap-components": "^0.0.1" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /js/src/components/dispatcher.js: -------------------------------------------------------------------------------- 1 | import { Dispatcher } from 'flux'; 2 | 3 | export default new Dispatcher(); 4 | -------------------------------------------------------------------------------- /js/src/components/footprint_overlay.js: -------------------------------------------------------------------------------- 1 | import React, {PropTypes, Component} from 'react'; 2 | import rtree from 'rtree'; 3 | import tilebelt from 'tilebelt'; 4 | import autobind from 'autobind-decorator'; 5 | 6 | import { CanvasOverlay } from 'react-map-gl'; 7 | 8 | export default class FootprintOverlay extends Component { 9 | 10 | tree: null 11 | 12 | constructor( props ) { 13 | super( props ); 14 | this.tree = rtree( 9 ); 15 | this.state = { 16 | }; 17 | } 18 | 19 | componentWillReceiveProps( newProps ) { 20 | if ( this.props.features && this.props.features.length < newProps.features.length ) { 21 | const newFeatures = newProps.features.slice( this.props.features.length-1, -1 ); 22 | this._indexFeatures( newFeatures ); 23 | } 24 | } 25 | 26 | componentWillMount(){ 27 | this._indexFeatures( this.props.features ); 28 | } 29 | 30 | _indexFeatures( features ) { 31 | this.tree.geoJSON( { 32 | "type":"FeatureCollection", 33 | "features": features 34 | } ); 35 | } 36 | 37 | // project center to pxy, find min/max pixel xy, unproject to lat/lon 38 | _getBounds( center, width, height, project, unproject ){ 39 | const xy = project( center ); 40 | const ul = unproject( [ xy[0] - ( width / 2 ), xy[1] - ( height / 2 ) ] ); 41 | const lr = unproject( [ xy[0] + ( width / 2 ), xy[1] + ( height / 2 ) ] ); 42 | return [ ul[0], lr[1], lr[0], ul[1] ]; 43 | } 44 | 45 | @autobind 46 | _redraw( opts ) { 47 | const { ctx, project, unproject, width, height } = opts; 48 | opts.ctx.clearRect(0, 0, width, height); 49 | 50 | if ( !opts.isDragging ) { 51 | const { longitude, latitude, fill = '#1FBAD6', stroke = '#ffffff', strokeWidth = 1, zoom } = this.props; 52 | const bounds = this._getBounds( [ longitude, latitude ], width, height, project, unproject ) ; 53 | const points = this.tree.bbox( ...bounds ); 54 | if ( points.length ) { 55 | ctx.strokeStyle = stroke; 56 | ctx.lineWidth = strokeWidth; 57 | ctx.fillStyle = fill; 58 | let tiles = []; 59 | points.forEach( pnt => { 60 | const pntTiles = this._renderGeom( pnt, ctx, zoom, project, width, height ); 61 | pntTiles.forEach( pTile => tiles.push( pTile ) ) 62 | }); 63 | this.props.notify( { features: tiles } ); 64 | } else { 65 | this.props.notify( { features: [] } ); 66 | } 67 | } 68 | } 69 | 70 | _bboxToTiles( bbox, zoom ) { 71 | const tiles = []; 72 | const ll = tilebelt.pointToTile( bbox[0], bbox[1], zoom ); 73 | const ur = tilebelt.pointToTile( bbox[2], bbox[3], zoom ); 74 | 75 | for ( let i = ll[0]; i < Math.min(ur[ 0 ], 2**zoom); i++ ) { 76 | for ( let j = ur[1]; j < Math.min(ll[ 1 ], 2**zoom); j++ ) { 77 | tiles.push( [ i, j, zoom ] ); 78 | } 79 | } 80 | return tiles; 81 | } 82 | 83 | _renderBox( ctx, bbox, project, width, height ) { 84 | const ul = project( [ bbox[0], bbox[3] ] ); 85 | const lr = project( [ bbox[2], bbox[1] ] ); 86 | const buf = -50.0; 87 | const shouldDraw = ( ul[0] > buf && ul[0] < width + Math.abs( buf ) ) && ( ul[1] > buf && ul[1] < height + Math.abs( buf ) ) 88 | //&& ( lr[0] < width + (buf * -1) && lr[0] < buf ); 89 | if ( shouldDraw ) { 90 | ctx.beginPath(); 91 | ctx.rect(ul[0], ul[1], lr[0] - ul[0], lr[1] - ul[1]); 92 | ctx.stroke(); 93 | } 94 | return shouldDraw; 95 | } 96 | 97 | @autobind 98 | _renderGeom( loc, ctx, zoom, project, width, height ) { 99 | const tileSet = []; 100 | if ( Math.floor( zoom ) < 5 ) { 101 | const px = project( loc.properties.center.coordinates ); 102 | ctx.beginPath(); 103 | ctx.arc( px[0], px[1], Math.max(3, Math.floor( zoom * .75 )), 0, 2 * Math.PI ); 104 | ctx.fill(); 105 | } else if ( Math.floor( zoom ) <= 8 ){ 106 | this._renderBox( ctx, loc.properties.bounds, project, width, height ); 107 | } else if ( Math.floor( zoom ) > 8 ) { 108 | const tiles = this._bboxToTiles( loc.properties.bounds, 15 ); 109 | tiles.forEach( tile => { 110 | const bbox = tilebelt.tileToBBOX( tile ); 111 | const _drawn = this._renderBox( ctx, bbox, project, width, height ); 112 | if ( _drawn ) { 113 | const _loc = { ...loc, properties: { ...loc.properties, zxy: tile, bounds: bbox } }; 114 | tileSet.push( _loc ); 115 | } 116 | }); 117 | } 118 | return tileSet; 119 | } 120 | 121 | render() { 122 | return ( 123 | 124 | ); 125 | } 126 | 127 | }; 128 | -------------------------------------------------------------------------------- /js/src/components/glmap.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import MapGL, {ScatterplotOverlay, ChoroplethOverlay} from 'react-map-gl'; 3 | import FootprintOverlay from './footprint_overlay'; 4 | import dispatcher from './dispatcher.js'; 5 | import rasterTileStyle from 'raster-tile-style'; 6 | import Immutable from 'immutable'; 7 | import autobind from 'autobind-decorator'; 8 | 9 | class GlMap extends React.Component { 10 | 11 | constructor( props ) { 12 | super( props ); 13 | this.state = { 14 | mapStyle: null, 15 | layers: props.layers || [], 16 | viewport: { 17 | width: props.width || 500, 18 | height: props.height || 500, 19 | latitude: props.latitude || 0, 20 | longitude: props.longitude || 0, 21 | zoom: props.zoom || 2, 22 | showZoomControls: props.showZoomControls || true, 23 | startDragLngLat: null, 24 | isDragging: null 25 | }, 26 | }; 27 | } 28 | 29 | componentWillReceiveProps( newProps ) { 30 | if ( newProps.layers && this.state.layers.length != newProps.layers.length ){ 31 | this._updateLayers( newProps.layers ); 32 | } 33 | 34 | if ( newProps.tileSource ) { 35 | const { tileSource, width, height, latitude, longitude } = newProps 36 | const source = newProps.accessToken ? newProps.tileSource + `?access_token=${newProps.accessToken}` : newProps.tileSource; 37 | 38 | this.setState( { 39 | viewport: { ...this.state.viewport, width, height, latitude, longitude }, 40 | mapStyle: Immutable.fromJS( rasterTileStyle( [ source ] ) ) 41 | } ); 42 | } 43 | 44 | } 45 | 46 | componentWillMount(){ 47 | if ( this.props.layers && this.state.layers !== this.props.layers.length ) { 48 | this._updateLayers( this.props.layers ); 49 | } 50 | 51 | if ( this.props.tileSource ) { 52 | const source = this.props.accessToken ? this.props.tileSource + `?access_token=${this.props.accessToken}` : this.props.tileSource; 53 | this.setState( { mapStyle: Immutable.fromJS(rasterTileStyle([source])) } ); 54 | } 55 | 56 | dispatcher.register( payload => { 57 | if ( payload.actionType === 'glmap_update' ) { 58 | const { data = {} } = payload; 59 | if ( data.features && data.layerId ) { 60 | this._updateFeatures( data.layerId, data.features ); 61 | } else if ( data.layers ) { 62 | this._updateLayers( data.layers ); 63 | } 64 | } 65 | } ); 66 | } 67 | 68 | _updateFeatures( layerId, newFeatures ) { 69 | // find layer and set features... then set state 70 | const _layers = [ ...this.state.layers ]; 71 | // prob dont need foreach, find would work 72 | _layers.forEach( layer => { 73 | if ( layer.id === layerId ) { 74 | layer.features = layer.features.concat( newFeatures ); 75 | } 76 | }); 77 | this.setState( { layers: [ ..._layers ] } ); 78 | } 79 | 80 | _updateLayers( layers ){ 81 | const _layers = layers.map( layer => { 82 | layer.features = layer.geojson.features; 83 | return layer; 84 | }); 85 | this.setState( { layers: _layers } ); 86 | } 87 | 88 | @autobind 89 | _onChangeViewport(opt) { 90 | if (this.props.onChangeViewport) { 91 | return this.props.onChangeViewport(opt); 92 | } 93 | const viewport = { 94 | ...this.state.viewport, 95 | zoom: opt.zoom, 96 | latitude: opt.latitude, 97 | longitude: opt.longitude, 98 | startDragLngLat: opt.startDragLngLat, 99 | isDragging: opt.isDragging 100 | }; 101 | 102 | this.setState( { viewport } ); 103 | } 104 | 105 | buildLayers( layers, mapProps ) { 106 | return layers.map( ( layer, i ) => { 107 | const layerProps = { ...mapProps, ...layer.props, notify: this.notify_python, features: layer.features }; 108 | if ( layer.type === 'footprint' ) { 109 | return ; 110 | } else if ( layer.type === 'choropleth' ) { 111 | return f.geometry.coordinates ) ) } />; 112 | } else if ( layer.type === 'scatter' ) { 113 | return ; 114 | } 115 | }); 116 | } 117 | 118 | @autobind 119 | notify_python( data ) { 120 | this.props.comm.send({ method: "notify", data } ); 121 | } 122 | 123 | render() { 124 | const mapProps = { ...this.state.viewport, mapboxApiAccessToken: this.props.mapboxApiAccessToken }; 125 | const { width, height } = this.state.viewport; 126 | const { mapStyle, layers } = this.state; 127 | 128 | return ( 129 |
130 | 131 | { this.buildLayers( layers, mapProps ) } 132 | 133 |
134 | ); 135 | } 136 | } 137 | 138 | export default GlMap; 139 | -------------------------------------------------------------------------------- /js/src/components/index.js: -------------------------------------------------------------------------------- 1 | import GlMap from './GlMap.js'; 2 | 3 | export default { 4 | GlMap 5 | }; 6 | -------------------------------------------------------------------------------- /js/src/index.js: -------------------------------------------------------------------------------- 1 | import JupyterReact from 'jupyter-react-js'; 2 | import components from 'jupyter-glmap-components'; 3 | import react from 'react'; 4 | import reactDom from 'react-dom'; 5 | 6 | // An option component update method passed to every component 7 | // when an update message is received over the comm, 8 | // components will dispatch an event to every other component 9 | const on_update = ( module, props ) => { 10 | components.dispatcher.dispatch({ 11 | actionType: module.toLowerCase() + '_update', 12 | data: props 13 | }); 14 | } 15 | 16 | function load_ipython_extension () { 17 | requirejs([ 18 | "base/js/namespace", 19 | "base/js/events", 20 | ], function( Jupyter, events ) { 21 | JupyterReact.init( Jupyter, events, 'react.gl', { components, on_update, save: false, react, reactDom } ); 22 | }); 23 | } 24 | 25 | module.exports = { 26 | load_ipython_extension: load_ipython_extension 27 | }; 28 | -------------------------------------------------------------------------------- /js/webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var version = require('./package.json').version; 3 | var path = require( 'path' ); 4 | 5 | const babelSettings = { 6 | plugins: [ 7 | 'transform-flow-strip-types', 8 | 'add-module-exports', 9 | 'transform-regenerator', 10 | 'transform-decorators-legacy' 11 | ], 12 | presets: [ 'es2015', 'react', 'stage-1' ] 13 | }; 14 | 15 | 16 | module.exports = [ 17 | { 18 | entry: './src/index.js', 19 | output: { 20 | filename: 'index.js', 21 | path: '../jupyter_map_gl/static', 22 | libraryTarget: 'amd' 23 | }, 24 | plugins:[ 25 | new webpack.DefinePlugin({ 26 | 'process.env':{ 27 | 'NODE_ENV': JSON.stringify('production') 28 | } 29 | }) 30 | ], 31 | module : { 32 | loaders : [ 33 | { 34 | test: /\.js?$/, 35 | exclude: /(node_modules|bower_components)/, 36 | loaders: [`babel?${JSON.stringify( babelSettings )}`] 37 | }, 38 | { 39 | test: /\.css$/, loader: "style-loader!css-loader" 40 | }, 41 | { 42 | test: /\.json$/, loader: 'json-loader' 43 | } 44 | ] 45 | } 46 | } 47 | ]; 48 | -------------------------------------------------------------------------------- /jupyter_map_gl/__init__.py: -------------------------------------------------------------------------------- 1 | from .glmap import GlMap 2 | 3 | def _jupyter_nbextension_paths(): 4 | return [{ 5 | 'section': 'notebook', 6 | 'src': 'static', 7 | 'dest': 'jupyter_map_gl', 8 | 'require': 'jupyter_map_gl/index' 9 | }] 10 | -------------------------------------------------------------------------------- /jupyter_map_gl/glmap.py: -------------------------------------------------------------------------------- 1 | from jupyter_react import Component 2 | 3 | class GlMap(Component): 4 | module = 'GlMap' 5 | features = [] 6 | 7 | def __init__(self, **kwargs): 8 | super(GlMap, self).__init__(target_name='react.gl', props=kwargs.get('props', {})) 9 | self.layers = self.props.get('layers', []) 10 | self.on_msg(self._handle_msg) 11 | 12 | def add_layer(self, layer): 13 | self.layers[layer['id']] = layer; 14 | self.send({ "method": "update", "props": {"layers": self.layers}} ) 15 | 16 | def add_features(self, layer_id, features): 17 | self.send({ "method": "update", "props": {"layerId": layer_id, "features": features}}) 18 | 19 | def _handle_msg(self, msg): 20 | data = msg['content']['data'] 21 | if data.get('method', '') == 'notify': 22 | self.features = data.get('data', {}).get('features', []) 23 | -------------------------------------------------------------------------------- /resources/React_Mapbox_GL.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chelm/jupyter-map-gl/f536d56bebf84ce13f5dddcb0723b4134a6fb67d/resources/React_Mapbox_GL.png -------------------------------------------------------------------------------- /resources/map.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chelm/jupyter-map-gl/f536d56bebf84ce13f5dddcb0723b4134a6fb67d/resources/map.gif -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | from setuptools.command.develop import develop as _develop 3 | from setuptools.command.install import install as _install 4 | from notebook.nbextensions import install_nbextension 5 | from notebook.services.config import ConfigManager 6 | import os 7 | 8 | extension_dir = os.path.join(os.path.dirname(__file__), "jupyter_map_gl", "static") 9 | 10 | class develop(_develop): 11 | def run(self): 12 | _develop.run(self) 13 | install_nbextension(extension_dir, symlink=True, 14 | overwrite=True, user=False, destination="jupyter_map_gl") 15 | cm = ConfigManager() 16 | cm.update('notebook', {"load_extensions": {"jupyter_map_gl/index": True } }) 17 | 18 | class install(_install): 19 | def run(self): 20 | _install.run(self) 21 | cm = ConfigManager() 22 | cm.update('notebook', {"load_extensions": {"jupyter_map_gl/index": True } }) 23 | 24 | setup(name='jupyter-map-gl', 25 | cmdclass={'develop': develop, 'install': install}, 26 | version='0.2.2', 27 | description='A wrapper around react-map-gl components for use in jupyter notebooks', 28 | url='https://bitbucket.com/timbr-io/jupyter-map-gl', 29 | author='Chris Helm', 30 | author_email='chelm@timbr.io', 31 | license='MIT', 32 | packages=['jupyter_map_gl'], 33 | zip_safe=False, 34 | data_files=[ 35 | ('share/jupyter/nbextensions/jupyter_map_gl', [ 36 | 'jupyter_map_gl/static/index.js' 37 | ]), 38 | ], 39 | install_requires=[ 40 | "ipython", 41 | "jupyter-react" 42 | ] 43 | ) 44 | --------------------------------------------------------------------------------