Skip to content

Instantly share code, notes, and snippets.

@mthh
Last active April 5, 2017 20:24
Show Gist options
  • Save mthh/53fab941feea5bebb050f9a6a3c3ee81 to your computer and use it in GitHub Desktop.
Save mthh/53fab941feea5bebb050f9a6a3c3ee81 to your computer and use it in GitHub Desktop.
Hatano AEA projection
license: gpl-3.0
border: no

Hatano Asymmetrical Equal Area projection (rotated by 10.2°).

var NITER = 20,
EPS = 1e-7,
ONETOL = 1.000001,
CN = 2.67595,
CS = 2.43763,
RCN = 0.37369906014686373063,
RCS = 0.41023453108141924738,
FYCN = 1.75859,
FYCS = 1.93052,
RYCN = 0.56863737426006061674,
RYCS = 0.51799515156538134803,
FXC = 0.85,
RXC = 1.17647058823529411764,
M_HALFPI = Math.PI / 2,
sin = Math.sin,
asin = Math.asin,
abs = Math.abs,
cos = Math.cos;
function hatanoRaw(lambda, phi) {
var th1, c, i;
c = sin(phi) * (phi < 0 ? CS : CN);
for(i = NITER; i; --i){
phi -= th1 = (phi + sin(phi) - c) / (1 + cos(phi));
if(abs(th1) < EPS) break;
}
return [
FXC * lambda * cos(phi *= 0.5),
sin(phi) * (phi < 0 ? FYCS : FYCN)
];
}
hatanoRaw.invert = function(x, y) {
var th = y * (y < 0 ? RYCS : RYCN);
if(abs(th) > 1){
if(abs(th) > ONETOL){
// return;
console.log('Error?');
} else {
th = th > 0 ? M_HALFPI : -M_HALFPI;
}
} else {
th = asin(th);
}
x = RXC * x / cos(th);
th += th;
y = (th + sin(th)) * (y < 0 ? RCS : RCN);
if(abs(y) > 1){
if(abs(y) > ONETOL){
console.log('Error?');
// return;
} else {
y = y > 0 ? M_HALFPI : -M_HALFPI;
}
} else {
y = asin(y);
}
return [x, y];
};
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.graticule {
fill: none;
stroke: #777;
stroke-width: 0.5px;
stroke-opacity: 0.5;
}
.land {
fill: #222;
}
.boundary {
fill: none;
stroke: #fff;
stroke-width: 0.5px;
}
</style>
<svg width="960" height="500"></svg>
<script src="//d3js.org/d3.v4.min.js"></script>
<script src="//d3js.org/topojson.v1.min.js"></script>
<script src="hatano.js"></script>
<script>
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
g = svg.append("g");
var projection = d3.geoProjection(hatanoRaw)
.scale(160)
.rotate([-10.2, 0, 0])
.translate([width / 2, height / 2])
.precision(0.1);
var path = d3.geoPath()
.projection(projection);
var graticule = d3.geoGraticule();
g.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);
g.append("path")
.datum({type: "Sphere"})
.attr("class", "sphere")
.attr("d", path)
.attr("fill", "none")
.attr("stroke", "black");
d3.json("world-50m.json", function(error, world) {
if (error) throw error;
g.insert("path", ".graticule")
.datum(topojson.feature(world, world.objects.land))
.attr("class", "land")
.attr("d", path);
g.insert("path", ".graticule")
.datum(topojson.mesh(world, world.objects.countries, function(a, b) { return a !== b; }))
.attr("class", "boundary")
.attr("d", path);
});
</script>
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment