I'm currently working with d3.js and basically want to display a globe, which when hovering over the individual countries displays the names in a div. But at the moment I can't output the names, respectively I don't know exactly how to access the names so I can output them in the mouseover.
What do I have to consider here? I would like to output the name of the country from the csv file.
Here is my globe:
// The svg
const svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
// Map and projection
const path = d3.geoPath();
const projection = d3.geoMercator()
.scale(70)
.center([0,20])
.translate([width / 2, height / 2]);
// Data and color scale
const data = new Map();
const colorScale = d3.scaleThreshold()
.domain([100000, 1000000, 10000000, 30000000, 100000000, 500000000])
.range(d3.schemeBlues[8]);
// Load external data and boot
Promise.all([
d3.json("https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/world.geojson"),
d3.csv("https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/world_population.csv", function(d) {
data.set(d.code, +d.pop)
})]).then(function(loadData){
let topo = loadData[0]
let mouseOver = function(d) {
d3.selectAll(".Country")
.style("opacity", .5)
d3.select(this)
.style("opacity", 1)
.style("stroke", "black")
}
let mouseLeave = function(d) {
d3.selectAll(".Country")
.style("opacity", .8)
d3.select(this)
.style("stroke", "transparent")
}
// Draw the map
svg.append("g")
.selectAll("path")
.data(topo.features)
.enter()
.append("path")
// draw each country
.attr("d", d3.geoPath()
.projection(projection)
)
// set the color of each country
.attr("fill", function (d) {
d.total = data.get(d.id) || 0;
return colorScale(d.total);
})
.style("stroke", "transparent")
.attr("class", function(d){ return "Country" } )
.style("opacity", .8)
.on("mouseover", mouseOver )
.on("mouseleave", mouseLeave )
})
<!DOCTYPE html>
<meta charset="utf-8">
<!-- Load d3.js -->
<script src="https://d3js.org/d3.v6.js"></script>
<!-- Create an element where the map will take place -->
<svg id="my_dataviz" width="400" height="300"></svg>
One approach is pushing the CSV names into the geoJSON objects. For instance:
loadData[1].forEach(row => {
const foundGeometry = loadData[0].features.find(e => e.id === row.code);
if (foundGeometry) foundGeometry.properties.countryName = row.name;
});
Then, supposing you have a div, just do:
div.html(d.properties.countryName);
Pay attention to the fact that this is D3 v6, so the datum needs to be the second argument in your mouseOver function:
let mouseOver = function(event, d) {
Here is your code with those changes:
// The svg
const svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
const div = d3.select("#mydiv");
// Map and projection
const path = d3.geoPath();
const projection = d3.geoMercator()
.scale(70)
.center([0, 20])
.translate([width / 2, height / 2]);
// Data and color scale
const data = new Map();
const colorScale = d3.scaleThreshold()
.domain([100000, 1000000, 10000000, 30000000, 100000000, 500000000])
.range(d3.schemeBlues[8]);
// Load external data and boot
Promise.all([
d3.json("https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/world.geojson"),
d3.csv("https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/world_population.csv", function(d) {
data.set(d.code, +d.pop);
return d;
})
]).then(function(loadData) {
let topo = loadData[0];
loadData[1].forEach(row => {
const foundGeometry = loadData[0].features.find(e => e.id === row.code);
if (foundGeometry) foundGeometry.properties.countryName = row.name;
});
let mouseOver = function(event, d) {
div.html(d.properties.countryName)
d3.selectAll(".Country")
.style("opacity", .5)
d3.select(this)
.style("opacity", 1)
.style("stroke", "black")
}
let mouseLeave = function(d) {
div.html(null)
d3.selectAll(".Country")
.style("opacity", .8)
d3.select(this)
.style("stroke", "transparent")
}
// Draw the map
svg.append("g")
.selectAll("path")
.data(topo.features)
.enter()
.append("path")
// draw each country
.attr("d", d3.geoPath()
.projection(projection)
)
// set the color of each country
.attr("fill", function(d) {
d.total = data.get(d.id) || 0;
return colorScale(d.total);
})
.style("stroke", "transparent")
.attr("class", function(d) {
return "Country"
})
.style("opacity", .8)
.on("mouseover", mouseOver)
.on("mouseleave", mouseLeave)
})
#mydiv {
height: 1.2em;
}
<!DOCTYPE html>
<meta charset="utf-8">
<!-- Load d3.js -->
<script src="https://d3js.org/d3.v6.js"></script>
<!-- Create an element where the map will take place -->
<div id="mydiv"></div>
<svg id="my_dataviz" width="400" height="300"></svg>
Related
I have a Pie Chart which updates as you move the slider, the chart also has labels which I want to update and reposition as the data changes. When I update the chart new labels are drawn but the old labels are not removed. I have managed to get the Join/Enter/Update/Remove sequence working on the chart itself but not the labels.
Is there anything different about text that means this update sequence does not work?
function update() {
// Scales
const colors = d3.quantize(d3.interpolateSpectral, dataset.length);
const colorScale = d3
.scaleOrdinal()
.domain(dataset.map((d) => d.type))
.range(colors);
//Define Pie Chart
const generationPie = d3
.pie()
.padAngle(0.005)
.value((d) => d.generation)
.sort(null);
//Pass dataset into Pie Chart
const slices = generationPie(dataset);
//JOIN DATA
const arcs = arcGroup.selectAll("path").data(slices);
//ENTER NEW DATA
arcs
.enter()
.append("path")
.attr("d", arc)
.attr("fill", (d) => colorScale(d.data.type))
.each(function (d) {
this._current = d;
});
//UPDATE
arcs.transition().duration(1000).attrTween("d", arcTween);
//REMOVE OLD DATA
arcs.exit().remove();
function arcTween(a) {
var i = d3.interpolate(this._current, a);
this._current = i(0);
return function (t) {
return arc(i(t));
};
}
//==============================================================
//Data timestamp
//Join
const dataTimeLabel = dataTimestamp.text(timeFormat(dataTime));
//Update
dataTimeLabel.transition().duration(1000);
//ENTER
dataTimeLabel.enter().text(timeFormat(dataTime));
//REMOVE
dataTimestamp.exit().remove();
//==============================================================
//Labels
const labelsGroup = ctr
.append("g")
.attr(
"transform",
`translate(${dimensions.ctrHeight / 2}, ${dimensions.ctrWidth / 2})`
);
//JOIN
const labelArcs = labelsGroup.selectAll("text").data(slices);
//ENTER
labelArcs
.enter()
.append("text")
.attr("transform", (d) => `translate(${arcLabels.centroid(d)})`)
.append("tspan")
.attr("font-weight", "bold")
.attr("font-size", 14)
.attr("x", "-2em")
.text((d) => d.data.type)
.append("tspan")
.attr("x", "-2.3em")
.attr("y", "+1.2em")
.attr("fill-opacity", 0.7)
.attr("font-size", 12)
.text((d) => commaFormat(d.data.percentage).toLocaleString() + " %")
//UPDATE
labelArcs.transition().duration(1000)
//REMOVE
labelArcs.exit().remove();
}
Any help would be really welcome.
On top of the comment about re-appending the groups and the answer about splitting the selection, you have to change the text position in your update selection...
labelArcs.transition().duration(1000)
.attr("transform", (d) => `translate(${arcLabels.centroid(d)})`);
...as well as changing the text values in the <tspan> elements:
labelArcs.select(".type")
.text((d) => d.data.type);
labelArcs.select(".value")
.text((d) => commaFormat(d.data.percentage).toLocaleString() + " %");
Here is the resulting code:
async function draw() {
// Data
let dataset;
let dataTime;
const commaFormat = d3.format(",");
const timeFormat = d3.timeFormat("%d-%m-%Y %I:%M");
// Dimensions
let dimensions = {
width: 700,
height: 700,
margins: 10,
};
dimensions.ctrWidth = dimensions.width - dimensions.margins * 2;
dimensions.ctrHeight = dimensions.height - dimensions.margins * 2;
const radius = dimensions.ctrHeight / 3;
// Draw Image
const svg = d3
.select("#chart-area")
.append("svg")
.attr("width", dimensions.width)
.attr("height", dimensions.height);
const ctr = svg
.append("g") // <g>
.attr(
"transform",
`translate(${dimensions.margins}, ${dimensions.margins})`
);
const arc = d3
.arc()
.outerRadius(radius)
.innerRadius(radius * 0.8);
const arcLabels = d3
.arc()
.outerRadius(radius * 1.2)
.innerRadius(radius * 1.2);
const arcGroup = ctr
.append("g")
.attr(
"transform",
`translate(${dimensions.ctrHeight / 2}, ${dimensions.ctrWidth / 2})`
);
const dataTimestamp = svg
.append("text")
.attr(
"transform",
`translate(${dimensions.ctrHeight / 2}, ${dimensions.ctrWidth / 2})`
)
.attr("text-anchor", "middle")
.append("tspan")
.attr("x", "+0.2em")
.attr("y", "+2.5em")
.attr("font-size", 20);
//Labels
const labelsGroup = ctr
.append("g")
.attr(
"transform",
`translate(${dimensions.ctrHeight / 2}, ${dimensions.ctrWidth / 2})`
);
d3.json("https://raw.githubusercontent.com/1crisl/test/main/data.json").then((data) => {
const timeArray = data.map((record) => {
return record.unixTime;
});
const minTime = d3.min(timeArray);
const maxTime = d3.max(timeArray);
let i = timeArray.length - 1;
$("#dateLabel1").text(timeFormat(minTime));
$("#dateLabel2").text(timeFormat(maxTime));
$("#date-slider").slider({
max: timeArray.length - 1,
min: 0,
value: timeArray.length - 1,
change: (event, ui) => {
i = $("#date-slider").slider("value");
dataTime = data[i].unixTime;
dataset = data[i].data.filter(function(obj) {
return obj.percentage > "1";
});
update();
},
});
dataTime = data[i].unixTime;
dataset = data[i].data.filter(function(obj) {
return obj.percentage > "1";
});
update();
});
function update() {
// Scales
const colors = d3.quantize(d3.interpolateSpectral, dataset.length); //Generates a colour for each item in the dataset array
const colorScale = d3
.scaleOrdinal()
.domain(dataset.map((d) => d.type))
.range(colors);
//Define Pie Chart
const generationPie = d3
.pie()
.padAngle(0.005)
.value((d) => d.generation)
.sort(null);
//Pass dataset into Pie Chart
const slices = generationPie(dataset);
//JOIN DATA
const arcs = arcGroup.selectAll("path").data(slices);
//ENTER NEW DATA
arcs
.enter()
.append("path")
.attr("d", arc)
.attr("fill", (d) => colorScale(d.data.type))
.each(function(d) {
this._current = d;
});
//UPDATE
arcs.transition().duration(1000).attrTween("d", arcTween);
//REMOVE OLD DATA
arcs.exit().remove();
function arcTween(a) {
var i = d3.interpolate(this._current, a);
this._current = i(0);
return function(t) {
return arc(i(t));
};
}
//==============================================================
//Data timestamp
//Join
const dataTimeLabel = dataTimestamp.text(timeFormat(dataTime));
//Update
dataTimeLabel.transition().duration(1000);
//ENTER
dataTimeLabel.enter().text(timeFormat(dataTime));
//REMOVE
dataTimeLabel.exit().remove();
//==============================================================
//JOIN
const labelArcs = labelsGroup.selectAll("text").data(slices, d => d.data.type);
//ENTER
const textGroup = labelArcs
.enter()
.append("text")
.attr("transform", (d) => `translate(${arcLabels.centroid(d)})`);
textGroup
.append("tspan")
.attr("font-weight", "bold")
.attr("font-size", 14)
.attr("x", "-2em")
.attr("class", "type")
.text((d) => d.data.type);
textGroup
.append("tspan")
.attr("x", "-2.3em")
.attr("y", "+1.2em")
.attr("fill-opacity", 0.7)
.attr("font-size", 12)
.attr("class", "value")
.text((d) => commaFormat(d.data.percentage).toLocaleString() + " %");
//UPDATE
labelArcs.select(".type")
.text((d) => d.data.type);
labelArcs.select(".value")
.text((d) => commaFormat(d.data.percentage).toLocaleString() + " %");
labelArcs.transition().duration(1000)
.attr("transform", (d) => `translate(${arcLabels.centroid(d)})`);
//REMOVE
labelArcs.exit().remove();
}
}
draw();
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="description" content="">
<title>Visualisation</title>
<!-- Bootstrap -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<!-- jQueryUI styling -->
<link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
</head>
<body>
<!-- Bootstrap grid setup -->
<div class="container">
<div class="row">
<div class="col-md-12 d-flex justify-content-center">
<div id="chart-area"></div>
</div>
<div class="col-md-12 d-flex justify-content-center">
<div id="slider-div">
<label>Data Extent</label>
<br>
<label><span id="dateLabel1">01/01/2000</span> to <span id="dateLabel2">18/03/2021</span></label>
<div id="date-slider"></div>
</div>
</div>
</div>
</div>
<!-- External JS libraries -->
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js#1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js" integrity="sha256-VazP97ZCwtekAsvgPBSUwPFKdrwD3unUfSGVYrahUqU=" crossorigin="anonymous"></script>
<!-- Custom JS -->
<script src="js/main.js"></script>
</body>
</html>
PS: your percentages are way off.
Try to split code:
const textGroup = labelArcs
.enter()
.append("text")
.attr("transform", (d) => `translate(${arcLabels.centroid(d)})`)
textGroup.append("tspan")
.attr("font-weight", "bold")
.attr("font-size", 14)
.attr("x", "-2em")
.text((d) => d.data.type)
textGroup.append("tspan")
.attr("x", "-2.3em")
.attr("y", "+1.2em")
.attr("fill-opacity", 0.7)
.attr("font-size", 12)
.text((d) => commaFormat(d.data.percentage).toLocaleString() + " %")
If it does not help, please provide a snippet of a fiddle to work with
I've been trying to do a widget with some information from Google Analytics. I am getting the real time visitors and then displaying them in bubbles with flags with the help of d3.js. I do manage to get them on the screen and the force simulation is working properly. However I can't seem to grasp how could I cluster the bubbles according to their respective country, as I don't know which countries will be displayed in advance, and I'm not good at web programming so as do some on the fly links and use the forceLink. Maybe there is a better way that I'm missing? I would greatly appreciate any pointers that you could give me.
(function() {
var width = 500;
height = 500;
var svg = d3.select("#chart")
.append("svg")
.attr("height", height)
.attr("width", width)
.append("g")
.attr("transform", "translate(0,0)")
// SO question preparation :
var data_csv = "Country,Count\nUkraine,1\nDenmark,1\nDenmark,1";
data = d3.csvParse(data_csv);
d3.queue().await(ready, data);
var forceX = d3.forceX(function(d) {
return width / 2
}).strength(0.05)
var forceY = d3.forceY(function(d) {
return height / 2
}).strength(0.05)
var simulation = d3.forceSimulation()
.force("xTowardsTheCenter", forceX)
.force("yTowardsTheCenter", forceY)
.force("antiColliding", d3.forceCollide(function(d) {
return radiusScale(d.Count) + 1;
}))
.force("charge", d3.forceManyBody().strength(-15))
// this function sets up a variation scale so that the circles can have different sizes without
// occupying all of the screen
var radiusScale = d3.scaleSqrt().domain([1, 40]).range([30, 130]);
function ready(error, datapoints) {
var circles = svg.selectAll(".Country")
.data(datapoints)
.enter().append("circle")
.attr("id", function(d) {
return d.Country;
})
.attr("class", "country")
.attr("r", function(d) {
return radiusScale(d.Count);
})
.attr("text", function(d) {
return d.Count;
})
.style("stroke", "black")
.style("fill", "blue")
simulation.nodes(datapoints)
.on('tick', ticked)
function ticked() {
circles
.attr("cx", function(d) {
return d.x
})
.attr("cy", function(d) {
return d.y
})
}
})();
<!DOCTYPE html>
<html>
<title>Neets visitors </title>
<b>Neets visitors</b>
<body>
<div id="chart"></div>
<script src="https://d3js.org/d3.v4.js"></script>
<script src="bubble.js"></script>
</body>
</html>
Expected result
Hopefully this is the right place for this. This is my first time working in d3 and I'm tracking some paths across latitudes (.bars) on a mercator projection map of the world.
While I'm nowhere near where I want to be with this map (trying to animate change over time, and then include tooltips to the .bars): I'm stuck with the zoom feature. I've gotten it to change the scale of the projection when zooming, but it has no effect on my rectangle shapes (.bars). Could someone take a look and let me know what's going on? Why are the rectangles not scaling on zoom? Am I not correctly accessing them in my zoom function?
var width = 960,
height = 550,
scale0 = (width - 1) / 2 / Math.PI;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
// .append("g");
var g = svg.append("g");
var bars = svg.append("rect");
var zoom = d3.behavior.zoom()
.translate([width / 2, height / 2])
.scale(scale0)
.scaleExtent([scale0, 8 * scale0])
.on("zoom", zoomed);
var color = d3.scale.quantize() // Takes data value inputs and will return colors
.range(['rgb(215,48,39)','rgb(244,109,67)','rgb(253,174,97)','rgb(254,224,144)','rgb(255,255,191)','rgb(224,243,248)','rgb(171,217,233)','rgb(116,173,209)','rgb(69,117,180)']);
var projection = d3.geo.mercator()
.scale((width + 1) / 2 / Math.PI)
.translate([width / 2, height / 2])
.precision(.1);
var path = d3.geo.path()
.projection(projection);
svg
.call(zoom)
.call(zoom.event);
d3.json("world-110m.json", function(error, world) {
if (error) throw error;
g.insert("path", ".graticule")
.datum(topojson.feature(world, world.objects.land))
.attr("class", "land")
.attr("d", path);
g.insert("path", ".graticule")
.datum(topojson.mesh(world, world.objects.countries, function(a, b) { return a !== b; }))
.attr("class", "boundary")
.attr("d", path);
d3.csv("data/apr_dove.csv", function(data) {
// Returns value based on min/max of data set to Colorbrewer colors
color.domain([
d3.max(data, function(d) { return d.average_revisit; }),
d3.min(data, function(d) { return d.average_revisit; })
]);
// Defining the rectangle's attributes by monthly data of satellites (latitude and average revisit_rate)
g.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("width", width)
.attr("class", "bars")
.attr("height", function(d) {
// console.log(projection.translate([0, 0.5])[1])
return projection([0, d.latitude - 0.5])[1] - projection([0, d.latitude])[1];
})
.attr("opacity", .6)
.style("fill", function(d) {
//Get data value
var value = d.average_revisit;
if (value) {
//If value exists…
return color(value);
} else {
//If value is undefined…
return "#ccc";
}
})
//Define position of each rectangle by it's latitude from the data
.attr("transform", function(d) {
return "translate(" + projection([-180, d.latitude]) + ")"
})
.attr("d", path);
});
});
function zoomed() {
projection
.translate(zoom.translate())
.scale(zoom.scale());
svg.selectAll("*")
.attr("d", path);
}
d3.select(self.frameElement).style("height", height + "px");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script src="https://gist.github.com/sadbumblebee/64581d15b89b361a17a85e8f37172673.js"></script>
<script src="https://gist.github.com/sadbumblebee/359fc826e4e55c8c3dcb3d279783e910.js"></script>
One idea to get the appropriate width would be to basis it on the size of your land path:
var land;
d3.json("world-110m.json", function(error, world) {
if (error) throw error;
land = g.insert("path", ".graticule")
.datum(topojson.feature(world, world.objects.land))
.attr("class", "land")
.attr("d", path);
....
function getLandWidth() {
return land ? land.node().getBBox().width : 0;
}
...
function zoomed() {
projection
.translate(zoom.translate())
.scale(zoom.scale());
svg.selectAll("path")
.attr("d", path);
g.selectAll(".bars")
.attr("height", function(d) {
return projection([0, d.latitude - 0.5])[1] - projection([0, d.latitude])[1];
})
.attr("transform", function(d) {
return "translate(" + projection([-180, d.latitude]) + ")"
})
.attr("width", getLandWidth());
}
Full code here.
I have a scatter plot. Now if I click on one of the points, how can I generate a line passing through that point?
I am stuck at two places:
With the following code, why is my line now showing?
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="d3.min.js"></script>
<script>
var width = 500, height = 500;
var randomX=[], randomY=[];
for (var i=0; i<=50; i++) {
randomX[i] = Math.random()*400;
randomY[i] = Math.random()*400;}
var data = randomX.concat(randomY);
var x = d3.scale.linear()
.domain([0, d3.max(randomX)])
.range([0, width]);
var y = d3.scale.linear()
.domain([0, d3.max(randomY)])
.range([height, 0]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g");
svg.selectAll("scatter-dots")
.data(randomY)
.enter().append("svg:circle")
.attr("cy", function(d) {return y(d); } )
.attr("cx", function(d,i) {return x(randomX[i]); } )
.style("fill", "brown")
.attr("r", 5)
.on("click", function(d,i) {
d3.select(this)
.append("svg:line")
.attr("x1", 300).attr("y1", 300)
.attr("x2", 50).attr("y2", 50)
.style("stroke", "steelblue")
.style("stroke-width", 3);
});
</script>
Where is the coordinates of my clicked point stored? I tried this.cx and this.cy, but none of them gave me the actual coordinates.
First, you need to append the line element to the top-level SVG or a g element, not a circle element, otherwise it won't be shown. So in your click handler, you would need to do this:
.on("click", function(d,i) {
svg.append("svg:line")
.attr("x1", 300).attr("y1", 300)
.attr("x2", 50).attr("y2", 50)
.style("stroke", "steelblue")
.style("stroke-width", 3);
});
You can get the coordinates of the click either through d3.event or the coordinates of the circle itself, i.e.
.on("click", function(d,i) {
var x = x(randomX[i]),
y = y(d);
});
or even
.on("click", function(d,i) {
var x = d3.select(this).attr("cx"),
y = d3.select(this).attr("cy");
});
I have plotted points on a TopoJSON map using D3, with the following code:
<!DOCTYPE html>
<meta charset="utf-8">
<head>
<script src="js/d3.js"></script>
<script src="http://d3js.org/topojson.v0.min.js"></script>
<style>
path {
stroke: white;
stroke-width: 0.25px;
fill: grey;
}
</style>
</head>
<body>
<script>
var width = 960,
height = 500;
var projection = d3.geo.mercator()
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var path = d3.geo.path()
.projection(projection);
var g = svg.append("g");
// load and display the World
d3.json("data/world-110m2.json", function(error, topology) {
// load and display the cities
d3.json("data/commodities3.json", function(error, data) {
g.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("cx", function(d) {
return projection([d.location_lon, d.location_lat])[0];})
.attr("cy", function(d) {
return projection([d.location_lon, d.location_lat])[1];})
.attr("r", 4)
.style("fill", "green");
});
//plot the path
g.selectAll("path")
.data(topojson.object(topology, topology.objects.countries)
.geometries)
.enter()
.append("path")
.attr("d", path)
});
// zoom and pan
var zoom = d3.behavior.zoom()
.on("zoom",function() {
g.attr("transform","translate("+
d3.event.translate.join(",")+")scale("+d3.event.scale+")");
g.selectAll("circle")
.attr("d", path.projection(projection));
g.selectAll("path")
.attr("d", path.projection(projection));
});
svg.call(zoom)
</script>
</body>
</html>
I am now looking to size these points based on the value of d.commodity_text. Therefore if the commodity_text were to be equal to "Iron" for instance, the circle would be bigger? Thanks.
You simply need to replace the static value for the radius with a function:
.attr("r", function(d) {
if(d.commodity_text == "Iron") {
return 6;
} else {
return 4;
}
});