This post will demonstrate how to build a simple Clojure Web application, with Compojure, that will use Incanter to generate a sample from a normal distribution with parameters (size, mean, and standard deviation) provided by the user, and then return a PNG image of the histogram of the data.
This example is simple, but it demonstrates how to dynamically generate an Incanter chart and pass it directly to the Web client as a PNG image.
For more information on the Compojure Web framework, visit its Github repository, its Google group, its WikiBooks site, and this quick tutorial.
I will use the Leiningen build tool to download dependencies, compile the application, and package it up as jar file. For more information on installing and using Leiningen, see my previous post.
Let’s start off by creating a project directory, I’ll call it incanter-webapp
, and give it a src
subdirectory. Then create the following Leiningen project.clj file in the project directory.
(defproject incanter-webapp "0.1.0"
:description "A simple Incanter web-app."
:dependencies [[incanter "1.2.1-SNAPSHOT"]
[compojure "0.3.2"]]
:main simple_web_app)
This project file includes two dependencies, Incanter and Compojure, and indicates that the -main
function for the project is in the file simple_web_app.clj
in the src
subdirectory of the project.
Next create the file called simple_web_app.clj in the src
subdirectory.
(ns simple_web_app
(:gen-class)
(:use [compojure]
[compojure.http response]
[incanter core stats charts])
(:import (java.io ByteArrayOutputStream
ByteArrayInputStream)))
Note that the namespace for this file is simple_web_app
with underscores like in the file name and in the :main line of the project.clj
file.
In Compojure, you need to define routes, which in this case associate the function sample-form
with “/”, and the function gen-samp-hist-png
with “/sample-normal”.
(defroutes webservice
(GET "/"
sample-form)
(GET "/sample-normal"
(gen-samp-hist-png request
(params :size)
(params :mean)
(params :sd))))
The function gen-samp-hist-png
takes four arguments, a request object, the sample size, the population mean, and the population standard deviation, and then updates the Compojure response object to include an Incanter histogram of a sample from a normal distribution with the given parameters.
Now define the gen-samp-hist-png
function; start by converting the string-arguments, passed in from the params
object, into numbers.
(defn gen-samp-hist-png
[request size-str mean-str sd-str]
(let [size (if (nil? size-str)
1000
(Integer/parseInt size-str))
m (if (nil? mean-str)
0
(Double/parseDouble mean-str))
s (if (nil? sd-str)
1
(Double/parseDouble sd-str))
Each argument has a default value, 1000 for size, 0 for mean, and 1 for the standard deviation (sd).
Next generate the sample from the normal distribution using Incanter’s sample-normal
function with the given size, mean, and standard deviation, and then create a histogram of the resulting data.
samp (sample-normal size
:mean m
:sd s)
chart (histogram
samp
:title "Normal Sample"
:x-label (str "sample-size = " size
", mean = " m
", sd = " s))
I’ve used the x-label
option of the histogram
function to customize the x-axis label so that it includes the given sample size, mean, and standard deviation.
Next, we need to convert the histogram into a byte array, and feed that into a ByteArrayInputStream
that can be passed to Compojure’s update-response
function, along with a header that sets the Content-Type to “image/png”.
out-stream (ByteArrayOutputStream.)
in-stream (do
(save chart out-stream)
(ByteArrayInputStream.
(.toByteArray out-stream)))
header {:status 200
:headers {"Content-Type" "image/png"}}]
(update-response request
header
in-stream)))
The above code is the key to sending a dynamically generated chart directly to the client. In addition to a filename, an OutputStream
can be passed to Incanter’s save
function; in this case, we use a ByteArrayOutputStream
, which is then converted to a byte array that is used to initialize the ByteArrayInputStream
that is passed to update-response
.
We can create a Web form for submitting requests to /sample-normal
. The following function is a helper function that creates a simple html page skeleton.
(defn html-doc
[title & body]
(html
(doctype :html4)
[:html
[:head
[:title title]]
[:body
[:div
[:h2
[:a {:href "/"}
"Generate a normal sample"]]]
body]]))
Now define the sample-form
function, which will generate a Web form using html-doc
. This function was associated with “/” earlier using the defroutes
macro.
(def sample-form
(html-doc "sample-normal histogram"
(form-to [:get "/sample-normal"]
"sample size: " (text-field {:size 4} :size)
"mean: " (text-field {:size 4} :mean)
"sd: " (text-field {:size 4} :sd)
(submit-button "view"))))
Now, we need to create a -main
function that will be called when the incanter-webapp.jar
is executed. This function will call Compojure’s run-server
function, starting the built-in Jetty server on port 8080.
(defn -main [& args]
(run-server {:port 8080}
"/*" (servlet webservice)))
Now we can use Leiningen to download and install all the necessary dependencies, including Incanter and Compojure, and then build and package the app.
$ lein deps
$ lein compile
$ lein uberjar
Finally, to start the server, run
$ java -jar incanter-webapp-standalone.jar
and visit http://localhost:8080
to submit a request, or go directly to the sample-normal app by constructing a URL like the following:
http://localhost:8080/sample-normal?size=500&mean=100&sd=10
Which returns a PNG image of a histogram like this:

The complete code for this example can be found here, and the Leiningen project file can be found here.