Changing the symbol in d3's GeoPaths MultiPoints - javascript

When I render latitude/longitude points (my_coords) on my globe with:
svg.append("path")
.datum({type: "MultiPoint", coordinates: my_coords})
.attr("class", "points")
.attr("d", path);
I get circles. I can change the color with CSS, but how can I change the symbol from circles to triangles? I've tried:
svg.append("path")
.datum({type: "MultiPoint", coordinates: my_coords})
.attr("class", "point")
.attr("d", d3.svg.symbol().type("triangle-up"));
But this doesn't work.

You can try this:
//define triangle
var arc = d3.svg.symbol().type('triangle-up');
// put a triangle on every city
svg.selectAll(".tripath")
.data(topojson.feature(uk, uk.objects.places).features)
.enter().append("path")
.attr('d',arc)
.attr("transform", function(d) {
return "translate(" + projection(d.geometry.coordinates) + ")"; });
Here the working code: http://jsfiddle.net/6d3ansfn/
Here the SVG Shape reference from Mike's github: https://github.com/mbostock/d3/wiki/SVG-Shapes#symbol
Example code is from Mike: http://bost.ocks.org/mike/map/
If you has a .tsv file width coordinates, use this:
svg.selectAll(".tripath")
.data( my_coords)
.enter().append("path")
.attr('d',arc)
.attr("transform", function(d) {
return "translate(" + projection(d) + ")"; });
remember update proyection on drag and/or zoom.

Related

Draw markers/add pins on d3 map in react native

Something like this:
Normally, on the browser, we append the points to the svg like so:
svg.append("g")
.selectAll("g")
.data(data.features)
.enter()
.append("g")
.attr("transform", function(d) { return "translate(" + projection(d.geometry.coordinates) + ")"; })
.append("circle")
.attr("r", 1)
what would be the react native equivalent of the above snippet?

d3.js - Pie chart with interactive legend hover problems

I made d3.js pie chart and related legend with population data popu. When I hover over pie segments I achieved to enlarge related legend square parts and the pie segment itself (larger outerRadius). Now I am trying to do contrary. When I hover over square of legend I want to enlarge square itself and related pie segment as well. Something like this example here https://www.amcharts.com/demos/pie-chart-with-legend/. I will write down just part of the code related to pie chart problem that I have.
var pie = d3.pie()
.value(function(d) {return d.pop})(popu);
var seg = d3.arc()
.innerRadius(100)
.outerRadius(150)
.padAngle(.1)
.padRadius(45);
var segover = d3.arc()
.innerRadius(100)
.outerRadius(170)
.padAngle(.1)
.padRadius(45);
So this part is working great.
svg.append("g")
.attr("class", "pieChart")
.attr("transform", "translate(1250,570)")
.selectAll("path")
.data(pie)
.append("path")
.attr("class", "pie")
.attr("id", function(d){return d.data.id})
.attr("d", seg)
.on("mouseenter", function(d){
d3.select(this)
.transition(10)
.duration(100)
.attr("d", segover)
})
Then I tried to change pie chart segment when hovering on legend related segments.
var pieEl = svg.selectAll(".pie");
var piePath = pieEl.nodes();
svg.append("g")
.attr("class", "legend")
.attr("transform", "translate(-50,280)")
.selectAll(".mySquers")
.data(pie)
.enter()
.append("rect")
.attr("class", "rec")
.attr("x", 100)
.attr("y", function(d,i){ return 100 + i*25})
.attr("width", "15")
.attr("height", "15")
.attr("id", function(d,i){ return (popu[d,i].id)})
.style("fill",function(d,i){
if (this.id == piePath[i].id){
return piePath[i].getAttribute("fill")
}
})
.on("mouseenter", function(d){
for (var i=0; i<piePath.length; i++){
if (piePath[i].id == d.data.id){
piePath[i].setAttribute("d", segover);
}}
})
When I tray to setAttribute("d", segover) in DOM instead of d attribute written as string as usually (d="M144.58.....") I have a function (d="function(pie){ _e);}" and on hover pie segment dissapear. But for example if I set attribute fill to red on hover it change and segment is painted. So the notation of code is good. Is there some behavior of d path generated with d3.arc() that I am missing? Any suggestion is welcome.
I think you should be passing your data as an argument in your function. Normally, it is taken as default argument when you return the function directly.
piePath[i].setAttribute("d", segover(*data associated with segment*));
svg.append("g")
.attr("class", "pieChart")
.attr("transform", "translate(1250,570)")
.selectAll("path")...
.attr("d", seg) // this is same as : attr("d", seg(d))
.on("mouseenter", function(d){
d3.select(this)
.transition(10)
.duration(100)
.attr("d", segover) // same here
})

Tooltip for Line Chart with Clip Path in D3

I have put together a D3 line chart and added threshold encoding using clip path / clipping. The only problem I am facing is I am not able to add tooltips to this chart. I want a tooltip when I hover anywhere in the chart and the corresponding y axis value on the chart shows up in the tooltip.
I have added threshold encoding using this example by Mike Bostock.
var line = d3.svg.line()
.interpolate("basis")
.x(function(d) { return _config.xScale(d.vtc); })
.y(function(d) { return _config.yScale(d.values); });
svg.append("clipPath")
.attr("id", "clip-above")
.append("rect")
.attr("width", _config.width)
.attr("height", _config.yScale(55));
svg.append("clipPath")
.attr("id", "clip-below")
.append("rect")
.attr("y", _config.yScale(55))
.attr("width", _config.width)
.attr("height", _config.height - _config.yScale(55));
svg.selectAll(".line")
.data(["above", "below"])
.enter().append("path")
.attr("class", function(d) { return "line " + d; })
.attr("clip-path", function(d) { return "url(#clip-" + d + ")"; })
.datum(data)
.attr("d", line);
I didn't know how to go about adding a tooltip for this particular chart as there is clip rectangle over the path and the path is broken down into above and below segment to give the colour effects.
Do we have a unified way to add a tooltip to normal path and this one? If yes I would like to know some sources/links I can look at.
Something like this, but not that complicated (without any indicator on the line, just the tooltip)
My CODEPEN LINK
You can add mouseOver handler for the line and translate back the mouse y position to yAxis value using the .invert function of d3 linear scale. Now, you can dynamically add a tooltip text element and set the position, value to it
Here is the updated Codepen link
NOTE: You still need to increase the capture area of the line. This can be done by adding a transparent stroke to the line.
svg.selectAll(".line")
.data(["above", "below"])
.enter().append("path")
.attr("class", function(d) { return "line " + d; })
.attr("clip-path", function(d) { return "url(#clip-" + d + ")"; })
.datum(data)
.attr("d", line)
.on("mouseover", function() {
var mousePos = d3.mouse(this);
var yAxisValue = _config.yScale.invert(mousePos[1]);
svg.selectAll(".tooltip").data([mousePos])
.enter().append("text")
.classed("tooltip", true)
.attr("x", function(d) { return d[0]})
.attr("y", function(d) { return d[1]})
.text(yAxisValue);
})
.on("mouseout", function() {
svg.selectAll(".tooltip").data([]).exit().remove();
});

Placing Lat/Long on a azimuthalEqualArea map with D3

I'm trying to plot some lat/long points onto a map, but I can't get them to appear in the correct place.
The dots should be in San Francisco. I have a JSfiddle of the code.
var width = 400,
height = 400;
var projection = d3.geo.azimuthalEqualArea()
.clipAngle(180 - 1e-3)
.scale(100)
.translate([width / 2, height / 2])
.precision(.1);
var path = d3.geo.path()
.projection(projection);
var graticule = d3.geo.graticule();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
svg.append("defs").append("path")
.datum({type: "Sphere"})
.attr("id", "sphere")
.attr("d", path);
svg.append("use")
.attr("class", "stroke")
.attr("xlink:href", "#sphere");
svg.append("use")
.attr("class", "fill")
.attr("xlink:href", "#sphere");
svg.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);
d3.json("http://bl.ocks.org/mbostock/raw/4090846/world-50m.json", function(error, world) {
svg.insert("path", ".graticule")
.datum(topojson.feature(world, world.objects.land))
.attr("class", "land")
.attr("d", path);
svg.insert("path", ".graticule")
.datum(topojson.mesh(world, world.objects.countries, function(a, b) { return a !== b; }))
.attr("class", "boundary")
.attr("d", path);
});
var latlong = {"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Point","coordinates":[37.3838312,-122.0423922]},"properties":{"timestampMs":1415894666875}},{"type":"Feature","geometry":{"type":"Point","coordinates":[37.3838474,-122.0423972]},"properties":{"timestampMs":1415894601718}},{"type":"Feature","geometry":{"type":"Point","coordinates":[37.3838474,-122.0423972]},"properties":{"timestampMs":1415894536288}},{"type":"Feature","geometry":{"type":"Point","coordinates":[37.3838411,-122.0424015]},"properties":{"timestampMs":1415894471356}},{"type":"Feature","geometry":{"type":"Point","coordinates":[37.383878,-122.0423925]},"properties":{"timestampMs":1415894406257}},{"type":"Feature","geometry":{"type":"Point","coordinates":[37.3838317,-122.0423856]},"properties":{"timestampMs":1415894326769}},{"type":"Feature","geometry":{"type":"Point","coordinates":[37.3838287,-122.0423933]},"properties":{"timestampMs":1415894261810}},{"type":"Feature","geometry":{"type":"Point","coordinates":[37.383829,-122.0423847]},"properties":{"timestampMs":1415894196224}},{"type":"Feature","geometry":{"type":"Point","coordinates":[37.3838765,-122.0424141]},"properties":{"timestampMs":1415894131768}},{"type":"Feature","geometry":{"type":"Point","coordinates":[37.3838177,-122.0423668]},"properties":{"timestampMs":1415894066809}}]};
// THESE ARE THE POINTS THAT ARE NOT BEING PLACED CORRECTLY
svg.selectAll("circle")
.data(latlong.features).enter()
.append("circle")
.attr("cx", function (d) { return projection(d.geometry.coordinates)[1]; })
.attr("cy", function (d) { return projection(d.geometry.coordinates)[0]; })
.attr("r", "2px")
.attr("fill", "red");
You've specified the latitude and longitude the wrong way round for d3.geo and you're also taking the output the wrong way round. It is counter to the way that they are displayed by convention (N/S then E/W) but it is more consistent with a drawing convention of across then up/down.
From path.projection in the D3 API Geo reference:
A projection function takes a two-element array of numbers
representing the coordinates of a location, [longitude, latitude], and
returns a similar two-element array of numbers representing the
projected pixel position [x, y].
To fix this, I've reversed the coordinates of your FeatureCollection:
var latlong = {"type":"FeatureCollection","features":[
{"type":"Feature","geometry":{"type":"Point","coordinates":[-122.0423922,37.3838312]},"properties":{"timestampMs":1415894666875}},
etc...
and reversed the coordinates of your plot.
.attr("cx", function (d) { return projection(d.geometry.coordinates)[0]; })
.attr("cy", function (d) { return projection(d.geometry.coordinates)[1]; })
Everything else was fine. Amended JSFiddle here. So often it's the little things!

Points on a map with D3.js

I have a geoJSON of zip code centroid points that I am plotting on a D3.js map. I can get them to display but I am unable to adjust the size of the points. I was assuming that .attr("r", 1) would be doing that but I must be missing something.
d3.json("ZipPoints.json", function (zipPoints) {
svg.selectAll("g")
.data(zipPoints.features)
.enter()
.append("path")
.attr("d", path)
.attr("r", 1)
.style("fill", "red");
});
EDIT:
d3.json("ZipPoints.json", function (zipPoints) {
points.selectAll("circle")
.data(zipPoints.features)
.enter()
.append("circle")
.attr("r", 1.5)
.attr("transform", function(d, i) {
return "translate(" + projection(zipPoints.features[i].geometry.coordinates) + ")";
})
.style("fill", "red")
.classed("point", true);
});
You could try the following.
var pins = svg.append("g");
d3.json("ZipPoints.json", function(zipPoints) {
pins.selectAll("circle")
.data(zipPoints.features)
.enter()
.append("circle")
.attr("r", 5)
.style("fill", "red")
.classed("pin", true);
});
You may need transformation on these points to render them correctly (I guess).
In that case you could use the following bit of code. (The transformation function I used was required to plot data that had lat, long information on a map built using a specific projection).
.attr("transform", function(d) {
/*whatever transformation that needs to be done*/
return "translate(" + projection([ d.lon, d.lat ]) + ")";
})

Categories