# Hands-On Tutorial: Create an HTML/JavaScript Webapp to Draw the San Francisco Crime Map[¶](https://knowledge.dataiku.com/latest/kb/reporting/web-apps/standard.html#hands-on-tutorial-create-an-html-javascript-webapp-to-draw-the-san-francisco-crime-map "Permalink to this headline")

In this tutorial, you will learn how to create a webapp in Dataiku DSS, using HTML and JavaScript (and later adding a Python backend), to draw a map of San Francisco with information on the number of crimes by year and location.

The final webapp can be found in the SFPD Incidents project on the Dataiku gallery.

## Prerequisites[¶](https://knowledge.dataiku.com/latest/kb/reporting/web-apps/standard.html#prerequisites "Permalink to this headline")

* Some familiarity with HTML and JavaScript

* Some familiarity with Python (to use the Python backend)

## Supporting Data[¶](https://knowledge.dataiku.com/latest/kb/reporting/web-apps/standard.html#supporting-data "Permalink to this headline")

We will work with the San Francisco Police Department Crime Incidents data. The SFPD crime data is used under the Open Data Commons PDDL license.

## Create Your Project[¶](https://knowledge.dataiku.com/latest/kb/reporting/web-apps/standard.html#create-your-project "Permalink to this headline")

From the Dataiku homepage, click **+New Project > DSS Tutorials > Visualizations > SFPD Incidents**.

Note

You can also download the starter project from this website and import it as a zip file.

Alternatively you can download the full dataset from the San Francisco Open Data Portal and import it into a new blank project.

## Prepare the Dataset[¶](https://knowledge.dataiku.com/latest/kb/reporting/web-apps/standard.html#prepare-the-dataset "Permalink to this headline")

On the map, we are going to display the data with year-based filters. In order to do that efficiently, we are going to start by creating a new enriched dataset with a “year” column.

Select the dataset and create a new **Prepare** recipe with `sfpd\_enriched` as the output.

Parse the *Date* column as a new step in the script.

From the parsed date, add a new **Extract date components** step to the script. Only extract the year to a new column named `year` (empty the column names for month and day).

Rename column *X* to `longitude` and column *Y* to `latitude`.

Click **Run**, updating the schema if necessary.

## Create a New Webapp[¶](https://knowledge.dataiku.com/latest/kb/reporting/web-apps/standard.html#create-a-new-webapp "Permalink to this headline")

In the top navigation bar, select **‘</>’ > Webapps**.

Click **+ New Webapp** (or + Create Your First Webapp).

Select **Code Webapp > Standard**.

Choose **Starter code for creating map visualizations** and type a name like `sfpd` for the webapp.

### The Webapp Interface[¶](https://knowledge.dataiku.com/latest/kb/reporting/web-apps/standard.html#the-webapp-interface "Permalink to this headline")

Webapps, like other objects in Dataiku, have tabs containing different types of information.

* The **View** tab displays the present state of the webapp, as generated by the code found in the Settings tab.

* The **Settings** tab is divided into two panes, the size of which can be adjusted.

Within the Settings tab, the left side panel contains the code for the webapp:

* A standard webapp will have tabs for HTML, CSS, JavaScript (JS), and Python.

* A Shiny app will have tabs labeled UI and Server.

* A Bokeh app will just have Python.

The right side panel additionally has tabs for Preview and Settings.

### Webapp Settings[¶](https://knowledge.dataiku.com/latest/kb/reporting/web-apps/standard.html#webapp-settings "Permalink to this headline")

Since we’ll be reading a dataset, we first need to authorize it in the security settings.

* Navigate to the **Settings** subtab in the webapp editor.

* Click **Configure** under the Security heading.

* Select **Read data** for the enriched dataset.

Note

Often we might also click the link “Add snippet to read dataset”. However, because we have the starter code for creating map visualizations, we don’t need to do this.

We are going to use the JavaScript libraries Leaflet to draw the map, jQuery to add a slider to select the year, and d3.js to tune colors.

* On the Settings subtab, ensure that these three libraries are selected, and then return to the **Preview** subtab.

### Create a Map with Leaflet[¶](https://knowledge.dataiku.com/latest/kb/reporting/web-apps/standard.html#create-a-map-with-leaflet "Permalink to this headline")

The starter code provides us with a default map, with no data, centered on France. We want it to be centered on San Francisco. In the JavaScript code, find the line where `map` is defined and change it to the following.

§ // Create a Map centered on San Francisco

§ var map = L.map('map').setView([37.76, -122.4487], 11);

* Click **Save** to update the preview output.

Perfect! A beautiful map now appears. Let’s add some data to it.

Note

The map is displaying data from OpenStreetMap, a free and open world database, and the tiles (the actual images) are provided courtesy of CartoDB.

### Add Static Data through the fetch() Method[¶](https://knowledge.dataiku.com/latest/kb/reporting/web-apps/standard.html#add-static-data-through-the-fetch-method "Permalink to this headline")

Still in the JavaScript code, in the `dataiku.fetch()` call:

* Change `REPLACE\_WITH\_YOUR\_DATASET\_NAME` to `sfpd\_enriched`.

* To improve performance, change the value of `limit` from `20000` to `2000` (optional).

* Click **Save** to update the preview output.

### Load Data in the Python Backend[¶](https://knowledge.dataiku.com/latest/kb/reporting/web-apps/standard.html#load-data-in-the-python-backend "Permalink to this headline")

While our webapp can retrieve the contents of a dataset in JavaScript, this dataset has a lot of data, and it would not be realistic to load it all in the browser.

We are going to use a Python backend to load the dataset into a pandas dataframe, filter it by year, and aggregate it by area.

* In the Python tab, click to **Enable** the Python backend.

* Remove all of the automatically generated code from the Python tab.

* In the JS tab, leave the creation of the `map` and `cartodb` variables and the line `map.addLayer(cartodb);`. Remove all of the remaining code, including the `dataiku.fetch()` call.

* Then paste the code below in the empty **Python** tab.

This code creates a square lattice on the city. Inside each square, it counts the number of incidents. The result is returned in a JSON that will be used by our JavaScript code to add information to the map.

Note

Notice below how we use the `app.route` decorator before the `count\_crime()` function declaration to define a backend entry point that will execute the function.

§ import dataiku

§ import pandas as pd

§ # import dataset - NB: update this to fit your dataset name

§ sfpd = dataiku.Dataset("sfpd\_enriched").get\_dataframe()

§ # Only keep points with a valid location and only the criminal incidents

§ sfpd= sfpd[(sfpd.longitude!=0) & (sfpd.latitude!=0) & (sfpd.Category !="NON-CRIMINAL")]

§ @app.route('/Count\_crime')

§ def count\_crime():

§ year = 2014

§ # filter data for the chosen year

§ tab = sfpd[['longitude','latitude']][(sfpd.year == year) ]

§ #group incident locations into bins

§ X\_B = pd.cut(tab.longitude, 25, retbins=True, labels=False )

§ Y\_B = pd.cut(tab.latitude,25, retbins=True, labels=False)

§ tab['longitude'] = X\_B[0]

§ tab['latitude'] = Y\_B[0]

§ tab['C'] = 1

§ # group incident by binned locations

§ gp = tab.groupby(['longitude','latitude'])

§ # and count them

§ df = gp['C'].count().reset\_index()

§ max\_cr = max(df.C)

§ min\_cr = min(df.C)

§ gp\_js = df.to\_json(orient='records')

§ #return a JSON containing incident count by location and location limits

§ return json.dumps({

§ "bin\_X" : list(X\_B[1]) ,

§ "bin\_Y": list(Y\_B[1]),

§ "NB\_crime" : eval(gp\_js),

§ "min\_nb":min\_cr, "max\_nb":max\_cr

§ })

When you run the webapp, the Python backend automatically starts. You can find the logs of the Python backend in the Log tab next to your Python code. You can click on the Refresh button to get up-to-date logs.

### Query data from the backend and draw it on the map[¶](https://knowledge.dataiku.com/latest/kb/reporting/web-apps/standard.html#query-data-from-the-backend-and-draw-it-on-the-map "Permalink to this headline")

* Add the following beneath the existing code in the **JS** tab to query the Python backend and draw it on the map.

Note

The function `draw\_map()` calls the Python backend to retrieve the lattice and goes through each lattice square to draw it on the map with a proper color (the more red the more crimes).

§ var draw\_map = function() {

§ //request python backend aggregated data

§ $.getJSON(getWebAppBackendUrl('Count\_crime')).done(function(data) {

§ //then draw data on map

§ //use d3 scale for color map

§ var cmap = d3.scale.sqrt()

§ .domain([data.min\_nb,data.max\_nb])

§ .range(["steelblue","red"])

§ .interpolate(d3.interpolateHcl);

§ for(var i = 0; i < data.NB\_crime.length; i++) {

§ //retrieve corner of square

§ C1 = [data.bin\_Y[data.NB\_crime[i].latitude],data.bin\_X[data.NB\_crime[i].longitude]];

§ C2 = [data.bin\_Y[data.NB\_crime[i].latitude],data.bin\_X[data.NB\_crime[i].longitude+1]];

§ C3 = [data.bin\_Y[data.NB\_crime[i].latitude+1],data.bin\_X[data.NB\_crime[i].longitude+1]];

§ C4 = [data.bin\_Y[data.NB\_crime[i].latitude+1],data.bin\_X[data.NB\_crime[i].longitude]];

§ //draw square with color coding for the number of crime

§ var polygon = L.polygon([C1,C2,C3,C4], {

§ fillOpacity:0.4,clickable:false,

§ color: cmap(data.NB\_crime[i].C)

§ })

§ .addTo(map);

§ }

§ });

§ };

§ draw\_map();

Excellent! We have the map of San Francisco with a transparent lattice representing the number of crimes by area for the year 2014.

Note

**What if it does not work?**

If the lattice does not appear, you can check for errors in two places:

* Backend errors will appear in the **Log** tab. Don’t forget to refresh the logs to get the most recent logs

* Frontend (JavaScript) errors will appear in the JavaScript console of your browser.

+ Chrome: View > Developer Tools > JavaScript Console.

+ Firefox: Tools > Web developer > Web Console

Although we can adjust the map’s zoom level, our application is not very interactive. We are going to add a slider that allows the user to select the year displayed. Each time we move the slider, the backend is called to process data for the selected year.

### Add Interactivity[¶](https://knowledge.dataiku.com/latest/kb/reporting/web-apps/standard.html#add-interactivity "Permalink to this headline")

First add the following code to the top of the **HTML** tab. This sources jQuery add-ons:

§ <!-- sourcing add-ons for jquery-->

§ <link rel="stylesheet" href="//code.jquery.com/ui/1.11.4/themes/smoothness/jquery-ui.css">

§ <script src="//code.jquery.com/ui/1.11.4/jquery-ui.js"></script>

Still in the HTML tab, add an anchor for the slider and an input to display the year selected. The exact placement is up to you. We have placed it directly above the `h1` heading.

§ <p>

§ <label for="amount"> Year:</label>

§ <input type="text" id="amount" readonly style="border:0; color:#f6931f; font-weight:bold;">

§ </p>

§ <div id ='slider'></div>

Now, in the **JS** tab, we are going to change slightly the `draw\_map()` function to pass the selected year to the Python backend. As shown below, two changes are required:

* Pass the argument year to the `draw\_map()` function.

* Add the JSON `{year:year}` in the request to the backend. In the backend, we’ll retrieve the passed argument and modify the `count\_crime()` function.

§ var draw\_map = function(year) {

§ //request python backend aggregated data

§ $.getJSON(getWebAppBackendUrl('Count\_crime'), {year:year})

§ .done(

§ function(data) {

§ ...

§ });

§ }

The “routes” in the backend are made with Flask.

* In the Python tab, let’s import the functions to access the parameters.

§ from flask import request

Still in the Python tab, modify the `count\_crime()` function, replacing `year = 2014` with the line below:

§ def count\_crime():

§ year = int(request.args.get("year"))

§ # more python code

Finally, append the sample below to the code in the **JS** tab. It adds a slider and a function to clear the map each time we change the year.

§ function clearMap() {

§ for(i in map.\_layers) {

§ if(map.\_layers[i].\_path != undefined) {

§ try {

§ map.removeLayer(map.\_layers[i]);

§ } catch(e) {

§ console.log("problem with " + e + map.\_layers[i]);

§ }

§ }

§ }

§ }

§ //Create a slider to select the year

§ $('#slider').slider({

§ value:2014,

§ min: 2004,

§ max: 2014,

§ step: 1,

§ create:function(event, ui ) {

§ $('#amount').val($(this).slider('value'));

§ draw\_map($(this).slider('value'));

§ },

§ change: function(event, ui) {

§ $('#amount').val( ui.value );

§ clearMap();

§ draw\_map(ui.value);

§ }

§ });

§ $('#amount').val( $('#slider').slider('value') );

We now have a beautiful interactive map of hot areas in San Francisco.

## What’s Next?[¶](https://knowledge.dataiku.com/latest/kb/reporting/web-apps/standard.html#what-s-next "Permalink to this headline")

Congratulations! Using Dataiku, you have created a basic interactive HTML/JS webapp. You might now publish it to a dashboard.

Recall you can find the completed version of this webapp in the Dataiku gallery.

Take this project further by adding more information or selectors to the app. You could try to correlate business areas with thefts or see if trees have a calming effect on criminal activity.

For further information on standard webapps, please consult the product documentation.
