Consolidating and enhancing the visualization of CYMH Service Areas in Ontario

Preamble

This article was originally posted on May 14, 2016 and revised on July 28, 2016 to take account of changes in the geospatial representation of Children and Youth Mental Health Service Areas in Ontario. For more details, see … and then there were 33.

In previous posts, we provided the following:

  • method for partitioning the Ontario government’s Shapefile archive of the thirty-four MCYS Children and Youth Mental Health (CYMH) Service Areas into five groupings, corresponding to the MCYS Integrated Service Regions (ISRs)
  • a method for using the TopoJSON files to visualize the CYMH Service Areas within the separate ISRs
  • the addition of a responsive framework to ensure that our visualizations accommodate to the display capabilities of various PCs, laptops, tablets, and smart phones

In this post, we describe how to enhance the functionality of our visualization of the Integrated Service Regions and CYMH Service Areas and Integrated across the entire province of Ontario.

Consolidated Geodata Files for CYMH Service Areas in Ontario

In a previous post, we provided a set of GeoJSON and TopoJSON files for the individual CYMH Service Areas and their groupings into the five distinct ISRs.

For present purposes, we have created two geodata files – in GeoJSON and TopoJSON formats – for the entire set of CYMH Service Areas in Ontario.

Field Property of the CYMH Service Area
area_name Name
id ID
isr Integrated Service Region
color Color

Simple Maps of the CYMH Service Areas

By way of a quick review, let’s start with a simple map outlining the CYMH Service Areas in Ontario:

<!DOCTYPE html>
<meta charset="utf-8">
<style>

/* CSS goes here */

/* define the container element for our map */
#map {
 margin:5% 5%;
 border:2px solid #000;
 border-radius: 5px;
 height:100%;
 overflow:hidden;
 background: #FFF;
}

</style>
<body>

/* create the container element #map */
<div id="map"></div>

/* load Javascript libraries for D3 and TopoJSON */
<script src="//d3js.org/d3.v3.min.js"></script>
<script src= "//d3js.org/topojson.v1.min.js"></script>

<script> // begin Javascript for visualizing the geo data

/* define some global variables */

var topo, projection, path, svg, g;

/* 1. Set the width and height (in pixels) based on the offsetWidth property of the container element #map */

var width = document.getElementById('map').offsetWidth;
var height = width / 2;

/* Call function setup() to create empty root SVG element with width, height of #map */

setup(width,height);

function setup(width,height){

/* 2. Create an empty root SVG element */

svg = d3.select('#map').append('svg')
 .style('height', height + 'px')
 .style('width', width + 'px')
 .append('g');

g = svg.append('g');

} // end setup()

/* 3. Define Unit Projection using Albers equal-area conic projection */

var projection = d3.geo.albers()
 .scale(1)
 .translate([0,0]);

/* 4. Define the path generator - to format the projected 2D geometry for SVG */

var path = d3.geo.path()
 .projection(projection);

/* 5.0 Start function d3.json() */
/* 5.1 Load the TopoJSON data file */

d3.json("http://cartoserve.com/maps/ontario/cymhsas33_data/cymhsas_topo.json", function(error, cymhsas_topo) {
if (error) return console.error(error);

/* 5.2 Convert the TopoJSON data back to GeoJSON format */
/* and render the map using Unit Projection */

var areas_var = topojson.feature(cymhsas_topo, cymhsas_topo.objects.cymhsas_geo);

/* 5.2.1 Calculate new values for scale and translate using bounding box of the service areas */
 
var b = path.bounds(areas_var);
var s = .95 / Math.max((b[1][0] - b[0][0]) / width, (b[1][1] - b[0][1]) / height);
var t = [(width - s * (b[1][0] + b[0][0])) / 2, (height - s * (b[1][1] + b[0][1])) / 2];

/* 5.2.2 New projection, using new values for scale and translate */
projection
 .scale(s)
 .translate(t);

/* redefine areas_var in terms of the .features array, assign array to topo */
var areas_var = topojson.feature(cymhsas_topo, cymhsas_topo.objects.cymhsas_geo).features;

topo = areas_var;

/* make the map by calling our draw() function initially within the d3.json callback function */
 
draw(topo);

}); // end function d3.json


function draw(topo) {

 var service_area = g.selectAll(".area_name").data(topo);

 service_area.enter().insert("path") 
 .attr("class", "service_area")
 .attr("d", path);

} // end function draw()

</script> // end Javascript for visualizing the geo data

Giving us the following visualization of 33 Service Areas [actual web page]:

CYMH Service Areas 33
Figure 1. Basic outline of 33 CYMH Service Areas in Ontario.

Note that the Javascript for creating our map includes three functions:

  • d3.json() – a built-in function to load geo data from a TopoJSON file
  • setup() – our function to create an empty root SVG element
  • draw() – our function to render the geo data

By adding just two lines of code to our draw() function, we can colour the CYMH Service Areas:


...
function draw(topo) {
 var service_area = g.selectAll(".area_name").data(topo);
 service_area.enter().insert("path") 
 .attr("class", "service_area")
 .attr("d", path)
 .attr("id", function(d,i) { return d.id; })
 .style("fill", function(d, i) { return d.properties.color; });

...

Giving us this figure [actual web page]:

CYMH Service Areas 33 colour
Figure 2. Thirty-three CYMH Service Areas in Ontario.

Adding Tooltips

Figures 1 and 2 suffer from one obvious shortcoming: none of the CYMH Service Areas is labelled. Unfortunately, our approach to labeling the CYMH Service Areas within a single Integrated Service Region breaks down when we have to contend with the scale of Ontario taken as a whole:

cymhsas02.html w labels screenshot
Figure 3. CYMH Service Areas cluttered with fixed labels.

Our best alternative is to use a tooltips to display the name of a CYMH Service Area when the user’s mouse hovers over the corresponding area of the visualization. Adding this functionality requires two sorts of modification of our Javascript.

First, we must style the Tooltips, including:

  • styling the <div> container element corresponding to the text box within which the name of the CYMH Service Area will be displayed
  • styling the text that is displayed when the user’s mouse is hovering over a service area
  • styling the tooltip so that it is hidden when the user’s mouse is not hovering over any service area

… like so:

<style>

...

/* style of the text box containing the tooltip */

div.tooltip {
 color: #222; 
 background: #fff; 
 padding: .5em; 
 text-shadow: #f5f5f5 0 1px 0;
 border-radius: 2px; 
 box-shadow: 0px 0px 2px 0px #a6a6a6; 
 opacity: 0.9; 
 position: absolute;
}

/* style of the text displayed in text box of the tooltip when mouse is hovering over a CYMH Service Area */

.service_area:hover{ stroke: #fff; stroke-width: 1.5px; }

.text{ font-size:10px; }

/* otherwise, the text box of the tooltip is hidden */
.hidden { 
 display: none; 
}

</style>

Second, we must modify our draw() function to display/hide tooltips in response to the user’s mousemove and mouseout behaviours:


<script>

var tooltip = d3.select("#map").append("div").attr("class", "tooltip hidden");

...

function draw(topo) {

var service_area = g.selectAll(".area_name").data(topo);

service_area.enter().insert("path")
.attr("class", "service_area")
.attr("d", path)
.attr("id", function(d,i) { return d.id; })
.style("fill", function(d, i) { return d.properties.color; })
.attr("title", function(d,i) { return d.properties.area_name; });

/* define offsets for displaying the tooltips */
var offsetL = document.getElementById('map').offsetLeft+20;
var offsetT = document.getElementById('map').offsetTop+10;

/* toggle display of tooltips in response to user mouse behaviours*/
service_area
//mousemove behaviour
.on("mousemove", function(d,i) {
var mouse = d3.mouse(svg.node()).map( function(d) { return parseInt(d); } );
tooltip.classed("hidden", false)
.attr("style", "left:"+(mouse[0]+offsetL)+"px;top:"+(mouse[1]+offsetT)+"px")
.html(d.properties.area_name);
}) // end mousemove
// mouseout behaviour
.on("mouseout", function(d,i) {
tooltip.classed("hidden", true);
}); // end mouseout

} // end draw()

Giving us this sort of visualization [interactive web page]:

CYMH Service Areas 33 tooltipsr
Figure 4. Thirty-three Colour CYMH Service Areas in Ontario with Tooltips.

Next time: We’ll enhance the functionality of our visualization to allow the user to pan and zoom our map of the CYMH Service Areas in Ontario.