d3js - how to set albers projection properly? - d3.js

I have some geojson data for Japan, which I managed to position properly on a mercator projection, but I'm a bit lost as to how to position it properly using an albers projection, other than trial and error.
Is there a good tool to use?
blocks example: http://bl.ocks.org/4043986
long, lat for japan (wikipedia):
latitudes 24° - 46°N,
longitudes 122° - 146°E.
geojson link: https://gist.github.com/raw/4043986/f53b85ab0af1585cd0461b4865ca4acd1fb79e9f/japan.json

As of now, it's the version 3 of D3.js.
It might be worth looking at the original source albers.js at github, which contains :
d3.geo.albers = function() {
return d3.geo.conicEqualArea()
.parallels([29.5, 45.5])
.rotate([98, 0])
.center([0, 38])
.scale(1000);
};
Now, d3.js use combination of projection.rotate and projection.center to place center of the projection to long 98°W, lat 38°N (around Hutchinson, Kansas).
From Geo Projections API,d3.geo.conicEqualArea()
.parallels([29.5, 45.5]) sets the Albers projection’s two standard parallels latitudes 29.5°N and
45.5°N, respectively. But what is two standard parallels?
To understand what parallels setting is, one need to know that Albers projection is a kind of conic projection.
A conic projection projects information from the spherical Earth to a cone that is either tangent to the Earth at a single parallel, or that is secant at two standard parallels.
Choosing the best standard parallels setting seems to be a subtle task, of which the goal is to minimize the projection distortion when mapping between surfaces. Anyway, choosing the two values to be closed to a country top/bottom edges is intuitively good, as it helps minimize the distance between the [conic/sphere] surfaces enclosing a country.

I found the answer looking through the repository - the tool is right there!
clone d3.js from the github repository.
edit /d3/examples/albers.html line 53 to point at your GEOJSON file:
Put the origin long / lat sliders to the center of your country / region (for me, it was 134° / 25°)
Change the paralells to be as close to the edges of your country / region.
adjust scale & offset to a nice size & position.
There are similar tools for the other projections.
edit: The repository has changed (and is constantly changing), so I've created a gist to preserve the example: https://gist.github.com/4552802
The examples are no longer part of the github repository.

Related

Pre-projected geometry v getting the browser to do it (aka efficiency v flexibility)

To improve the performance of my online maps, especially on smartphones, I'm following Mike Bostock's advice to prepare the geodata as much as possible before uploading it to the server (as per his command-line cartography). For example, I'm projecting the TopoJSON data, usually via d3.geoConicEqualArea(), at the command line rather than making the viewer's browser do this grunt work when loading the map.
However, I also want to use methods like .scale, .fitSize, .fitExtent and .translate dynamically, which means I can't "bake" the scale or translate values into the TopoJSON file beforehand.
Bostock recommends using d3.geoTransform() as a proxy for projections like d3.geoConicEqualArea() if you're working with already-projected data but still want to scale or translate it. For example, to flip a projection on the y-axis, he suggests:
var reflectY = d3.geoTransform({
point: function(x, y) {
this.stream.point(x, -y);
}
}),
path = d3.geoPath()
.projection(reflectY);
My question: If I use this D3 function, aren't I still forcing the viewer's browser to do a lot of data processing, which will worsen the performance? The point of pre-processing the data is to avoid this. Or am I overestimating the processing work involved in the d3.geoTransform() function above?
If I use this D3 function, aren't I still forcing the viewer's browser
to do a lot of data processing, which will worsen the performance? The
point of pre-processing the data is to avoid this. Or am I
overestimating the processing work involved in the d3.geoTransform()
function above?
Short Answer: You are overestimating the amount of work required to transform projected data.
Spherical Nature of D3 geoProjections
A d3 geoProjection is relatively unique. Many platforms, tools, or libraries take points consisting of latitude and longitude pairs and treat them as though they are on a Cartesian plane. This simplifies the math to a huge extent, but comes at a cost: paths follow Cartesian routing.
D3 treats longitude latitude points as what they are: points on a three dimensional ellipsoid. This costs more computationally but provides other benefits - such as routing path segments along great circle routes.
The extra computational costs d3 incurs in treating coordinates as points on a 3d globe are:
Spherical Math
Take a look at a simple geographic projection before scaling, centering, etc:
function mercator(x, y) {
return [x, Math.log(Math.tan(Math.PI / 4 + y / 2))];
}
This is likely to take longer than the transform you propose above.
Pathing
On a Cartesian plane, lines between two points are easy, on a sphere, this is difficult. Take a line stretching from 179 degrees East to 179 degrees West - treating these as though they were on a Cartesian plane that is easy - draw a line across the earth. On a spherical earth, the line crosses the anti-meridian.
Consequently, in flattening the paths, sampling is required along the route, great circle distance between points requires bends, and therefore additional points.I'm not certain on the process of this in d3, but it certainly occurs.
Points on a cartesian plane don't require additional sampling - they are already flat, lines between points are straight. There is no need to detect if lines wrap around the earth another way.
Operations post Projection
Once projected, something like .fitSize will force additional work that is essentially what you are proposing with the d3.geoTransform(): the features need to be transformed and scaled based on their projected location and size.
This is very visible in d3v3 (before there was fitSize()) when autocentering features: calculations involve the svg extent of the projected features.
Basic Quasi Scientific Performance Comparison
Using a US census bureau shapefile of the United States, I created three geojson files:
One using WGS84 (long/lat) (file size: 389 kb)
One using geoproject in node with a plain d3.geoAlbers transform (file size: 386 kb)
One using geoproject in node with d3.geoAlbers().fitSize([500,500],d) (file size 385 kb)
The gold standard of speed should be option 3, the data is scaled and centered based on an anticipated display extent, no transform is required here and I will use a null projection to test it
I proceeded to project these to a 500x500 svg using:
// For the unprojected data
var projection = d3.geoAlbers()
.fitSize([500,500],wgs84);
var geoPath = d3.geoPath().projection(projection)
// for the projected but unscaled and uncentered data
var transform = d3.geoIdentity()
.fitSize([500,500],albers);
var projectedPath = d3.geoPath()
.projection(transform);
// for the projected, centered, and scaled data
var nullProjection = d3.geoPath()
Running this a few hundred times, I got average rendering times (data was preloaded) of:
71 ms: WGS84
33 ms: Projected but unscaled and uncentered
21 ms: Projected, scaled, and centered
I feel safe in saying there is a significant performance bump in pre-projecting the data, regardless of if it is actually centered and scaled.
Note I used d3.geoIdentity() as opposed to d3.geoTransform() as it allows the use of fitSize(), and you can reflect if needed on the y: .reflectY(true);

Can I turn a D3 map projection clockwise around its centre point?

How can I turn my map - if I can - clockwise by about 15-20 degrees, so that is looks like the map of the middle east I would see in an atlas?
Intuitively, .rotate looks like it should do it, but I've tried inserting different values and it just 'uncentres' the map.
The important bit of D3 code, I believe, is:
var projection = d3.geo.albers()
.center([49.7, 27.4])
.rotate([0, 0, 0])
.parallels([12.6, 40])
.scale(800)
.translate([width / 2, height / 2]);
(1)
Thanks.
(2)
I'm trying to replicate a map the looks like this (2) - just because it is what people are familiar with seeing in a regular atlas.
Not knowing what you have tried thus far using projection.rotate() I still think this method will give the desired result. For example, a clockwise rotation by 20 degrees around LAT=49.7N, LON=27.4E as specified in your example could be done by:
projection.rotate([-49.7,-27.4,-20])
I set up a Plunk demonstrating the outcome.
Update
If you are not bound to using the Albers projection, there might be other options giving results which better fit your needs of
what people are familiar with seeing in a regular atlas.
I looked it up in three atlases where the Arabian Peninsula was depicted using the equirectangular projection which looks like your desired output:
var projection = d3.geo.equirectangular()
.rotate([-49.7,-27.4])
You just center on LAT=49.7N, LON=27.4E by applying .rotate([-49.7,-27.4]) without the need to further roll the projection, i.e. you won't have the third element in the array supplied to rotate(). See my updated Plunk. To me this looks like what I would expect it look when seeing it in an atlas.

Drawing custom paths in D3.js without using fake data points

Trying to implement EKG style "heartbeat" chart from a design and I'm having a hard time getting D3 to draw a path like I need.
The design spec states that the graph needs to return to nuetral/zero point between each and every data point, and that the curved path from the zero point should be close to the data point itself and rise sharply. See the attached images below
Here is the design....
And here is my attempt to match the curve with dummy data (black circle data points)...
The graph has a time scale X axis and a linear Y axis that ranges from 0 to 2 (my data points are 0,1, or 2 respectively). The line is using 'monotone' interpolation which is the least terrible looking.
Question:
Is there a better way to get this appearance without dummy data points?
Question-behind-the-question:
What is the best way to get D3 draw a custom paths (e.g. from a function)?
Sub-question:
Why does the monotone interpolation curve the path inward so sharply between the last 2 data points?
Any help is appreciated! The designers and client won't budge on this one, so I have to get it as close possible :(

Mapping with D3: Feature clipping error in my spinning globe

I'm using D3 to create a world map with an orthographic projection that the user can "spin" with their mouse like they would a globe.
I ran into some problems with jittery rendering in Firefox so I simplified my map features using an implementation of the Douglas-Peuker Algorithm in R. I dumped this into geoJSON and have it rendered by D3 as in this example: http://jsfiddle.net/cmksA/8/. (Note that the problem I describe below doesn't occur with the non-simplified features, but Firefox is unusable if I don't simplify.)
Performance is still poor (getting better) in Firefox, but a new issue has crept in. When you pan the globe so that Indonesia is roughly in the center of the globe, one of the polygons gets transformed to cover the entire globe. The same issue happens when North and South America are centered.
As part of the panning, I re-project/re-draw the globe using the following function (line 287 of the jsfiddle):
function panglobe(){
var x=d3.event.dx;
var y=d3.event.dy;
var r = mapProj.rotate();
r[0] = r[0]+lonScale(x)
r[1] = r[1]+latScale(y)
mapProj.rotate(r);
countries.attr("d",function(d){
var dee=mapPath(d)
return dee ? dee : "M0,0";
});
}
Any help/insight/advice would be much appreciated. Cheers
A common problem with line-simplification algorithms when applied to polygons is that they can introduce self-intersections, which generally cause havoc with geometry algorithms.
It's quite possible that your simplified polygons contain some self-intersections, e.g. a segment that goes back on itself. This might cause problems for D3, e.g. when sorting intersections along the clip region edge (although in future releases I hope to support self-intersecting polygons, too).
A better algorithm to use might be Visvalingam–Whyatt, e.g. as used by TopoJSON, as it simplifies based on area. However, it can also produce self-intersecting polygons, although perhaps less often than Douglas–Peucker.
For interactive globes I’d recommend world-110m.json from Mike Bostock’s world-atlas.

cartesian to geographical/sperical coordinates

I have a dxf file of a town in meter coordinates. I need to extract polylines from the map and convert them to geographical coordinates. My idea is to add 3 or 4 points on the map with known coordinates. I will place them using google map as reference. These 3 or 4 points will be placed at intersections roads so I can not place them as they defined a rectangle (I think it would be simpler).
I can not found out what calculation I have to do to convert all the coordinates of the objects of the map.
google map earth radius: 6378137 meter.
so, If I consider 3 points, I will have 3 relations:
(x1,y1) with (lat1, lng1)
(x2,y2) with (lat2, lng2)
(x3,y3) with (lat3, lng3)
I have done one simple conversion with only 2 points but I'd like a more accurate result.
I preferably use c/c++ to do it.
example of one equivalent point:
latitude: -2.148707
longitude: -79.876270
x: 2012078.15
y: 498355.88
It's not a UTM, I verify it from here. Because I do not know if it s a normalized.
I googled a lot, I found libraries, but without knowing if tmy coordinates meet a specific format, I don't think I can use one.
Anyway, thanks to read and I hope someone could help me.
It is not as easy at that. First you need to know which reference ellipsoid you are using (e.g. WGS-84) and then which projection. I wouldn't try to implement this by hand, but use postgis instead, which would do all this ugly work for you.
The correct way is to ask the provider of the file what the coordinate system is related to. dxf is not realy a suitable format. you need a format like ESRI Shp file or mif/mid with a defined geographic coordinate system.
otherwise it is a bit unclear if the data are precise enough, to be used for geographic reference.
However it is not difficult to transform between meters and wgs84 lat longitude, especially if the area is not more than 10-20 miles.
you could use as first try the cylyndrical equidistant transformation, which is only a few lines of simple code. look also if the y-achsis in the dxf file points to the nort pole, otherwise you must find out that amount of false northing and rotate back to north.
MapInfo Professional is a tool with free evaluation period, this tool alows to specify reference points for such unknown coordinate systems. (at least for bitmaps i rememer that feature).
But if you are a professional sw developper, You should reject that file and demand a version in wgs84 lat lon.

Resources