UNPKG

4.91 kBJavaScriptView Raw
1import {
2 geoProjection as projection,
3 geoStereographicRaw,
4 geoCentroid,
5 geoContains
6} from "d3-geo";
7import polyhedral from "./polyhedral/index.js";
8import { scan } from "d3-array";
9import { abs, asin, degrees, sqrt } from "./math.js";
10import {
11 complexAdd,
12 complexMul,
13 complexNorm,
14 complexPow,
15 complexSub
16} from "./complex";
17import {solve2d} from "./newton.js";
18
19export function leeRaw(lambda, phi) {
20 // return d3.geoGnomonicRaw(...arguments);
21 var w = [-1 / 2, sqrt(3) / 2],
22 k = [0, 0],
23 h = [0, 0],
24 i,
25 z = complexMul(geoStereographicRaw(lambda, phi), [sqrt(2), 0]);
26
27 // rotate to have s ~= 1
28 var sector = scan(
29 [0, 1, 2].map(function(i) {
30 return -complexMul(z, complexPow(w, [i, 0]))[0];
31 })
32 );
33 var rot = complexPow(w, [sector, 0]);
34
35 var n = complexNorm(z);
36
37 if (n > 0.3) {
38 // if |z| > 0.5, use the approx based on y = (1-z)
39 // McIlroy formula 6 p6 and table for G page 16
40 var y = complexSub([1, 0], complexMul(rot, z));
41
42 // w1 = gamma(1/3) * gamma(1/2) / 3 / gamma(5/6);
43 // https://bl.ocks.org/Fil/1aeff1cfda7188e9fbf037d8e466c95c
44 var w1 = 1.4021821053254548;
45
46 var G0 = [
47 1.15470053837925,
48 0.192450089729875,
49 0.0481125224324687,
50 0.010309826235529,
51 3.34114739114366e-4,
52 -1.50351632601465e-3,
53 -1.2304417796231e-3,
54 -6.75190201960282e-4,
55 -2.84084537293856e-4,
56 -8.21205120500051e-5,
57 -1.59257630018706e-6,
58 1.91691805888369e-5,
59 1.73095888028726e-5,
60 1.03865580818367e-5,
61 4.70614523937179e-6,
62 1.4413500104181e-6,
63 1.92757960170179e-8,
64 -3.82869799649063e-7,
65 -3.57526015225576e-7,
66 -2.2175964844211e-7
67 ];
68
69 var G = [0, 0];
70 for (i = G0.length; i--; ) {
71 G = complexAdd([G0[i], 0], complexMul(G, y));
72 }
73
74 k = complexSub([w1, 0], complexMul(complexPow(y, 1 / 2), G));
75 k = complexMul(k, rot);
76 k = complexMul(k, rot);
77 }
78
79 if (n < 0.5) {
80 // if |z| < 0.3
81 // https://www.wolframalpha.com/input/?i=series+of+((1-z%5E3))+%5E+(-1%2F2)+at+z%3D0 (and ask for "more terms")
82 // 1 + z^3/2 + (3 z^6)/8 + (5 z^9)/16 + (35 z^12)/128 + (63 z^15)/256 + (231 z^18)/1024 + O(z^21)
83 // https://www.wolframalpha.com/input/?i=integral+of+1+%2B+z%5E3%2F2+%2B+(3+z%5E6)%2F8+%2B+(5+z%5E9)%2F16+%2B+(35+z%5E12)%2F128+%2B+(63+z%5E15)%2F256+%2B+(231+z%5E18)%2F1024
84 // (231 z^19)/19456 + (63 z^16)/4096 + (35 z^13)/1664 + z^10/32 + (3 z^7)/56 + z^4/8 + z + constant
85 var H0 = [1, 1 / 8, 3 / 56, 1 / 32, 35 / 1664, 63 / 4096, 231 / 19456];
86 var z3 = complexPow(z, [3, 0]);
87 for (i = H0.length; i--; ) {
88 h = complexAdd([H0[i], 0], complexMul(h, z3));
89 }
90 h = complexMul(h, z);
91 }
92
93 if (n < 0.3) return h;
94 if (n > 0.5) return k;
95
96 // in between 0.3 and 0.5, interpolate
97 var t = (n - 0.3) / (0.5 - 0.3);
98 return complexAdd(complexMul(k, [t, 0]), complexMul(h, [1 - t, 0]));
99}
100
101var leeSolver = solve2d(leeRaw);
102leeRaw.invert = function (x,y) {
103 if (x > 1.5) return false; // immediately avoid using the wrong face
104 var p = leeSolver(x, y, x, y * 0.5),
105 q = leeRaw(p[0], p[1]);
106 q[0] -= x; q[1] -= y;
107 if (q[0]*q[0] + q[1]*q[1] < 1e-8) return p;
108 return [-10, 0]; // far out of the face
109}
110
111var asin1_3 = asin(1 / 3);
112var centers = [
113 [0, 90],
114 [-180, -asin1_3 * degrees],
115 [-60, -asin1_3 * degrees],
116 [60, -asin1_3 * degrees]
117];
118var tetrahedron = [[1, 2, 3], [0, 2, 1], [0, 3, 2], [0, 1, 3]].map(function(
119 face
120) {
121 return face.map(function(i) {
122 return centers[i];
123 });
124});
125
126export default function() {
127 var faceProjection = function(face) {
128 var c = geoCentroid({ type: "MultiPoint", coordinates: face }),
129 rotate = [-c[0], -c[1], 30];
130 if (abs(c[1]) == 90) {
131 rotate = [0, -c[1], -30];
132 }
133 return projection(leeRaw)
134 .scale(1)
135 .translate([0, 0])
136 .rotate(rotate);
137 };
138
139 var faces = tetrahedron.map(function(face) {
140 return { face: face, project: faceProjection(face) };
141 });
142
143 [-1, 0, 0, 0].forEach(function(d, i) {
144 var node = faces[d];
145 node && (node.children || (node.children = [])).push(faces[i]);
146 });
147
148 var p = polyhedral(
149 faces[0],
150 function(lambda, phi) {
151 lambda *= degrees;
152 phi *= degrees;
153 for (var i = 0; i < faces.length; i++) {
154 if (
155 geoContains(
156 {
157 type: "Polygon",
158 coordinates: [
159 [
160 tetrahedron[i][0],
161 tetrahedron[i][1],
162 tetrahedron[i][2],
163 tetrahedron[i][0]
164 ]
165 ]
166 },
167 [lambda, phi]
168 )
169 ) {
170 return faces[i];
171 }
172 }
173 }
174 );
175
176 return p
177 .rotate([30, 180]) // North Pole aspect, needs clipPolygon
178 // .rotate([-30, 0]) // South Pole aspect
179 .angle(30)
180 .scale(118.662)
181 .translate([480, 195.47]);
182}