I am trying to plot a png map file with graticules. The graticule extent should correspond to the width and height of the map file (see end of script). Although the left and upper extent show correctly, the lower and right extent do not correspond to the map dimensions.
I also played around with the extent values in the map function and only the left and upper extent are responsive.
Any suggestions?
<!doctype html>
<meta charset="utf-8">
<script src="./js/d3.js"></script>
<script src="./js/topojson.js"></script>
<script src="./js/jquery3.1.0.min.js"></script>
<style>
.MapPad {
padding: 30px 30px 30px 30px;
}
.graticule {
fill: none;
stroke: #000;
stroke-opacity: .15;
}
.graticule.outline {
stroke: black;
stroke-opacity: 1;
stroke-width: 2px;
stroke-dasharray: initial;
}
.LonLatLabel {
font-family: helvetica;
font-size: 22px;
dominant-baseline: central;
text-anchor: middle;
</style>
<body>
<div id='cont1_1'></div>
<script charset="utf-8">
//The function to plot the maps
function plotMaps (container, width, height, rasterBounds, demFile){
var projection = d3.geoMercator()
.scale(1)
.translate([0, 0]);
var b = [projection(rasterBounds[0]), projection(rasterBounds[1])],
s = 1 / Math.max((b[1][0] - b[0][0]) / width, (b[1][1] - b[0][1]) / height),
t = [(width - s * (b[1][0] + b[0][0])) / 2, (height - s * (b[1][1] + b[0][1])) / 2]
//update projection
projection
.scale(s)
.translate(t)
// geo path generator
var path = d3.geoPath()
.projection(projection)
var map = d3.select(container).append('svg')
.attr('width', width)
.attr('height', height)
.attr('class', 'MapPad');
//define the data layers before drawing to ensure the order of appearance
var gratLines = map.append('g');
var demLayer = map.append('g');
var samplPointsLayer = map.append('g');
var outline = map.append('g');
//make the graticule
var graticule = d3.geoGraticule().extent([[rasterBounds[0][0], rasterBounds[1][0]], [rasterBounds[0][1], rasterBounds[1][1]]]).step([1, 1]);
gratLines.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);
// get the coordinates of the line paths and use them as labels
map.selectAll('text')
.data(graticule.lines())
.enter().append("text")
.text(function(d) {
if (d.coordinates[0][0] == d.coordinates[1][0]) {return (d.coordinates[0][0]);}
else if (d.coordinates[0][1] == d.coordinates[1][1]) {return (d.coordinates[0][1]);}
})
.attr("class","LonLatLabel")
.attr('transform', function(d) { return ('translate(' + projection(d.coordinates[0])[0] + ',' + projection(d.coordinates[1])[1] + ')')
});
//outline of the map
outline.append("path")
.datum(graticule.outline)
.attr("class", "graticule outline")
.attr("d", path);
/*var color = d3.scale.ordinal()
.domain(["1", "2", "3"])
.range(["#ffd633", "#aaff00" , "#267300"]);
*/
demLayer.append('svg:image')
.attr('xlink:href', demFile)
.attr('width', width)
.attr('height', height);
d3.json('SamplingPoints.json', function(err, data) {
samplPointsLayer.selectAll('circles')
.data(data.features)
.enter().append('circle')
.attr('r', 5)
.each(function(d) {
var lonlat = projection(d.geometry.coordinates);
d3.select(this)
.attr('cx', lonlat[0])
.attr('cy', lonlat[1])
.style('fill', 'black')
.style("opacity", .5)
});
});
}
//calculate the number with which the size of each map should be divided
var mainWidth = 230
//Plot the maps in each div
//Alps
var widthAlps = 4665;
var heightAlps = 3589;
var resCoefAlps = widthAlps/mainWidth
var rasterBoundsAlps = [[ 5.907077970880465 , 45.29815864865324 ] , [ 11.330836684119511 , 48.15780097787413 ]];
plotMaps('#cont1_1', widthAlps/resCoefAlps, heightAlps/resCoefAlps, rasterBoundsAlps, 'dem_alps.png');
</script>
</body>
Here's the result:
enter image description here
Issue solved! The [xmin, ymin], [xmax, ymax] values in the d3.geoGraticule().extent() function should be:
[rasterBounds[0][0], rasterBounds[0][1]], [rasterBounds[1][0], rasterBounds[1][1]]]
Related
I have succesfully drawn a map and plotted points from a csv. file on it.
But when I try to add a brush (which should color the circles within the brush in the original color, and the ones outside should have a lower opacity - and when releasing the brush all circles should again have the same color), something goes wrong - The map is shown very quickly and then the entire svg just turns into a single color.
I am pretty new to d3 and have just tried to follow this example: http://bl.ocks.org/feyderm/6bdbc74236c27a843db633981ad22c1b . I can't really figure out if it might have something to do with the projection or something totally different..
My attempt is shown below:
<!DOCTYPE html>
...
<style type="text/css">
.brushed {
fill: white;
stroke: black;
stroke-width: 0.5;
opacity: 0.95;
}
.non_brushed {
fill: grey;
opacity: 0.15;
}
</style>
</head>
<body>
<script type="text/javascript">
//Width and height
var w = 500;
var h = 500;
var padding = 60;
//Define path generator, using the mercator projection
var projection = d3.geoMercator()
.scale(90*w)
.translate([58350, 35330]);
var path = d3.geoPath()
.projection(projection);
//define borough colors
var color = ["rgb(0,59,86)","rgb(63,72,77)",
"rgb(243,142,50)", "rgb(246,99,36)", "rgb(21,108,108)"];
//Create SVG element
var svg_map = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
//Load in GeoJSON data
d3.json("boroughs.json", function(json) {
//Bind data and create one path per GeoJSON feature
svg_map.selectAll("path")
.data(json.features)
.enter()
.append("path")
.attr("d", path)
.style("stroke","white")
.style("stroke-width","1px")
.style("fill",function(d,i){
return color[i];
});
//load in csv data
d3.csv("blabla.csv",function(data){
//create circle elements
var circles = svg_map.append("g")
.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("class","brushed") //original color
.attr("cx", function(d){
return projection([d.Lon,d.Lat])[0];
})
.attr("cy", function(d){
return projection([d.Lon,d.Lat])[1];
})
.attr("r",3);
//create brush
var brush = d3.brush()
.on("brush", highlightBrushedCircles)
.on("end", brushEnd);
svg_map.append("g")
.call(brush);
function highlightBrushedCircles() {
if (d3.event.selection != null) {
// set circles to "non_brushed"
circles.attr("class", "non_brushed");
//coordinates describing the corners of the brush
var brush_coords = d3.brushSelection(this);
// set the circles within the brush to class "brushed" to style them accordingly
circles.filter(function (){
var cx = d3.select(this).attr("cx"),
cy = d3.select(this).attr("cy");
return isBrushed(brush_coords, cx, cy);
})
.attr("class", "brushed");
}
}
function isBrushed(brush_coords, cx, cy) {
//the corners of the brush
var x0 = brush_coords[0][0],
x1 = brush_coords[1][0],
y0 = brush_coords[0][1],
y1 = brush_coords[1][1];
//checks whether the circle is within the brush
return x0 <= cx && cx <= x1 && y0 <= cy && cy <= y1;
}
function brushEnd() {
if (!d3.event.selection) return;
// programmed clearing of brush after mouse-up
d3.select(this).call(brush.move, null);
//set all circles to original color
svg_map.selectAll(".non_brushed").classed("brushed", true);
}
});
});
</script>
</body>
I got it to work - it seemed that the problem was that I in my style css file also had this:
rect {
fill: rgb(21,108,108);
shape-rendering: CrispEdges;
}
Which then just colored the entire svg like that :)
I happened to play around with the D3js-Library to visualize some SQL-JSON_LD data and want to do the following:
attach individual id-TAG as well as data-set (Matrix with various elements) to each slice
My Code right now looks like this
<!DOCTYPE html>
<meta charset="utf-8">
<style>
path {
fill: #ccc;
stroke: #333;
stroke-width: 1.5px;
transition: fill 250ms linear;
transition-delay: 150ms;
}
path:hover {
fill: #999;
stroke: #000;
transition-delay: 0;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var data = {
{"year":"2017-07-01","value":"1"},
{"year":"2017-07-02","value":"1"},
{"year":"2017-07-03","value":"2"},
{"year":"2017-07-04","value":"3"},
{"year":"2017-07-05","value":"5"},
{"year":"2017-07-06","value":"8"},
{"year":"2017-07-07","value":"13"},
{"year":"2017-07-08","value":"21"},
{"year":"2017-07-09","value":"24"},
{"year":"2017-07-10","value":"55"},
{"year":"2017-07-11","value":"89"},};
var width = 960,
height = 500;
arc_ids = d3.range(data.length); // for naming the arcs
var outerRadius = height / 2 - 20,
innerRadius = outerRadius / 3,
cornerRadius = 10;
var pie = d3.layout.pie()
.padAngle(.02);
var arc = d3.svg.arc()
.padRadius(outerRadius)
.innerRadius(innerRadius);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.attr("id","viz_pieChart") // adds an ID to the whole chart
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
svg.selectAll("path")
.data(pie(data.map(function(d) { return parseInt(d.value); })))
.attr("id", function(d, i) { console.log('CP1'); return "arc-" + arc_ids[i]; }) // This was intended to add an individual id to each arc, but it doesn't
.attr("data", function(d) { return d.data; }) // attach data to arc according to value, as e.g.: {"year":"2017-07-01","value":"1"}
.enter().append("path")
.each(function(d) {
d.outerRadius = outerRadius - 20;
})
.attr("d", arc)
.on("mouseover", arcTween(outerRadius, 0))
on("click", function(d){console.log(d.id);console.log(d.data.toString())}); //print id of the clicked arc as well as saved data
.on("mouseout", arcTween(outerRadius - 20, 150));
function arcTween(outerRadius, delay) {
return function() {
d3.select(this).transition().delay(delay).attrTween("d", function(d) {
var i = d3.interpolate(d.outerRadius, outerRadius);
return function(t) {
d.outerRadius = i(t);
return arc(d);
};
});
};
}
//test whether an arc can be reached, e.g. the 2nd Element
console.log(document.getElementById('slice-1')); // gives an error
</script>
I also checked this1, this2 and this3 as they seemed promising, but it still does not work for me.
Afterwards I want to use the attached data of an arc to print it into another svg-graphic. But first adressing has to work.
And I'm sorry for the post with more than one specific question!
Thank you for your help!
you must append the path before give it an id or data
<!DOCTYPE html>
<meta charset="utf-8">
<style>
path {
fill: #ccc;
stroke: #333;
stroke-width: 1.5px;
transition: fill 250ms linear;
transition-delay: 150ms;
}
path:hover {
fill: #999;
stroke: #000;
transition-delay: 0;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var data = [
{"year":"2017-07-01","value":"1"},
{"year":"2017-07-02","value":"1"},
{"year":"2017-07-03","value":"2"},
{"year":"2017-07-04","value":"3"},
{"year":"2017-07-05","value":"5"},
{"year":"2017-07-06","value":"8"},
{"year":"2017-07-07","value":"13"},
{"year":"2017-07-08","value":"21"},
{"year":"2017-07-09","value":"24"},
{"year":"2017-07-10","value":"55"},
{"year":"2017-07-11","value":"89"}];
var width = 960,
height = 500;
arc_ids = d3.range(data.length); // for naming the arcs
var outerRadius = height / 2 - 20,
innerRadius = outerRadius / 3,
cornerRadius = 10;
var pie = d3.layout.pie()
.padAngle(.02);
var arc = d3.svg.arc()
.padRadius(outerRadius)
.innerRadius(innerRadius);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.attr("id","viz_pieChart") // adds an ID to the whole chart
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
svg.selectAll("path")
.data(pie(data.map(function(d) {
return parseInt(d.value);
})))
.enter().append("path")
.each(function(d) {
d.outerRadius = outerRadius - 20;
})
.attr("id", function(d, i) { return "arc-" + arc_ids[i]; })
// This was intended to add an individual id to each arc, but it doesn't
.attr("data", function(d) { return d.data; }) // attach data to arc according to value, as e.g.: {"year":"2017-07-01","value":"1"}
.attr("d", arc)
.on("mouseover", arcTween(outerRadius, 0))
.on("click", function(d){
console.log(this.id);
console.log(d.data.toString())
}) //print id of the clicked arc as well as saved data
.on("mouseout", arcTween(outerRadius - 20, 150));
function arcTween(outerRadius, delay) {
return function() {
d3.select(this).transition().delay(delay).attrTween("d", function(d) {
var i = d3.interpolate(d.outerRadius, outerRadius);
return function(t) {
d.outerRadius = i(t);
return arc(d);
};
});
};
}
//test whether an arc can be reached, e.g. the 2nd Element
console.log(document.getElementById('slice-1')); // gives an error
</script>
I'm trying to modify the example of a zoomable map of the United States, provided by mbostock to fit with Europe.
At the point I've been able to get it sort of, it's so small in the beginning!
I've looked at everything trying to change the presentation size in the beginning, i.e. before the zoom, to be larger, but I've not been able to figure it out. How to achieve this?
All the code is here.
But really, it's just the following short file and an eu.json file:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.background {
fill: none;
pointer-events: all;
}
#states {
fill: #aaa;
}
#states .active {
fill: orange;
}
#state-borders {
fill: none;
stroke: #fff;
stroke-width: 0.5px;
stroke-linejoin: round;
stroke-linecap: round;
pointer-events: none;
}
</style>
<body>
<div id="map"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/topojson/1.6.19/topojson.min.js"></script>
<script>
var width = 960;
var height = 500;
var centered;
var projection = d3.geo.mercator();
var path = d3.geo.path()
.projection(projection);
var svg = d3.select("#map")
.append("svg")
.attr("width", width)
.attr("height", height);
svg.append("rect")
.attr("class", "background")
.attr("width", width)
.attr("height", height)
.on("click", clicked);
var g = svg.append("g");
d3.json("eu.json", function(error, data) {
if (error) throw error;
g.append("g")
.attr("id", "states")
.selectAll("path")
.data(topojson.feature(data, data.objects.europe).features)
.enter().append("path")
.attr("d", path)
.on("click", clicked);
g.append("path")
.datum(topojson.mesh(data, data.objects.europe, function(a, b) { return a !== b; }))
.attr("id", "state-borders")
.attr("d", path);
});
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");
}
</script>
This question already has an answer here:
Importing data from .csv using d3.js
(1 answer)
Closed 7 years ago.
All I want is to load a basic pie/donut chart, (actually a few bar plots in addition to that , too), but looks like there is some error in my . If I comment the same, I am able to serve the bare-bones python rendered page(but not the pie chart, though).
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>{{title}}</title>
<link rel="stylesheet" href="/static/css/style.css">
<style>
.legend{
font-size: : 12px;
}
rect{
stroke-width: 2;
}
#chart_Imp,#chart_Bid{
width: 50%;
}
.axis{
font: 10px sans-serif;
}
.axis path,
.axis line{
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
</style>
</head>
<body>
<div style="max-width: 800px; border:2px black solid">
<h1>{{haider_Imp}}</h1>
<div id="chart_Imp"></div>
<h1>{{haider_Bid}}</h1>
<div id="chart_Bid"></div>
</div>
<div id="Bar-plots">
<div id="Bar-plots 1st set">
<h1>{{haider_cpa}}</h1>
<div id="cpa"></div>
<h1>{{haider_cpc}}</h1>
<div id="cpc"></div>
<h1>{{haider_cpm}}</h1>
<div id="cpm"></div>
</div>
<div id="Bar-plots 2nd set">
<h1>{{haider_avgbid}}</h1>
<div id="avg_bid"></div>
<h1>{{haider_winrate}}</h1>
<div id="winrate"></div>
</div>
</div>
<script src="/static/script/d3.min.js"></script>
<script>
(function(d3){
'use strict';
var width = 360;
var height = 360;
var radius = Math.min(width,height)/2;
var donutWidth = 75;
var legendRectSize = 18;
var legendSpacing = 4;
var color = d3.scale.category20b();
var svg = d3.select('#chart_Imp')
.append('svg')
.attr('width',width)
.attr('height',height)
.append('g')
.attr('transform','translate('+(width/2)+','+(height/2)+')');
var arc = d3.svg.arc()
.innerRadius(radius-donutWidth)
.outerRadius(radius);
var pie = d3.layout.pie()
.value(function(d) { return d.impsplit; })
.sort(null);
d3.csv('./static/summary.csv',function(error,dataset){
dataset.forEach(function(d) {
d.impsplit = +d.impsplit;
});
var path = svg.selectAll('path')
.data(pie(dataset))
.enter()
.append('path')
.attr('d',arc)
.attr('fill',function(d,i)
{
return color(d.data.label);
});
var legend = svg.selectAll('.legend1')
.data(color.domain())
.enter()
.append('g')
.attr('class', 'legend')
.attr('transform', function(d, i) {
var height = legendRectSize + legendSpacing;
var offset = height * color.domain().length / 2;
var horz = -2 * legendRectSize;
var vert = i * height - offset;
return 'translate(' + horz + ',' + vert + ')';
});
legend.append('rect')
.attr('width', legendRectSize)
.attr('height', legendRectSize)
.style('fill', color)
.style('stroke', color);
legend.append('text')
.attr('x', legendRectSize + legendSpacing)
.attr('y', legendRectSize - legendSpacing)
.text(function(d) { return d; });
});
(window.d3);
var margin = {top: 20,right:20, bottom: 70, left: 40},
width = 600-margin.left-margin.right,
height = 300 - margin.top - margin.bottom;
</script>
</body>
</html>
It might be because you use your piechart dataset outside of the .csv callback function. I am under the impression that, when using the d3.csv() or d3.tsv() functions, you need to use the retrieved data in the callback function. You however, use your data outside the callback function.
check out this answer, it might help out.
I'm trying to combine these two d3 examples:
http://bl.ocks.org/mbostock/4183330
http://bl.ocks.org/mbostock/2206590
I have a sphere with the projection displaying correctly, and the zoom working correctly. All I'm trying to do now is style it.
I got the world tour example working previously, it uses canvas and I was able to give it a shadow to create a glow effect that I really liked.
After merging these two code pieces I'm now using svg elements and I cannot seem to get the glow effect to work.
Here is my code (the fill attribute of the .globe class seems to be working):
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
background: #000000;
}
.background {
fill: none;
pointer-events: all;
}
.feature {
fill: #ccc;
cursor: pointer;
}
.feature.active {
fill: #00FF15;
}
.globe
{
fill:#fff;
strokeStyle: #35C441;
lineWidth: 5;
shadowColor: #35C441;
shadowBlur: 40;
shadowOffsetX: 0;
shadowOffsetY: 0;
}
.mesh {
fill: none;
stroke: #fff;
stroke-linecap: round;
stroke-linejoin: round;
}
</style>
<body>
<script src="d3/d3.v3.min.js"></script>
<script src="d3/topojson.v1.min.js"></script>
<script>
var width = 960,
height = 720;
active = d3.select(null);
var globe = {type: "Sphere"};
var projection = d3.geo.orthographic()
.scale(height / 2.1)
.translate([width / 2, height / 2])
.clipAngle(90)
.precision(.5);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
//append a rectange to the svg element. give it the background css style class.
//on click do reset?
svg.append("rect")
.attr("class", "background")
.attr("width", width)
.attr("height", height)
.on("click", reset);
//append "g" to the svg element
var g = svg.append("g")
.style("stroke-width", "1.5px");
var path = d3.geo.path()
.projection(projection)
d3.json("./world-110m.json", function(error, world) {
g.append("path")
.datum(globe)
.attr("class", "globe")
.attr("d", path);
g.selectAll("path")
.data(topojson.feature(world, world.objects.countries).features)
.enter().append("path")
.attr("d", path)
.attr("class", "feature")
.on("click", clicked);
g.append("path")
.datum(topojson.mesh(world, world.objects.countries, function(a, b) { return a !== b; }))
.attr("class", "mesh")
.attr("d", path);
});
function clicked(d) {
if (active.node() === this) return reset();
active.classed("active", false);
active = d3.select(this).classed("active", true);
var bounds = path.bounds(d),
dx = bounds[1][0] - bounds[0][0],
dy = bounds[1][1] - bounds[0][1],
x = (bounds[0][0] + bounds[1][0]) / 2,
y = (bounds[0][1] + bounds[1][1]) / 2,
scale = .9 / Math.max(dx / width, dy / height),
translate = [width / 2 - scale * x, height / 2 - scale * y];
g.transition()
.duration(750)
.style("stroke-width", 1.5 / scale + "px")
.attr("transform", "translate(" + translate + ")scale(" + scale + ")");
}
function reset() {
active.classed("active", false);
active = d3.select(null);
g.transition()
.duration(750)
.style("stroke-width", "1.5px")
.attr("transform", "");
}
</script>
</body>
</html>
If anyone can help that would be great, or if the answer already exists on here could you please point me in the right direction
Thanks!
It would have helped if you included a picture of the effect you want.
That said, your CSS is simply not valid with SVG elements:
The first two have corresponding styles:
.globe {
fill:#fff;
stroke: #35C441;
stroke-width: 5;
}
Shadows, though, are a bit trickier.