How to display country names on globe d3.js - javascript

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

How to use an exit selection on text with tspan

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

Clustering bubbles in d3.js

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

D3 Zooming and Pan not working for entire map, just the projection

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.

D3: adding a line when clicking on a circle in scatter plot

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");
});

Dynamically size points on D3 TopoJSON map

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;
}
});

Categories