Leaflet control layer: selector - javascript

I am displaying two Geojson files on leaflet. The two files are displaying fine on my map but I want to be able to have the selector working on both layers.
As you can see here, my selector only display on province : http://bl.ocks.org/renauld94/8493ca671ce8de63bfab9fafd3f3f574/363f40907203cc431de22e16987669b7bae13fe8
var ward = [];
var wardOverlay = L.d3SvgOverlay(function(sel, proj) {
var upd = sel.selectAll('path').data(ward);
upd.enter()
.append('path')
.attr('d', proj.pathFromGeojson)
.attr('stroke', 'red')
.attr('fill-opacity', '0.2');
upd.attr('stroke-width', 1 / proj.scale);
});
var province = [];
var provinceOverlay = L.d3SvgOverlay(function(sel, proj) {
var upd = sel.selectAll('path').data(province);
upd.enter()
.append('path')
.attr('d', proj.pathFromGeojson)
.attr('stroke', 'black')
.attr('fill-opacity', '0.1');
upd.attr('stroke-width', 1 / proj.scale);
});
L.control.layers({"Geo Tiles": tiles}, {"province": provinceOverlay}, {"ward": wardOverlay}).addTo(map);
d3.json("ward.geo.json", function(data) { ward = data.features; wardOverlay.addTo(map) });
d3.json("province.geo.json", function(data) { province = data.features; provinceOverlay.addTo(map) });
</script>
</body>
</html>
How I can have a selector for both layers?

Have a look at the creation of Control.Layers :
L.control.layers(<Object> baselayers?, <Object> overlays?, <Control.Layers options> options?)
Creates an attribution control with the given layers. Base layers will be switched with radio buttons, while overlays will be switched with checkboxes. [...]
It means that the second object contains the entries for each switchable layer. Try:
L.control.layers({"Geo Tiles": tiles}, {
"province": provinceOverlay,
"ward": wardOverlay
}).addTo(map);

Related

Not able to append label on edge of graph in d3.js

I am new to js & d3. I am trying to plot a graph with d3.js forced layout.
My purpose is to append labels ("XXX" & "OOO") on either sides of edges but it's not working.
Here the code snippet:
link.append('svg:text')
.attr('class', 'aEnd')
.text(function(d) { return "XXX"; });
link.append('svg:text')
.attr('class', 'zEnd')
.text(function(d) { return "OOO"; });
Here is the complete code I am working on.
JSFIDDLE : d3_graph_labelled_edge.js
Nesting a text element inside a line element is not allowed, as summed up here.
What you can do is wrapping the line elements inside g containers and append the link labels to them:
var glink = vis.selectAll("line.link")
.data(data.links)
.enter().append("svg:g");
var link = glink.append("svg:line")
[...]
glink.append('svg:text')
.attr('class', 'aEnd')
.text(function(d) { return "XXX"; });
Updated fiddle.

D3.js Problems Filtering topojson data

I've setup an example to demonstrate the issue's I've encountered:
To be brief, I'm using d3 to render a map of the united states. I'm appending relevant data attributes to handle click events and identify which state was clicked.
On click events the following is preformed:
I'm grabbing the US County topojson file (which contains ALL US
Counties).
Removing irrelevant counties from the data and rendering them on the
map of the state that was clicked.
I can't seem to figure out what is going on behind the scenes that is causing some of the counties to be drawn while others are ignored.
When I log the data that is returned from the filtered list, I'm displaying the accurate number of counties, but they are only partially drawn on the map. Some states don't return any. Pennsylvania and Texas partially work.
I've checked the data and the comparison operations, but I'm thinking this may have to do with arcs properties being mismatched.
If I utilize the Counties JSON file to render the entire map of the united states they are all present.
If anyone can help shed some light on what might be happening that would be great.
svg {
fill:#cccccc;
height:100%;
width:100%;
}
.subunit{
outline:#000000;
stroke:#FFFFFF;
stroke-width: 1px;
}
.subunit:hover{
fill:#ffffff;
stroke:#FFFFFF;
stroke-width="10";
}
<body>
<script src="http://www.cleanandgreenfuels.org/jquery-1.11.3.min.js"></script>
<script src="http://www.cleanandgreenfuels.org/d3.v3.min.js"></script>
<script src="http://www.cleanandgreenfuels.org/topojson.v1.min.js"></script>
<script>
var width = window.innerWidth;
height = window.innerHeight;
var projection = d3.geo.albers()
.scale(1500)
.translate([width / 2, height / 2]);
//d3.geo.transverseMercator()
//.rotate([77 + 45 / 60, -39 - 20 / 60]);
//.rotate([77+45/60,-40-10/60])
//.scale(500)
//.translate([width / 2, height / 2]);
var path = d3.geo.path()
.projection(projection);
var svg = d3.select("body").append("svg")
.attr("width", width+"px")
.attr("height", height+"px");
d3.json("http://www.cleanandgreenfuels.org/usstates2.json.php", function(error, us, init) {
//svg.append("path")
// .datum(topojson.feature(us, us.objects.counties))
//.attr("d", path);
function init(){
$( document ).ready(function() {
$('.subunit').on('click',function(){
var stateid = $(this).attr("data-stateid");
function clearStates(stateid){
d3.json("http://www.cleanandgreenfuels.org/uscounties2.json.php", function(error, us) {
console.log(us);
console.log("DATA CLICKED ID "+stateid);
test = jQuery.grep(us.objects.counties.geometries, function(n){
return (n.properties.stateid == stateid);
});
us.objects.counties.geometries = test;
console.log(test.length);
console.log(us.objects.counties.geometries.length);
var test = topojson.feature(us, us.objects.counties).features;
console.log(test);
console.log(test.length);
svg.selectAll(".subunit")
.data(test)
.enter().append("path")
.attr("class", function(d) { return "subunit"; })
.attr("d", path)
.attr("data-countyid", function(r){ return r.id; });
});
}
clearStates(stateid);
});
});
}
svg.selectAll(".subunit")
.data(topojson.feature(us, us.objects.us).features)
.enter().append("path")
.attr("class", function(d) { return "subunit"; })
.attr("d", path)
.attr("data-stateid", function(r){ return r.id; });
init();
});
</script>
</body>
It appears as if I was attempting to utilize some outdated features, using topojson.mesh and .datum() to add the new data has resolved this issue, but has introduced a new error.
Now it appears as if the polygons that are rendered must be in sequence to be drawn properly this way.
I think the data going in should be cleaned up to optimize the way d3 is designed to function, but I'd still like to know more about how it is rendering this information that is obtained from the dataset.
function clearStates(stateid){
d3.json("http://www.cleanandgreenfuels.org/uscounties2.json.php", function(error, us) {
console.log(us);
console.log("DATA CLICKED ID "+stateid);
test = jQuery.grep(us.objects.counties.geometries, function(n){
return (n.properties.stateid == stateid);
});
us.objects.counties.geometries = test;
console.log(test.length);
console.log(us.objects.counties.geometries.length);
**var test = topojson.mesh(us, us.objects.counties);**
console.log(test);
console.log(test.length);
**svg.append("path")
.datum(test)
.attr("class", function(d) { return "subunit"; })
.attr("d", path)
.attr("data-countyid", function(r){ return r.id; });**
});
}

How to update/overwrite map and legend content using d3

I've put together a choropleth map using d3, helped by examples written by Mike Bostock. I'm new to d3 (and HTML, JavaScript, CSS to be honest).
I've got as far as creating the map and the legend, and being able to switch between different data sets. The map and source code can be viewed here on bl.ocks.org
Glasgow Index of Deprivation 2012
The problem I'm having now is working out how to replace the map and legend content when switching between the different datasets. As you can see at the moment, when a different dataset is selected, the content is simply added on top of the existing content.
I've tried following the advice given by Stephen Spann in this answer, and the code he provided in a working fiddle. But to no avail.
As I understand, I should add the g append to the svg variable in the beginning like so...
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g");
Then select it when updating like so...
var appending = svg.selectAll("g")
.attr("class", "S12000046_geo")
.data(topojson.feature(glasgowdep, glasgowdep.objects.S12000046_geo).features);
// add new elements
appending.enter().append("path");
// update existing elements
appending.style("fill",
function (d) {
return color(choro[d.id]);
})
.style("stroke", "#cfcfcf")
.attr("d", path)
// rollover functionality to display tool tips
.on("mouseover", function (d) {
tip.show(d)
d3.select(this)
.transition().duration(200)
.style("fill", "red");
})
.on("mouseout", function () {
tip.hide()
d3.select(this)
.transition().duration(200)
.style("fill",
function (d) {
return color(choro[d.id]);
});
})
// build the map legend
var legend = d3.select('#legend')
.append('ul')
.attr('class', 'list-inline');
var keys = legend.selectAll('li.key')
.data(color.range());
var legend_items = ["Low", "", "", "", "", "", "", "", "High"];
keys.enter().append('li')
.attr('class', 'key')
.style('border-top-color', String)
.text(function (d, i) {
return legend_items[i];
});
// remove old elements
appending.exit().remove();
A solution could be the following: at your code in http://bl.ocks.org/niallmackenzie/8a763afd14e195154e63 try adding the following line just before you build the map legend (line 220 in index.html):
d3.select('#legend').selectAll('ul').remove();
Every time you update your data, you empty first the #legend.
Thanks to the advice from Lars and the solution proposed by nipro, the following works. By adding the following code just above the section that builds the legend, the legend is emptied first before it gets updated:
d3.select('#legend')
.selectAll('ul')
.remove();
// build the map legend
var legend = d3.select('#legend')
...
And by doing the same for the main map, we can first empty the map before updating it:
d3.select("g")
.selectAll("path")
.remove();
// build the choropleth map
var appending = svg.selectAll("g")
...
The full working updated code can been seen on bl.ocks.org here.

Legend toggling for d3.js pie chart

I'm working on a d3.js application.
In this example I am trying to toggle the slices when the user clicks on the legend components. It will initially take the complete data as its source, but if there is a previous manipulated data source will use that as a base. I've tried to hook into the toggling functionality as the legend is manipulated. I would prefer to separate the functionality - but wasn't sure how else to know if the slice is to be active or not.
Its not working as expected though, especially when trying to handle multiple active/non active slices.
http://jsfiddle.net/Qh9X5/3282/
onLegendClick: function(dt, i){
//_toggle rectangle in legend
var completeData = jQuery.extend(true, [], methods.currentDataSet);
newDataSet = completeData;
if(methods.manipulatedData){
newDataSet = methods.manipulatedData;
}
d3.selectAll('rect')
.data([dt], function(d) {
return d.data.label;
})
.style("fill-opacity", function(d, j) {
var isActive = Math.abs(1-d3.select(this).style("fill-opacity"));
if(isActive){
newDataSet[j].total = completeData[j].total;
}else{
newDataSet[j].total = 0;
}
return isActive;
});
//animate slices
methods.animateSlices(newDataSet);
//stash manipulated data
methods.manipulatedData = newDataSet;
}
Here is the onLegendClick function.
I am toggling the opacity of the inner fill on the rectangle when the user clicks.
I've tried to modify the value of the data accordingly - although it is not handling multiple toggles.
http://jsfiddle.net/Qh9X5/3324/
Ideally if the user tries to deactivate all slices, I would want it to reset the chart and restore all the slices in the process. Would be keen to streamline the code and maybe separate the logic and presentation layer for the styling of the rectangles in the legend.
line 234
onLegendClick: function(dt, i){
//_toggle rectangle in legend
var completeData = jQuery.extend(true, [], methods.currentDataSet);
newDataSet = completeData;
if(methods.manipulatedData){
newDataSet = methods.manipulatedData;
}
d3.selectAll('rect')
.data([dt], function(d) {
return d.data.label;
})
.style("fill-opacity", function(d, j) {
var isActive = Math.abs(1-d3.select(this).style("fill-opacity"));
if(isActive){
newDataSet[j].total = completeData[j].total;
}else{
newDataSet[j].total = 0;
}
return isActive;
});
//animate slices
methods.animateSlices(newDataSet);
//stash manipulated data
methods.manipulatedData = newDataSet;
}

Display original and updated data of the bar chart

I am trying to modify the following code found on one of the sites to display both the original and updated data in the graph. I want the updated data be in different color and still show the original data and see the change. Can anyone point me the error.
<title>d3 example</title>
<style>
.original{
fill: rgb(7, 130, 180);
}
.updated{
fill: rgb(7,200,200);
}
</style>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script type="text/javascript" src="http://d3js.org/d3.v2.min.js"></script>
<script type="text/javascript">
// Suppose there is currently one div with id "d3TutoGraphContainer" in the DOM
// We append a 600x300 empty SVG container in the div
var chart = d3.select("#d3TutoGraphContainer").append("svg").attr("width", "600").attr("height", "300");
// Create the bar chart which consists of ten SVG rectangles, one for each piece of data
var rects = chart.selectAll('rect').data([1 ,4, 5, 6, 24, 8, 12, 1, 1, 20])
.enter().append('rect')
.attr("stroke", "none")
//.attr("fill", "rgb(7, 130, 180)")
.attr('class','original')
.attr("x", 0)
.attr("y", function(d, i) { return 25 * i; } )
.attr("width", function(d) { return 20 * d; } )
.attr("height", "20");
// Transition on click managed by jQuery
rects.on('click', function() {
// Generate randomly a data set with 10 elements
var newData = [1,2,3,4];
//for (var i=0; i<10; i+=1) { newData.push(Math.floor(24 * Math.random())); }
var newRects = d3.selectAll('rects.original');
newRects.data(newData)
.transition().duration(2000).delay(200)
.attr("width", function(d) { return d * 20; } )
//.attr("fill", newColor);
.attr('class','updated');
});
</script>
I want to know if I can get control of the original data using d3.selectAll('rects.original')
If you select "rects.original" and bind data to it, you create a join with an Update, Exit and Enter selection. Im not sure i fully understand what you are trying to achieve, but if you want new data to be drawn independently of the old rects and data, you have to create a new selection for it:
var newRects = chart.selectAll("rect.new")
.data(newData)
.enter()
(...)
and draw it.
Beware that overlapping in SVG means that underlying Elements will not be displayed anymore.
Im not sure what you mean by "getting control over the original Data", it is still bound to the selection you bound it to. If you want to modify it, you have to modify the data, rebind it, and then apply a transition on the update selection.

Categories

Resources