celestial-chart 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/form.js ADDED
@@ -0,0 +1,837 @@
1
+ import * as d3 from "./d3.js";
2
+ import { formats, formats_all, globalConfig, settings } from "./config.js";
3
+ import { Celestial } from "./core.js";
4
+ import { exportSVG } from "./svg.js";
5
+ import { euler, transformDeg } from "./transform.js";
6
+ import { findPos, has, isArray, isNumber, isObject, px, styles_ } from "./util.js";
7
+
8
+ // The settings form belonging to one map.
9
+ //
10
+ // It receives the instance rather than the configuration: the form belongs to
11
+ // the map, and its helper functions (fldEnable, setCenter, $form and the rest)
12
+ // live in this closure too. They used to be module-level functions reached
13
+ // through a shared "current map" pointer — which meant two interactive maps on
14
+ // one page would have written each other's fields.
15
+ function form(sky) {
16
+ var cfg = sky.cfg;
17
+ var config = settings.set(cfg);
18
+
19
+ var depends = {
20
+ "stars-show": ["stars-limit", "stars-colors", "stars-style-fill", "stars-designation", "stars-propername", "stars-size", "stars-exponent"],
21
+ "stars-designation": ["stars-designationType", "stars-designationLimit"],
22
+ "stars-propername": ["stars-propernameLimit", "stars-propernameType"],
23
+ "dsos-show": ["dsos-limit", "dsos-colors", "dsos-style-fill", "dsos-names", "dsos-size", "dsos-exponent"],
24
+ "dsos-names": ["dsos-namesType", "dsos-nameLimit"],
25
+ "mw-show": ["mw-style-opacity", "mw-style-fill"],
26
+ "constellations-names": ["constellations-namesType"],
27
+ "planets-show": ["planets-symbolType", "planets-names"],
28
+ "planets-names": ["planets-namesType"]
29
+ };
30
+
31
+ function enable(source) {
32
+ var fld = source.id, off;
33
+
34
+ switch (fld) {
35
+ case "stars-show":
36
+ off = !$form(fld).checked;
37
+ for (var i=0; i< depends[fld].length; i++) { fldEnable(depends[fld][i], off); }
38
+ /* falls through */
39
+ case "stars-designation":
40
+ off = !$form("stars-designation").checked || !$form("stars-show").checked;
41
+ for (i=0; i< depends["stars-designation"].length; i++) { fldEnable(depends["stars-designation"][i], off); }
42
+ /* falls through */
43
+ case "stars-propername":
44
+ off = !$form("stars-propername").checked || !$form("stars-show").checked;
45
+ for (i=0; i< depends["stars-propername"].length; i++) { fldEnable(depends["stars-propername"][i], off); }
46
+ break;
47
+ case "dsos-show":
48
+ off = !$form(fld).checked;
49
+ for (i=0; i< depends[fld].length; i++) { fldEnable(depends[fld][i], off); }
50
+ /* falls through */
51
+ case "dsos-names":
52
+ off = !$form("dsos-names").checked || !$form("dsos-show").checked;
53
+ for (i=0; i< depends["dsos-names"].length; i++) { fldEnable(depends["dsos-names"][i], off); }
54
+ break;
55
+ case "planets-show":
56
+ off = !$form(fld).checked;
57
+ for (i=0; i< depends[fld].length; i++) { fldEnable(depends[fld][i], off); }
58
+ /* falls through */
59
+ case "planets-names":
60
+ off = !$form("planets-names").checked || !$form("planets-show").checked;
61
+ for (i=0; i< depends["planets-names"].length; i++) { fldEnable(depends["planets-names"][i], off); }
62
+ break;
63
+ case "constellations-names":
64
+ case "mw-show":
65
+ off = !$form(fld).checked;
66
+ for (i=0; i< depends[fld].length; i++) { fldEnable(depends[fld][i], off); }
67
+ break;
68
+ }
69
+ }
70
+
71
+ function fldEnable(d, off) {
72
+ var node = $form(d);
73
+ if (!node) return;
74
+ node.disabled = off;
75
+ node.style.color = off ? "#999" : "#000";
76
+ node.previousSibling.style.color = off ? "#999" : "#000";
77
+ //if (node.previousSibling.previousSibling && node.previousSibling.previousSibling.tagName === "LABEL")
78
+ // node.previousSibling.previousSibling.style.color = off ? "#999" : "#000";
79
+ }
80
+
81
+ function setUnit(trans, old) {
82
+ var cx = $form("centerx");
83
+ if (!cx) return null;
84
+
85
+ if (old) {
86
+ if (trans === "equatorial" && old !== "equatorial") {
87
+ cx.value = (cx.value/15).toFixed(1);
88
+ if (cx.value < 0) cx.value += 24;
89
+ } else if (trans !== "equatorial" && old === "equatorial") {
90
+ cx.value = (cx.value * 15).toFixed(1);
91
+ if (cx.value > 180) cx.value -= 360;
92
+ }
93
+ }
94
+ if (trans === 'equatorial') {
95
+ cx.min = "0";
96
+ cx.max = "24";
97
+ $form("cxunit").innerHTML = "h";
98
+ } else {
99
+ cx.min = "-180";
100
+ cx.max = "180";
101
+ $form("cxunit").innerHTML = "\u00b0";
102
+ }
103
+ return cx.value;
104
+ }
105
+
106
+ function setCenter(ctr, trans) {
107
+ var cx = $form("centerx"), cy = $form("centery"), cz = $form("centerz");
108
+ if (!cx || !cy) return;
109
+
110
+ if (ctr === null || ctr.length < 1) ctr = [0,0,0];
111
+ if (ctr.length <= 2 || ctr[2] === undefined) ctr[2] = 0;
112
+ //config.center = ctr;
113
+ if (trans !== "equatorial") cx.value = ctr[0].toFixed(1);
114
+ else cx.value = ctr[0] < 0 ? (ctr[0] / 15 + 24).toFixed(1) : (ctr[0] / 15).toFixed(1);
115
+
116
+ cy.value = ctr[1].toFixed(1);
117
+ cz.value = ctr[2] !== null ? ctr[2].toFixed(1) : 0;
118
+ settings.set({center: ctr});
119
+ }
120
+
121
+ function setLimits() {
122
+ var t, rx = /\d+(\.\d+)?/g,
123
+ s, d, res = {s:6, d:6},
124
+ config = Celestial.settings();
125
+
126
+ d = config.dsos.data;
127
+
128
+ //test dso limit
129
+ t = d.match(rx);
130
+ if (t !== null) {
131
+ res.d = parseFloat(t[t.length-1]);
132
+ }
133
+
134
+ if (res.d !== 6) {
135
+ $form("dsos-limit").max = res.d;
136
+ $form("dsos-nameLimit").max = res.d;
137
+ }
138
+
139
+ s = config.stars.data;
140
+
141
+ //test star limit
142
+ t = s.match(rx);
143
+ if (t !== null) {
144
+ res.s = parseFloat(t[t.length-1]);
145
+ }
146
+
147
+ if (res.s != 6) {
148
+ $form("stars-limit").max = res.s;
149
+ $form("stars-designationLimit").max = res.s;
150
+ $form("stars-propernameLimit").max = res.s;
151
+ }
152
+
153
+ return res;
154
+ }
155
+
156
+ function showAdvanced(showit) {
157
+ var vis = showit ? "inline-block" : "none";
158
+ d3.select(sky.parentElement + " ~ #celestial-form").selectAll(".advanced").style("display", vis);
159
+ d3.select(sky.parentElement + " ~ #celestial-form").selectAll("#label-propername").style("display", showit ? "none" : "inline-block");
160
+ }
161
+
162
+ function setVisibility(cfg, which) {
163
+ var vis, fld;
164
+ if (!has(cfg, "formFields")) return;
165
+ if (which && has(cfg.formFields, which)) {
166
+ styles_(d3.select(sky.parentElement + " ~ #celestial-form").select("#" + which), {"display": "none"});
167
+ return;
168
+ }
169
+ // Special case for backward compatibility
170
+ if (cfg.form === false && cfg.location === true) {
171
+ d3.select(sky.parentElement + " ~ #celestial-form").style("display", "inline-block");
172
+ for (fld in cfg.formFields) {
173
+ if (!has(cfg.formFields, fld)) continue;
174
+ if (fld === "location") continue;
175
+ styles_(d3.select(sky.parentElement + " ~ #celestial-form").select("#" + fld), {"display": "none"});
176
+ }
177
+ return;
178
+ }
179
+ // hide if not desired
180
+ if (cfg.form === false) d3.select(sky.parentElement + " ~ #celestial-form").style("display", "none");
181
+
182
+ for (fld in cfg.formFields) {
183
+ if (!has(cfg.formFields, fld)) continue;
184
+ if (fld === "location") continue;
185
+ vis = cfg.formFields[fld] === false ? "none" : "block";
186
+ styles_(d3.select(sky.parentElement + " ~ #celestial-form").select("#" + fld), {"display": vis});
187
+ }
188
+
189
+ }
190
+
191
+ function listConstellations() {
192
+ var sel = d3.select(sky.parentElement + " ~ #celestial-form").select("#constellation"),
193
+ list = [], selected = 0, id, name, config = sky.cfg;
194
+
195
+ sky.container.selectAll(".constname").each( function(d, i) {
196
+ id = d.id;
197
+ if (id === config.constellation) selected = i;
198
+ name = d.properties[config.constellations.namesType];
199
+ if (name !== id) name += " (" + id + ")";
200
+ list.push({o:id, n:name});
201
+ });
202
+ if (list.length < 1 || sel.length < 1) {
203
+ setTimeout(listConstellations, 1000);
204
+ return;
205
+ }
206
+ list = [{o:"---", n:"(Select constellation)"}].concat(list);
207
+
208
+ sel.selectAll('option').remove();
209
+ sel.selectAll('option').data(list).enter().append('option')
210
+ .attr("value", function (d) { return d.o; })
211
+ .text(function (d) { return d.n; });
212
+ sel.property("selectedIndex", selected);
213
+ //$form("constellation").firstChild.disabled = true;
214
+
215
+ //sky.constellations = list;
216
+ }
217
+
218
+ function $form(id) { return document.querySelector(sky.parentElement + " ~ #celestial-form" + " #" + id); }
219
+
220
+
221
+ var prj = Celestial.projections(), leo = Celestial.eulerAngles();
222
+ var div = d3.select(sky.parentElement + " ~ #celestial-form");
223
+ //if div doesn't exist, create it
224
+ if (div.size() < 1) {
225
+ //var container = (config.container || "celestial-map");
226
+ div = d3.select(sky.parentElement).select(function() { return this.parentNode; }).append("div").attr("id", "celestial-form");
227
+ } else {
228
+ // Empty the existing form before rebuilding it. Without this, every
229
+ // Celestial.display() call appended another complete form: after six calls
230
+ // there were 469 fields instead of 67, sharing ids — and the $("...")
231
+ // lookups always found the first one. (A symptom of #96 and #131.)
232
+ div.selectAll("*").remove();
233
+ }
234
+ var ctrl = div.append("div").attr("class", "ctrl");
235
+ var frm = ctrl.append("form").attr("id", "params").attr("name", "params").attr("method", "get").attr("action" ,"#");
236
+
237
+ //Map parameters
238
+ var col = frm.append("div").attr("class", "col").attr("id", "general");
239
+
240
+ col.append("label").attr("title", "Map width in pixel, 0 indicates full width").attr("for", "width").html("Width ");
241
+ col.append("input").attr("type", "number").attr("maxlength", "4").attr("max", "20000").attr("min", "0").attr("title", "Map width").attr("id", "width").attr("value", config.width).on("change", resize);
242
+ col.append("span").html("px");
243
+
244
+ col.append("label").attr("title", "Map projection, (hemi) indicates hemispherical projection").attr("for", "projection").html("Projection");
245
+ var sel = col.append("select").attr("id", "projection").on("change", reproject);
246
+ var selected = 0;
247
+ var list = Object.keys(prj).map( function (key, i) {
248
+ var n = prj[key].clip && prj[key].clip === true ? prj[key].n + " (hemi)" : prj[key].n;
249
+ if (key === config.projection) selected = i;
250
+ return {o:key, n:n};
251
+ });
252
+ sel.selectAll('option').data(list).enter().append('option')
253
+ .attr("value", function (d) { return d.o; })
254
+ .text(function (d) { return d.n; });
255
+ sel.property("selectedIndex", selected);
256
+
257
+ selected = 0;
258
+ col.append("label").attr("title", "Coordinate space in which the map is displayed").attr("for", "transform").html("Coordinates");
259
+ sel = col.append("select").attr("id", "transform").on("change", reload);
260
+ list = Object.keys(leo).map(function (key, i) {
261
+ if (key === config.transform) selected = i;
262
+ return {o:key, n:key.replace(/^([a-z])/, function(s, m) { return m.toUpperCase(); } )};
263
+ });
264
+ sel.selectAll("option").data(list).enter().append('option')
265
+ .attr("value", function (d) { return d.o; })
266
+ .text(function (d) { return d.n; });
267
+ sel.property("selectedIndex", selected);
268
+
269
+ col.append("br");
270
+ col.append("label").attr("title", "Center coordinates long/lat in selected coordinate space").attr("for", "centerx").html("Center");
271
+ col.append("input").attr("type", "number").attr("id", "centerx").attr("title", "Center right ascension/longitude").attr("max", "24").attr("min", "0").attr("step", "0.1").on("change", turn);
272
+ col.append("span").attr("id", "cxunit").html("h");
273
+ //addList("centerx", "ra");
274
+
275
+ col.append("input").attr("type", "number").attr("id", "centery").attr("title", "Center declination/latitude").attr("max", "90").attr("min", "-90").attr("step", "0.1").on("change", turn);
276
+ col.append("span").html("\u00b0");
277
+
278
+ col.append("label").attr("title", "Orientation").attr("for", "centerz").html("Orientation");
279
+ col.append("input").attr("type", "number").attr("id", "centerz").attr("title", "Center orientation").attr("max", "180").attr("min", "-180").attr("step", "0.1").on("change", turn);
280
+ col.append("span").html("\u00b0");
281
+
282
+ col.append("label").attr("for", "orientationfixed").attr("class", "advanced").html("Fixed");
283
+ col.append("input").attr("type", "checkbox").attr("id", "orientationfixed").attr("class", "advanced").property("checked", config.orientationfixed).on("change", apply);
284
+
285
+ col.append("label").attr("title", "Center and zoom in on this constellation").attr("for", "constellation").html("Show");
286
+ col.append("select").attr("id", "constellation").on("change", showConstellation);
287
+
288
+ setCenter(config.center, config.transform);
289
+
290
+ // Stars
291
+ col = frm.append("div").attr("class", "col").attr("id", "stars");
292
+
293
+ col.append("label").attr("class", "header").attr("for", "stars-show").html("Stars");
294
+ col.append("input").attr("type", "checkbox").attr("id", "stars-show").property("checked", config.stars.show).on("change", apply);
295
+
296
+ col.append("label").attr("for", "stars-limit").html("down to magnitude");
297
+ col.append("input").attr("type", "number").attr("id", "stars-limit").attr("title", "Star display limit (magnitude)").attr("value", config.stars.limit).attr("max", "6").attr("min", "-1").attr("step", "0.1").on("change", apply);
298
+
299
+ col.append("label").attr("for", "stars-colors").html("with spectral colors");
300
+ col.append("input").attr("type", "checkbox").attr("id", "stars-colors").property("checked", config.stars.colors).on("change", apply);
301
+
302
+ col.append("label").attr("for", "stars-color").html("or default color ");
303
+ col.append("input").attr("type", "color").attr("autocomplete", "off").attr("id", "stars-style-fill").attr("title", "Star color").property("value", config.stars.style.fill).on("change", apply);
304
+
305
+ col.append("br");
306
+
307
+ var names = formats.starnames[config.culture] || formats.starnames.iau;
308
+
309
+ for (var fld in names) {
310
+ if (!has(names, fld)) continue;
311
+ var keys = Object.keys(names[fld]);
312
+ if (keys.length > 1) {
313
+ //Select List
314
+ col.append("label").attr("for", "stars-" + fld).html("Show");
315
+
316
+ selected = 0;
317
+ col.append("label").attr("title", "Type of star name").attr("id", "label-propername").attr("for", "stars-" + fld + "Type").html(function () { return fld === "propername" ? "proper names" : ""; });
318
+ sel = col.append("select").attr("id", "stars-" + fld + "Type").attr("class", function () { return fld === "propername" ? "advanced" : ""; }).on("change", apply);
319
+ list = keys.map(function (key, i) {
320
+ if (key === config.stars[fld + "Type"]) selected = i;
321
+ return {o:key, n:names[fld][key]};
322
+ });
323
+ sel.selectAll("option").data(list).enter().append('option')
324
+ .attr("value", function (d) { return d.o; })
325
+ .text(function (d) { return d.n; });
326
+ sel.property("selectedIndex", selected);
327
+
328
+ col.append("input").attr("type", "checkbox").attr("id", "stars-" + fld).property("checked", config.stars[fld]).on("change", apply);
329
+ } else if (keys.length === 1) {
330
+ //Simple field
331
+ col.append("label").attr("for", "stars-" + fld).html(" " + names[fld][keys[0]]);
332
+ col.append("input").attr("type", "checkbox").attr("id", "stars-" + fld).property("checked", config.stars[fld]).on("change", apply);
333
+ }
334
+ col.append("label").attr("for", "stars-" + fld + "Limit").html("down to mag");
335
+ col.append("input").attr("type", "number").attr("id", "stars-" + fld + "Limit").attr("title", "Star name display limit (magnitude)").attr("value", config.stars[fld + "Limit"]).attr("max", "6").attr("min", "-1").attr("step", "0.1").on("change", apply);
336
+
337
+ }
338
+
339
+ col.append("br");
340
+
341
+ col.append("label").attr("for", "stars-size").attr("class", "advanced").html("Stellar disk size: base");
342
+ col.append("input").attr("type", "number").attr("id", "stars-size").attr("class", "advanced").attr("title", "Size of the displayed star disk; base").attr("value", config.stars.size).attr("max", "100").attr("min", "0").attr("step", "0.1").on("change", apply);
343
+
344
+ col.append("label").attr("for", "stars-exponent").attr("class", "advanced").html(" * e ^ (exponent");
345
+ col.append("input").attr("type", "number").attr("id", "stars-exponent").attr("class", "advanced").attr("title", "Size of the displayed star disk; exponent").attr("value", config.stars.exponent).attr("max", "3").attr("min", "-1").attr("step", "0.01").on("change", apply);
346
+ col.append("span").attr("class", "advanced").text(" * (magnitude + 2)) [* adaptation]");
347
+
348
+ enable($form("stars-show"));
349
+
350
+ // DSOs
351
+ col = frm.append("div").attr("class", "col").attr("id", "dsos");
352
+
353
+ col.append("label").attr("class", "header").attr("title", "Deep Space Objects").attr("for", "dsos-show").html("DSOs");
354
+ col.append("input").attr("type", "checkbox").attr("id", "dsos-show").property("checked", config.dsos.show).on("change", apply);
355
+
356
+ col.append("label").attr("for", "dsos-limit").html("down to mag");
357
+ col.append("input").attr("type", "number").attr("id", "dsos-limit").attr("title", "DSO display limit (magnitude)").attr("value", config.dsos.limit).attr("max", "6").attr("min", "0").attr("step", "0.1").on("change", apply);
358
+
359
+
360
+ col.append("label").attr("for", "dsos-colors").html("with symbol colors");
361
+ col.append("input").attr("type", "checkbox").attr("id", "dsos-colors").property("checked", config.dsos.colors).on("change", apply);
362
+
363
+ col.append("label").attr("for", "dsos-color").html("or default color ");
364
+ col.append("input").attr("type", "color").attr("autocomplete", "off").attr("id", "dsos-style-fill").attr("title", "DSO color").property("value", config.dsos.style.fill).on("change", apply);
365
+
366
+ col.append("br");
367
+
368
+ names = formats.dsonames[config.culture] || formats.dsonames.iau;
369
+
370
+ for (fld in names) {
371
+ if (!has(names, fld)) continue;
372
+ var dsoKeys = Object.keys(names[fld]);
373
+ col.append("label").attr("for", "dsos-" + fld).html("Show");
374
+
375
+ selected = 0;
376
+ col.append("label").attr("title", "Type of DSO name").attr("for", "dsos-" + fld + "Type").attr("class", "advanced").html("");
377
+ sel = col.append("select").attr("id", "dsos-" + fld + "Type").attr("class", "advanced").on("change", apply);
378
+ list = dsoKeys.map(function (key, i) {
379
+ if (key === config.stars[fld + "Type"]) selected = i;
380
+ return {o:key, n:names[fld][key]};
381
+ });
382
+ sel.selectAll("option").data(list).enter().append('option')
383
+ .attr("value", function (d) { return d.o; })
384
+ .text(function (d) { return d.n; });
385
+ sel.property("selectedIndex", selected);
386
+
387
+ col.append("label").attr("for", "dsos-" + fld).html("names");
388
+ col.append("input").attr("type", "checkbox").attr("id", "dsos-" + fld).property("checked", config.dsos[fld]).on("change", apply);
389
+ }
390
+
391
+ col.append("label").attr("for", "dsos-nameLimit").html("down to mag");
392
+ col.append("input").attr("type", "number").attr("id", "dsos-nameLimit").attr("title", "DSO name display limit (magnitude)").attr("value", config.dsos.nameLimit).attr("max", "6").attr("min", "0").attr("step", "0.1").on("change", apply);
393
+ col.append("br");
394
+
395
+ col.append("label").attr("for", "dsos-size").attr("class", "advanced").html("DSO symbol size: (base");
396
+ col.append("input").attr("type", "number").attr("id", "dsos-size").attr("class", "advanced").attr("title", "Size of the displayed symbol: base").attr("value", config.dsos.size).attr("max", "100").attr("min", "0").attr("step", "0.1").on("change", apply);
397
+
398
+ col.append("label").attr("for", "dsos-exponent").attr("class", "advanced").html(" * 2 [* adaptation] - magnitude) ^ exponent");
399
+ col.append("input").attr("type", "number").attr("id", "dsos-exponent").attr("class", "advanced").attr("title", "Size of the displayed symbol; exponent").attr("value", config.dsos.exponent).attr("max", "3").attr("min", "-1").attr("step", "0.01").on("change", apply);
400
+
401
+ enable($form("dsos-show"));
402
+
403
+ // Constellations
404
+ col = frm.append("div").attr("class", "col").attr("id", "constellations");
405
+ col.append("label").attr("class", "header").html("Constellations");
406
+ //col.append("input").attr("type", "checkbox").attr("id", "constellations-show").property("checked", config.constellations.show).on("change", apply);
407
+
408
+
409
+ names = formats.constellations[config.culture] || formats.constellations.iau;
410
+
411
+ for (fld in names) {
412
+ if (!has(names, fld)) continue;
413
+ var nameKeys = Object.keys(names[fld]);
414
+ if (nameKeys.length > 1) {
415
+ //Select List
416
+ col.append("label").attr("for", "constellations-" + fld).html("Show");
417
+
418
+ selected = 0;
419
+ col.append("label").attr("title", "Language of constellation names").attr("for", "constellations-" + fld + "Type").attr("class", "advanced").html("");
420
+ sel = col.append("select").attr("id", "constellations-" + fld + "Type").attr("class", "advanced").on("change", apply);
421
+ list = nameKeys.map(function (key, i) {
422
+ if (key === config.constellations[fld + "Type"]) selected = i;
423
+ return {o:key, n:names[fld][key]};
424
+ });
425
+ sel.selectAll("option").data(list).enter().append('option')
426
+ .attr("value", function (d) { return d.o; })
427
+ .text(function (d) { return d.n; });
428
+ sel.property("selectedIndex", selected);
429
+
430
+ col.append("label").attr("for", "constellations-" + fld).html("names");
431
+ col.append("input").attr("type", "checkbox").attr("id", "constellations-" + fld).property("checked", config.constellations[fld]).on("change", apply);
432
+ } else if (nameKeys.length === 1) {
433
+ //Simple field
434
+ col.append("label").attr("for", "constellations-" + fld).attr("class", "advanced").html(" " + names[fld][nameKeys[0]]);
435
+ col.append("input").attr("type", "checkbox").attr("id", "constellations-" + fld).attr("class", "advanced").property("checked", config.constellations[fld]).on("change", apply);
436
+ }
437
+ }
438
+ col.append("label").attr("for", "constellations-lines").html(" lines");
439
+ col.append("input").attr("type", "checkbox").attr("id", "constellations-lines").property("checked", config.constellations.lines).on("change", apply);
440
+
441
+ col.append("label").attr("for", "constellations-bounds").html(" boundaries");
442
+ col.append("input").attr("type", "checkbox").attr("id", "constellations-bounds").property("checked", config.constellations.bounds).on("change", apply);
443
+
444
+ enable($form("constellations-names"));
445
+
446
+ // graticules & planes
447
+ col = frm.append("div").attr("class", "col").attr("id", "lines");
448
+ col.append("label").attr("class", "header").html("Lines");
449
+
450
+ col.append("label").attr("title", "Latitude/longitude grid lines").attr("for", "lines-graticule").html("Graticule");
451
+ col.append("input").attr("type", "checkbox").attr("id", "lines-graticule-show").property("checked", config.lines.graticule.show).on("change", apply);
452
+
453
+ col.append("label").attr("for", "lines-equatorial").html("Equator");
454
+ col.append("input").attr("type", "checkbox").attr("id", "lines-equatorial-show").property("checked", config.lines.equatorial.show).on("change", apply);
455
+
456
+ col.append("label").attr("for", "lines-ecliptic").html("Ecliptic");
457
+ col.append("input").attr("type", "checkbox").attr("id", "lines-ecliptic-show").property("checked", config.lines.ecliptic.show).on("change", apply);
458
+
459
+ col.append("label").attr("for", "lines-galactic").html("Galactic plane");
460
+ col.append("input").attr("type", "checkbox").attr("id", "lines-galactic-show").property("checked", config.lines.galactic.show).on("change", apply);
461
+
462
+ col.append("label").attr("for", "lines-supergalactic").html("Supergalactic plane");
463
+ col.append("input").attr("type", "checkbox").attr("id", "lines-supergalactic-show").property("checked", config.lines.supergalactic.show).on("change", apply);
464
+
465
+ // Other
466
+ col = frm.append("div").attr("class", "col").attr("id", "other");
467
+ col.append("label").attr("class", "header").html("Other");
468
+
469
+ col.append("label").attr("for", "mw-show").html("Milky Way");
470
+ col.append("input").attr("type", "checkbox").attr("id", "mw-show").property("checked", config.mw.show).on("change", apply);
471
+
472
+ col.append("label").attr("for", "mw-style-fill").attr("class", "advanced").html(" color");
473
+ col.append("input").attr("type", "color").attr("id", "mw-style-fill").attr("class", "advanced").attr("title", "Milky Way color").attr("value", config.mw.style.fill).on("change", apply);
474
+
475
+ col.append("label").attr("for", "mw-style-opacity").attr("class", "advanced").html(" opacity");
476
+ col.append("input").attr("type", "number").attr("id", "mw-style-opacity").attr("class", "advanced").attr("title", "Transparency of each Milky Way layer").attr("value", config.mw.style.opacity).attr("max", "1").attr("min", "0").attr("step", "0.01").on("change", apply);
477
+
478
+ col.append("label").attr("for", "advanced").html("Advanced options");
479
+ col.append("input").attr("type", "checkbox").attr("id", "advanced").property("checked", config.advanced).on("change", apply);
480
+
481
+ col.append("br");
482
+
483
+ col.append("label").attr("for", "background-fill").html("Background color");
484
+ col.append("input").attr("type", "color").attr("id", "background-fill").attr("title", "Background color").attr("value", config.background.fill).on("change", apply);
485
+
486
+ col.append("label").attr("for", "background-opacity").attr("class", "advanced").html("opacity");
487
+ col.append("input").attr("type", "number").attr("id", "background-opacity").attr("class", "advanced").attr("title", "Background opacity").attr("value", config.background.opacity).attr("max", "1").attr("min", "0").attr("step", "0.01").on("change", apply);
488
+
489
+ col.append("label").attr("title", "Star/DSO sizes are increased with higher zoom-levels").attr("for", "adaptable").attr("class", "advanced").html("Adaptable object sizes");
490
+ col.append("input").attr("type", "checkbox").attr("id", "adaptable").attr("class", "advanced").property("checked", config.adaptable).on("change", apply);
491
+
492
+ // General language setting
493
+ var langKeys = formats_all[config.culture];
494
+
495
+ selected = 0;
496
+ col.append("label").attr("title", "General language setting").attr("for", "lang").html("Object names ");
497
+ sel = col.append("select").attr("id", "lang").on("change", apply);
498
+ list = langKeys.map(function (key, i) {
499
+ if (key === config.lang) selected = i;
500
+ return {o:key, n:formats.constellations[config.culture].names[key]};
501
+ });
502
+ list = [{o:"---", n:"(Select language)"}].concat(list);
503
+ sel.selectAll("option").data(list).enter().append('option')
504
+ .attr("value", function (d) { return d.o; })
505
+ .text(function (d) { return d.n; });
506
+ sel.property("selectedIndex", selected);
507
+
508
+ col = frm.append("div").attr("class", "col").attr("id", "download");
509
+ col.append("label").attr("class", "header").html("Download");
510
+
511
+ col.append("input").attr("type", "button").attr("id", "download-png").attr("value", "PNG Image").on("click", function() {
512
+ var a = d3.select("body").append("a").node(),
513
+ canvas = document.querySelector(sky.parentElement + ' canvas');
514
+ a.download = getFilename(".png");
515
+ a.rel = "noopener";
516
+ a.href = canvas.toDataURL('image/png').replace('image/png', 'image/octet-stream');
517
+ a.click();
518
+ d3.select(a).remove();
519
+ });
520
+
521
+ col.append("input").attr("type", "button").attr("id", "download-svg").attr("value", "SVG File").on("click", function() {
522
+ exportSVG(getFilename(".svg"));
523
+ return false;
524
+ });
525
+
526
+ setLimits();
527
+ setUnit(config.transform);
528
+ setVisibility(cfg);
529
+ showAdvanced(config.advanced);
530
+
531
+ function resize() {
532
+ var src = this,
533
+ w = src.value;
534
+ if (testNumber(src) === false) return;
535
+ config.width = w;
536
+ sky.resize({width:w});
537
+ }
538
+
539
+ function reload() {
540
+ var src = this,
541
+ trans = src.value,
542
+ cx = setUnit(trans, config.transform);
543
+ if (cx !== null) config.center[0] = cx;
544
+ config.transform = trans;
545
+ settings.set(config);
546
+ sky.reload(config);
547
+ }
548
+
549
+ function reproject() {
550
+ var src = this;
551
+ if (!src) return;
552
+ config.projection = src.value;
553
+ settings.set(config);
554
+ sky.reproject(config);
555
+ }
556
+
557
+ function turn() {
558
+ if (testNumber(this) === false) return;
559
+ if (getCenter() === false) return;
560
+ sky.rotate(config);
561
+ }
562
+
563
+ function getCenter() {
564
+ var cx = $form("centerx"), cy = $form("centery"), cz = $form("centerz"),
565
+ rot = [];
566
+
567
+ if (!cx || !cy) return;
568
+
569
+ if (config.transform !== "equatorial") config.center[0] = parseFloat(cx.value);
570
+ else {
571
+ var vx = parseFloat(cx.value);
572
+ config.center[0] = vx > 12 ? vx * 15 - 360 : vx * 15;
573
+ }
574
+ config.center[1] = parseFloat(cy.value);
575
+
576
+ var vz = parseFloat(cz.value);
577
+ config.center[2] = isNaN(vz) ? 0 : vz;
578
+
579
+ return cx.value !== "" && cy.value !== "";
580
+ }
581
+
582
+ function getFilename(ext) {
583
+ var dateFormat = d3.timeFormat("%Y%m%dT%H%M%S%Z"),
584
+ filename = "d3-celestial",
585
+ dt = sky.date();
586
+ if (dt) filename += dateFormat(dt);
587
+ return filename + ext;
588
+ }
589
+
590
+ function showConstellation() {
591
+ var id = this.value;
592
+ if (!id) return;
593
+ showCon(id);
594
+ }
595
+
596
+ function showCon(id) {
597
+ var z, anims = [],
598
+ config = globalConfig;
599
+ if (id === "---") {
600
+ sky.constellation = null;
601
+ z = sky.zoomBy();
602
+ if (z !== 1) {
603
+ anims.push({param:"zoom", value:1/z, duration:0});
604
+ }
605
+ sky.animate(anims, false);
606
+ //sky.redraw();
607
+ return;
608
+ }
609
+ if (!isObject(sky.constellations) || !has(sky.constellations, id)) return;
610
+
611
+ var con = sky.constellations[id];
612
+ //transform according to settings
613
+ var center = transformDeg(con.center, euler[config.transform]);
614
+ config.center = center;
615
+ setCenter(config.center, config.transform);
616
+ //config.lines.graticule.lat.pos = [Round(con.center[0])];
617
+ //config.lines.graticule.lon.pos = [Round(con.center[1])];
618
+ //sky.apply(config);
619
+
620
+ //if zoomed, zoom out
621
+ z = sky.zoomBy();
622
+ if (z !== 1) anims.push({param:"zoom", value:1/z, duration:0});
623
+ //rotate
624
+ anims.push({param:"center", value:center, duration:0});
625
+ //and zoom in
626
+ var sc = 1 + (360/con.scale); // > 10 ? 10 : con.scale;
627
+ anims.push({param:"zoom", value:sc, duration:0});
628
+ sky.constellation = id;
629
+ //Object.assign(globalConfig, config);
630
+ sky.animate(anims, false);
631
+ }
632
+
633
+ function apply() {
634
+ var value, src = this;
635
+ //Get current configuration
636
+ Object.assign(config, settings.set());
637
+
638
+ switch (src.type) {
639
+ case "checkbox": value = src.checked; enable(src); break;
640
+ case "number": if (testNumber(src) === false) return;
641
+ value = parseFloat(src.value); break;
642
+ case "color": if (testColor(src) === false) return;
643
+ value = src.value; break;
644
+ case "text": if (src.id.search(/fill$/) === -1) return;
645
+ if (testColor(src) === false) return;
646
+ value = src.value; break;
647
+ case "select-one": value = src.value; break;
648
+ }
649
+ if (value === null) return;
650
+ set(src.id, value);
651
+ if (src.id === "dsos-style-fill") {
652
+ set("dsos-style-stroke", value);
653
+ set("dsos-nameStyle-fill", value);
654
+ } else if (src.id === "constellations-namesType") {
655
+ listConstellations();
656
+ } else if (src.id === "lang") {
657
+ setLanguage(value);
658
+ } else if (src.id === "advanced") {
659
+ showAdvanced(value);
660
+ }
661
+
662
+ getCenter();
663
+ Object.assign(globalConfig, config);
664
+ sky.apply(config);
665
+ }
666
+
667
+ function set(prop, val) {
668
+ var a = prop.split("-");
669
+ switch (a.length) {
670
+ case 1: config[a[0]] = val; break;
671
+ case 2: config[a[0]][a[1]] = val; break;
672
+ case 3: config[a[0]][a[1]][a[2]] = val; break;
673
+ default: return;
674
+ }
675
+ }
676
+
677
+
678
+ function setLanguage(lang) {
679
+ Object.assign(config, globalConfig);
680
+ config.lang = lang;
681
+ var keys = ["constellations", "planets"];
682
+ for (var i=0; i < keys.length; i++) {
683
+ if (has(formats[keys[i]][config.culture].names, lang)) config[keys[i]].namesType = lang;
684
+ else if (has(formats[keys[i]][config.culture].names, "desig")) config[keys[i]].namesType = "desig";
685
+ else config[keys[i]].namesType = "name";
686
+ }
687
+ if (has(formats.dsonames[config.culture].names, lang)) config.dsos.namesType = lang;
688
+ else config.dsos.namesType = "desig";
689
+ if (has(formats.starnames[config.culture].propername, lang)) config.stars.propernameType = lang;
690
+ else config.stars.propernameType = "desig";
691
+ //update cont. list
692
+ Object.assign(globalConfig, config);
693
+ update();
694
+ listConstellations();
695
+ return config;
696
+ }
697
+
698
+
699
+ function update() {
700
+ // Update all form fields
701
+ d3.selectAll(sky.parentElement + " ~ #celestial-form input, " + sky.parentElement + " ~ #celestial-form select").each( function(d, i) {
702
+ if (this === undefined) return;
703
+ var id = this.id;
704
+
705
+ // geopos -> lat, lon
706
+ if (id === "lat" || id === "lon") {
707
+ if (isArray(config.geopos)) this.value = id === "lat" ? config.geopos[0] : config.geopos[1];
708
+ // center -> centerx, centery
709
+ } else if (id.search(/center/) !== -1) {
710
+ if (isArray(config.center)) {
711
+ switch (id) {
712
+ case "centerx": this.value = config.center[0]; break;
713
+ case "centery": this.value = config.center[1]; break;
714
+ case "centerz": this.value = config.center[2] || 0; break;
715
+ }
716
+ }
717
+ } else if (id === "datetime" || id === "hr" || id === "min" || id === "sec" || id === "tz") {
718
+ return;//skip, timezone?
719
+ } else if (this.type !== "button") {
720
+ var value = get(id);
721
+ switch (this.type) {
722
+ case "checkbox": this.checked = value; enable(id); break;
723
+ case "number": if (testNumber(this) === false) break;
724
+ this.value = parseFloat(get(id)); break;
725
+ case "color": if (testColor(this) === false) break;
726
+ this.value = value; break;
727
+ case "text": if (id.search(/fill$/) === -1) break;
728
+ if (testColor(this) === false) break;
729
+ this.value = value; break;
730
+ case "select-one": this.value = value; break;
731
+ }
732
+ }
733
+ });
734
+ }
735
+
736
+ function get(id) {
737
+ var a = id.split("-");
738
+ switch (a.length) {
739
+ case 1: return config[a[0]];
740
+ case 2: return config[a[0]][a[1]];
741
+ case 3: return config[a[0]][a[1]][a[2]];
742
+ default: return;
743
+ }
744
+ }
745
+
746
+ sky.updateForm = update;
747
+ sky.showConstellation = showCon;
748
+ sky.setLanguage = function(lang) {
749
+ var cfg = settings.set();
750
+ if (formats_all[config.culture].indexOf(lang) !== -1) cfg = setLanguage(lang);
751
+ return cfg;
752
+ };
753
+ // This map's own form interface. These used to be module-level functions
754
+ // reached through a shared "current map" pointer — which is why two
755
+ // interactive maps on one page would have written each other's fields.
756
+ return {
757
+ "$form": $form,
758
+ "enable": enable,
759
+ "fldEnable": fldEnable,
760
+ "listConstellations": listConstellations,
761
+ "setCenter": setCenter,
762
+ "setLimits": setLimits,
763
+ "setVisibility": setVisibility,
764
+ "showAdvanced": showAdvanced
765
+ };
766
+
767
+ }
768
+
769
+
770
+ // Dependend fields relations
771
+
772
+
773
+ // De/activate fields depending on selection of dependencies
774
+
775
+
776
+ // Enable/disable field d to status off
777
+
778
+
779
+ // Error notification
780
+ function popError(nd, err) {
781
+ var p = findPos(nd);
782
+ styles_(d3.select("#error").html(err), {top:px(p[1] + nd.offsetHeight + 1), left:px(p[0]), opacity:1});
783
+ nd.focus();
784
+ }
785
+
786
+ //Check numeric field
787
+ function testNumber(node) {
788
+ var v, adj = node.id === "hr" || node.id === "min" || node.id === "sec" ? 1 : 0;
789
+ if (node.validity) {
790
+ v = node.validity;
791
+ if (v.typeMismatch || v.badInput) { popError(node, node.title + ": check field value"); return false; }
792
+ if (v.rangeOverflow || v.rangeUnderflow) { popError(node, node.title + " must be between " + (parseInt(node.min) + adj) + " and " + (parseInt(node.max) - adj)); return false; }
793
+ } else {
794
+ v = node.value;
795
+ if (!isNumber(v)) { popError(node, node.title + ": check field value"); return false; }
796
+ v = parseFloat(v);
797
+ if (v < node.min || v > node.max ) { popError(node, node.title + " must be between " + (node.min + adj) + " and " + (+node.max - adj)); return false; }
798
+ }
799
+ styles_(d3.select("#error"), {top:"-9999px", left:"-9999px", opacity:0});
800
+ return true;
801
+ }
802
+
803
+ //Check color field
804
+ function testColor(node) {
805
+ var v;
806
+ if (node.validity) {
807
+ v = node.validity;
808
+ if (v.typeMismatch || v.badInput) { popError(node, node.title + ": check field value"); return false; }
809
+ if (node.value.search(/^#[0-9A-F]{6}$/i) === -1) { popError(node, node.title + ": not a color value"); return false; }
810
+ } else {
811
+ v = node.value;
812
+ if (v === "") return true;
813
+ if (v.search(/^#[0-9A-F]{6}$/i) === -1) { popError(node, node.title + ": not a color value"); return false; }
814
+ }
815
+ styles_(d3.select("#error"), {top:"-9999px", left:"-9999px", opacity:0});
816
+ return true;
817
+ }
818
+
819
+
820
+
821
+
822
+
823
+ // Set max input limits depending on data
824
+
825
+
826
+ // Options only visible in advanced mode
827
+ //"stars-designationType", "stars-propernameType", "stars-size", "stars-exponent", "stars-size", "stars-exponent", //"constellations-namesType", "planets-namesType", "planets-symbolType"
828
+
829
+
830
+
831
+
832
+
833
+
834
+
835
+
836
+
837
+ export { form, popError, testColor, testNumber };