D3 map transition fail - javascript

I using D3 to transition one India map (India.json) to its cartogram (India_simple.json).Both of these are in topojson format. My final goals is to achieve a transition like the one shown in this demonstration by Max Galka - http://metrocosm.com/how-to-make-cartograms-with-animation/ . In particular Im referring to the US map transition demo - view-source:http://metrocosm.com/shape-transition.html
But unfortunately the transition function is not working. The code is as follows -
<!DOCTYPE html>
<meta charset="utf-8">
<!-- Set a style for our worldshape-data -->
<style>
path {
stroke: red;
stroke-width: 0.5px;
fill: grey;
}
</style>
<body>
<!-- implementation of the hosted D3- and TopoJson js-libraries -->
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/topojson.v0.min.js"></script>
<!-- map creation -->
<script>
// canvas resolution
var width = 1000,
height = 600;
// projection-settings for mercator
var projection = d3.geo.mercator()
// where to center the map in degrees
.center([100, 30 ])
// zoomlevel
.scale(700)
// map-rotation
.rotate([0,0]);
// defines "svg" as data type and "make canvas" command
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
// defines "path" as return of geographic features
var path = d3.geo.path()
.projection(projection);
// group the svg layers
var g = svg.append("g");
// load data and display the map on the canvas with country geometries
d3.json("india.json", function(error, topology) {
d3.json("india_simple.json", function(error2, topology2) {
var map = g.selectAll("path")
.data(topojson.object(topology, topology.objects.collection)
.geometries)
.enter()
.append("path")
.attr("d", path);
setInterval(function(){
map
.data(topojson.object(topology2, topology2.objects.collection))
.transition()
.duration(3000)
.attr("d", path);
setTimeout(function(){
map
.data(topojson.object(topology, topology.objects.collection))
.transition()
.duration(3000)
.attr("d", path);
}, 3000)
}, 6000);
});
});
// zoom and pan functionality
/* var zoom = d3.behavior.zoom()
.on("zoom",function() {
g.attr("transform","translate("+
d3.event.translate.join(",")+")scale("+d3.event.scale+")");
g.selectAll("path")
.attr("d", path.projection(projection));
});
svg.call(zoom)*/
</script>
</body>
</html>
The observed output is only the india_simple.json map.
The setInterval and setTimeout functions are not working. I cant see any error in the console either.
The india.json and india_simple.json files are placed in dropbox - https://www.dropbox.com/sh/wrxyngyq4jdie9c/AACG2-dTzC79rRvlxlOC5poBa?dl=0
Please help me obtain the required transition

Related

How to plot longitude latitude coordinates for geoAlbersUsa() projection?

I am trying to create a simple bubblemap that plots longitude-latitude pairs by a number of admissions for a county using US DHS data. My CSV file has the name of county, longitude, latitude, type of Admission, class of admission, number of admissions, and country of origin. I've created some checkboxes that will allow the user to see a bubblemap of the different classes of admissions that were admitted to the United States.
I've learned that the d3geoAlbersUsa projection projects the map of the USA at [0,0] which is off the coast of Africa. From the photo below (see Imgur link), you can see that my points seem to plot at the correct coordinates. However, the background map is not visible. When I use d3.geoMercator() and center the projection on [0,0] I see the map. In both cases, I don't know how to make the bubbles appear on the map.
I'm new to d3 so I'm not sure how to go about this. How do I create a bubblemap using long lat coordinates with the d3geoAlbersUsa projection? Thanks for the help.
Here is my index.html:
var width = 750
var height = 750
// The svg
var svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width)
.attr("height", height)
// Map and projection
// Map and projection
// var projection = d3.geoMercator()
// //.center([-100, 30]) // GPS of location to zoom on
// .center([0, 0])
// .scale(200) // This is like the zoom
// .translate([ width/2, height/2 ])
var projection = d3.geoAlbersUsa()
//.center([0,0])
.scale([1000]) // This is like the zoom
.translate([width / 2, height / 2])
var data = d3.csv("sheet.csv", function(data) {
var markers = data.filter(function(d) {
if (
(d["MajorClassAdmission"] == "EmploymentPreference1st" ||
d["MajorClassAdmission"] == "EmploymentPreference2nd" ||
d["MajorClassAdmission"] == "EmploymentPreference3rd") &&
d["CountryofBirth"] == "Bangladesh" && d["Admissions"] != "D" && d["lon"] != "NA" && d["lat"] != "NA") {
return d;
}
})
//console.log(markers)
// Load external data and boot
//d3.json("projectedgeography.json", function(data){
d3.json("projectedgeography.geojson", function(data) {
//d3.json("https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/world.geojson", function(data){
// Filter data
//data.features = data.features.filter( function(d){return d.properties.name=="USA"} )
//data.features = data.features.filter( function(d){return d.properties.name=="USA"} )
// Create a color scale
var color = d3.scaleOrdinal()
.domain(["EmploymentPreference1st", "EmploymentPreference2nd", "EmploymentPreference3rd"])
.range(["#402D54", "#D18975", "#8FD175"])
// Add a scale for bubble size
var size = d3.scaleLinear()
.domain([1, 100]) // What's in the data
.range([4, 50]) // Size in pixel
//var path = d3.geo.path().projection(projection)
//Draw the map
svg.append("g")
.selectAll("path")
.data(data.features)
.enter()
.append("path")
.style("stroke", "#black")
.style("opacity", .3)
//create a tooltip (hover information)
var Tooltip = d3.select("#my_dataviz")
.append("div")
.attr("class", "tooltip")
.style("opacity", 1)
.style("background-color", "white")
.style("border", "solid")
.style("border-width", "2px")
.style("border-radius", "5px")
.style("padding", "5px")
// Three function that change the tooltip when user hover / move / leave a cell
var mouseover = function(d) {
Tooltip.style("opacity", 1)
}
var mousemove = function(d) {
Tooltip
.html(d.CountyState + "<br>" + "long: " + d.lon + "<br>" + "lat: " + d.lat + "<br>" + "Admissions: " + d.Admissions)
.style("left", (d3.mouse(this)[0] + 10) + "px")
.style("top", (d3.mouse(this)[1]) + "px")
}
var mouseleave = function(d) {
Tooltip.style("opacity", 0)
}
// Add circles:
svg
.selectAll("myCircles")
.data(markers)
.enter()
.append("circle")
.attr("class", function(d) {
return (d.MajorClassAdmission)
})
.attr("cx", function(d) {
return projection([d.lon, d.lat])[0]
})
.attr("cy", function(d) {
return projection([d.lon, d.lat])[1]
})
.attr("r", function(d) {
return d.Admissions
})
.style("fill", function(d) {
return color(d.MajorClassAdmission)
})
.attr("stroke", function(d) {
return color(d.MajorClassAdmission)
})
.attr("stroke-width", 3)
.attr("fill-opacity", .4)
.on("mouseover", mouseover)
.on("mousemove", mousemove)
.on("mouseleave", mouseleave)
// This function is gonna change the opacity and size of selected and unselected circles
function update() {
// For each check box:
d3.selectAll(".checkbox").each(function(d) {
cb = d3.select(this);
group = cb.property("value")
//console.log(group)
// If the box is check, I show the group
if (cb.property("checked")) {
//console.log("checked")
svg.selectAll("." + group).transition().duration(1000).style("opacity", 1).attr("r", function(d) {
return d.Admissions
})
// Otherwise I hide it
} else {
//console.log("unchecked")
svg.selectAll("." + group).transition().duration(1000).style("opacity", 0).attr("r", 0)
}
})
}
// When a button change, I run the update function
d3.selectAll(".checkbox").on("change", update)
// And I initialize it at the beginning
update()
})
})
<!DOCTYPE html>
<html>
<meta charset="utf-8">
<style>
.circle:hover {
stroke: black;
stroke-width: 4px;
}
.legend circle {
fill: none;
stroke: #ccc;
}
.legend text {
fill: #777;
font: 10px sans-serif;
text-anchor: middle;
}
</style>
<!-- Load d3.js and the geo projection plugin -->
<script src="https://d3js.org/d3.v4.js"></script>
<script src="https://d3js.org/d3-geo-projection.v2.min.js"></script>
<script src="https://d3js.org/queue.v1.min.js"></script>
<!-- <script src="http://d3js.org/d3.geo.projection.v0.min.js"></script> optional, depending on projection -->
<h1>LPR Top 200 Bangladesh</h1>
<!-- Button -->
<div>
<input type="checkbox" class="checkbox" value="EmploymentPreference1st" checked><label>Employment Preference 1</label>
<input type="checkbox" class="checkbox" value="EmploymentPreference2nd" checked><label>Employment Preference 2</label>
<input type="checkbox" class="checkbox" value="EmploymentPreference3rd" checked><label>Employment Preference 3</label>
</div>
<!-- Create an element where the map will take place -->
<!-- <svg id="my_dataviz" width="500" height="500"></svg> -->
<div id="my_dataviz"></div>
Attached are screenshots of my data and how my bubblemap looks like using d3.geoMercator() and d3.geoAlbersUsa()
Here are the screenshots: https://imgur.com/gallery/dRghqAf
Edit: It looks like I was using a bad geojson. I had initially downloaded the shapefile from US Census and used Mapshaper to convert to a geojson file. Using that geojson, I used a projection app to create a geojson with the geoalbersUsa projection. In effect, I was using d3.geoAlbersUsa() on a geojson already converted to that projection. I misunderstood how geoAlbersUsa() works. Using the original geojson from Mapshaper, I get the map I'm looking for: https://imgur.com/gallery/9sj68SM
D3 projections are all pretty similar. If one projection projects a given point correctly, chances are so does every other one. They take coordinates in decimal degrees and spit out coordinates in pixels and not much else. Consequently, your statement: "d3geoAlbersUsa projection projects the map of the USA at [0,0] which is off the coast of Africa." is incorrect. [0,0] in degrees is off the coast of Africa, [0,0] in pixels can be anywhere. D3 is giving you pixels, not degrees.
If you have features/tiles/raster that show Africa where the US is or vice versa, you have conflicting projections or coordinate systems, not a failure of d3.geoAlbersUsa.
Also, if you are mixing pre-projected geometry and unprojected geometry, you're making it too difficult: you'll need to ensure that the projections used to pre-project one geometry matches the projection used to project the second, which unless you use d3 to preproject the geometry offline somewhere will cause you some headaches.
If you have a csv with coordinates in decimal degrees and a geojson with the same, you can assume that the projection (and consequently, any paths) will be rendered consistently, and correctly (where correctly is not necessarily equivalent to as desired...).
Now d3.geoAlbersUsa is a bit of a special projection in that it is a composite projection combining several distinct projections (all Albers, while also shrinking Alaska down several times). It is calibrated to be centered on the US assuming a screen size of 960x600 pixels. You shouldn't need to alter the centering point, it has been set for you. The translate must be modified in your case, however, as this translates the projected coordinates. The default translate expects a container of 960x600 pixels. You want the translate to be equal to width/2,height/2, as you have. Trickier is the scale, the default scale is 1070, which extends the US across 960 pixels. This scale factor is linear, so we can use: 1070/960*width to create a new scale factor (assuming width is the limiting factor).
d3.geoMercator is more plain, but we need to center that projection properly because it isn't by default centered on the US. We can use:
d3.geoMercator()
.center([-100,30]) // as you had
.scale(1000) // or whatever
.translate([width/2,height/2])
We still need to apply the translate, because the default values don't expect a square 750x750 svg.
Note there is also projection.fitSize() which will automatically adjust scale and translate to center a geojson feature like so:
d3.geoMercator()
.fitSize([width,height],geojson) // geojson must be a valid geojson object, not an array of geojson features.
We pass the projection to the path generator and then draw points and geojson relatively straightforwardly:
The following use the commented out line accessing a geojson, if you share projectedgeography.geojson, I may be able to provide you with some information on why your code doesn't work as expected. But given its name, it sure seems like it isn't unprojected data
AlbersUsa
var width = 750
var height = 400
// The svg
var svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width)
.attr("height", height);
// Create a color scale
var color = d3.scaleOrdinal()
.domain(["A", "B", "C" ])
.range([ "#402D54", "#D18975", "#8FD175"])
var projection = d3.geoAlbersUsa()
.translate([width/2,height/2])
.scale(1070/960*width); // scale the scale factor, otherwise map will overflow SVG bounds.
var path = d3.geoPath(projection);
d3.json("https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/world.geojson", function(geojson){
geojson.features = geojson.features.filter( function(d){return d.properties.name=="USA"} )
// d3.csv("file.csv", function(csv) {
// As I cannot access your csv, I'm reproducing a few lines here:
var csv = [
{lon: -116.2,lat: 43.5, Admissions: 24, MajorClassAdmission: "A"},
{lon: -81.7,lat: 41.4, Admissions: 13, MajorClassAdmission: "B"},
{lon: -74.1,lat: 40.9, Admissions: 35, MajorClassAdmission: "C"},
{lon: -121.6,lat: 37.3, Admissions: 14, MajorClassAdmission: "B"},
{lon: -73.9,lat: 40.68, Admissions: 13, MajorClassAdmission: "A"},
]
svg.selectAll("path")
.data(geojson.features)
.enter()
.append("path")
.attr("d", path);
svg.selectAll("circle")
.data(csv)
.enter()
.append("circle")
.attr("cx", function(d) {
return projection([d.lon,d.lat])[0];
})
.attr("cy", function(d) {
return projection([d.lon,d.lat])[1];
})
.attr("r", function(d) {
return d.Admissions
})
.attr("fill", function(d) {
return color(d.MajorClassAdmission);
})
// ...
// })
})
.circle:hover{
stroke: black;
stroke-width: 1px;
}
path {
fill: none;
stroke: #ccc;
stroke-width: 1px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<script src="https://d3js.org/d3.v4.js"></script>
<div id="my_dataviz"></div>
Mercator
var width = 750
var height = 400
// The svg
var svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width)
.attr("height", height);
// Create a color scale
var color = d3.scaleOrdinal()
.domain(["A", "B", "C" ])
.range([ "#402D54", "#D18975", "#8FD175"])
var projection = d3.geoMercator()
var path = d3.geoPath(projection);
d3.json("https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/world.geojson", function(geojson){
geojson.features = geojson.features.filter( function(d){return d.properties.name=="USA"} )
projection.fitSize([width,height],geojson)
// d3.csv("file.csv", function(csv) {
// As I cannot access your csv, I'm reproducing a few lines here:
var csv = [
{lon: -116.2,lat: 43.5, Admissions: 24, MajorClassAdmission: "A"},
{lon: -81.7,lat: 41.4, Admissions: 13, MajorClassAdmission: "B"},
{lon: -74.1,lat: 40.9, Admissions: 35, MajorClassAdmission: "C"},
{lon: -121.6,lat: 37.3, Admissions: 14, MajorClassAdmission: "B"},
{lon: -73.9,lat: 40.68, Admissions: 13, MajorClassAdmission: "A"},
]
svg.selectAll("path")
.data(geojson.features)
.enter()
.append("path")
.attr("d", path);
svg.selectAll("circle")
.data(csv)
.enter()
.append("circle")
.attr("cx", function(d) {
return projection([d.lon,d.lat])[0];
})
.attr("cy", function(d) {
return projection([d.lon,d.lat])[1];
})
.attr("r", function(d) {
return d.Admissions
})
.attr("fill", function(d) {
return color(d.MajorClassAdmission);
})
// ...
// })
})
.circle:hover{
stroke: black;
stroke-width: 1px;
}
path {
fill: none;
stroke: #ccc;
stroke-width: 1px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<script src="https://d3js.org/d3.v4.js"></script>
<div id="my_dataviz"></div>
While the comments in your code obfuscate the code you are using to generate problematic maps, there are a few potential simplifications in your code: you appear to be using both d3v4 and d3v3 given the use of d3.geo.path (v3) instead of d3.geoPath (v4+). The base d3 bundle includes all the geographic functions you need, so you don't need to import https://d3js.org/d3-geo-projection.v2.min.js or any other modules.

Why is my choropleth coming out all the same colour?

This is my first question on here so please bear with.
I'm trying to make a choropleth (a map where different sections are coloured in based on some value assigned to them) using d3.js. I'm using the example given at https://www.d3-graph-gallery.com/graph/choropleth_basic.html, but changing the map to one of Scotland and changing the values to population density.
When I run it, I get a map but it's all coloured in the same shade of blue. I've tried changing the domain of colorScale but to no avail.
This is what I've got at the minute:
// The svg
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
// Map and projection
var path = d3.geoPath();
var projection = d3.geoNaturalEarth()
.scale(20 * width / Math.PI)
.translate([width / 2 + 150, height / 2 + 2500]);
// Data and color scale
var data = d3.map();
var colorScale = d3.scaleThreshold()
.domain([0, 600])
.range(d3.schemeBlues[7]);
// Load external data and boot
d3.queue()
.defer(d3.json, "https://raw.githubusercontent.com/squirrel-star/scotland/main/geojsonscotlandladjson.geojson")
.defer(d3.csv, "https://raw.githubusercontent.com/squirrel-star/scotland/main/scotlanddensitywithid.csv", function(d) {
data.set(d.code, +d.density);
})
.await(ready);
function ready(error, topo) {
console.log(data);
// 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);
});
}
<script src="https://d3js.org/d3.v4.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<script src="https://d3js.org/d3-geo-projection.v2.min.js"></script>
<svg id="my_dataviz" width="400" height="400"></svg>
Any suggestions for fixing it would be greatly appreciated. Thank you!
I think you may have misread the documentation for d3.scaleThreshold, because it says you need to have N values in your domain if you have N + 1 values in your range. In your case, that makes N = 6.
Also, d.id didn't exist. I used d.properties.LAD13NM instead, because that field contained the name of the relevant county.
Finally, there was no need to use a map, since you were only using it as an object of some sorts, so I just replaced it with a regular object.
// The svg
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
// Map and projection
var path = d3.geoPath();
var projection = d3.geoNaturalEarth()
.scale(20 * width / Math.PI)
.translate([width / 2 + 150, height / 2 + 2500]);
// Data and color scale
var data = {};
var colorScale = d3.scaleThreshold()
.domain([100, 200, 300, 400, 500, 600])
.range(d3.schemeBlues[7]);
// Load external data and boot
d3.queue()
.defer(d3.json, "https://raw.githubusercontent.com/squirrel-star/scotland/main/geojsonscotlandladjson.geojson")
.defer(d3.csv, "https://raw.githubusercontent.com/squirrel-star/scotland/main/scotlanddensitywithid.csv", function(d) {
data[d.code] = +d.density;
})
.await(ready);
function ready(error, topo) {
// 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[d.properties.LAD13NM] || 0;
return colorScale(d.total);
});
}
<script src="https://d3js.org/d3.v4.js"></script>
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
<script src="https://d3js.org/d3-geo-projection.v2.min.js"></script>
<svg id="my_dataviz" width="400" height="400"></svg>

d3.js projection scale doesn't increase a size of a country

I'm trying to build something with d3.js and GeoJSON for the first time. I have managed to get a country - Estonia to be displayed but it is so small you can barely see it. I tried to play with projection geoMercator().scale but it doesn't work - no increase in size.
Please see a picture attached below(under bar chart):
Here is my js:
var projection = d3.geoMercator()
.translate([w/2, h/2])
.scale([100]);
var path = d3.geoPath()
.projection(projection)
var w3 = 2000;
var h3 = 1500;
var svg = d3.select("body")
.append("svg")
.attr("width", w3)
.attr("height", h3)
d3.json("estonia.json", function (json){
svg.selectAll("path")
.data(json.features)
.enter()
.append("path")
.attr("d", path)
.style("fill", "#2294AA");
})
What am I doing wrong?
I took your code and saw that when I increased the scale, Estonia disappeared. Had to center the projection like this:
var projection = d3.geoMercator()
.center([24.312863, 57.793424])
.scale([500])
.translate([w/2, h/2])
I took the coordinates [24.312863, 57.793424] from the .json file.

User input to Project on JSON map

I have the following D3.js project that is available here:
http://bl.ocks.org/diggetybo/raw/e75dcb649ae3b26e2312a63434fc970c/
The latitude and longitude inputs are below the map.
It's supposed to take user input numbers of latitude and longitude and "project" svg circles at the given coordinate. The issue is I'm either getting ____ is not a function error or dev tools throws no errors at all, but the circles are never projected.
It's a short file, can someone explain why it's not working the way I thought?
Your update function doesn't make any sense.
It accepts two inputs, but you only ever call it with one.
.selectAll("circle").enter() is not valid d3 syntax.
You need to call projection with both the latitude and longitude, you pass 0 which will result in it returning null since it's outside of the projection.
After you fix all this, you'll still be off because you've moved your paths by your margin and would have been better off putting them in a g moved by the margins.
All that said, a simple rewrite would be:
var lat = d3.select("#latValue").on("input", function() {
update();
}).node();
var long = d3.select("#lonValue").on("input", function() {
update();
}).node();
function update() {
// lat/long to pixel
var coors = projection([long.value, lat.value]);
// if outside projection don't add circle
if (coors === null) return;
// add circle
container
.append("circle")
.attr("cx", coors[0])
.attr("cy", coors[1])
.attr("r", Math.sqrt(5) * 4)
.style("fill", "black")
.style("opacity", 0.85);
}
Running code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script src="http://d3js.org/d3.v3.min.js"></script>
<style type="text/css">
/* On mouse hover, lighten state color */
path:hover {
fill-opacity: .7;
}
</style>
</head>
<body>
<script type="text/javascript">
//Width and height of map
var width = 960;
var height = 500;
var margins = { left: 0, top: 100, right: 0, bottom: 0 };
// D3 Projection
var projection = d3.geo.albersUsa()
.translate([width/2, height/2]) // translate to center of screen
.scale([1000]); // scale things down so see entire US
// Define path generator
var path = d3.geo.path() // path generator that will convert GeoJSON to SVG paths
.projection(projection); // tell path generator to use albersUsa projection
// Define linear scale for output
var color = d3.scale.linear()
.range(["#c3e2ff","#15198e"]);
//Create SVG element and append map to the SVG
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height+margins.top);
svg.append('text')
.text('Coordinate Query')
.attr('font-size','24px')
.attr('transform', 'translate(' + 30 + ',' +70 + ')')
.attr('font-family','Calibri');
svg.append('text')
.text('Data as of 12/2016')
.attr('font-size','12px')
.attr('transform', 'translate(' + 35 + ',' +100 + ')')
.attr('font-family','Calibri');
// Load in my states data!
color.domain([0,100]); // setting the range of the input data
// Load GeoJSON data and merge with states data
d3.json("https://jsonblob.com/api/573228c3-d068-11e6-b16a-b501dc8d2b08", function(json) {
//var coordinates = d3.mouse(this);
// Bind the data to the SVG and create one path per GeoJSON feature
var container = svg.append("g")
.attr('transform', 'translate(' + margins.left + ',' + margins.top + ')');
container.selectAll("path")
.data(json.features)
.enter()
.append("path")
.attr("d", path)
.style("stroke", "#fff")
.style("stroke-linejoin","round")
.style("stroke-width", "1.5")
.style("fill", 'steelblue');
// Modified Legend Code from Mike Bostock: http://bl.ocks.org/mbostock/3888852
var lat = d3.select("#latValue").on("input", function() {
update();
}).node();
var long = d3.select("#lonValue").on("input", function() {
update();
}).node();
function update() {
// lat/long to pixel
var coors = projection([long.value, lat.value]);
// if outside projection don't add circle
if (coors === null) return;
// add circle
container
.append("circle")
.attr("cx", coors[0])
.attr("cy", coors[1])
.attr("r", Math.sqrt(5) * 4)
.style("fill", "black")
.style("opacity", 0.85);
}
});
</script>
<p>
<label for="latValue"
style="display: inline-block;width:240px;text-align:right;font-size:18px;font-family:Play">
Lattitude:<span id="latValue-value"></span>
</label>
<input type="number"min="-360"max="360"step="1"value="0" id="latValue">
<label for="lonValue"
style="display: inline-block;width:240px;text-align:right;font-size:18px;font-family:Play">
Longitude:<span id="lonValue-value"></span>
</label>
<input type="number"min="-360"max="360"step="1"value="0" id="lonValue">
</p>
</body>
</html>

How to control the order of the layers on a map in d3js

I have a simple question about making maps with d3js. I want to make a map with two layers. One layer is the contour of the map (geoJson) and the other layer contains the streets (topoJson). My problem is that streets layer always is loaded in front of the layer of contour, no matter which one is written before in the code. I would like the opposite situation: the contour layer in front of the streets. I think this problem happen because both request are asynchronous and the contour layer is loaded before because is the lightest.
Here is the code...
//Width and height
var w = 900;
var h = 500;
//Define map projection
var projection = d3.geo.transverseMercator()
.center([2.5, -34.65])
.rotate([61.5, 0])
.scale((h * 750.5) / 3)
.translate([(w/2), (h/2)]);
//Define path generator
var path = d3.geo.path()
.projection(projection);
//Create SVG element
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
//Load in TopoJSON data
d3.json("streets.json", function(error, topology) {
svg.selectAll("path")
.data(topojson.feature(topology, topology.objects.layer1).features)
.enter()
.append("path")
.attr("d", path)
.style("fill", "white")
.style("stroke", "#cccccc");
});
//Load in GeoJSON data
d3.json("contour.geojson", function(json) {
//Bind data and create one path per GeoJSON feature
svg.selectAll("path")
.data(json.features)
.enter()
.append("path")
.attr("d", path)
.style("fill","white")
.style("stroke","black")
.style("stroke-width",4);
});
Is it possible load first the layer of streets, wait until the streets were drawn and then load the layer of contour?
Thanks in advance.
I would create two separate g elements for the layers in the beginning. This way you can control their order:
var countourG = svg.append("g"),
streetG = svg.append("g");
d3.json("streets.json", function(error, topology) {
streetG.selectAll("path")...
});
d3.json("contour.json", function(error, topology) {
contourG.selectAll("path")...
});

Categories

Resources