I have this Line Chart here:
jsfiddle.net/yfqQ4/
My Problem now is, that I don't get a proper grid in the background working with a legend (y and x-Axis) like this: http://lab.creativebrains.net/linechart.png
Can anybody can post me a code snippet how I should implement it or something like that?
Thanks!
I would suggest to use d3.svg.axis().scale() to tie up the grid to your coordinates. I drew a quick example based on your code: http://jsfiddle.net/yfqQ4/5/
The gist is to use the existing scales, x and y, and to use ticks as grid. Notice that height and width are the variable defining the size of your container. Here is the relevant code:
var numberOfTicks = 6;
var yAxisGrid = d3.svg.axis().scale(y)
.ticks(numberOfTicks)
.tickSize(width, 0)
.tickFormat("")
.orient("right");
var xAxisGrid = d3.svg.axis().scale(x)
.ticks(numberOfTicks)
.tickSize(-height, 0)
.tickFormat("")
.orient("top");
svg.append("g")
.classed('y', true)
.classed('axis', true)
.call(yAxisGrid);
svg.append("g")
.classed('x', true)
.classed('axis', true)
.call(xAxisGrid);
You can draw background grid like this:
//vertical lines
svg.selectAll(".vline").data(d3.range(26)).enter()
.append("line")
.attr("x1", function (d) {
return d * 20;
})
.attr("x2", function (d) {
return d * 20;
})
.attr("y1", function (d) {
return 0;
})
.attr("y2", function (d) {
return 500;
})
.style("stroke", "#eee");
// horizontal lines
svg.selectAll(".vline").data(d3.range(26)).enter()
.append("line")
.attr("y1", function (d) {
return d * 20;
})
.attr("y2", function (d) {
return d * 20;
})
.attr("x1", function (d) {
return 0;
})
.attr("x2", function (d) {
return 500;
})
.style("stroke", "#eee");
You can see here how it works with your jsfiddle (updated):
http://jsfiddle.net/cuckovic/Phzvy/
Related
Work in progress on a d3 navigation menu :
thatOneGuy suggested I set hover state for level 2 to keep level 2 rect's visible, that way they wont hide after 1 second but I need some help to get started. How do I set the hover state?
code snippet:
lvl2 = svg.selectAll(".lvl2")
.data(data2, function (d) { return d.name; });
gEnter2 = lvl2.enter()
.append("g")
.attr("class", "lvl2")
.style("fill", function (d, i) { return "url(#gradA" + d.id + ")"; })
.attr("id", function (d, i) { return d.id; })
.on('mouseenter', lighten)
.on('mouseleave', darken)
.attr("transform", function (d, i) { return "translate(" + d.x + "," + 0 + ")"; })
.style("visibility", "hidden");
Here is an acceptable alternative: https://jsfiddle.net/sjp700/3stdk6L6/2/
var tree = d3.layout.tree().nodeSize([70, 40]);
var diagonal = d3.svg.diagonal()
.projection(function (d) {
return [d.x + rectW / 2, d.y + rectH / 2];
});
I need to add multiple arcs to one svg elements (every one got different animation). I need to fill them with radial gradients, but now, the center of radial gradient is not in the centre of whole svg element, but in the centre of specific arc. How it looks now. First i make all of the gradients i need in defs.
var tmpgrad=null;
for( var k = 0; k<data.length;k++){
tmpgrad = grads
.append("radialGradient")
.attr("gradientUnits", "userSpaceOnUse")
.attr("r", "50%")
.attr("id", function(d, i) { return "grad" + k; });
tmpgrad
.append("stop")
.attr("offset", "0%")
.style("stop-color", data[k].endColor)
.style("stop-opasity", 0);
tmpgrad
.append("stop")
.attr("offset", "100%")
.style("stop-color",data[k].startColor)
.style("stop-opasity", 1);
}
Then I make my arcs with deferent options:
var width= 1200;
var height=360;
var oArc = d3.svg.arc()
.innerRadius(iRadius)
.outerRadius(oRadius);
var oPie = d3.layout.pie()
.startAngle( sA )
.endAngle( eA )
.sort(null);
var group = svgDrawer.append("g");
var oPath = group.selectAll("g")
.data(oPie([0,200]))
.enter()
.append("path");
oPath
.attr("fill",function(d, i){ if(i==1){ i=0; } else { i = id; } return "url(#grad" + i +")"; })
.attr("d",oArc)
.each(function(d){
this._current = d;
});
But in the result as i said before i got the separated arcs with gradient starting in the middle of every arc element (path actually). How can i solve that.
I am the beginner in svg and javascript subject.
I started to use dc.js Library to create all kinds of graphs and I bumped into a problem when I was trying to create a Geo Choropleth map using dc.js and couldn't add the ability to zoom and move the map.
All the examples I saw were using d3 and svg.. but once I used those examples, I couldn't use the data of dc.dimention and all the crossfilter calculations.
for example my code is:
d3.json("world-countries.json", function (statesJson) {
geoChart.width(1000)
.height(600)
.dimension(countryDim)
.projection(d3.geo.mercator()
.scale((960 + 1) / 4 )
.translate([960 / 4, 960 / 4])
.precision(.1))
.group(countryGroup)
.colors(d3.scale.quantize().range(["#E2F2FF","#C4E4FF","#9ED2FF","#81C5FF","#6BBAFF","#51AEFF","#36A2FF","#1E96FF","#0089FF","#0061B5"]))
.colorDomain([0, 200])
.colorCalculator(function(d){ returnd ?geoChart.colors()(d) :'#ccc'; })
.overlayGeoJson(statesJson.features,"state",function(d){
return d.properties.name;
})
.title(function (d) {
return "State: " + d.key + (d.value ? d.value : 0) + "Impressions";
});
Which works nicely, but I want to add the zoom effect and to be able to move the map. how can I do that?!?!
thanks in advance!
So, the answer is:
var width = 960,
height = 400;
var projection = d3.geo.mercator()
.scale(200)
.translate([width/2, height]);
function zoomed() {
projection
.translate(d3.event.translate)
.scale(d3.event.scale);
geoChart.render();
}
var zoom = d3.behavior.zoom()
.translate(projection.translate())
.scale(projection.scale())
.scaleExtent([height/2, 8 * height])
.on("zoom", zoomed);
var svg = d3.select("#geo-chart")
.attr("width", width)
.attr("height", height)
.call(zoom);
geoChart
.projection(projection)
.width(1000)
.height(400)
.transitionDuration(1000)
.dimension(countryDim)
.group(ctrGroup)
.filterHandler(function(dimension, filter){
dimension.filter(function(d) {return geoChart.filter() != null ? d.indexOf
(geoChart.filter()) >= 0 : true;}); // perform filtering
return filter; // return the actual filter value
})
.colors(d3.scale.quantize().range(["#E2F2FF", "#C4E4FF", "#9ED2FF", "#81C5FF",
"#6BBAFF", "#51AEFF", "#36A2FF", "#1E96FF", "#0089FF", "#0061B5"]))
.colorDomain([0, 200])
.colorCalculator(function (d) { return d ? geoChart.colors()(d) : '#ccc'; })
.overlayGeoJson(statesJson.features, "state", function (d) { return d.id; })
.title(function (d) {
return "State: " + d.key + " " + (d.value ? d.value : 0) + " Impressions";
});
I have a curve running above or below a sloped line and I want to fill the area just above the line and below the curve, but not the area below the line and above the curve, as illustrated in the left image.
Again, it is easy to fill like right image, but I want to fill like in left image. So, how to get rid of the unwanted fill?
svg.append("path").attr("class", "line").attr("d", peak(pp)).style({ fill: peakColor, opacity: 0.5 });
Okay, this is something I came up with for me in case anybody cares about it.
var slope = (dp.rightY - dp.leftY) / (dp.rightX - dp.leftX); // slope of the line
var pp = [];
pp.push({ x: dp.leftX, y: dp.leftY });
var wasAbove = true; // track if it's above the line
data.forEach(function (d) {
if (d.x >= dp.leftX && d.x <= dp.rightX) {
var yAtLine = (d.x - dp.leftX) * slope + dp.leftY;
if (d.y > yAtLine) {
if (!wasAbove)
pp.push({ x: d.x, y: yAtLine });
pp.push(d);
wasAbove = true;
} else if (wasAbove) {
pp.push({ x: d.x, y: yAtLine });
wasAbove = false;
}
}
});
pp.push({ x: dp.rightX, y: dp.rightY });
var peak = d3.svg.line().x(function (d) { return xScale(d.x) }).y(function (d) { return yScale(d.y) });
svg.append("path").attr("class", "line").attr("d", peak(pp)).style({ fill: peakColor, opacity: 0.5 });
Needing some help... i was able to find an example of a rotating globe, that works great, i even found a way to put red circles at a point. Even better to setup a timer and everything rotates with the globe great. But if i put text on the map at the same point as the red circles it shows up at the starting point that i placed it, but as the world turns the red circle moves with the globe, but the text is frozen at the points that it was written. i am trying to get the text to rotate with the world and the red circles. think in the country of united states i want to put a number, brazil would have number when the globe rotates to china the values would still be on the countries i put it and when it rotates US and Brazil back to the front the numbers are there showing. This is what i have in code, bear with me I am still a noob when working with D3. thanks for any input...
// Initialize some variables:
var element = '#home1',
width = $("#home1").width(),
height = $("#home1").height();
var diameter = 460,
radius = diameter/2,
velocity = .001,
then = Date.now();
var features, circles;
var projection = d3.geo.orthographic()
.scale(radius - 2)
.translate([radius, radius])
.clipAngle(90);
// Save the path generator for the current projection:
var path = d3.geo.path()
.projection(projection)
.pointRadius( function(d,i) {
return radius;
});
// Define the longitude and latitude scales, which allow us to map lon/lat coordinates to pixel values:
var lambda = d3.scale.linear()
.domain([0, width])
.range([-180, 180]);
var phi = d3.scale.linear()
.domain([0, height])
.range([90, -90]);
// Create the drawing canvas:
var svg = d3.select("#home1").append("svg:svg")
.attr("width", diameter)
.attr("height", diameter);
//Create a base circle: (could use this to color oceans)
var backgroundCircle = svg.append("svg:circle")
.attr('cx', diameter / 2)
.attr('cy', diameter / 2)
.attr('r', 0)
.attr('class', 'geo-globe');
// Make a tag to group all our countries, which is useful for zoom purposes. (child elements belong to a 'group', which we can zoom all-at-once)
var world = svg.append('svg:g');
var zoomScale = 1; // default
// Create the element group to mark individual locations:
var locations = svg.append('svg:g').attr('id', 'locations');
// Having defined the projection, update the backgroundCircle radius:
backgroundCircle.attr('r', projection.scale() );
// Construct our world map based on the projection:
d3.json('world-countries.json', function(collection) {
features = world.selectAll('path')
.data(collection.features)
.enter()
.append('svg:path')
.attr('class', 'geo-path')
.attr('d', path);
// features.append('svg:title')
// .text( function(d) { return d.properties.name; });
}); // end FUNCTION d3.json()
d3.json("data.geojson", function(collection) {
console.log("2");
cs = locations.selectAll('path')
.data(collection.features)
.enter().append('svg:path')
.datum(function(d) {return {type: "Point", coordinates: [d.geometry.coordinates[0], d.geometry.coordinates[1]]}; })
.attr('class', 'geo-node')
.attr("d", path.pointRadius(5))
.attr('d', path);
cs1 = locations.selectAll('text')
.data(collection.features)
.enter().append('svg:text')
.attr("transform", function(d) {return "translate(" + projection(d.geometry.coordinates) + ")"; })
.attr("dy", ".35em")
.attr('d', path)
.text(function(d) { return d.properties.name; });
}); // end FUNCTION d3.json()
d3.timer(function() {
if(offpage === 0)
{
var angle = velocity * (Date.now() - then);
projection.rotate([angle,0,0])
svg.selectAll("path").attr("d", path.projection(projection));
}
});
d3.select(window)
.on("touchmove", mousemove)
.on("touchstart", mousedown);
function mousemove() {
offpage = 0;
}
function mousedown() {
offpage=1
}
In your code, features(the world map) is a path, and cs(the city points) is a path, but cs1(the city names) is a text. In your timer you rotate the paths, which doesn't rotate the text.
My solution uses rotation degrees, instead of angle, so you'll have to adapt the formula.
d3.timer(function() {
tcounter++
rotation++
if (rotation>=360) rotation = 0
projection.rotate([rotation,0,0])
www.attr("d", path.projection(projection));
citydot.attr("d", path.projection(projection));
ctext.attr("transform", function(d) {
return "translate(" + projection(d.geometry.coordinates) + ")"; })
.text(function(d) {
if (((rotation + d.geometry.coordinates[0] > -90) && (rotation + d.geometry.coordinates[0] <90)) ||
((rotation + d.geometry.coordinates[0] > 270) && (rotation + d.geometry.coordinates[0] <450)))
return d.properties.city;
else return "" });
if (tcounter > 360) return true
else return false
})