Creating Interactive and Zoomable Map in HDI (Helical Dashboard Insight)

 
The Goal of this blog is how to make responsive, interactive and zoomable Map in HDI (Helical Dashboard Insights):

For creating the Map in HDI, we are using D3.js , a javascript library.

The Data to use :

A special geospatial file called a Topojson. Here we are going to use a file that is comprised of all US counties. If u go to this link and copy into text file and save it as “us.json” (or anything .json) .

Since we have a county map of USA, we will need some data that is broken down by county. The us.json file we are using only has counties drawn. For this tutorial we are using data from query in json format.

Whatever data you have, make sure that there is a column that associates the information to a naming or id standard that is also present in your map/topojson.

Integrating Map in HDI:

To start integration of Map we have to change four files in HDI.

1) EFW file: EFW contain the Title, author, description, Template name, visibility of the Dashboard.

2) HTML File:HTML file name should be the same that specified in the EFW file under the Template Section.

In HTML File On Top we specify links of the external link and CSS properties.
Here we are using the ‘topojson.js’ external Library and it specified in the HDI as below:

<script src="getExternalResource.html?=Map/topojson.js"></script>

And CSS used to create Map is as follows:

.states {
  fill: none;
  stroke: #fff;
  stroke-linejoin: round;
}
body {
 font-family: Arial, sans-serif;
}
.city {
 font: 10px sans-serif;
 font-weight: bold;
}
.legend {
 font-size: 12px;
}
div.tooltip {
 position: absolute;
 text-align: left;
 width: 150px;
 height: 25px;
 padding: 2px;
 font-size: 10px;
 background: #FFFFE0;
 border: 1px;
 border-radius: 8px;
 pointer-events: none;
}

We have to declare one Map component in “Map.html” file and in this component we need to provide the link of the file where the Map chart property is defined.

3) EFWD File: EFWD file contain the Data Source Connection Properties such as connection id and connection type.It also contain Url of the connection to the database, User name and Password to the Database.

The DataSource Details used in our demo is shown as below:-

<DataSources>
        <Connection id="1" type="sql.jdbc">
           <Driver>com.mysql.jdbc.Driver</Driver>
           <Url>jdbc:mysql://192.168.2.9:3306/sampledata</Url>
            <User>devuser</User>
            <Pass>devuser</Pass>
        </Connection>
    </DataSources>

Data Map contains Map id and connection and connection Type. Map id is same as that specified in the EFWVF.

Query for the Data Map and the Parameter to be used is specified in the Tags and Parameter in the Tags.

<DataMap id="2" connection="1" type="sql" >
       <Name>Query for Tooltip </Name>
		<Query>
			<![CDATA[
					SELECT  cl.ID as id,cl.Name as name,sum(fact.votes) as votes
					FROM
					Voting_Summary as fact,region as r,contest as ct,county_list as cl
					where
					fact.region_id=r.region_id and
					fact.contest_id=ct.contest_id and
                   		        cl.Name = r.county	
			]]>
              </Query>
</DataMap>

4)EFWVF File :-

In EFWVF file we first set the chart id the chart we set the chart properties. For Map, we set the Map Properties between the tag. The properties such as Chart name, Chart type, Chart Data Source.

“Path” refers to lines drawn as instructed by our topojson file (us.json). Notice that .legend and .tooltip refer to objects we’ll designate with our javascript, but we can still set what they’ll look like here in the CSS.

You’ll see a lot of “var=”, which is setting up our variables for the code. Note that the first of the variables affect what values map to what colors. See that changing up these variables is an easy way to change the appearance of this map (as well as the CSS).

Colors are coded by RGB HEX value . There are multiple ways to scale colors, but this is the one we’ll go with here.

In Script we set the Map as Below :

Setting the Map Size, Position. And translation.

var width = 960,
 height = 500,
centered; 

Setting up the view:

var projection = d3.geo.albersUsa()
    .scale(1070)   // If scale is specified, this sets the projection’s scale factor to the specified value.
    .translate([width / 2, height / 2]);

Defing Map and Legend Color :

var color_domain = [5000,10000, 15000, 20000, 25000, 30000, 35000, 40000, 45000, 50000, 55000, 60000]
 var ext_color_domain = [0, 5000,10000, 15000, 20000, 25000, 30000, 35000, 40000, 45000, 50000, 55000, 60000]
 var legend_labels = ["< 5000","10000", "15000+", "15000+", "20000+", "25000+", "30000+", "35000+", "40000+", "45000+", "50000+", "55000+", "60000+"]
 var color = d3.scale.threshold()
 .domain(color_domain)
 .range(["#CCE0FF","#B2D1FF","#99C2FF","#80B2FF","#66A3FF","#4D94FF","#3385FF","#1975FF","#005CE6","#0052CC","#0047B2","#003D99","#003380","#002966"]);

The follow portion of code creates a new geographic path generator


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

The next block of code sets our svg window;

var svg = d3.select("#chart_4").append("svg")
.attr("viewBox", "0 0 " + width + " " + height)
 .style("margin", "10px auto");
var div = d3.select("#chart_4").append("div")
 .attr("class", "tooltip")
 .style("opacity", 0);
svg.append("rect")
    .attr("class", "background")
    .attr("viewBox", "0 0 " + width + " " + height)
    .on("click", clicked);

Since our data file contains the json data returned from query and this data is used to map the tooltip.

 var pairIdWithId = {};
 var pairNameWithId = {};
var pairVotesWithId = {};
 
data.forEach(function(d) {
 pairIdWithId[d.id] = +d.id;
 pairNameWithId[d.id] = d.name;
 pairVotesWithId[d.id] = d.votes; 
 });

here d.id ,d.name and d.votes refer to the column headers of our query.And now we’ll select the svg objects we’ve created but not specified, and map our data onto them:

var g = svg.append("g");
 g.append("g")
 .attr("class", "county")
 .selectAll("path")
 .data(topojson.feature(data1, data1.objects.counties).features)
 .enter().append("path")
 .attr("d", path)
 .on("click", clicked)
 .style ( "fill" , function (d) {
 return color (pairIdWithId[d.id]);
 })
.style("opacity", 0.8)

This will draw each county as an object, each with its own values. Notice that we’ve named this class of object “county”.

If we wanted to change the style of the counties in CSS up at the top, we could just refer to .county and make changes Also the “.data” line associates information from our us.json file with the county objects.

Also important is that “color” refers to the function set above in the code. “Color” expects a number as input, but instead of a specific number, we’re going to give it our container filled with pairs of ID numbers and rate values, and use [d.id] to make sure that we read in a value for each id number.The rest is what happens when the mouse glances over the county:


.on("mouseover", function(d) {
 d3.select(this).transition().duration(300).style("opacity", 1);
 div.transition().duration(300)
 .style("opacity", 1)
 div.text(pairNameWithId[d.id] + " : " + pairVotesWithId[d.id])
 .style("left", (d3.event.pageX) + "px")
 .style("top", (d3.event.pageY -30) + "px");
 })
 .on("mouseout", function() {
 d3.select(this)
 .transition().duration(300)
 .style("opacity", 0.8);
 div.transition().duration(300)
 .style("opacity", 0);
 })

If you want to change what each label is, make sure to adjust the variable “legend_labels.”


var legend = svg.selectAll("g.legend")
 .data(ext_color_domain)
 .enter().append("g")
 .attr("class", "legend");
 
var ls_w = 20, ls_h = 20;
legend.append("rect")
 .attr("x", 20)
 .attr("y", function(d, i){ return height - (i*ls_h) - 2*ls_h;})
 .attr("width", ls_w)
 .attr("height", ls_h)
 .style("fill", function(d, i) { return color(d); })
 .style("opacity", 0.8);
 
legend.append("text")
 .attr("x", 50)
 .attr("y", function(d, i){ return height - (i*ls_h) - ls_h - 4;})
 .text(function(d, i){ return legend_labels[i]; });

Function that gives Zoom Functionality to the map


function clicked(d) {
  var x, y, k;
  if (d && centered !== d) {
    var centroid = path.centroid(d);
    x = centroid[0];
    y = centroid[1];
    k = 4;
    centered = d;
  } else {
    x = width / 2;
    y = height / 2;
    k = 1;
    centered = null;
  }

  g.selectAll("path")
      .classed("active", centered && function(d) { return d === centered; });

  g.transition()
      .duration(750)
      .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")scale(" + k + ")translate(" + -x + "," + -y + ")")
      .style("stroke-width", 1.5 / k + "px");
}

By following these we are able to see the output which is as follows:
D3 Map

-By
Nitin Uttarwar
Helical It Solution

Leave a Reply