On making chart responsive the x,y axis line disappears - angularjs

I made my chart Responsive , but what i noticed was on resize the line of x,y axis was disappearing .
I am sharing the
var app = angular.module('chartApp', []);
app.controller('LineGraphCtrl', ['$scope','$http',
function($scope,$http){
$scope.lineGraph={
72:{company:{ais_time:0},user:{ais_time:0}},
73:{company:{ais_time:3},user:{ais_time:2}},
74:{company:{ais_time:2},user:{ais_time:1}},
75:{company:{ais_time:2},user:{ais_time:3}},
76:{company:{ais_time:3},user:{ais_time:4}},
77:{company:{ais_time:1},user:{ais_time:1}},
78:{company:{ais_time:2},user:{ais_time:0}},
79:{company:{ais_time:4},user:{ais_time:3}}};
console.log($scope.lineGraph);
$scope.lineaGraphData=[];
$scope.UservsCompanyAIData=[];
for(var i in $scope.lineGraph) {
console.log(i);
for (var j in $scope.lineGraph[i]) {
if(j=="company") {
$scope.lineaGraphData.push({
"week": i,
"AI": $scope.lineGraph[i].company.ais_time,
"key": "company"
});
} else {
$scope.lineaGraphData.push({
"week": i,
"AI": $scope.lineGraph[i].user.ais_time,
"key": "user"
});
}
}
}
function getDateOfISOWeek(w, y) {
var simple = new Date(y, 0, 1 + (w - 1) * 7);
var dow = simple.getDay();
var ISOweekStart = simple;
if (dow <= 4)
ISOweekStart.setDate(simple.getDate() - simple.getDay() + 1);
else
ISOweekStart.setDate(simple.getDate() + 8 - simple.getDay());
return ISOweekStart;
};
for(var i=0;i<$scope.lineaGraphData.length;i++){
var weekStart=getDateOfISOWeek($scope.lineaGraphData[i].week,2015);
$scope.UservsCompanyAIData.push({"Date":weekStart,"Label":$scope.lineaGraphData[i].key,"AIvalue":$scope.lineaGraphData[i].AI});
}
console.log($scope.UservsCompanyAIData);
}]);
app.directive('userVsCompanyAI', function($parse, $window){
return{
restrict:'EA',
scope: {data: '=data'},
link: function(scope, element, attrs) {
scope.$watch('data', function (data) {
if (data) {
var data = scope.data;
//Dimensions of the Graph
var margin={top:30,right:20,left:50,bottom:80},
width=800-margin.left -margin.right,
height=400-margin.top-margin.bottom;
var color = d3.scale.category10();
var parseDate =d3.time.format("%d/%m").parse;
//Range of the Graph
var x = d3.time.scale().range([0,width]);
var y = d3.scale.linear().range([height,0]);
//Defining the Axis
var xAxis=d3.svg.axis().scale(x).orient("bottom").tickFormat(d3.time.format("%d/%m")).ticks(5);
var yAxis=d3.svg.axis().scale(y).orient("left").ticks(5);
//Defining the line for user & company's Avg
var AILine =d3.svg.line()
.x(function (d){return x(d.Date)})
.y(function (d) {return y(d.AIvalue)});
var el = element[0];
//Adding the SVG to the Graph
var UserVsCompanyAILineChart=d3.select(el)
.classed("svg-container", true) //container class to make it responsive
.append("svg")
//responsive SVG needs these 2 attributes and no width and height attr
.attr("preserveAspectRatio", "xMinYMin meet")
.attr("viewBox", "0 0 900 400")
//class to make it responsive
.classed("svg-content-responsive", true)
.append("g")
.attr("transform","translate("+margin.left+","+margin.top+")");
data.forEach(function (d) {
d.Date=d.Date;
d.AIvalue=+d.AIvalue;
});
//Setting the Domain of Scales based on Data
x.domain(d3.extent(data,function (d) {
return d.Date;
}));
y.domain([0,d3.max(data,function (d) {
return d.AIvalue;
})]);
var dataNest=d3.nest()
.key(function (d) {
return d.Label;
})
.entries(data);
var legendRectSize = 16;
var legendSpace = (width)/dataNest.length;
dataNest.forEach(function (d,i) {
UserVsCompanyAILineChart.append("path")
.attr("class","line")
.style("stroke", function() {
return d.color = color(d.key); })
.attr("d",AILine(d.values));
UserVsCompanyAILineChart.append("text")
.attr("x", (legendSpace/2)+i*legendSpace)
.attr("y", height + (margin.bottom/2)+ 30)
.attr("class", "WizUservsCompanyAvgReportLegend")
.style("fill", function() {
return d.color = color(d.key); })
.text(d.key);
UserVsCompanyAILineChart.append('rect')
.attr('width', legendRectSize)
.attr('height', legendRectSize)
.attr("x", ((legendSpace/2)-30)+i*(legendSpace))
.attr("y", height + (margin.bottom/2)+ 15)
.style('fill', function() {
return d.color = color(d.key); })
.style('stroke', function() {
return d.color = color(d.key); });
});
UserVsCompanyAILineChart.append("g")
.attr("class","x axis")
.attr("transform","translate(0,"+height+")")
.call(xAxis)
UserVsCompanyAILineChart.append("g")
.attr("class","y axis")
.call(yAxis)
}
})
}
};
});
/* Styles go here */
body { font: 12px Arial;}
path {
stroke: steelblue;
stroke-width: 2;
fill: none;
}
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
.legend {
font-size: 16px;
font-weight: bold;
text-anchor: middle;
}
/*SVG Responsive*/
.svg-container {
display: inline-block;
position: relative;
width: 100%;
padding-bottom: 100%; /* aspect ratio */
vertical-align: top;
overflow: hidden;
}
.svg-content-responsive {
display: inline-block;
position: absolute;
top: 10px;
left: 0;
}
/*SVG Responsive Ends*/
<html>
<head>
<script data-require="angularjs#1.5.5" data-semver="1.5.5" src="https://code.angularjs.org/1.5.5/angular.js"></script>
<link rel="stylesheet" href="style.css" type="text/css" />
<link rel="stylesheet" href="bootstrap/dist/css/bootstrap.css" type="text/css" />
</head>
<body>
<script src="angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="script.js"></script>
<div ng-app="chartApp">
<div class="panel panel-default" data-toggle="modal" data-target="#myModal" ng-controller="LineGraphCtrl" style="height: 30%;width: 30%;float:right">
<div class="panel-body">
<div user-vs-company-a-i="" data="UservsCompanyAIData"></div>
</div>
</div>
<div class="panel panel-default" data-toggle="modal" data-target="#myModal" ng-controller="LineGraphCtrl" style="height: 70%;width: 70%;float:right" >
<div class="panel-body" >
<div user-vs-company-a-i data="UservsCompanyAIData"></div>
</div>
</div>
</div>
</div>
</body>
</html>
link here .
There are 2 charts one with smaller size , other with bigger one . Here u can see that the chart with bigger size is showing x,y axis line but in the smaller graph the x,y axis line is not visible . Is there any possible way in which we can make the chart responsive but still the x,y axis line will be viible .
Any help is appreciated .
Thanks in advance .

To stop them disappearing, make them thicker.
At the moment the stroke-width setting for your axes is 1. The main graph is at roughly 1:1 scale, so they axis lines are approximately one pixel in width. In your small scale version the axis lines are being drawn with a correspondingly smaller thickness - less than half a pixel. You can't draw half a pixel, so the browser draws the lines with a light grey colour instead, making them barely visible.
To fix, make the axis lines thicker:
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 2;
shape-rendering: crispEdges;
}
If "2" is too thick for you, you can use an intermediate value, such as 1.5.

The Solution
I suggest to redraw the chart when the browser is resized - simply by registering a listener to the window resize event. Don't forget to debounce / throttle the event so that redrawing just happens once (i.e. debouncing) (or only a few times, i.e. throttling) the browser has finished resizing.
Another hack would be to make the stroke-width thicker, but in my opinion this leads to results that look good in some resolution and bad in other. Plus, when the chart with a thicker stroke-width is rendered in a mobile-sized browser window, you end up with awkwardly looking thick axis.
So in my opinion, the best option is to redraw the chart on resize.
The Reason
When the browser is resized to a smaller size, the already rendered SVG scales down. As the first answer already pointed out, the line with a stroke-width=1 at some point becomes less than 0.5 from its original thickness and the browser decides to not draw it anymore.

Related

How to create dynamic y scale values in D3 js depending on the screen size?

Im creating a line chart graph using d3 js. I need a solution to change the y scale values when I resize the window instead of scroll bar.
I have added the code below which adds scroll bar when I resize the screen size. I want design dynamic y scale values when we resize for different screen sizes.
`
<!DOCTYPE html>
<meta charset="utf-8">
<style> /* set the CSS */
body { font: 12px Arial;}
path {
stroke: steelblue;
stroke-width: 2;
fill: none;
}
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
</style>
<body>
<!-- load the d3.js library -->
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
// Set the dimensions of the canvas / graph
var margin = {top: 30, right: 20, bottom: 30, left: 50},
width = 600 - margin.left - margin.right,
height = 270 - margin.top - margin.bottom;
// Parse the date / time
var parseDate = d3.time.format("%d-%b-%y").parse;
// Set the ranges
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
// Define the axes
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(5);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(5);
// Define the line
var valueline = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.close); });
// Adds the svg canvas
var svg = d3.select("body")
.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 + ")");
// Get the data
d3.csv("data.csv", function(error, data) {
data.forEach(function(d) {
d.date = parseDate(d.date);
d.close = +d.close;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.close; })]);
// Add the valueline path.
svg.append("path")
.attr("class", "line")
.attr("d", valueline(data));
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
});
</script>
</body>
`
A method I like to use is to wrap the code in a function (lets call it main()), and re-run when the screen size changes.
At the beginning of this new main() function, remove the old (and now redundantly sized) svg.
d3.select("#<id of svg>").remove();
Then, create the new y scale using
var new_width = document.getElementById("<Div ID>").clientWidth;
var new_height = document.getElementById("<Div ID>").clientHeight;
and apply these to the new svg as you create it. D3 should allow you to run the .remove() line before the initial svg has been created. Make sure to then add an id when you create the svg (using attr("id", "<id of svg>")).
After that, you can call the main() function on resizing with
d3.select(window).on( "resize", main() );
The way in which you want to actually size your Div will now rely on your CSS, so you can use something like {height:50vh} or whatever you like.
Hope this helps.
P.S. By the way, why are you using D3 version 3? We're up to v5 :)

Half Solid Gauge Chart with trends

do you have a type of "half gauge", that show the trends on top of it?like in this picture:enter image description here
For business purpose, so we are open to buy the licence
Yes, you can create the required chart with AnyChart library. We have prepared a similar sample, you can check it below.
For purchasing AnyChart, please, contact us by email - sales#anychart.com
var data = [45, 67, 56];
var axisMaximum = 100;
data.push(axisMaximum);
var dataSet = anychart.data.set(data);
anychart.onDocumentReady(function () {
var stage = anychart.graphics.create("container");
var circularGauge = anychart.gauges.circular();
circularGauge.data(dataSet);
circularGauge.fill('#fff')
.stroke(null)
.padding(0)
.startAngle(270)
.sweepAngle(180);
var circularAxis = circularGauge.axis().radius(100).width(1).fill(null);
circularAxis.scale()
.minimum(0)
.maximum(axisMaximum);
circularAxis.labels().enabled(false);
circularAxis.ticks().enabled(false);
circularAxis.minorTicks().enabled(false);
circularGauge.bar(0).dataIndex(0)
.radius(100)
.width(15)
.fill('red')
.stroke(null)
.zIndex(5);
circularGauge.bar(1).dataIndex(3)
.radius(100)
.width(15)
.fill('#cecece')
.stroke(null)
.zIndex(3);
// marker
circularGauge.marker(0)
.axisIndex(0)
.dataIndex(1)
.size(7)
.stroke(null)
.fill('blue')
.type("triangle-down")
.position("outside")
.radius(108);
// marker
circularGauge.marker(1)
.axisIndex(0)
.dataIndex(2)
.size(7)
.stroke(null)
.fill('green')
.type("triangle-down")
.position("outside")
.radius(108);
var circularLabel0 = circularGauge.label(0);
circularLabel0
.enabled(true)
.anchor('center-bottom')
.useHtml(true)
.hAlign("center")
.text("<p style='color:red; font-size:20'>30 %</p><br>" +
"<p style='color:black; font-size:20; text-decoration:overline;'>% of Gross sales</p>");
var circularLabel1 = circularGauge.label(1);
circularLabel1
.enabled(true)
.anchor('center-top')
.padding(15)
.fontSize(20)
.fontColor('black')
.text('Product 1.1');
var circularTitle = circularGauge.title();
circularTitle.enabled(true)
.text('Division 1')
.fontColor('black')
.fontSize(25);
circularGauge.container(stage).draw();
});
html, body, #container {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
<link href="https://cdn.anychart.com/releases/8.2.1/fonts/css/anychart-font.min.css" rel="stylesheet"/>
<link href="https://cdn.anychart.com/releases/8.2.1/css/anychart-ui.min.css" rel="stylesheet"/>
<script src="https://cdn.anychart.com/releases/8.2.1/js/anychart-base.min.js"></script>
<script src="https://cdn.anychart.com/releases/8.2.1/js/anychart-ui.min.js"></script>
<script src="https://cdn.anychart.com/releases/8.2.1/js/anychart-exports.min.js"></script>
<script src="https://cdn.anychart.com/releases/8.2.1/js/anychart-circular-gauge.min.js"></script>
<div id="container"></div>

Add horizontal lines on y-axis below labels

I have a scatter plot graph in my reactJS app using d3 and svg. I need to display horizontal lines on y-axis below the labels. Currently, the horizontal lines are appearing beside the labels.
here's my code:
const yAxis = d3.axisLeft(yScale).tickFormat(function(d) {
if (d != minYFloor)
return d + " yds";
else return "";
})
.tickSize(-width - 20, 0, 0);
tickSize function in above code renders horizontal lines in the graph like this:
This is my css:
.axisY line {
fill: none;
stroke: #fff;
opacity: 0.3;
shape-rendering: crispEdges;
}
While I need something like this:
How do I go about achieving that?
Here is a basic example of how you can translate the ticks in the axis.
Let's suppose this running snippet:
var svg = d3.select("svg");
var scale = d3.scalePoint()
.domain([0, 50, 100, 150, 200])
.range([140, 10]);
var axis = d3.axisLeft(scale)
.tickFormat(function(d) {
return d + " yds"
})
.tickSize(-250);
var g = svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(50,0)")
.call(axis);
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg></svg>
As you can see, it's a common y axis, just like yours.
To translate the ticks we first assign that axis'group a specific class (or ID):
.attr("class", "axis")
Then, we select the texts with the ticks class, and move them:
svg.selectAll(".axis .tick text")
.style("text-anchor", "start")
.attr("transform", "translate(4,-6)")
Here is the demo:
var svg = d3.select("svg");
var scale = d3.scalePoint()
.domain([0, 50, 100, 150, 200])
.range([140, 10]);
var axis = d3.axisLeft(scale)
.tickFormat(function(d) {
return d + " yds"
})
.tickSize(-250);
var g = svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(50,0)")
.call(axis);
svg.selectAll(".axis .tick text")
.style("text-anchor", "start")
.attr("transform", "translate(4,-6)")
.axis path {
stroke: none;
}
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg></svg>
Here I'm using magic numbers, change them accordingly.
Finally, there is an important advice: unlike what your question's title suggest, move the labels, not the lines. The lines are the actual indicator of the position to the users seeing the datavis.

d3.js Help getting Y axis dynamically labeled on simple bar chart

The data is in an array of objects where the
Data = [object{name:"FIRST TECH", value:2477}];
The technology names change and I am trying to place them to the left of the bar they are associated with.
I'm not sure if I'm doing this in the best practice, but when I get the text appended to the side it is invisible. I've tried changing the class name, changing the color, z-index, position:absolute, it always remains hidden.
What do I need to do in order to get the label correctly on the side?
Code:
<style>
h2{
text-align:center;
margin-left: auto;
margin-right: auto;
}
.abcxyz{
fill:black;
color:#000;
text-anchor: end;
}
.chart {
width:800px;
display:block;
margin:auto;
background-color:#c7d9e8;
}
.chart rect {
fill: steelblue;
}
.chart rect:hover{
fill:brown;
}
.values {
fill: white;
font: 12px sans-serif;
text-anchor: end;
}
</style>
<script>
$(document).ready(function(){
var return_data;
$.ajax({
url: '<?php echo site_url('metrics/getTechDocs') ?>',
type: 'GET',
success: function(data){
data = JSON.parse(data);
set_return_data(data);
drawGraph(data);
},
error: function(data){
console.log("error");
}
});
});
function set_return_data(data){
return_data = data;
console.log (return_data);
}
function formatGraphData(data){
var newData = new Array();
for(x in data){
tmp = {name: x , value: data[x].length};
newData.push(tmp);
}
newData.sort(function(a,b){return b.value - a.value} );
return newData;
}
function drawGraph(rawData){
rawData = formatGraphData(rawData);
var width = 800,
barHeight = 30;
var x = d3.scale.linear()
.domain([0, rawData[0].value])
.range([10, width]);
var y = d3.scale.linear()
.domain(rawData.map(function(d){return d.name}))
.range([0, barHeight * rawData.length]);
var chart = d3.select(".chart")
.attr("height", barHeight * rawData.length);
var bar = chart.selectAll("g")
.data(rawData)
.enter().append("g")
.attr("transform", function(d, i) { return "translate(0," + i * barHeight + ")"; });
bar.append("text")
.attr("class", "abcxyz")
.attr("y", barHeight / 2 )
.text(function(d){return d.name});
bar.append("rect")
.attr("height", barHeight - 1)
.attr("width", function(d){return x(d.value);});
bar.append("text")
.attr("class", "values")
.attr("x", function(d) { return x(d.value) - 3; })
.attr("y", barHeight / 2)
.attr("dy", ".35em")
.text(function(d){return d.value;});
}
</script>
<div>
<h2>Frequency of Technology Terms</h2>
<svg class="chart"></svg>
</div>
SVG doesn't know z-order or anything similar. The elements are displayed in the order they are added in. So to make the text appear on top of the bars, draw the bars first and then the text.

google maps v3 draw radius around a point

I am trying to create a map which allows a person to enter a zip code and a radius and will draw a radius around that point. The codeAddress function seems to work, but the drawCircle function is not working. Perhaps someone can pinpoint the error
see code below:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<style type="text/css">
html { height: 100% }
body { height: 100%; margin: 0px; padding: 0px }
#map_canvas { height: 100% }
</style>
<script type="text/javascript"
src="http://maps.google.com/maps/api/js?sensor=false">
</script>
</script>
<script type= "text/javascript">
var geocoder;
var map;
function initialize() {
geocoder = new google.maps.Geocoder();
var latlng = new google.maps.LatLng(-34.397, 150.644);
var myOptions = {
zoom: 8,
center: latlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
map = new google.maps.Map(document.getElementById("map_canvas"),myOptions);
}
function codeAddress() {
var address = document.getElementById("address").value;
geocoder.geocode( { 'address': address}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
map.setCenter(results[0].geometry.location);
var marker = new google.maps.Marker({
map: map,
position: results[0].geometry.location
});
}
else {
alert("Geocode was not successful for the following reason: " + status);
}
});
}
function drawCircle() {
var radius=document.getElementById("radius").value;
geocoder.geocode( { 'address': address}, function(results, status){
if (status==google.maps.GeocoderStatus.OK){
var latlng=results[0].geometry.location;
var latitude=latlng.lat();
var longitude=latlng.lng();
}
else{
alert("Geocode was not successful for the following reason: " + status);
}
});
// Degrees to radians
var d2r = Math.PI / 180;
// Radians to degrees
var r2d = 180 / Math.PI;
// Earth radius is 3,963 miles
var cLat = (radius / 3963) * r2d;
var cLng = cLat / Math.cos(latitude * d2r);
//Store points in array
var points = [];
// Calculate the points
// Work around 360 points on circle
for (var i=0; i < 360; i++) {
var theta = Math.PI * (i/16);
// Calculate next X point
circleX = longitude + (cLng * Math.cos(theta));
// Calculate next Y point
circleY = latitude + (cLat * Math.sin(theta));
// Add point to array
points.push(new GPoint(circleX, circleY));
};
//Add points to map
var sColor=003F87;
var stroke=.5;
map.addOverlay(new GPolyline(points, sColor, stroke));
}
</script>
</head>
<body onload="initialize()">
<div id="map_canvas" style="width:500px; height:460px;
-moz-outline-radius:20px; -moz-box-sizing:padding-box; -moz-outline-style:solid ;-moz-outline-color:#9FB6CD;
-moz-outline-width:10px;"></div>
<div>
Zip Code: <input id="address" type="textbox" value="">
Radius:<input id="radius" type="textbox" value="">
<input type="button" value="Find" onclick="codeAddress() ">
<input type="button" value="Draw Radius" onclick= "drawCircle() ">
</div>
</body>
</html>

Resources