-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathflowchart.html
More file actions
375 lines (359 loc) · 16.4 KB
/
flowchart.html
File metadata and controls
375 lines (359 loc) · 16.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>flowchart.html</title>
<style type="text/css">
.end-element { fill : #FFCCFF; }
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/raphael/2.3.0/raphael.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/flowchart/1.17.1/flowchart.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.1/js/bootstrap.bundle.min.js"></script>
<!-- <script src="../release/flowchart.min.js"></script> -->
<script>
window.onload = function () {
var btn = document.getElementById("run"),
cd = document.getElementById("code"),
chart;
(btn.onclick = function () {
var code = cd.value;
if (chart) {
chart.clean();
}
chart = flowchart.parse(code);
chart.drawSVG('canvas', {
'x': 0,
'y': 0,
'line-width': 3,
//'maxWidth': 15,//ensures the flowcharts fits within a certain width
'line-length': 50,
'text-margin': 10,
'font-size': 14,
'font': 'normal',
'font-family': 'Helvetica',
'font-weight': 'normal',
'font-color': 'black',
'line-color': 'black',
'element-color': 'black',
'fill': 'white',
'yes-text': 'yes',
'no-text': 'no',
'arrow-end': 'block',
'scale': 1,
'symbols': {
'start': {
'font-size': 14,
'font-color': 'yellow',
'element-color': 'blue',
'fill': 'green',
'class': 'start-element'
},
'inputoutput': {
'font-color': 'black',
'element-color': 'black',
'fill': 'bisque'
},
'operation': {
'font-color': 'black',
'element-color': 'black',
'fill': 'linen'
},
'subroutine': {
'font-color': 'black',
'element-color': 'blue',
'fill': 'lightgreen'
},
'condition': {
'font-color': 'red',
'element-color': 'black',
'fill': 'yellow'
},
'end':{
'font-size': 20,
'class': 'end-element'
}
},
'flowstate' : {
//'past' : { 'fill' : '#CCCCCC', 'font-size' : 12},
//'current' : {'fill' : 'yellow', 'font-color' : 'red', 'font-weight' : 'bold'},
//'future' : { 'fill' : '#FFFF99'},
'request' : { 'fill' : 'blue'},
'invalid': {'fill' : '#444444'},
'approved' : { 'fill' : '#58C4A3', 'font-size' : 12, 'yes-text' : 'APPROVED', 'no-text' : 'n/a' },
'rejected' : { 'fill' : '#C45879', 'font-size' : 12, 'yes-text' : 'n/a', 'no-text' : 'REJECTED' }
}
});
//create base64 encoding of SVG to generate download link for title(without html or htm).SVG
var currentCanvasDIV = document.getElementById('canvas')
var currentDrawSVG = currentCanvasDIV.innerHTML.replaceAll('ë','e');
const OUTsvgBASE64 = btoa(currentDrawSVG)
doctitle = document.title.replace('.html','');
doctitle = doctitle.replace('.htm','');
var currentCanvasDIV = document.getElementById('canvas')
var currentDrawSVG = currentCanvasDIV.innerHTML.replaceAll('ë','e');
svgSource = currentDrawSVG
svgXML = currentDrawSVG;
// Use SVG Height and Width from the SVG XML to set canvas size
svgXMLsubstringHeight = svgXML.substring(svgXML.indexOf('height='), svgXML.indexOf('version='));
svgXMLsubstringWidth = svgXML.substring(svgXML.indexOf('width='), svgXML.indexOf('xmlns='));
HeightValue = svgXMLsubstringHeight.substring(svgXMLsubstringHeight.indexOf('"')+1,svgXMLsubstringHeight.lastIndexOf('"'));
WidthValue = svgXMLsubstringWidth.substring(svgXMLsubstringWidth.indexOf('"')+1,svgXMLsubstringWidth.lastIndexOf('"'));
HeightValueInt = Math.round(HeightValue)
WidthValueInt = Math.round(WidthValue)
// setup input for base64SvgToBase64Png
let svgSrc = "data:image/svg+xml;base64,"+OUTsvgBASE64;
var pngBase
imageUtil.base64SvgToBase64Png(svgSrc, WidthValueInt, HeightValueInt).then(pngSrc => {
pngBase = pngSrc
// output download link for base64 PNG converted on download from base64
var pngOutHtml = `<a href="${pngBase}" download="${doctitle}.png">PNG - Click here to download current rendered flowchart as ${doctitle}.png</a>`
document.getElementById("pngbase64").innerHTML=pngOutHtml;
});
// output download link for base64 SVG converted on download from base64
var svgOutHtml = `<a href="data:image/svg+xml;base64,${OUTsvgBASE64}" download=${doctitle}.svg>SVG - Click here to download current rendered flowchart as ${doctitle}.svg</a> `
document.getElementById("svgbase64").innerHTML=svgOutHtml;
})();
};
// derived from https://stackoverflow.com/a/64800570
// we need to use web browser canvas to generate a image. In this case png
let imageUtil = {};
/**
* converts a base64 encoded data url SVG image to a PNG image
* @param originalBase64 data url of svg image
* @param width target width in pixel of PNG image
* @param secondTry used internally to prevent endless recursion
* @return {Promise<unknown>} resolves to png data url of the image
*/
imageUtil.base64SvgToBase64Png = function (originalBase64, width, height, secondTry) {
return new Promise(resolve => {
let img = document.createElement('img');
img.onload = function () {
if (!secondTry && (img.naturalWidth === 0 || img.naturalHeight === 0)) {
let svgDoc = base64ToSvgDocument(originalBase64);
let fixedDoc = fixSvgDocumentFF(svgDoc);
return imageUtil.base64SvgToBase64Png(svgDocumentToBase64(fixedDoc), width, height, true).then(result => {
resolve(result);
});
}
//document.body.appendChild(img);
let canvas2 = document.createElement("canvas");
//document.body.removeChild(img);
canvas2.width = width;
canvas2.height = height;
let ctx = canvas2.getContext("2d");
ctx.drawImage(img, 0, 0, canvas2.width, canvas2.height);
try {
let data = canvas2.toDataURL('image/png');
resolve(data);
} catch (e) {
resolve(null);
}
};
img.src = originalBase64;
});
}
//needed because Firefox doesn't correctly handle SVG with size = 0, see https://bugzilla.mozilla.org/show_bug.cgi?id=700533
function fixSvgDocumentFF(svgDocument) {
try {
let widthInt = parseInt(svgDocument.documentElement.width.baseVal.value) || 500;
let heightInt = parseInt(svgDocument.documentElement.height.baseVal.value) || 500;
svgDocument.documentElement.width.baseVal.newValueSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_PX, widthInt);
svgDocument.documentElement.height.baseVal.newValueSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_PX, heightInt);
return svgDocument;
} catch (e) {
return svgDocument;
}
}
function svgDocumentToBase64(svgDocument) {
try {
let base64EncodedSVG = btoa(new XMLSerializer().serializeToString(svgDocument));
return 'data:image/svg+xml;base64,' + base64EncodedSVG;
} catch (e) {
return null;
}
}
function base64ToSvgDocument(base64) {
let svg = atob(base64.substring(base64.indexOf('base64,') + 7));
svg = svg.substring(svg.indexOf('<svg'));
let parser = new DOMParser();
return parser.parseFromString(svg, "image/svg+xml");
}
</script>
<script>
function HelpText() {
var x = document.getElementById("HelpTextBlock");
if (x.style.display === "none") {
x.style.display = "block";
} else {
x.style.display = "none";
}
}
</script>
</head>
<body>
<div><textarea id="code" style="width: 100%;" rows="11">op2=>operation: 'Functions for computing geomagnetic conjugate points.'
op4=>operation: import datetime as dt
op6=>operation: import logging
op8=>operation: import os
op10=>operation: import aacgmv2
op12=>operation: from geopack import geopack as gp
op14=>operation: import gpxpy
op16=>operation: import gpxpy.gpx
op18=>operation: import numpy as np
op20=>operation: import pandas as pd
op22=>operation: logger = logging.getLogger(__name__)
sub24=>subroutine: logging.basicConfig(filename='conjcalc.log', level=logging.INFO)
st27=>start: start findconj
io29=>inputoutput: input: lat, lon, ut, method, limit
op32=>operation: "Calculate the geographic latitudes and longitudes of conjugate point for\n given set of coordinates.\n\n Parameters\n ----------\n lat : float\n Geographic latitude of station.\n lon : float\n Geographic longitude of station.\n ut : datetime\n Datetime used in conversion.\n method : string\n Defines method used in conversion. Options are 'auto', 'geopack',\n which uses IGRF + T89 to run field line traces,\n or 'aacgm', which uses AACGM v2.\n limit : float\n Latitude limit, in degrees, used to switch between\n methods in auto mode. Default: 60.\n AACGM will converge above 35 degrees, but may be\n erroneous. See www.doi.org/10.1002/2014JA020264\n\n Returns\n -------\n lat, lon : float\n Latitude, longitude of conjugate points\n\n "
op34=>operation: method = method.lower()
cond37=>condition: if (np.isnan(lat) or np.isnan(lon))
sub41=>subroutine: logger.info("Received NaN for a coordinate; can't compute.")
io46=>inputoutput: output: (0, 0)
e44=>end: end function return
cond53=>condition: if (method == 'auto')
cond58=>condition: if (abs(lat) > limit)
op62=>operation: method = 'aacgm'
sub69=>subroutine: logger.info('Setting method according to latitude limits: %s', method)
cond75=>condition: if (method == 'geopack')
op79=>operation: ut = ut.timestamp()
op81=>operation: ps = gp.recalc(ut)
sub83=>subroutine: logger.info('.....Calculating conjugate for %s, %s at %s via geopack:', str(lat), str(lon), str(ut))
op85=>operation: (r, theta, phi) = [1, (90 - lat), lon]
op87=>operation: (theta, phi) = np.deg2rad([theta, phi])
sub89=>subroutine: logger.info('r, theta, phi: ')
sub91=>subroutine: logger.info([r, theta, phi])
op93=>operation: (xgeo, ygeo, zgeo) = gp.sphcar(r, theta, phi, 1)
sub95=>subroutine: logger.info('Cartesian output: ')
sub97=>subroutine: logger.info([xgeo, ygeo, zgeo])
sub99=>subroutine: logger.info('Sum of squares (should be 1):')
sub101=>subroutine: logger.info((((xgeo ** 2) + (ygeo ** 2)) + (zgeo ** 2)))
sub103=>subroutine: logger.info('GSM coordinates: ')
op105=>operation: (xgsm, ygsm, zgsm) = gp.geogsm(xgeo, ygeo, zgeo, 1)
sub107=>subroutine: logger.info([xgsm, ygsm, zgsm])
sub109=>subroutine: logger.info('Sum of squares (should be 1):')
sub111=>subroutine: logger.info((((xgsm ** 2) + (ygsm ** 2)) + (zgsm ** 2)))
op113=>operation: (rlim, r0) = [1000, 0.9]
op115=>operation: fieldline = gp.trace(xgsm, ygsm, zgsm, dir=(- 1), rlim=rlim, r0=r0, parmod=2, exname='t89', inname='igrf')
op117=>operation: (x1gsm, y1gsm, z1gsm) = fieldline[0:3]
sub119=>subroutine: logger.info('Traced GSM Coordinates, Cartesian: ')
sub121=>subroutine: logger.info([x1gsm, y1gsm, z1gsm])
sub123=>subroutine: logger.info('%f points in traced vector.', len(fieldline[4]))
sub125=>subroutine: logger.info('Sum of squares (should be 1):')
sub127=>subroutine: logger.info((((x1gsm ** 2) + (y1gsm ** 2)) + (z1gsm ** 2)))
op129=>operation: (x1geo, y1geo, z1geo) = gp.geogsm(x1gsm, y1gsm, z1gsm, (- 1))
sub131=>subroutine: logger.info('Geographic coordinates, Cartesian: ')
sub133=>subroutine: logger.info([x1geo, y1geo, z1geo])
sub135=>subroutine: logger.info('Sum of squares (should be 1):')
sub137=>subroutine: logger.info((((x1geo ** 2) + (y1geo ** 2)) + (z1geo ** 2)))
sub139=>subroutine: logger.info('Geographic coordinates, spherical: ')
op141=>operation: [x1_r, x1_theta, x1_phi] = gp.sphcar(x1geo, y1geo, z1geo, (- 1))
sub143=>subroutine: logger.info([x1_r, x1_theta, x1_phi])
op145=>operation: (x1_theta, x1_phi) = np.rad2deg([x1_theta, x1_phi])
sub147=>subroutine: logger.info('Lat/lon of conjugate point: ')
op149=>operation: lat = (90 - x1_theta)
op151=>operation: lon = x1_phi
sub153=>subroutine: logger.info([lat, lon])
io158=>inputoutput: output: (lat, lon)
e156=>end: end function return
cond165=>condition: if (method == 'aacgm')
sub169=>subroutine: logger.info('...Calculating conjugate for %s, %s at %s via AACGMv2:', str(lat), str(lon), str(ut))
op171=>operation: (mlat, mlon, _) = aacgmv2.convert_latlon(lat, lon, 0, ut, 'G2A')
sub173=>subroutine: logger.info('Magnetic lat/lon: %s', str([mlat, mlon]))
op175=>operation: (glat_con, glon_con, _) = aacgmv2.convert_latlon(((- 1.0) * mlat), mlon, 0, ut, 'A2G')
sub177=>subroutine: logger.info('Conjugate geographic lat/lon: %f, %f', glat_con, glon_con)
io182=>inputoutput: output: (glat_con, glon_con)
e180=>end: end function return
sub188=>subroutine: logger.info('Method is not listed.')
io193=>inputoutput: output: (0, 0)
e191=>end: end function return
op66=>operation: method = 'geopack'
op2->op4
op4->op6
op6->op8
op8->op10
op10->op12
op12->op14
op14->op16
op16->op18
op18->op20
op20->op22
op22->sub24
sub24->st27
st27->io29
io29->op32
op32->op34
op34->cond37
cond37(yes)->sub41
sub41->io46
io46->e44
cond37(no)->cond53
cond53(yes)->cond58
cond58(yes)->op62
op62->sub69
sub69->cond75
cond75(yes)->op79
op79->op81
op81->sub83
sub83->op85
op85->op87
op87->sub89
sub89->sub91
sub91->op93
op93->sub95
sub95->sub97
sub97->sub99
sub99->sub101
sub101->sub103
sub103->op105
op105->sub107
sub107->sub109
sub109->sub111
sub111->op113
op113->op115
op115->op117
op117->sub119
sub119->sub121
sub121->sub123
sub123->sub125
sub125->sub127
sub127->op129
op129->sub131
sub131->sub133
sub133->sub135
sub135->sub137
sub137->sub139
sub139->op141
op141->sub143
sub143->op145
op145->sub147
sub147->op149
op149->op151
op151->sub153
sub153->io158
io158->e156
cond75(no)->cond165
cond165(yes)->sub169
sub169->op171
op171->sub173
sub173->op175
op175->sub177
sub177->io182
io182->e180
cond165(no)->sub188
sub188->io193
io193->e191
cond58(no)->op66
op66->sub69
cond53(no)->cond75
</textarea></div>
<div><button id="run" type="button">Run</button> <button onclick="HelpText()">Format Help</button></div>
<div id="HelpTextBlock" style="display:none"><br/>Conditions can also be redirected like cond(yes, bottom) or cond(yes, right)
... and the other symbols too... like sub1(right)<br/>
You can also tweak the <b>diagram.drawSVG('diagram', {});</b> script in this file for more changes<br/>
This is based on <a href="https://github.com/adrai/flowchart.js">flowchart.js on github</a> and <a href="http://flowchart.js.org">http://flowchart.js.org</a> more documentation can be found over there.
</div><br/><div id="svgbase64"></div>
<div id="pngbase64"></div>
<div id="canvas"></div>
</body>
</html>