Why is this TopoJSON rendered so small with D3? (Simple example) - javascript

I'm trying to do something really simple. I have a GeoJSON file of all the congressional districts of New York and I simply wanted be able to view it in my SVG element. I converted it to TopoJSON and tried to follow this tutorial (except I tried using the new updated d3 v4 API). The main problem is that the map, I think, does get rendered but it is very small. If I try to scale it up then I lose sight the map entirely.
Here is my html and d3 js:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>NY State Districts Demo</title>
</head>
<body>
<h1>Hello</h1>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.2/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/topojson/3.0.2/topojson.js"></script>
<script>
/* JavaScript goes here */
var width = 960, height = 1160;
var svg = d3.select("body").append("svg").attr("width", width).attr("height", height);
d3.json("./ny_bounds.json", function(error, ny){
if(error) return console.error(error);
console.log(ny);
svg.append("path")
.datum(topojson.feature(ny, ny.objects.ny_bounds))
.attr("d", d3.geoPath().projection(d3.geoAlbersUsa()));
});
</script>
</body>
</html>
Here is a link to the TopoJSON in question.
I will admit that I don't have much knowledge on d3 and geo/topoJSON but I'm just stuck at this point. If anyone has any resources where I can learn more about these subjects that would be great too. But as I said before the primary problem is that the map is too small and I can't seem to center/scale it.
Thanks in advance for helping.

You are projecting data with a projection that is intended to project points across the entire US, including Hawaii and Alaska. New York will be a small portion of this projection area. Scaling any map projection will zoom in towards the center of your projection - d3 does not know to zoom in to a particular location, so as you zoom in, New York will fall off the edge of your projection.
As an AlbersUsa projection is a composite projection (to allow inclusion of Alaska, Hawaii), it is actually several Albers projections combined on one plane. Because of its composite nature, it is harder to manipulate. To keep things simple, I would recommend using a plain Albers projection. This also allows you to tailor the projection to New York state.
An Albers projection has two standard parallels, these are parallels of the projected plane that intersect the surface of the globe (it is a conical projection). The standard parallels should be located in the area of interest, one in the lower half and one in the uppper (as these are the parallels that intersect the globe, distortion is minimized along these parallels). For New York state, parallels like 41 and 44 degrees north could work.
You also need to center the projection. To do so you need the geographic center of your area of interest. The center of New York is 42.954 degrees north and 75.527 degrees west (-75.527 degrees East). To center a Albers projection, rotate on the x(by the inverse of the longitude as we spin the globe under us) and center on the y. Altogether this looks like:
d3.geoAlbers()
.center([0,42.954])
.rotate([75.527,0])
.parallels([41,44])
.translate([width/2,height/2])
.scale(k) // scale factor
Now you need to make sure your geoPath uses this projection ( yes, you need to specify the translate if your width or height is not the default 960x500 as this centers the center of your projection). The scale zooms in with larger numbers, a factor of 2000 should get you started.

Related

how to use .scale and .translate with d3.js?

I have been trying to use .scale().translate on projection. However, I was unable to create the right scale. Mine seems to show tiny map on the webpage and was unable to change its size(when I try to change it, it disappear which is probable not the right scale....). In the photo, you probably see a tiny map with my h1 heading on the top of the page. Does anyone know how to make the right scale? I've been stuck on this really long time, please help :(
Here is my HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<link rel="stylesheet" href="./dist/main.css" />
<script src="./dist/main.js"></script>
<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>
<script src="https://d3js.org/topojson.v2.min.js"></script>
<script src="neighborhood.js"></script>
<title>Project Name</title>
</head>
<body>
<h1>House Price in the Bay Area</h1>
<svg id="my_dataviz" width="400" height="300"></svg>
<script src="index.js"></script>
<script src="neighborhood.js"></script>
</body>
</html>
and here is my .js file
var canvas = d3.select("body").append("svg")
.attr("width", 900)
.attr("height", 600)
d3.json("Bay_Area_Counties.geojson", function (data) {
var group = canvas.selectAll("g")
.data(data.features)
.enter()
.append("g")
var projection = d3.geoMercator().**scale().translate([])**;
var path = d3.geoPath(projection);
var areas = group.append("path")
.attr("d", path)[![ ][1]][1]
.attr("class", "area")
.attr("fill", "steelblue");
});
If the point disappears as you zoom in your probably have the wrong translate.
A d3 projection has a center. A coordinate in degrees that marks the center of the projection. The translate value is to what coordinate in pixels the center should be projected to.
You have no center coordinate specified, so for a Mercator projection it is the default: [0°E,0°N]: a point off the coast of Africa.
You don't show what coordinate you have for the translate, but the default is [480,250] - this is where the center coordinate will be renedered: 250 pixels from the left of the SVG/Canvas/Container, 480 pixels down from the top.
As we zoom in the center point remains projected to the translate point, everything else moves. If we zoom in far enough (ie increase the scale value enough), everything other than the center point will be out of frame:
So if when zooming in your feature drops off the map, we need a new center coordinate. Preferably select a coordinate within your features of interest and roughly in their geographic center. Now you can toy with a scale factor that will properly show your areas of interest.
Without setting a center - how is D3 to know what you want to focus on when you set the scale higher?
You haven't provided a geojson of your features or a description, so hopefully the above is enough for you to get a good result. However, there is an easier way.
When setting the map parameters by hand a center coordinate is useful and traditionally the translate value is the middle of the SVG/Canvas/Container. However, projection.fitSize/fitExtent will set the translate and the scale values so that the features of interest will fill the specified pixel extent automagically.
This approach does not set the center coordinate - it translates the center coordinate off screen if necessary so that the geographic features of interests intersect the specified pixel extent. This means the translate value is dependent on scale, and we can't modify projection parameters after we use these fitting methods as easily as before.
With projection.fitSize() we specify the width and height of our SVG and the geojson object we wish to show: projection.fitSize([width,height],feature). fitExtent is similar but allows margins to be specified (D3's documentation has details), there are also a few similar methods for different spacing options (again covered in the docs better than I can do here).

D3 radial bar chart adjust radius scaling

If one examines this block:
https://bl.ocks.org/mbostock/8d2112a115ad95f4a6848001389182fb
The gridlines are in increments of 20. However the radius of each gridline does not appear to be equal as it scales up:
I'm guessing there is some geometric justification for this, but that's not what I'm after for my chart. I only want aesthetics, I need the gridline circles to be equidistant from each other.
Question
Using Bostock's radial scale script as seen in the above block, is there any way to adjust the scaling of the radii? I want the scaling to be equidistant.
The only thing you need is to change this...
var y = d3.scaleRadial()
... for this:
var y = d3.scaleLinear()
Here is the bl.ocks with that change only: https://bl.ocks.org/GerardoFurtado/0a0b22d15c4e715e4c748335e37330fb/1670bbcdfdcbed6b6a0ae2a56d5f153570d969d1
PS: There is indeed a geometrical explanation for this: a circle with radius 2r has an area four times bigger than a circle with a radius r. That's why we always (at least in truthful charts) scale the circle's radius to the square root of the encoded datum. Well, you mentioned that "I only want aesthetics". As a data visualisation specialist/enthusiast who happens to be a D3 programmer, not the other way around, I suggest you reconsider your approach and keep the radial scale. Charts that prioritise aesthetics over information are normally bad charts, and charts that impose aesthetics ignoring information are simply untruthful charts.

Leaflet polyline precision loss on zoom out

When drawing multiple polylines, and zooming out the map, the line starts creating circles on vertexes:
The lines are being draw as follows:
L
.polyline(line, {weight: 4, color: color, smoothFactor: 0, offset:offset})
.addTo(Window.map);
I have tried with different values for the smoothFactor and offset with little difference. Why are the circles only visible when the map is not fully zoomed in? Can it be fixed?
Looks like you apply a pixel offset to the polylines.
When you zoom out, the vertices of your polyline become so close from each other that the offset algorithm determines directions for applying the offset much further than the general trend, leading to these funny circles.
The issue lies in the Leaflet.PolylineOffset plugin, I have created a new pull request which hopefully fixes it or at least serves as a basis to a better solution.
https://github.com/bbecquet/Leaflet.PolylineOffset/pull/21

Geojson map not showing up

I'm struggling with creating a map. I am simply using one of the new york times geojsons ("census_tracts_2010.geojson") from here ( https://github.com/dwillis/nyc-maps ).
I'd appreciate it if someone could look at my code below, and let me know why no map is showing up for me, especially I have no errors. If something is wrong, then it's probably in the last two lines.
STEP 1 - CONVERT GEOJSON TO TOPOJSON
ran this in terminal
geo2topo census_tracts_2010.geojson > nyc2.json
STEP 2
created index.html
(inspired by https://bost.ocks.org/mike/map/)
<!DOCTYPE html>
<meta charset="utf-8">
<style>
</style>
<body>
<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="//d3js.org/topojson.v1.min.js"></script>
<script>
var width = 960,
height = 1160;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
d3.json("nyc2.json", function(error, uk) {
if (error) return console.error(error);
console.log(uk.objects)
console.log(uk.objects.census_tracts_2010)
svg.append("path")
.datum(topojson.feature(uk, uk.objects.census_tracts_2010))
.attr("d", d3.geo.path().projection(d3.geo.albersUsa()));
});
</script>
My output:Plain white webpage
Updated code:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
</style>
<body>
<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="//d3js.org/topojson.v1.min.js"></script>
<script>
var width = 500,
height = 500;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
d3.json("census_tracts_2010.geojson", function(error, uk) {
if (error) return console.error(error);
var subunits = topojson.feature(uk, uk.objects.nyct2010);
var projection = d3.geo.albers()
.center([0,40.7])
.rotate([-74,0])
.translate([width/2,height/2])
.scale(65000);
var path = d3.geo.path()
.projection(projection);
svg.append("path")
.datum(subunits)
.attr("d", path);
});
</script>
Data Source
First, your referenced file is already a topopjson. You can contrast the differences with a regular geojson in this file (from the same repository).
The most visible difference is that geojson uses recognizable coordinates while topojson uses arcs and what might look like arbitrary coordinates. Topojsons also finish with scale and translate values.
The Problem
Why does your map not appear? Well that could be because of issues in topojsoning a file that is already a topojson, but a more likely option - and one that touches on your other question - is that you are not focusing your map on your area of interest. This appears to be the the issue in your previous question as well.
You are using a geo.albersUsa projection - this is by default focused on the entire continental US (it's a composite projection so it includes space for Alaska and Hawaii).
Changing your code only to use the topojson you reference (census_tracts_2010) I got:
Your map is displaying properly - or at least as coded - but the entire area of interest looks like it could be a small insect that hit the screen to fast.
AlbersUSA vs Albers
You will need to modify your map projection parameters but if you want to keep the AlbersUSA projection you will not be able to center or rotate, instead use a plain Albers projection. The AlbersUsa is intended for the entire country and I don't believe it has centering or rotation methods.
Setting Map Parameters
To set an Albers projection you'll want to know the center of latitude and longitude of your area of interest. Let's say about 40.7N and 74 W - I used Google Earth to generalize and then adjusted until I got a pleasant result.
Generally for an Albers you also want to know your standard parallels; however, in d3 the default parallels are for the US. Yes, they could be made more specific for your projection (by choosing two parallels that intersect the upper and lower portions of your area of interest), but I'll leave them out in this answer.
The general pattern for an Albers projection in D3 is:
var projection = d3.geo.albers()
.center([0,y])
.rotate([-x,0])
.parallels([a,b]) // don't worry about this in this instance
.translate([width/2,height/2])
.scale(k);
Using the center coordinates above and a few attempts to get the scale down I got this:
Using:
var projection = d3.geo.albers()
.center([0,40.7])
.rotate([74,0])
.translate([width/2,height/2])
.scale(65000);
Note: I've modified your svg dimensions to something more appropriate to the shape of your area of interest (as opposed to the reference map dimensions in the demonstration creating a map of the UK). My dimensions are: 500 x 500.
A relatively more detailed explanation of an Albers projection's parameters is in this answer.

Convert polyline to rectangle

So problem is:
I have a geocoordinates of polylines, example:
`var lines = [
{"type":"polyline","latLngs":[{"lat":55.8662574578028,"lng":37.90008544921874},
{"lat":55.94227871136694,"lng":38.32134246826172}],"color":"#a24ac3"},
{"type":"polyline","latLngs":
[{"lat":55.9643834839687,"lng":38.111915588378906},
{"lat":55.857394661151226,"lng":38.14659118652344}],
"color":"#a24ac3"}]`
So i need to build a rectangle around every polyline with width of rectangle what will be with of polilyne, and height will be ~100 meters. Polyline must penetrate at the center inside of this rectangle and not extend beyond the boundaries.
It will be calculated on nodejs. I have no idea how to do that with geo

Categories