Description
Hello,
I recently started using plotly.js and would like to congratulate with the amazing works that had gone into this nice plotting tool.
In my work I often have to deal with plots involving earth as viewed from a satellite, for which the general perspective projection would be very useful to have among the already available ones (it is basically an ortographic projection from an arbitrary distance instead of an infinite one).
I am relatively new to javascript but by looking at the code of plotly.js, given that projections seems to be handeld by the d3-geo package, it seems that such addition would be quite straightforward to include.
Specifically, based on how the other projections seem to be implemented, it seems it would be sufficient to add the new projection to src\plots\geo\projections.js according to the code in this page of the d3-geo-projections repo.
This is the code I tried to modify from the repo to fit the style and definitions found in the projections.js file (not so sure about the scale and clipAngle children of p at the end of this code as they don't seem to be defined in other projections in plotly.js)
function satelliteVertical(P) {
function forward(λ, φ) {
var cosφ = Math.cos(φ),
k = (P - 1) / (P - cosφ * Math.cos(λ));
return [
k * cosφ * Math.sin(λ),
k * Math.sin(φ)
];
}
forward.invert = function(x, y) {
var rho2 = x * x + y * y,
rho = asqrt(rho2),
sinc = (P - asqrt(1 - rho2 * (P + 1) / (P - 1))) / ((P - 1) / rho + rho / (P - 1));
return [
Math.atan2(x * sinc, rho * asqrt(1 - sinc * sinc)),
rho ? asin(y * sinc / rho) : 0
];
};
return forward;
}
function satellite(P, omega) {
var vertical = satelliteVerticalRaw(P);
if (!omega) return vertical;
var cosOmega = Math.cos(omega),
sinOmega = Math.sin(omega);
function forward(λ, φ) {
var coordinates = vertical(λ, φ),
y = coordinates[1],
A = y * sinOmega / (P - 1) + cosOmega;
return [
coordinates[0] * cosOmega / A,
y / A
];
}
forward.invert = function(x, y) {
var k = (P - 1) / (P - 1 - y * sinOmega);
return vertical.invert(k * x, k * y * cosOmega);
};
return forward;
}
function satelliteProjection() {
var distance = 2,
omega = 0,
m = projectionMutator(satelliteRaw),
p = m(distance, omega);
// As a multiple of radius.
p.distance = function(_) {
if (!arguments.length) return distance;
return m(distance = +_, omega);
};
p.tilt = function(_) {
if (!arguments.length) return omega * degrees;
return m(distance, omega = _ * radians);
};
return p
.scale(432.147)
.clipAngle(acos(1 / distance) * degrees - ε);
};
(d3.geo.satellite = satelliteProjection).raw = satellite;
And make the projection selectable by appending its name at the end of params.projNames in the file src\plots\geo\constants.js
// projection names to d3 function name`
params.projNames = {
// d3.geo.projection
...
'sinusoidal': 'sinusoidal'
'satellite': 'satellite'
};
Is this all that is needed to implement a new projection or are there other parts of the code that might be impacted that I did not realize?
Provided this is really all that is necessary, I will try to implement and test these changes myself if needed in the next few days but having never used node or really coded in javascript before, it might take a while before I am eventually able to test and learn how to do a pull request.