diff --git a/.ipynb_checkpoints/learning_bokeh_in_jupyter-checkpoint.ipynb b/.ipynb_checkpoints/learning_bokeh_in_jupyter-checkpoint.ipynb new file mode 100644 index 0000000..8df5d25 --- /dev/null +++ b/.ipynb_checkpoints/learning_bokeh_in_jupyter-checkpoint.ipynb @@ -0,0 +1,490 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here we are learning to bokeh in jupyter notebooks." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Using data directory: /home/lnielsen/.bokeh/data\n", + "Downloading: CGM.csv (1589982 bytes)\n", + " 1589982 [100.00%]\n", + "Downloading: US_Counties.zip (3182088 bytes)\n", + " 3182088 [100.00%]\n", + "Unpacking: US_Counties.csv\n", + "Downloading: us_cities.json (713565 bytes)\n", + " 713565 [100.00%]\n", + "Downloading: unemployment09.csv (253301 bytes)\n", + " 253301 [100.00%]\n", + "Downloading: AAPL.csv (166698 bytes)\n", + " 166698 [100.00%]\n", + "Downloading: FB.csv (9706 bytes)\n", + " 9706 [100.00%]\n", + "Downloading: GOOG.csv (113894 bytes)\n", + " 113894 [100.00%]\n", + "Downloading: IBM.csv (165625 bytes)\n", + " 165625 [100.00%]\n", + "Downloading: MSFT.csv (161614 bytes)\n", + " 161614 [100.00%]\n", + "Downloading: WPP2012_SA_DB03_POPULATION_QUINQUENNIAL.zip (5148539 bytes)\n", + " 5148539 [100.00%]\n", + "Unpacking: WPP2012_SA_DB03_POPULATION_QUINQUENNIAL.csv\n", + "Downloading: gapminder_fertility.csv (64346 bytes)\n", + " 64346 [100.00%]\n", + "Downloading: gapminder_population.csv (94509 bytes)\n", + " 94509 [100.00%]\n", + "Downloading: gapminder_life_expectancy.csv (73243 bytes)\n", + " 73243 [100.00%]\n", + "Downloading: gapminder_regions.csv (7781 bytes)\n", + " 7781 [100.00%]\n", + "Downloading: world_cities.zip (646858 bytes)\n", + " 646858 [100.00%]\n", + "Unpacking: world_cities.csv\n", + "Downloading: airports.json (6373 bytes)\n", + " 6373 [100.00%]\n", + "Downloading: movies.db.zip (5067833 bytes)\n", + " 5067833 [100.00%]\n", + "Unpacking: movies.db\n" + ] + } + ], + "source": [ + "import bokeh.sampledata; bokeh.sampledata.download()\n", + "from bokeh.models import HoverTool\n", + "from bokeh.plotting import figure, show, output_file, ColumnDataSource\n", + "from bokeh.sampledata.us_counties import data as counties\n", + "from bokeh.sampledata.unemployment import data as unemployment\n", + "\n", + "\n", + "counties = {\n", + " code: county for code, county in counties.items() if county[\"state\"] == \"ma\"\n", + "}\n", + "\n", + "county_xs = [county[\"lons\"] for county in counties.values()]\n", + "county_ys = [county[\"lats\"] for county in counties.values()]\n", + "\n", + "colors = [\"#F1EEF6\", \"#D4B9DA\", \"#C994C7\", \"#DF65B0\", \"#DD1C77\", \"#980043\"]\n", + "\n", + "county_names = [county['name'] for county in counties.values()]\n", + "county_rates = [unemployment[county_id] for county_id in counties]\n", + "county_colors = [colors[int(rate/3)] for rate in county_rates]\n", + "\n", + "source = ColumnDataSource(data=dict(\n", + " x=county_xs,\n", + " y=county_ys,\n", + " color=county_colors,\n", + " name=county_names,\n", + " rate=county_rates,\n", + "))\n", + "\n", + "TOOLS=\"pan,wheel_zoom,box_zoom,reset,hover,save\"\n", + "\n", + "p = figure(title=\"Texas Unemployment 2009\", tools=TOOLS)\n", + "\n", + "p.patches('x', 'y', source=source,\n", + " fill_color='color', fill_alpha=0.7,\n", + " line_color=\"white\", line_width=0.5)\n", + "\n", + "hover = p.select_one(HoverTool)\n", + "hover.point_policy = \"follow_mouse\"\n", + "hover.tooltips = [\n", + " (\"Name\", \"@name\"),\n", + " (\"Unemployment rate)\", \"@rate%\"),\n", + " (\"(Long, Lat)\", \"($x, $y)\"),\n", + "]\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "
\n", + " \n", + " Loading BokehJS ...\n", + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/javascript": [ + "\n", + "(function(global) {\n", + " function now() {\n", + " return new Date();\n", + " }\n", + "\n", + " if (typeof (window._bokeh_onload_callbacks) === \"undefined\") {\n", + " window._bokeh_onload_callbacks = [];\n", + " }\n", + "\n", + " function run_callbacks() {\n", + " window._bokeh_onload_callbacks.forEach(function(callback) { callback() });\n", + " delete window._bokeh_onload_callbacks\n", + " console.info(\"Bokeh: all callbacks have finished\");\n", + " }\n", + "\n", + " function load_libs(js_urls, callback) {\n", + " window._bokeh_onload_callbacks.push(callback);\n", + " if (window._bokeh_is_loading > 0) {\n", + " console.log(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n", + " return null;\n", + " }\n", + " if (js_urls == null || js_urls.length === 0) {\n", + " run_callbacks();\n", + " return null;\n", + " }\n", + " console.log(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n", + " window._bokeh_is_loading = js_urls.length;\n", + " for (var i = 0; i < js_urls.length; i++) {\n", + " var url = js_urls[i];\n", + " var s = document.createElement('script');\n", + " s.src = url;\n", + " s.async = false;\n", + " s.onreadystatechange = s.onload = function() {\n", + " window._bokeh_is_loading--;\n", + " if (window._bokeh_is_loading === 0) {\n", + " console.log(\"Bokeh: all BokehJS libraries loaded\");\n", + " run_callbacks()\n", + " }\n", + " };\n", + " s.onerror = function() {\n", + " console.warn(\"failed to load library \" + url);\n", + " };\n", + " console.log(\"Bokeh: injecting script tag for BokehJS library: \", url);\n", + " document.getElementsByTagName(\"head\")[0].appendChild(s);\n", + " }\n", + " };\n", + "\n", + " var js_urls = ['https://cdn.pydata.org/bokeh/release/bokeh-0.11.1.min.js', 'https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.11.1.min.js', 'https://cdn.pydata.org/bokeh/release/bokeh-compiler-0.11.1.min.js'];\n", + "\n", + " var inline_js = [\n", + " function(Bokeh) {\n", + " Bokeh.set_log_level(\"info\");\n", + " },\n", + " \n", + " function(Bokeh) {\n", + " Bokeh.$(\"#44e8e7a5-7363-4bde-ba9a-36305a462ef9\").text(\"BokehJS successfully loaded\");\n", + " },\n", + " function(Bokeh) {\n", + " console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-0.11.1.min.css\");\n", + " Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-0.11.1.min.css\");\n", + " console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.11.1.min.css\");\n", + " Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.11.1.min.css\");\n", + " }\n", + " ];\n", + "\n", + " function run_inline_js() {\n", + " for (var i = 0; i < inline_js.length; i++) {\n", + " inline_js[i](window.Bokeh);\n", + " }\n", + " }\n", + "\n", + " if (window._bokeh_is_loading === 0) {\n", + " console.log(\"Bokeh: BokehJS loaded, going straight to plotting\");\n", + " run_inline_js();\n", + " } else {\n", + " load_libs(js_urls, function() {\n", + " console.log(\"Bokeh: BokehJS plotting callback run at\", now());\n", + " run_inline_js();\n", + " });\n", + " }\n", + "}(this));" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from bokeh.io import output_notebook\n", + "\n", + "output_notebook()" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "
\n", + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "

<Bokeh Notebook handle for In[20]>

" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "show(p)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "
\n", + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "

<Bokeh Notebook handle for In[13]>

" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/README.md b/README.md index 61ec120..6a31cb4 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,5 @@ # InteractiveProgramming -This is the base repo for the interactive programming project for Software Design, Spring 2016 at Olin College. +This is the repo for an interactive map of queer resources in Massachusetts. +To run: +Have view_pandas.py, model_pandas.py, and test.csv in the same directory. +Run view_pandas.py. diff --git a/images/basic_plot.png b/images/basic_plot.png new file mode 100644 index 0000000..c4fd202 Binary files /dev/null and b/images/basic_plot.png differ diff --git a/images/cat_basic_with_overtext.png b/images/cat_basic_with_overtext.png new file mode 100644 index 0000000..8e147ea Binary files /dev/null and b/images/cat_basic_with_overtext.png differ diff --git a/images/cat_zoomed_in.png b/images/cat_zoomed_in.png new file mode 100644 index 0000000..5bd4a20 Binary files /dev/null and b/images/cat_zoomed_in.png differ diff --git a/model_pandas.py b/model_pandas.py new file mode 100644 index 0000000..8a5380b --- /dev/null +++ b/model_pandas.py @@ -0,0 +1,55 @@ +""" +This part of the code comprises the model portion of model-view-controller. +It takes in a filename, which is a csv file with information about resources for queer youth in MA. +It outputs a pandas DataFrame. + +@author: Louise Nielsen and Apurva Raman + nielsenlouise@github.com, apurvaraman@github.com +""" + +from pandas import * +from bokeh.plotting import ColumnDataSource as plotColumnDataSource + + +class Model(object): + """A Model() object contains a pandas.DataFrame of resources. + """ + + def __init__(self, source=None, filename='test.csv', frame=None): + self.source = source + self.filename = filename + if frame is None: + self.frame = DataFrame(read_csv(self.filename)) + else: + self.frame = frame + + def set_color(self): + """Sets colors for each glyph based on category. Defines attributes for each glyph. + """ + colormap = {'health': 'red', + 'support': 'green', + 'housing': 'blue', + 'advocacy': 'purple', + 'legal': 'cyan' + } + self.frame['color'] = self.frame['Category'].map(lambda x: colormap[x]) + self.source = plotColumnDataSource(dict( + name=self.frame['Name'], + lat=self.frame['Lat'], + lon=self.frame['Lon'], + address=self.frame['Address'], + category=self.frame['Category'], + color=self.frame['color'])) + + def update_model(self): + """This is going to update the model, probably has to filter things. + + We did not have time before the due date to implement buttons and + filtering, which was the intent of this. + """ + pass # TODO: Implement this. + +if __name__ == '__main__': + thing = Model() + thing.set_color + print thing.frame diff --git a/model_pandas.pyc b/model_pandas.pyc new file mode 100644 index 0000000..866303d Binary files /dev/null and b/model_pandas.pyc differ diff --git a/playing_with_packages/csv_read_for_pandas.py b/playing_with_packages/csv_read_for_pandas.py new file mode 100644 index 0000000..ce071fe --- /dev/null +++ b/playing_with_packages/csv_read_for_pandas.py @@ -0,0 +1,3 @@ +import pandas as pd + +print pd.read_csv('test_bokeh_data.csv') diff --git a/playing_with_packages/google_maps_stuff.py b/playing_with_packages/google_maps_stuff.py new file mode 100644 index 0000000..0a914da --- /dev/null +++ b/playing_with_packages/google_maps_stuff.py @@ -0,0 +1,51 @@ +from bokeh.models import HoverTool +from bokeh.plotting import figure +from bokeh.plotting import ColumnDataSource as plotColumnDataSource +from bokeh.io import output_file, show +from bokeh.models import ( + GMapPlot, GMapOptions, ColumnDataSource, Circle, DataRange1d, PanTool, WheelZoomTool, BoxSelectTool +) + +map_options = GMapOptions(lat=42.3601, lng=-71.0589, map_type="roadmap", zoom=9) + +plot = GMapPlot( + x_range=DataRange1d(), y_range=DataRange1d(), map_options=map_options, title="Massachusetts" +) + +# source = ColumnDataSource( +# data=dict( +# lat=[42.3601, 42.2926], +# lon=[-71.0589, -71.2644], +# ) +# ) + +data=dict( + x=[-71.0589, -71.2644], + y=[42.3601, 42.2926], + name=['Boston', 'Olin'], + address=['Boston Rite Aid', 'Olin The Awesomest'] +) +source = plotColumnDataSource(data) + +colormap = {'Boston': 'blue', 'Olin': 'green'} +colors = {colormap[x] for x in data['name']} +circle = Circle(x="x", y="y", size=15, fill_color=colors, fill_alpha=0.8) +plot.add_glyph(source, circle) + +# TOOLS="pan,wheel_zoom,box_zoom,reset,hover,save" + +# p = figure(title="Our Map", tools=TOOLS) + +plot.add_tools(PanTool(), WheelZoomTool(), BoxSelectTool(), HoverTool()) + +hover = plot.select_one(HoverTool) +hover.point_policy = "follow_mouse" +hover.tooltips = [ + ('Name', '@name'), + ('Title', '@address') +# ("(Long, Lat)", "($x, $y)"), +] + +# plot.add_tools(PanTool(), WheelZoomTool(), BoxSelectTool(), hover()) +output_file("gmap_plot.html") +show(plot) diff --git a/playing_with_packages/learning_bokeh_in_jupyter.ipynb b/playing_with_packages/learning_bokeh_in_jupyter.ipynb new file mode 100644 index 0000000..66aed75 --- /dev/null +++ b/playing_with_packages/learning_bokeh_in_jupyter.ipynb @@ -0,0 +1,380 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here we are learning to bokeh in jupyter notebooks." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Using data directory: /home/lnielsen/.bokeh/data\n", + "Downloading: CGM.csv (1589982 bytes)\n", + " 1589982 [100.00%]\n", + "Downloading: US_Counties.zip (3182088 bytes)\n", + " 3182088 [100.00%]\n", + "Unpacking: US_Counties.csv\n", + "Downloading: us_cities.json (713565 bytes)\n", + " 713565 [100.00%]\n", + "Downloading: unemployment09.csv (253301 bytes)\n", + " 253301 [100.00%]\n", + "Downloading: AAPL.csv (166698 bytes)\n", + " 166698 [100.00%]\n", + "Downloading: FB.csv (9706 bytes)\n", + " 9706 [100.00%]\n", + "Downloading: GOOG.csv (113894 bytes)\n", + " 113894 [100.00%]\n", + "Downloading: IBM.csv (165625 bytes)\n", + " 165625 [100.00%]\n", + "Downloading: MSFT.csv (161614 bytes)\n", + " 161614 [100.00%]\n", + "Downloading: WPP2012_SA_DB03_POPULATION_QUINQUENNIAL.zip (5148539 bytes)\n", + " 5148539 [100.00%]\n", + "Unpacking: WPP2012_SA_DB03_POPULATION_QUINQUENNIAL.csv\n", + "Downloading: gapminder_fertility.csv (64346 bytes)\n", + " 64346 [100.00%]\n", + "Downloading: gapminder_population.csv (94509 bytes)\n", + " 94509 [100.00%]\n", + "Downloading: gapminder_life_expectancy.csv (73243 bytes)\n", + " 73243 [100.00%]\n", + "Downloading: gapminder_regions.csv (7781 bytes)\n", + " 7781 [100.00%]\n", + "Downloading: world_cities.zip (646858 bytes)\n", + " 646858 [100.00%]\n", + "Unpacking: world_cities.csv\n", + "Downloading: airports.json (6373 bytes)\n", + " 6373 [100.00%]\n", + "Downloading: movies.db.zip (5067833 bytes)\n", + " 5067833 [100.00%]\n", + "Unpacking: movies.db\n" + ] + } + ], + "source": [ + "import bokeh.sampledata; bokeh.sampledata.download()\n", + "from bokeh.models import HoverTool\n", + "from bokeh.plotting import figure, show, output_file, ColumnDataSource\n", + "from bokeh.sampledata.us_counties import data as counties\n", + "from bokeh.sampledata.unemployment import data as unemployment\n", + "\n", + "\n", + "counties = {\n", + " code: county for code, county in counties.items() if county[\"state\"] == \"ma\"\n", + "}\n", + "\n", + "county_xs = [county[\"lons\"] for county in counties.values()]\n", + "county_ys = [county[\"lats\"] for county in counties.values()]\n", + "\n", + "colors = [\"#F1EEF6\", \"#D4B9DA\", \"#C994C7\", \"#DF65B0\", \"#DD1C77\", \"#980043\"]\n", + "\n", + "county_names = [county['name'] for county in counties.values()]\n", + "county_rates = [unemployment[county_id] for county_id in counties]\n", + "county_colors = [colors[int(rate/3)] for rate in county_rates]\n", + "\n", + "source = ColumnDataSource(data=dict(\n", + " x=county_xs,\n", + " y=county_ys,\n", + " color=county_colors,\n", + " name=county_names,\n", + " rate=county_rates,\n", + "))\n", + "\n", + "TOOLS=\"pan,wheel_zoom,box_zoom,reset,hover,save\"\n", + "\n", + "p = figure(title=\"Texas Unemployment 2009\", tools=TOOLS)\n", + "\n", + "p.patches('x', 'y', source=source,\n", + " fill_color='color', fill_alpha=0.7,\n", + " line_color=\"white\", line_width=0.5)\n", + "\n", + "hover = p.select_one(HoverTool)\n", + "hover.point_policy = \"follow_mouse\"\n", + "hover.tooltips = [\n", + " (\"Name\", \"@name\"),\n", + " (\"Unemployment rate)\", \"@rate%\"),\n", + " (\"(Long, Lat)\", \"($x, $y)\"),\n", + "]\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "
\n", + " \n", + " Loading BokehJS ...\n", + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/javascript": [ + "\n", + "(function(global) {\n", + " function now() {\n", + " return new Date();\n", + " }\n", + "\n", + " if (typeof (window._bokeh_onload_callbacks) === \"undefined\") {\n", + " window._bokeh_onload_callbacks = [];\n", + " }\n", + "\n", + " function run_callbacks() {\n", + " window._bokeh_onload_callbacks.forEach(function(callback) { callback() });\n", + " delete window._bokeh_onload_callbacks\n", + " console.info(\"Bokeh: all callbacks have finished\");\n", + " }\n", + "\n", + " function load_libs(js_urls, callback) {\n", + " window._bokeh_onload_callbacks.push(callback);\n", + " if (window._bokeh_is_loading > 0) {\n", + " console.log(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n", + " return null;\n", + " }\n", + " if (js_urls == null || js_urls.length === 0) {\n", + " run_callbacks();\n", + " return null;\n", + " }\n", + " console.log(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n", + " window._bokeh_is_loading = js_urls.length;\n", + " for (var i = 0; i < js_urls.length; i++) {\n", + " var url = js_urls[i];\n", + " var s = document.createElement('script');\n", + " s.src = url;\n", + " s.async = false;\n", + " s.onreadystatechange = s.onload = function() {\n", + " window._bokeh_is_loading--;\n", + " if (window._bokeh_is_loading === 0) {\n", + " console.log(\"Bokeh: all BokehJS libraries loaded\");\n", + " run_callbacks()\n", + " }\n", + " };\n", + " s.onerror = function() {\n", + " console.warn(\"failed to load library \" + url);\n", + " };\n", + " console.log(\"Bokeh: injecting script tag for BokehJS library: \", url);\n", + " document.getElementsByTagName(\"head\")[0].appendChild(s);\n", + " }\n", + " };\n", + "\n", + " var js_urls = ['https://cdn.pydata.org/bokeh/release/bokeh-0.11.1.min.js', 'https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.11.1.min.js', 'https://cdn.pydata.org/bokeh/release/bokeh-compiler-0.11.1.min.js'];\n", + "\n", + " var inline_js = [\n", + " function(Bokeh) {\n", + " Bokeh.set_log_level(\"info\");\n", + " },\n", + " \n", + " function(Bokeh) {\n", + " Bokeh.$(\"#44e8e7a5-7363-4bde-ba9a-36305a462ef9\").text(\"BokehJS successfully loaded\");\n", + " },\n", + " function(Bokeh) {\n", + " console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-0.11.1.min.css\");\n", + " Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-0.11.1.min.css\");\n", + " console.log(\"Bokeh: injecting CSS: https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.11.1.min.css\");\n", + " Bokeh.embed.inject_css(\"https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.11.1.min.css\");\n", + " }\n", + " ];\n", + "\n", + " function run_inline_js() {\n", + " for (var i = 0; i < inline_js.length; i++) {\n", + " inline_js[i](window.Bokeh);\n", + " }\n", + " }\n", + "\n", + " if (window._bokeh_is_loading === 0) {\n", + " console.log(\"Bokeh: BokehJS loaded, going straight to plotting\");\n", + " run_inline_js();\n", + " } else {\n", + " load_libs(js_urls, function() {\n", + " console.log(\"Bokeh: BokehJS plotting callback run at\", now());\n", + " run_inline_js();\n", + " });\n", + " }\n", + "}(this));" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from bokeh.io import output_notebook\n", + "\n", + "output_notebook()" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "collapsed": false + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + "
\n", + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "

<Bokeh Notebook handle for In[20]>

" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "show(p)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 2", + "language": "python", + "name": "python2" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/playing_with_packages/lets_try_pandas.py b/playing_with_packages/lets_try_pandas.py new file mode 100644 index 0000000..d3643f9 --- /dev/null +++ b/playing_with_packages/lets_try_pandas.py @@ -0,0 +1,78 @@ +import pandas +from bokeh.plotting import ColumnDataSource as plotColumnDataSource +from bokeh.models import (GMapPlot, + GMapOptions, + DataRange1d, + Circle, + PanTool, + WheelZoomTool, + BoxSelectTool, + HoverTool) +from bokeh.io import show, output_file + +test = pandas.read_csv('test.csv') +test2 = pandas.DataFrame(test) + + +types = ['health', 'support'] + +options = GMapOptions(lat=42.36, lng=-71.06, map_type='roadmap', zoom=11) +plot = GMapPlot(x_range=DataRange1d(), + y_range=DataRange1d(), + map_options=options, + title='heh') +plot.add_tools(PanTool(), + WheelZoomTool(), + BoxSelectTool(), + HoverTool()) + +colormap = {'health': 'red', 'support': 'green'} +test2['color'] = test2['Category'].map(lambda x: colormap[x]) + +source = plotColumnDataSource(dict( + name=test2['Name'], + lat=test2['Lat'], + lon=test2['Lon'], + address=test2['Address'], + category=test2['Category'], + color=test2['color'])) +print source + + +class Model(object): + """A Model() object contains a pandas.DataFrame of resources. + """ + + def __init__(self, filename='test.csv', frame=None): + self.filename = filename + self.frame = pandas.DataFrame(pandas.read_csv(self.filename)) + + +class View(object): + """I'm not really sure what this class is going to do. I guess I'll figure + it out. + """ + + def __init__(self, types, filename='test.csv', model=None): + self.types = types + self.filename = filename + if model is None: + self.model = Model(self.filename) + else: + self.model = model + + def make_map(self, map_type, title): + options = GMapOptions(lat=43.7, lng=-79.4, map_type=map_type, zoom=11) + plot = GMapPlot(x_range=DataRange1d(), + y_range=DataRange1d(), + map_options=options, + title=title) + output_file('plot.html') + show(plot) + + +circle = Circle(x='lon', y='lat', size=15, fill_color='color', fill_alpha=.8) +plot.add_glyph(source, circle) + +output_file('heh.html') +show(plot) diff --git a/playing_with_packages/myapp.py b/playing_with_packages/myapp.py new file mode 100644 index 0000000..67e6fcf --- /dev/null +++ b/playing_with_packages/myapp.py @@ -0,0 +1,39 @@ +# myapp.py + +import numpy as np + +from bokeh.models import Button +from bokeh.palettes import RdYlBu3 +from bokeh.plotting import figure, curdoc, vplot + +# create a plot and style its properties +p = figure(x_range=(0, 100), y_range=(0, 100), toolbar_location=None) +p.border_fill_color = 'black' +p.background_fill_color = 'black' +p.outline_line_color = None +p.grid.grid_line_color = None + +# add a text renderer to out plot (no data yet) +r = p.text(x=[], y=[], text=[], text_color=[], text_font_size="20pt", + text_baseline="middle", text_align="center") + +i = 0 + +ds = r.data_source + +# create a callback that will add a number in a random location +def callback(): + global i + ds.data['x'].append(np.random.random()*70 + 15) + ds.data['y'].append(np.random.random()*70 + 15) + ds.data['text_color'].append(RdYlBu3[i%3]) + ds.data['text'].append(str(i)) + ds.trigger('data', ds.data, ds.data) + i = i + 1 + +# add a button widget and configure with the call back +button = Button(label="Press Me") +button.on_click(callback) + +# put the button and plot in a layout and add to the document +curdoc().add_root(vplot(button, p)) diff --git a/playing_with_packages/play_with_bokeh_and_texas.py b/playing_with_packages/play_with_bokeh_and_texas.py new file mode 100644 index 0000000..932da5c --- /dev/null +++ b/playing_with_packages/play_with_bokeh_and_texas.py @@ -0,0 +1,45 @@ +from bokeh.models import HoverTool +from bokeh.plotting import figure, show, output_file, ColumnDataSource +from bokeh.sampledata.us_counties import data as counties +from bokeh.sampledata.unemployment import data as unemployment + +counties = { + code: county for code, county in counties.items() if county["state"] == "ma" +} + +county_xs = [county["lons"] for county in counties.values()] +county_ys = [county["lats"] for county in counties.values()] + +colors = ["#F1EEF6", "#D4B9DA", "#C994C7", "#DF65B0", "#DD1C77", "#980043"] + +county_names = [county['name'] for county in counties.values()] +county_rates = [unemployment[county_id] for county_id in counties] +county_colors = [colors[int(rate/3)] for rate in county_rates] + +source = ColumnDataSource(data=dict( + x=county_xs, + y=county_ys, + color=county_colors, + name=county_names, + rate=county_rates, +)) + +TOOLS = "pan, wheel_zoom, box_zoom, reset, hover, save" + +p = figure(title="Texas Unemployment 2009", tools=TOOLS) + +p.patches('x', 'y', source=source, + fill_color='color', fill_alpha=0.7, + line_color="white", line_width=0.5) + +hover = p.select_one(HoverTool) +hover.point_policy = "follow_mouse" +hover.tooltips = [ + ("Name", "@name"), + ("Unemployment rate)", "@rate%"), + ("(Long, Lat)", "($x, $y)"), +] + +output_file("texas.html", title="texas.py example") + +show(p) diff --git a/playing_with_packages/test_server.py b/playing_with_packages/test_server.py new file mode 100644 index 0000000..a28a0dd --- /dev/null +++ b/playing_with_packages/test_server.py @@ -0,0 +1,106 @@ +# from bokeh.plotting import figure, show, output_server +from bokeh.models import (GMapPlot, + GMapOptions, + DataRange1d, + Circle, + PanTool, + WheelZoomTool, + BoxSelectTool, + HoverTool) +# from bokeh.io import show, output_server, curdoc + + +# p = figure(title='ServerPlot') +# p.circle([1,2,3], [4,5,6]) + +map_options = GMapOptions(lat=42.3601, lng=-71.0589, + map_type='roadmap', zoom=9) +plot = GMapPlot(x_range=DataRange1d(), + y_range=DataRange1d(), + map_options=map_options, + title='sure') +plot.add_tools(PanTool(), + WheelZoomTool(), + BoxSelectTool(), + HoverTool()) + +# plot.show() + +from __future__ import print_function + +from datetime import date +from random import randint + +from bokeh.client import push_session +from bokeh.document import Document +from bokeh.models.glyphs import Line, Circle +from bokeh.models import ( + Plot, ColumnDataSource, DataRange1d, + LinearAxis, DatetimeAxis, Grid, HoverTool +) +from bokeh.models.widgets import ( + Button, TableColumn, DataTable, + DateEditor, DateFormatter, IntEditor) +from bokeh.models.layouts import VBox + +document = Document() +session = push_session(document) +""" +def make_data(): + n = randint(5, 10) + return dict( + dates=[ date(2014, 3, i+1) for i in range(n) ], + downloads=[ randint(0, 100) for i in range(n) ], + )""" + +source = ColumnDataSource(make_data()) + +# def make_plot(): +# xdr = DataRange1d() +# ydr = DataRange1d() + +# plot = Plot(title="Product downloads", x_range=xdr, y_range=ydr, plot_width=400, plot_height=400) + +# line = Line(x="dates", y="downloads", line_color="blue") +# plot.add_glyph(source, line) + +# circle = Circle(x="dates", y="downloads", fill_color="red") +# plot.add_glyph(source, circle) + +# xaxis = DatetimeAxis() +""" plot.add_layout(xaxis, 'below') + + yaxis = LinearAxis() + plot.add_layout(yaxis, 'left') + + plot.add_layout(Grid(dimension=0, ticker=xaxis.ticker)) + plot.add_layout(Grid(dimension=1, ticker=yaxis.ticker)) + + plot.add_tools(HoverTool(tooltips=dict(downloads="@downloads"))) + + return plot, source +""" +def click_handler(): + source.data = make_data() + +def make_layout(): + plot, source = make_plot() + columns = [ + TableColumn(field="dates", title="Date", editor=DateEditor(), formatter=DateFormatter()), + TableColumn(field="downloads", title="Downloads", editor=IntEditor()), + ] + data_table = DataTable(source=source, columns=columns, width=400, height=400, editable=True) + button = Button(label="Randomize data", type="success") + button.on_click(click_handler) + buttons = VBox(children=[button]) + vbox = VBox(children=[buttons, plot, data_table]) + return vbox + +layout = make_layout() +document.add_root(layout) + +session.show(layout) + +if __name__ == "__main__": + print("\npress ctrl-C to exit") + session.loop_until_closed() diff --git a/playing_with_packages/testing_stuff.py b/playing_with_packages/testing_stuff.py new file mode 100644 index 0000000..80493be --- /dev/null +++ b/playing_with_packages/testing_stuff.py @@ -0,0 +1,12 @@ +from bokeh.io import output_file, show +from bokeh.models import GeoJSONDataSource +from bokeh.plotting import figure +from bokeh.sampledata.sample_geojson import geojson + + +geo_source = GeoJSONDataSource(geojson=geojson) + +p = figure() +p.circle(x='x', y='y', alpha=0.9, source=geo_source) +output_file("geojson.html") +show(p) diff --git a/reflectionformp4.pdf b/reflectionformp4.pdf new file mode 100644 index 0000000..27cd84e Binary files /dev/null and b/reflectionformp4.pdf differ diff --git a/resources_plot.html b/resources_plot.html new file mode 100644 index 0000000..8381e88 --- /dev/null +++ b/resources_plot.html @@ -0,0 +1,28 @@ + + + + + + Bokeh Plot + + + + + + + + +
+ + + + \ No newline at end of file diff --git a/test.csv b/test.csv new file mode 100644 index 0000000..8adb191 --- /dev/null +++ b/test.csv @@ -0,0 +1,11 @@ +Name,Lat,Lon,Address,Category +Olin College,42.2926,-71.2644,"1000 Olin Way, Needham, MA 02492",housing +"BAGLY, Inc",42.357716,-71.062384,"14 Beacon Street, Suite 620, Boston, MA 02108",advocacy +"The Bridge of Central MA, Inc",42.261404,-71.827441,"4 Mann Street, Worcester, MA 01602",housing +"Community Action of the Franklin, Hampshire and North Quabbin Regions - Youth Programs",42.5876,-72.598691,"393 Main Street, Greenfield, MA 01301",advocacy +Health Imperatives,42.054669,-71.054591,"942 West Chestnut Street, Brockton, MA 02301",health +Justice Resource Institute,42.354847,-71.062414,"25 West Street - 5th floor, Boston, MA 02111",support +Massachusetts Asian AIDS Prevention Project,42.348575,-71.066334,"322 Tremont Street, Boston, MA 02116",health +Out Now,42.103675,-72.594993,"32 Hampden Street, Springfield, MA 01103",support +Youth on Fire,42.377929,-71.119691,"1555 Massachusetts Ave., Cambridge, MA 02138",housing +GLBTQ Domestic Violence Project Legal Program,42.352181,-71.120632,"989 Commonwealth Ave., Boston, MA 02215",legal \ No newline at end of file diff --git a/trying_to_bokeh_without_pandas/def_classes.py b/trying_to_bokeh_without_pandas/def_classes.py new file mode 100644 index 0000000..06c79ab --- /dev/null +++ b/trying_to_bokeh_without_pandas/def_classes.py @@ -0,0 +1,64 @@ +"""We're gonna define some classes here, probably just one actually, +we'll figure it out.""" + + +class Data(object): + """Data() object contains a list of Resource() objects. + """ + + def __init__(self): + """initializes as an empty list. + """ + pass + + def make_resource(self, filename, row): + """Takes in + """ + pass + + def populate(self, filename): + """ + """ + pass + + +class Resource(object): + """Resource() object contains the information about a particular resource. + + Attributes: lat, lon, name, address, category""" + + def __init__(self, + lat, lon, + name='', + street='', city='', state='', zipcode='', + category=''): + self.lat = lat + self.lon = lon + self.name = name + self.street = street + self.city = city + self.state = state + self.zipcode = zipcode + self.category = category + + def get_data(self, csv_file): + """Takes in the name of a csv file. + + Returns + """ + pass + + def location(self): + """Takes in lat and lon. + + Returns a list where the zeroeth entry is the latitude and the first + entry is longitude. + """ + return [self.lat, self.lon] + + def address(self): + """Takes in street, city, state, zip. + + Returns string of address. + """ + return str(self.street) + str(self.city) + str(self.state) + str(self.zipcode) diff --git a/trying_to_bokeh_without_pandas/def_model_classes.py b/trying_to_bokeh_without_pandas/def_model_classes.py new file mode 100644 index 0000000..9b741a9 --- /dev/null +++ b/trying_to_bokeh_without_pandas/def_model_classes.py @@ -0,0 +1,90 @@ +"""We're gonna define some classes here, probably just one actually, +we'll figure it out.""" + +import csv +import copy + + +class Model(object): + """Model() object contains a list of resources. + """ + + def __init__(self, filename, list_resources=None): + """initializes list_resources as an empty list. + """ + if list_resources is None: + list_resources = [] + self.list_resources = list_resources + self.filename = filename + + def list_of_resources(self): + """Takes in filename. + Calls make_resource. + Modifies self.list_resources by populating it from csv file. + """ + resources = [] + with open(self.filename, 'rb') as csvfile: + read_resource = csv.reader(csvfile) + for row in read_resource: + resources.append(row) + del resources[0] + self.list_resources = resources + + def filter_resources(self, categories): + """Takes in a list of categories. + + Returns a new list that contains only resources of those categories. + """ + filtered = [] + for category in categories: + listlist = [] + for resource in self.list_resources: + if resource[4] == category: + listlist.append(resource) + filtered.append(listlist) + return filtered + # TODO: make this work for a resource having multiple categories + + def sort_by_cat(self, categories): + """Takes in self. Calls list_of_resources. + Returns a list of dictionaries where the contents of each dictionary + only correspond to one category. + """ + list_dicts = [] + for category in categories: + print self.make_dict(category) + #return list_dicts + #should this just call filter resources? + + def make_dict(self, categories=None): + """Takes in itself. + Calls list_resources. + Returns a dict of names, latitudes, longitudes, addresses, and + categories. + """ + self.list_of_resources() + if categories is None: + resources = self.list_resources + else: + resources = Model.filter_resources(self, categories) + list_dicts = [] + print resources + for type_resource in resources: + names = [] + lats = [] + lons = [] + addresses = [] + categories = [] + for resource in type_resource: + names.append(resource[0]) + lats.append(resource[1]) + lons.append(resource[2]) + addresses.append(resource[3]) + categories.append(resource[4]) + dict_resources = {'name': names, + 'lat': lats, + 'lon': lons, + 'address': addresses, + 'category': categories} + list_dicts.append(dict_resources) + return list_dicts diff --git a/trying_to_bokeh_without_pandas/def_model_classes.pyc b/trying_to_bokeh_without_pandas/def_model_classes.pyc new file mode 100644 index 0000000..16bfe41 Binary files /dev/null and b/trying_to_bokeh_without_pandas/def_model_classes.pyc differ diff --git a/trying_to_bokeh_without_pandas/def_view_classes.py b/trying_to_bokeh_without_pandas/def_view_classes.py new file mode 100644 index 0000000..34f8ff7 --- /dev/null +++ b/trying_to_bokeh_without_pandas/def_view_classes.py @@ -0,0 +1,109 @@ +"""here we're gonna define the classes we need for viewing the thing. +""" + +from bokeh.models import HoverTool +from bokeh.plotting import figure +from bokeh.plotting import ColumnDataSource as plotColumnDataSource +from bokeh.io import output_file, show +from bokeh.models import (GMapPlot, + GMapOptions, + ColumnDataSource, + Circle, + DataRange1d, + PanTool, + WheelZoomTool, + BoxSelectTool) +import def_model_classes as mdl + + +class Make_plot(object): + """Draws a plot. View does everything else, this is just there so that the + plot does not have to be redrawn every time something is filtered. + """ + + def __init__(self, plot=None, map_type='roadmap', title='Resources'): + self.map_type = map_type + self.title = title + if plot is None: + map_options = GMapOptions(lat=42.3601, lng=-71.0589, + map_type=self.map_type, zoom=9) + plot = GMapPlot(x_range=DataRange1d(), + y_range=DataRange1d(), + map_options=map_options, + title=title) + plot.add_tools(PanTool(), + WheelZoomTool(), + BoxSelectTool(), + HoverTool()) + self.plot = plot + + def return_plot(self): + return self.plot + + +class View(object): + """Takes in model and list of criteria. + Returns interactive map. + """ + + def __init__(self, filename, model=None, plot=None, resources=None, criteria=['health', 'support']): + if model is None: + model = mdl.Model(filename) + if plot is None: + x = Make_plot() + plot = Make_plot.return_plot(x) + if resources is None: + for item in model: + resources = mdl.Model.make_dict(model[item], criteria) + self.filename = filename + self.model = model + self.plot = plot + self.resources = resources + self.criteria = criteria + + def marker(self, categories): + """Makes a circle of the color corresponding to its category. + """ + colormap = {'health': 'blue', 'support': 'green'} + colors = [colormap[x] for x in self.model['category']] + self.list_resources(categories) + circle = Circle(x="lon", y="lat", size=15, fill_color=colors, fill_alpha=0.8, line_color=None) + self.plot.add_glyph(self.resources, circle) + + def list_resources(self, categories): + """Calls Model.make_dict and plots it as ColumnDataSource. + """ + self.resources = plotColumnDataSource(data=mdl.Model.make_dict(self.model, categories)) + + def hover_tool(self): + """Makes the hover tool! Woot! + """ + hover = self.plot.select_one(HoverTool) + hover.point_policy = 'follow_mouse' + hover.tooltips = [('Name', '@name'), + ('Address', '@address')] + + def filter(self, catergories): + """Takes in the categories that should be shown. + + Displays only the categories that should be shown. + """ + pass + + def buttons_questionmarkquestionmarkquestionmark(self): + """we're going to look into how to button. + """ + pass + + def show_plot(self, categories): + """Saves the plot as an html file and opens it in a browser tab. + """ + self.list_resources(categories) + self.marker(categories) + self.hover_tool() + output_file('resources_plot.html') + show(self.plot) + +if __name__ == '__main__': + thing = View('test.csv') + thing.show_plot(['health', 'support']) diff --git a/various_outputs/geojson.html b/various_outputs/geojson.html new file mode 100644 index 0000000..e1d3c55 --- /dev/null +++ b/various_outputs/geojson.html @@ -0,0 +1,28 @@ + + + + + + Bokeh Plot + + + + + + + + +
+ + + + \ No newline at end of file diff --git a/various_outputs/gmap_plot.html b/various_outputs/gmap_plot.html new file mode 100644 index 0000000..8d0f6a5 --- /dev/null +++ b/various_outputs/gmap_plot.html @@ -0,0 +1,28 @@ + + + + + + Bokeh Plot + + + + + + + + +
+ + + + \ No newline at end of file diff --git a/various_outputs/heh.html b/various_outputs/heh.html new file mode 100644 index 0000000..c7724c1 --- /dev/null +++ b/various_outputs/heh.html @@ -0,0 +1,28 @@ + + + + + + Bokeh Plot + + + + + + + + +
+ + + + \ No newline at end of file diff --git a/various_outputs/mapthing.html b/various_outputs/mapthing.html new file mode 100644 index 0000000..5e9047a --- /dev/null +++ b/various_outputs/mapthing.html @@ -0,0 +1,28 @@ + + + + + + Bokeh Plot + + + + + + + + +
+ + + + \ No newline at end of file diff --git a/various_outputs/plot b/various_outputs/plot new file mode 100644 index 0000000..fdf886d --- /dev/null +++ b/various_outputs/plot @@ -0,0 +1,28 @@ + + + + + + Bokeh Plot + + + + + + + + +
+ + + + \ No newline at end of file diff --git a/various_outputs/plot.html b/various_outputs/plot.html new file mode 100644 index 0000000..ad47c1b --- /dev/null +++ b/various_outputs/plot.html @@ -0,0 +1,28 @@ + + + + + + Bokeh Plot + + + + + + + + +
+ + + + \ No newline at end of file diff --git a/various_outputs/texas.html b/various_outputs/texas.html new file mode 100644 index 0000000..df6cd05 --- /dev/null +++ b/various_outputs/texas.html @@ -0,0 +1,28 @@ + + + + + + texas.py example + + + + + + + + +
+ + + + \ No newline at end of file diff --git a/view_pandas.py b/view_pandas.py new file mode 100644 index 0000000..6619ef2 --- /dev/null +++ b/view_pandas.py @@ -0,0 +1,128 @@ +""" +This part of the code comprises the view portion of model-view-controller. +It takes in a model, which is a pandas dataframe with information about resources for queer youth in MA. +It outputs an interactive map using Bokeh. + +@author: Louise Nielsen and Apurva Raman + nielsenlouise@github.com, apurvaraman@github.com +""" + +from bokeh.models.widgets import CheckboxButtonGroup +from bokeh.models import (GMapPlot, + GMapOptions, + DataRange1d, + Circle, + PanTool, + WheelZoomTool, + BoxSelectTool, + HoverTool, + Legend) +from bokeh.io import show, output_file +import model_pandas as mdl + +# let's define glyphs +red_glyph = Circle(size=15, fill_color='red', fill_alpha=.8) + + +class MakePlot(object): + """Draws a plot. View does everything else, this is just there so that the + plot does not have to be redrawn every time something is filtered. + """ + + def __init__(self, plot=None, map_type='roadmap', title='Resources'): + self.map_type = map_type + self.title = title + if plot is None: + map_options = GMapOptions(lat=42.3601, lng=-71.0589, + map_type=self.map_type, zoom=9) + plot = GMapPlot(x_range=DataRange1d(), + y_range=DataRange1d(), + map_options=map_options, + title=title) + plot.add_tools(PanTool(), + WheelZoomTool(), + BoxSelectTool(), + HoverTool()) + self.plot = plot + + +class View(object): + """Puts information from Model() on top of the nice gmap it got from MakePlot(). + """ + + def __init__(self, filename='test.csv', model=None, plot=None, button=None): + self.filename = filename + if model is None: + self.model = mdl.Model(self.filename) + else: + self.model = model + if plot is None: + x = MakePlot() + self.plot = x.plot + else: + self.plot = plot + if button is None: + button = CheckboxButtonGroup(labels=['were trying', 'did we succeed']) + self.button = button + else: + self.button = button + + def marker(self): + """Defines a marker (a circle glyph) to represent a resource on the map. + """ + self.model.set_color() + circle = Circle(x='lon', y='lat', size=15, fill_color='color', fill_alpha=.8) + self.plot.add_glyph(self.model.source, circle) + + def legend(self): + """Defines and adds to plot a legend that describes what type of + resource each color glyph maps to. + + We did not have time to fully implement this before the due date. + The legend object would appear but would be empty. + """ + """ + red_glyph = self.plot.add_glyph(self.model.source, Circle(x='lon', y='lat', size=15, fill_color='red', fill_alpha=.8)) + Legend.legends = [ + ('health', red_glyph)] + ('support', green_glyph), + ('housing', blue_glyph), + ('advocacy', purple_glyph), + ('legal', cyan_glyph)] + legend = Legend(location='bottom_left') + self.plot.add_layout(legend) + """ + pass # TODO: Implement this + + def hover_tool(self): + """Makes the hover tool! Woot! + """ + hover = self.plot.select_one(HoverTool) + hover.point_policy = 'follow_mouse' + hover.tooltips = [('Name', '@name'), + ('Address', '@address'), + ('Type', '@category')] + + def buttons(self): + """Makes buttons appear. + + Right now we're struggling with this because we probably have to use + either a server or callbacks, one of which is being difficult and also + one that involves a JS snippet. + """ + pass # TODO: Implement this. + + def show_plot(self): + """Outputs and displays a plot on a static webpage. + """ + self.marker() + self.hover_tool() +# self.legend() + self.buttons() + output_file('resources_plot.html') + show(self.plot) + + +if __name__ == '__main__': + thing = View() + thing.show_plot()