Skip to content

Commit

Permalink
Updating template text readme.md
Browse files Browse the repository at this point in the history
  • Loading branch information
jackiekazil committed Jul 15, 2016
1 parent 5008deb commit 4003597
Showing 1 changed file with 26 additions and 28 deletions.
54 changes: 26 additions & 28 deletions mesa/visualization/ModularVisualization.md
@@ -1,32 +1,30 @@
Modular Visualization - An In-Depth Look
========================================

Modular visualization is (well, will be) one of Mesa's core features. Mesa does (will) include a (wide -- some day) range of predefined visualization modules, which can be easily subclassed for your needs, and mixed-and-matched to visualize your particular model. It should also be fairly straightforward to create new visualization modules. In this document, I will attempt to provide an overview of how to do both.
Modular visualization is one of Mesa's core features. Mesa is designed to provide predefined visualization modules, which can be easily subclassed for your needs, and mixed-and-matched to visualize your particular model. (Some day, Mesa hopes to host a wide variety.) This document describes how to use and create new visualization modules.

## Overview

An interactive modular visualization is a set of **Elements**, each of which is an instance of a visualization **Module**. To visualize a model, create a new ModularServer with a list of the elements you want to visualize, in the order you want them to appear on the visualization web page.
An interactive modular visualization is a set of **Elements**, each of which is an instance of a visualization **Module**. To visualize a model, create a new ModularServer with a list of the elements you want to visualize, in the order you want them to appear on the visualization web page.

For example, if you have a model MyModel, and two elements, *canvas_vis* and *graph_vis*, you would create a visualization with them via:
```python
For example, if you have a model `MyModel`, and two elements, *canvas_vis* and *graph_vis*, you would create a visualization with them via:
```python
server = ModularServer(MyModel, [canvas_vis, graph_vis])
server.launch()
```
And then open your browser to http://127.0.0.1:8888/ in order to open it. If you prefer a different port, for example 8887, you can pass it to the server:
Then you will be able to view the elements in your browser at http://127.0.0.1:8888/. If you prefer a different port, for example 8887, you can pass it to the server as an argument.
```python
server.launch(8887)
```
Under the hood, each visualization module consists of two parts: Python code which can take a model object and renders it into some data describing the visualization, and a JavaScript object which in turn receives data from the Python render (via the ModularServer) and actually draws it in the browser.

Phew.

Okay, so what do you actually need to know?
Under the hood, each visualization module consists of two parts:
1. **Data rending** - Python code which can take a model object and renders it into some data describing the visualization.
2. **Browser rendering** - JavaScript object which in turn receives data from the Python render (via the ModularServer) and actually draws it in the browser.

## Using Pre-Built Modules

Most of the time, you should be able to build your visualization using pre-built modules. To do this, you don't need to worry about HTML and JavaScript at all, and only look at the documentation for the particular module you want to use.
Part of the Mesa library are pre-built modules. Using the built-ins allow you to build a visualization without worrying about the HTML and javascript. Consult the documention for a variety of modules.

One included module is **CanvasGrid**, which you can use to visualize objects located on grid cells. This is probably good for a good chunk of agent-based models out there, particularly the simpler ones.
One built-in module is **CanvasGrid**, which you can use to visualize objects located on grid cells. The CanvasGrid will cover a majority of agent-based models, particularly the simpler ones.

CanvasGrid iterates over every object in every cell of your model's grid (it assumes that your model has a grid named **grid**) and converts it into a dictionary which defines how it will be drawn. It does this via a **portrayal_method**: a function which the user defines, which takes an object as an input and outputs a dictionary with the following keys:
"Shape": Can be either "circle" or "rect"
Expand All @@ -53,7 +51,7 @@ For example, suppose for a Schelling model, we want to draw all agents as circle
return portrayal
```

In addition, a CanvasGrid needs to know the width and height of the grid (in number of cells), and the width and height in pixels of the grid to draw in the browser.
In addition, a CanvasGrid needs to know the width and height of the grid (in number of cells), and the width and height in pixels of the grid to draw in the browser.

To continue our Schelling example: suppose we have a Schelling model with a grid of 10x10, which we want to draw at 500px X 500px. using the portrayal function we wrote above, we would instantiate our visualization element as follows:
```python
Expand All @@ -67,25 +65,25 @@ Then, to launch a server with this grid as the only visualization element:
```
## Sub-Classing Modules

In some cases, you may want or need to tweak the internals of an existing visualization module. The best way to do this is to create a subclass of it.
If you want to tweak the internals of an existing visualization module, try subclassing it.

For example, the TextElement module provides an HTML template to render raw text, but nothing else. To use it, we need to create our own subclass, which implements a **render** method to get visualization-ready data (in this case, just a text string) out of a model object.

Suppose we want a module which can get an arbitrary variable out of a model, and display its name and value. Let's create a new subclass:
```python
from mesa.visualization.ModularTextVisualization import TextElement

class AttributeElement(TextElement):
def __init__(self, attr_name):
'''
Create a new text attribute element.
Args:
attr_name: The name of the attribute to extract from the model.
'''
self.attr_name = attr_name
def render(self, model):

def render(self, model):
val = getattr(model, self.attr_name)
return attr_name + ": " + str(val)
```
Expand All @@ -99,40 +97,40 @@ Now, if we wanted to use our new AttributeElement to add the number of happy age

Note that, in this case, we only wanted to change the Python-side render method. We're still using the parent module's HTML and JavaScript template.

## Module Internals
## Creating a new browser display

But what if we want more than just a different Python renderer; we want to substantially change how a module displays in the browser, or create a completely new module. To do this, we need to open up the JavaScript as well:
But what if we want more than just a different Python renderer; we want to substantially change how a module displays in the browser, or create a completely new module? To do this, we need to open up the JavaScript as well:

Let's take a look at the internals of **TextModule.js**, the JavaScript for the TextVisualization. Here it is, in all its glory:
```javascript
var TextModule = function() {
var tag = "<p></p>";
var text = $(tag)[0];
$("body").append(text);

this.render = function(data) {
$(text).html(data);
};

this.reset = function() {
$(text).html("");
};
};
```

This code is the JavaScript equivalent of defining a class. When instantiated, a TextModule object will create a new paragraph tag and append it to the parent HTML page's *body*. The object will have two methods attached: *render(data)* uses JQuery to replace the HTML contents of the paragraph with the text it gets as an input. This function will be called at each step of the model, to draw the data associated with the model coming over the websocket.

The second method, *reset* just replaces the contents of the div with a blank. This function will be called when the user presses the Reset button.
This code is the JavaScript equivalent of defining a class. When instantiated, a TextModule object will create a new paragraph tag and append it to the parent HTML page's *body*. The object will have two methods attached:
1. *render(data)* -- uses JQuery to replace the HTML contents of the paragraph with the text it gets as an input. This function will be called at each step of the model, to draw the data associated with the model coming over the websocket.
2. *reset* -- replaces the contents of the div with a blank. This function will be called when the user presses the Reset button.

Now let's take a look at the TextModule's Python counterpart, **TextElement** (which resides in **TextVisualization.py**). Again, here's the whole thing:
```python
from mesa.visualization.ModularVisualization import VisualizationElement

class TextElement(VisualizationElement):
js_includes = ["TextModule.js"]
js_code = "elements.push(new TextModule());"
```
That's it! Notice that it is lacking a *render()* method, like the one we defined above. Look at what is there: *js_includes* is a list of JavaScript files to import into the page when this element is present. In this case, it just imports **TextModule.js**.
That's it! Notice that it is lacking a *render()* method, like the one we defined above. Look at what is there: *js_includes* is a list of JavaScript files to import into the page when this element is present. In this case, it just imports **TextModule.js**.

Next, *js_code* is some JavaScript code, in Python string form, to run when the visualization page loads. In this case, *new TextModule()* creates a new TextModule object as defined above (which, remember, also appends a new paragraph to the page body) which is then appended to the array *elements*, that stores all the visualization elements currently on the page.

Expand All @@ -145,7 +143,7 @@ To help understand why it looks like this, here's a snippet of JavaScript from t
```
Data to visualize arrive over the websocket as a list. For each index of the list, the code passes that element of the data to the *render* function of the corresponding element, in the elements array.

Currently, module JavaScript files live in the *mesa/visualization/templates* directory, and the Python files live in *mesa/visualization/modules*.
Currently, module JavaScript files live in the *mesa/visualization/templates* directory, and the Python files live in *mesa/visualization/modules*.

When creating a new module, the Python and JavaScript code need to be written in synch: the module Python-side **render** method needs to output data in the exact same format that the JavaScript **render** function receives as an input.

Expand Down

0 comments on commit 4003597

Please sign in to comment.