diff --git a/README.md b/README.md index a916e44..3fe6213 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ To afford comparison and trend discovery between event happenings in two or more _3. Hover:_ -Hovering over a country in the World Map reveals the total number of killings in that country over the period. Hovering over a data point in the country wise view reveals more information about the event w.r.t. to the data point. +Details on demand - Hovering over a country in the World Map reveals the total number of killings in that country over the period. Also, hovering over a data point in the country wise view reveals more information about the event w.r.t. to the data point. ####Dataset The visualization heavily depends upon dataset acquired from [Global Terrorism Database](http://www.start.umd.edu/gtd) (GTD, University of Maryland). @@ -45,12 +45,12 @@ The visualization heavily depends upon dataset acquired from [Global Terrorism D Each event consists information on the date, location, number of people killed and injured, weapons used, intention, group or individual involved, target, summary, cites, etc. -The dataset was cleaned before being directly used. Many required values were `null` or inconsistent. For example, some of the country codes were not matching to that in the [Datamap](http://datamaps.github.io/) object. +The dataset was cleaned before being directly used. Many required values were `null` or inconsistent. For example, some of the 3 Letter country codes from the database were not matching to that in the [Datamap](http://datamaps.github.io/) object. ## Running Instructions -Access our visualization [here](http://cse512-14w.github.io/a3-chaoyu-aniket/). +Access our visualization [here](http://cse512-14w.github.io/a3-chaoyu-aniket/). View it in full screen for better experience. ####OR @@ -80,21 +80,27 @@ We started off with data search and found many datasets which called our attenti While exploring these charts using Tableau we also simulated them using pages year wise to evaluate various interactions possible. -####Sketching (TODO) +####Sketching +* During Brainstorm +![S1](images/sketch/s1.jpg) + +* Final sketch before we started coding +![S2](images/sketch/s2.jpg) + +The top portion is a timeline wherein one can select the months from 2000 to 2010. It is used to brush below two visualizations. It affords all the interactions mentioned above. #### Changes between Storyboard and the Final Implementation -There are not any significant changes between the final sketch and the final implementation. Though we kept on fidgeting with the placement of various elements, the colors, the scale used. But, the overall interaction, views and underlining objective remained the same. +There are not any significant changes between the final sketch and the final implementation. Though there were some stubble changes in placement of various elements, the colors, the scale used. But, the overall interaction, views and underlining objective remained the same. -* Its hard to sketch colors and exactly understand how they will be perceived without an actual running prototype. -* We didn't consider the use of log scale for color encoding, until we saw the running visualization. -* +* Its hard to sketch colors and exactly understand how they will be perceived without an actual running prototype. We didn't consider the use of log scale for color encoding, until we saw the running visualization. +* We first envisioned the country wise visualization to be similar to time stream from Tableau (above), but we ended up plotting translucent circles with size dependent upon killings to represent each event. ## Development Process ####Work Breakdown -The two member team divided the work as equally as possible and also helped each other out. Roughly we allocated the tasks as follows: +The two member team divided the work as equally as possible and also helped each other out on. Roughly we allocated the tasks as follows: **_Chaoyu Yang_** @@ -153,5 +159,6 @@ So, in total we individually spend approximately 60 hours on the assignment. - Data exploration and then cleaning - Dirty hack to select countries on the Datamap -####Conclusion - +####Future Work +- Optimize algorithm and queries +- Details on demand about the event, upon hovering on circles \ No newline at end of file diff --git a/include/css/main.css b/css/main.css similarity index 89% rename from include/css/main.css rename to css/main.css index c4f297b..d63a556 100644 --- a/include/css/main.css +++ b/css/main.css @@ -26,6 +26,10 @@ a{ width: 100%; } +.slider-bg { + background: #f1f1f1; +} + #map { position: relative; width: 100%; @@ -55,8 +59,7 @@ div#country_list{ fill: #ddd; } -.grid line, -.grid path { +.grid line, .grid path { fill: none; stroke: #fff; shape-rendering: crispEdges; @@ -67,8 +70,9 @@ div#country_list{ } .brush .extent { - stroke: #000; - fill-opacity: .25; + stroke: none; + fill: #000; + fill-opacity: .1; shape-rendering: crispEdges; } diff --git a/data/country_name_mapping.js b/data/country_name_mapping.js new file mode 100644 index 0000000..54ba83c --- /dev/null +++ b/data/country_name_mapping.js @@ -0,0 +1,121 @@ +var country_name_mapping = { + AFG:"Afghanistan", + DZA:"Algeria", + AGO:"Angola", + ARM:"Armenia", + AUS:"Australia", + AUT:"Austria", + AZE:"Azerbaijan", + BHR:"Bahrain", + BGD:"Bangladesh", + BTN:"Bhutan", + BIH:"Bosnia-Herzegov\"ina", + BRA:"Brazil", + BGR:"Bulgaria", + BDI:"Burundi", + KHM:"Cambodia", + CMR:"Cameroon", + CAN:"Canada", + CAF:"Central African Republic", + TCD:"Chad", + CHN:"China", + COL:"Colombia", + COG:"Congo (Brazzavi\"lle)", + COD:"Congo (Kinshasa)", + CRI:"Costa Rica", + HRV:"Croatia", + CUB:"Cuba", + EGY:"Egypt", + GNQ:"Equatorial Guin\"ea", + ERI:"Eritrea", + EST:"Estonia", + ETH:"Ethiopia", + FJI:"Fiji", + FIN:"Finland", + FRA:"France", + GEO:"Georgia", + GBR:"Great Britain", + GRC:"Greece", + GTM:"Guatemala", + GIN:"Guinea", + GNB:"Guinea-Bissau", + GUY:"Guyana", + HTI:"Haiti", + HND:"Honduras", + HUN:"Hungary", + IND:"India", + IDN:"Indonesia", + IRN:"Iran", + IRQ:"Iraq", + IRL:"Ireland", + ISR:"Israel", + ITA:"Italy", + CIV:"Ivory Coast", + JOR:"Jordan", + KAZ:"Kazakhstan", + KEN:"Kenya", + UNK:"Kosovo", + KWT:"Kuwait", + KGZ:"Kyrgyzstan", + LAO:"Laos", + LVA:"Latvia", + LBN:"Lebanon", + LBR:"Liberia", + MKD:"Macedonia", + MDG:"Madagascar", + MWI:"Malawi", + MYS:"Malaysia", + MLI:"Mali", + MRT:"Mauritania", + MEX:"Mexico", + MDA:"Moldova", + MNE:"Montenegro", + MAR:"Morocco", + MOZ:"Mozambique", + MMR:"Myanmar", + NAM:"Namibia", + NPL:"Nepal", + NLD:"Netherlands", + NIC:"Nicaragua", + NER:"Niger", + NGA:"Nigeria", + MNP:"Northern Ireland", + NOR:"Norway", + PAK:"Pakistan", + PRY:"Paraguay", + PER:"Peru", + PHL:"Philippines", + QAT:"Qatar", + RUS:"Russia", + RWA:"Rwanda", + SAU:"Saudi Arabia", + SEN:"Senegal", + SRB:"Serbia", + SLE:"Sierra Leone", + SLB:"Solomon Islands", + SOM:"Somalia", + ZAF:"South Africa", + ESP:"Spain", + LKA:"Sri Lanka", + LCA:"St. Lucia", + SDN:"Sudan", + SWE:"Sweden", + CHE:"Switzerland", + SYR:"Syria", + TWN:"Taiwan", + TJK:"Tajikistan", + TZA:"Tanzania", + THA:"Thailand", + TLS:"Timor-Leste", + TUN:"Tunisia", + TUR:"Turkey", + UGA:"Uganda", + UKR:"Ukraine", + USA:"United States", + UZB:"Uzbekistan", + VEN:"Venezuela", + PSE:"West Bank and Gaza Strip", + YEM:"Yemen", + YUG:"Yugoslavia", + ZWE:"Zimbabwe" +} diff --git a/images/sketch/s1.jpg b/images/sketch/s1.jpg new file mode 100644 index 0000000..74793f9 Binary files /dev/null and b/images/sketch/s1.jpg differ diff --git a/images/sketch/s2.jpg b/images/sketch/s2.jpg new file mode 100644 index 0000000..806e408 Binary files /dev/null and b/images/sketch/s2.jpg differ diff --git a/include/js/main.js b/include/js/main.js deleted file mode 100644 index e8a1037..0000000 --- a/include/js/main.js +++ /dev/null @@ -1,495 +0,0 @@ -var WORLDMAP = { - - data: {}, - world_data: {}, - - update: function (f_year, f_month, t_year, t_month) { - this.world_data = {}; - - f_year = typeof f_year !== 'undefined' ? f_year : 2000; - f_month = typeof f_month !== 'undefined' ? f_month : 1; - t_year = typeof t_year !== 'undefined' ? t_year : 2011; - t_month = typeof t_month !== 'undefined' ? t_month : 1; - - var totalKilled = 0; - //console.log(data); - - for(var i=0; i < data.length; i++){ - - var flag = new Boolean(); - flag = true; - - if(parseInt(data[i].year) < f_year){ - flag = false; - } - else if(parseInt(data[i].year) == f_year){ - if(parseInt(data[i].month) < f_month) - flag = false; - }; - - if(parseInt(data[i].year) > t_year){ - flag = false; - }else if(parseInt(data[i].year) == t_year){ - if(parseInt(data[i].month) > t_month - 1) - flag = false; - }; - - if(flag){ - totalKilled += parseInt(data[i].nkill); - if(this.world_data[data[i].country] == null){ - this.world_data[data[i].country] = parseInt(data[i].nkill) - } - else{ - this.world_data[data[i].country] = parseInt(data[i].nkill) + this.world_data[data[i].country]; - }; - }; - }; - - totalKilled = totalKilled.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); - - var get3LetterMonth = function(month) { - - var name = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC' ]; - return name[month-1]; - } - - var html = '' + totalKilled + ' people were killed due to Terrorism between ' - + get3LetterMonth(f_month)+', ' + f_year + " and "+ get3LetterMonth(t_month)+', '+ t_year+'.'; - //console.log(html); - - document.getElementById('totalKill').innerHTML = html; - //console.log(this.world_data); - - // create the color scale on the world map - var colorScale = d3.scale.log() - .clamp(true) - .domain([1, 3000]) - .range([0, 8]) - .nice(); - - for (var country in this.world_data) { - var obj = {} - obj['fillKey'] = Math.ceil(colorScale(this.world_data[country])); - obj['nkill'] = this.world_data[country]; - this.world_data[country] = obj; - } - - //console.log(this.world_data); - $("#map svg").remove(); - - var currentMap = new Datamap({ - element: document.getElementById('map'), - fills: { - 8: "#800000", - 7: "#8A1818", - 6: "#943131", - 5: "#9F4949", - 4: "#A96262", - 3: "#B37A7A", - 2: "#BE9393", - 1: "#C8ABAB", - 0: "#D2C4C4", - defaultFill: '#DDDDDD' - }, - data: this.world_data, - geographyConfig:{ - borderColor: 'hsl(0,0%,80%)', - //highlightBorderColor: 'hsl(0,0%,4%)', - //highlightBorderColor: '#ddd', - highlightFillColor: 'hsl(0,0%,20%)', - highlightOnHover: true, - highlightBorderWidth: 0, - fillOpacity: 0.9, - popupTemplate: function(geography, data) { - if(data) - return '
' + countries[country] + '
'; - //} - //document.getElementById('country_list').innerHTML = html; - }); - - d3.json("data/gtd.json", function(error, data) { - if (error) return console.warn(error); - this.data = data; - that.data = data; - slider.init(); - circlesmap.init(); - }); - } // end of init function -}; - - -// Slider area -var slider = (function(){ - // svg attributes - var margin = {top:0, right:20, bottom: 30, left: 20}, - canvas_width = +(d3.select('#slider').style('width').replace('px', '')), - w = canvas_width - margin.left - margin.right, - h = 70, - barPadding = 1; - - // Parsing data from sumTable.csv - // csv format example: - // time, nkill - // 2010-1, 10 - - // Time format - // usage: format.parse("2010-1"), reuturns a Date object - // doc: https://github.com/mbostock/d3/wiki/Time-Formatting - var format = d3.time.format("%Y-%m"); - - var dataset = []; - var draw = function(dataset) { - // append svg - console.log("create svg"); - var svg = d3.select("#slider") - .append("svg") - .attr("width", w + margin.left + margin.right) - .attr("height", h + margin.top + margin.bottom) - .append("g") - .attr("transform", "translate(" + margin.left + ", " + margin.top + ")"); - - //svg.append("rect") - //.attr("width", w) - //.attr("height", h) - //.attr("class", "grid-background") - //.append("g") - //.attr("transform", "translate(" + margin.left + ", " + margin.top + ")"); - - - // setting up scale - var nkill_range = [d3.min(dataset, function(d) { return d.nkill; }), - d3.max(dataset, function(d) { return d.nkill; })]; - var time_range = [d3.min(dataset, function(d) { return d.time; }), - d3.max(dataset, function(d) { return d.time; })]; - // y-axis scale - var yScale = d3.scale.linear() - .domain(nkill_range) - .range([0.05*h,h]) - .nice(); - - // color scale - var cScale = d3.scale.log() - .domain(nkill_range) - .range([80, 0]); - - // time scale for x-axis - var tScale = d3.time.scale() - .domain(time_range) - .nice(d3.time.year) - .range([0,w]); - //.ticks(d3.time.month, 1) - //.tickFormat(d3.time.format('%Y-%B')) - - var brush = d3.svg.brush() - .x(tScale) - .extent([new Date(2007, 1), new Date(2008, 1)]) - .on("brushend", function() { - if (!d3.event.sourceEvent) return; // only transition after input - - var extent0 = brush.extent(), - extent1 = extent0.map(d3.time.month.round); - - // if empty when rounded, use floor & ceil instead - if (extent1[0] >= extent1[1]) { - extent1[0] = d3.time.month.floor(extent0[0]); - extent1[1] = d3.time.month.ceil(extent0[1]); - } - - d3.select(this).transition() - .call(brush.extent(extent1)) - .call(brush.event); - //d3.select(this).call(brush.extent(extent1)); - }) - .on("brush", function(){ - var extent0 = brush.extent(), - extent1 = extent0.map(d3.time.month.round); - - if (extent1[0] >= extent1[1]) { - extent1[0] = d3.time.month.floor(extent0[0]); - extent1[1] = d3.time.month.ceil(extent0[1]); - } - - update_view(extent1); - }); - - // Draw the Chart - svg.selectAll("rect") - .data(dataset) - .enter() - .append("rect") - .attr({ - x: function(d, i) { return i* (w/dataset.length);}, - y: function(d) { return h - yScale(d.nkill);}, - width: w / dataset.length - barPadding, - height: function(d) { return yScale(d.nkill);}, - fill: function(d) { return "hsl(0, 0%,"+ cScale(d.nkill) + "%)";} - }); - - - // Draw grid - svg.append("g") - .attr("class", "x grid") - .attr("transform", "translate(0," + h + ")") - .call(d3.svg.axis() - .scale(tScale) - .orient("bottom") - .ticks(d3.time.year, 1) - .tickFormat("")) - .selectAll(".tick") - .classed("minor", function(d) { return d.getFullYear(); }); - - var xAxis = d3.svg.axis() - .scale(tScale) - .orient("bottom") - .ticks(d3.time.year, 1) - .tickFormat(d3.time.format('%Y')) - - svg.append("g") - .attr("class", "x axis") - .attr("transform", "translate(0," + h + ")") - .call(xAxis); - - var gBrush = svg.append("g") - .attr("class", "brush") - .call(brush) - .call(brush.event); - - gBrush.selectAll("rect") - .attr("height", h); - }; - - var update_view = function(month_range) { - //console.log(month_range); - - // update the world map - WORLDMAP.update( - month_range[0].getFullYear(), month_range[0].getMonth()+1, - month_range[1].getFullYear(), month_range[1].getMonth()+1 - ); - - // update the circlesmap - circlesmap.update(month_range); - }; - - var init = function() { - // Read csv file - d3.csv("data/sumTable.csv", function(data){ - dataset = data.map(function(d) { - return { - time: format.parse(d.time), - nkill: +d.nkill - } - }); - draw(dataset); - }); - }; - - return { - init: init, - dataset: function() { return dataset; } - }; -})(); - - -// event circlesmap -var circlesmap = (function(){ - var margin = {top:20, right:20, bottom: 20, left: 60}, - canvas_width = +(d3.select('#circlesmap').style('width').replace('px', '')), - width = canvas_width - margin.left - margin.right, - height = 40, - cell_height = 40; - - var current_time_range = [new Date(2007, 1), new Date(2008, 1)] - - var gtd_data = {}; - var formatting_data = function(raw_data) { - // if gtd data is already formatted, then return it - if (!_.isEmpty(gtd_data)) { return gtd_data; } - - //console.log(raw_data) - for(var i=0; i < raw_data.length; i++){ - var country = raw_data[i].country, - year = +raw_data[i].year, - month = +raw_data[i].month, - day = +raw_data[i].day, - nkill = +raw_data[i].nkill; - - if(_.isEmpty(gtd_data[country])) { gtd_data[country] = []; } - - date = new Date(year, month-1, day) - - gtd_data[country].push({ - time : date, - nkill : nkill - }); - } - - console.log(gtd_data); - return gtd_data; - }; - - var dateDiff = function(from, to) { - var milliseconds = to - from; - return milliseconds / 86400000; - - }; - - var update_view = function(time_range) { - // get the time range - if(typeof time_range !== 'undefined'){ - current_month_range = time_range; - } - - // countries added to list - var countries = WORLDMAP.countries; // ["3_Letters_Country_Code"] - - if(countries.length == 0){ - console.log("empty list"); - return ; - } - - // calculate svg height - height = countries.length * cell_height; - - // generating data to draw with - var data = _.map(countries, function(country) { - return { - country: country, - days:_.filter(gtd_data[country], function(day){ - return day.time > current_month_range[0] && - day.time < current_month_range[1]; - }) - } - }); - console.log(current_month_range); - console.log(data); - - // Remove old circlesmap if there is one - $("#circlesmap svg").remove(); - - // update the time scale to current range - var xScale = d3.time.scale() - .range([0, width]) - .domain(current_month_range); - - var rScale = d3.scale.log() - .domain([1, 3000]) - .range([0, 25]) - - // Draw the updated circlesmap - var svg = d3.select("#circlesmap") - .append("svg") - .attr("width", width + margin.left + margin.right) - .attr("height", height + margin.top + margin.bottom) - .append("g") - .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); - - // Draw the xAxis text - var days = dateDiff(current_month_range[0], current_month_range[1]) - var xAxis = d3.svg.axis() - .scale(xScale) - .orient('top'); - - if( days < 95) - xAxis.ticks(d3.time.month, 1) - .tickFormat(d3.time.format('%Y-%B')) - else if (days < 500) - xAxis.ticks(d3.time.month, 3) - .tickFormat(d3.time.format('%Y-%B')) - else - xAxis.ticks(d3.time.year, 1) - .tickFormat(d3.time.format('%Y')) - - // Draw xAxis grid - svg.append("g") - .attr("class", "x grid") - .call(xAxis); - - var circles = new Array(); - for(var i=0; i