Update README.md
[sw/motorsim] / src / com / billkuker / rocketry / motorsim / io / HTMLExporter.java
1 package com.billkuker.rocketry.motorsim.io;\r
2 \r
3 import java.io.OutputStream;\r
4 import java.io.PrintWriter;\r
5 import java.lang.reflect.InvocationTargetException;\r
6 import java.lang.reflect.Method;\r
7 import java.net.URLEncoder;\r
8 import java.text.DecimalFormat;\r
9 import java.text.NumberFormat;\r
10 import java.util.Iterator;\r
11 \r
12 import javax.measure.quantity.Duration;\r
13 import javax.measure.quantity.Force;\r
14 import javax.measure.quantity.Quantity;\r
15 import javax.measure.unit.SI;\r
16 import javax.measure.unit.Unit;\r
17 \r
18 import org.jscience.physics.amount.Amount;\r
19 \r
20 import com.billkuker.rocketry.motorsim.Burn;\r
21 import com.billkuker.rocketry.motorsim.BurnSummary;\r
22 import com.billkuker.rocketry.motorsim.GraphSimplifier;\r
23 import com.billkuker.rocketry.motorsim.RocketScience;\r
24 \r
25 public class HTMLExporter {\r
26         static final int WIDTH=400;\r
27         static final int HEIGHT=200;\r
28 \r
29         @SuppressWarnings("deprecation")\r
30         private static String encode(String s) {\r
31                 return URLEncoder.encode(s).replace("%B2", "%C2%B2");\r
32         }\r
33 \r
34         private static <X extends Quantity, Y extends Quantity> String toChart(\r
35                         final Unit<X> xUnit, final Unit<Y> yUnit, final Object source,\r
36                         final String method, final Iterator<Amount<X>> domain, String title)\r
37                         throws SecurityException, NoSuchMethodException,\r
38                         IllegalArgumentException, IllegalAccessException,\r
39                         InvocationTargetException {\r
40                 NumberFormat nf = new DecimalFormat("#.##");\r
41                 Method f = source.getClass().getMethod(method, Amount.class);\r
42 \r
43                 double xMin = 0, xMax = 0;\r
44                 double yMin = 0, yMax = 0;\r
45 \r
46                 StringBuffer xVals = new StringBuffer();\r
47                 StringBuffer yVals = new StringBuffer();\r
48 \r
49                 while (domain.hasNext()) {\r
50                         Amount<X> aX = domain.next();\r
51                         double x = aX.doubleValue(xUnit);\r
52                         @SuppressWarnings("unchecked")\r
53                         double y = ((Amount<Y>) f.invoke(source, aX)).doubleValue(yUnit);\r
54                         xMin = x < xMin ? x : xMin;\r
55                         xMax = x > xMax ? x : xMax;\r
56                         yMin = y < yMin ? y : yMin;\r
57                         yMax = y > yMax ? y : yMax;\r
58 \r
59                         xVals.append(nf.format(x));\r
60                         xVals.append(",");\r
61                         yVals.append(nf.format(y));\r
62                         yVals.append(",");\r
63                 }\r
64                 xVals.deleteCharAt(xVals.length() - 1);\r
65                 yVals.deleteCharAt(yVals.length() - 1);\r
66 \r
67                 // Get the non-preferred Y Unit\r
68                 Unit<Y> yUnit2 = RocketScience.UnitPreference.SI\r
69                                 .getPreferredUnit(yUnit);\r
70                 // if ( yUnit2.equals(yUnit) )\r
71                 yUnit2 = RocketScience.UnitPreference.NONSI.getPreferredUnit(yUnit);\r
72                 double y2Min, y2Max;\r
73                 y2Min = Amount.valueOf(yMin, yUnit).doubleValue(yUnit2);\r
74                 y2Max = Amount.valueOf(yMax, yUnit).doubleValue(yUnit2);\r
75 \r
76                 // Get the non-preferred X Unit\r
77                 Unit<X> xUnit2 = RocketScience.UnitPreference.SI\r
78                                 .getPreferredUnit(xUnit);\r
79                 // if ( xUnit2.equals(xUnit) )\r
80                 xUnit2 = RocketScience.UnitPreference.NONSI.getPreferredUnit(xUnit);\r
81                 double x2Min, x2Max;\r
82                 x2Min = Amount.valueOf(xMin, xUnit).doubleValue(xUnit2);\r
83                 x2Max = Amount.valueOf(xMax, xUnit).doubleValue(xUnit2);\r
84 \r
85                 boolean x2 = !xUnit.equals(xUnit2);\r
86                 boolean y2 = x2 || !yUnit.equals(yUnit2);\r
87 \r
88                 StringBuffer sb = new StringBuffer();\r
89                 sb.append("<img src='");\r
90                 sb.append("http://chart.apis.google.com/chart?chxt=x,x,y,y"\r
91                                 + (y2 ? ",r,r" : "") + (x2 ? ",t,t" : "")\r
92                                 + "&chs="+WIDTH+"x"+HEIGHT+"&cht=lxy&chco=3072F3");\r
93                 sb.append("&chds=" + nf.format(xMin) + "," + nf.format(xMax) + ","\r
94                                 + nf.format(yMin) + "," + nf.format(yMax));\r
95                 sb.append("&chxr=" + "0," + nf.format(xMin) + "," + nf.format(xMax)\r
96                                 + "|2," + nf.format(yMin) + "," + nf.format(yMax));\r
97                 if (y2)\r
98                         sb.append("|4," + nf.format(y2Min) + "," + nf.format(y2Max));\r
99                 if (x2)\r
100                         sb.append("|6," + nf.format(x2Min) + "," + nf.format(x2Max));\r
101                 sb.append("&chd=t:" + xVals.toString() + "|" + yVals.toString());\r
102                 sb.append("&chxl=1:|" + encode(xUnit.toString()) + "|3:|"\r
103                                 + encode(yUnit.toString()));\r
104                 if (y2)\r
105                         sb.append("|5:|" + encode(yUnit2.toString()));\r
106                 if (x2)\r
107                         sb.append("|7:|" + encode(xUnit2.toString()));\r
108                 sb.append("&chxp=1,50|3,50");\r
109                 if (y2)\r
110                         sb.append("|5,50");\r
111                 if (x2)\r
112                         sb.append("|7,50");\r
113                 if ( title != null )\r
114                         sb.append("&chtt=" + title);\r
115                 sb.append("' width='"+WIDTH+"' height='"+HEIGHT+"' alt='" + title + "' />");\r
116 \r
117                 return sb.toString();\r
118         }\r
119 \r
120         public static void export(Burn b, OutputStream os) throws Exception {\r
121                 PrintWriter out = new PrintWriter(os);\r
122 \r
123                 BurnSummary bs = new BurnSummary(b);\r
124                 \r
125                 out.println("<!--Begin motor " + b.getMotor().getName() + " HTML export from MotorSim-->");\r
126                 out.print("<table class='motor' style='text-align: left; border: 1px solid black'>");\r
127 \r
128                 out.print("<tr>");\r
129                 out.print("<th colspan='6' class='title'>" + b.getMotor().getName() + "</th>");\r
130                 out.print("</tr>");\r
131 \r
132                 out.print("<tr class='summary'>");\r
133 \r
134                 out.print("<th>Rating:</th>");\r
135                 out.print("<td>" + bs.getRating() + "</td>");\r
136                 \r
137                 \r
138                 out.print("<th>Max Pressure:</th>");\r
139                 out.print("<td>"\r
140                                 + RocketScience.ammountToRoundedString(bs.maxPressure())\r
141                                 + "</td>");\r
142 \r
143                 out.print("</tr>");\r
144                 \r
145                 \r
146                 out.print("<tr class='summary'>");\r
147                 \r
148                 out.print("<th>Total Impulse:</th>");\r
149                 out.print("<td>"\r
150                                 + RocketScience.ammountToRoundedString(bs.totalImpulse())\r
151                                 + "</td>");\r
152                 \r
153                 \r
154                 out.print("<th>Specific Impulse:</th>");\r
155                 out.print("<td>"\r
156                                 + RocketScience.ammountToRoundedString(bs.specificImpulse())\r
157                                 + "</td>");\r
158                 out.print("</tr>");\r
159                 \r
160                 out.print("<tr class='summary'>");\r
161                 \r
162                 out.print("<th>Max Thrust:</th>");\r
163                 out.print("<td>"\r
164                                 + RocketScience.ammountToRoundedString(bs.maxThrust())\r
165                                 + "</td>");\r
166                 \r
167                 out.print("<th>Volume Loading:</th>");\r
168                 out.print("<td>"\r
169                                 + (int)(bs.getVolumeLoading()*100)\r
170                                 + "%</td>");\r
171                 out.print("</tr>");\r
172                 \r
173                 out.print("<tr class='summary'>");\r
174                 \r
175                 out.print("<th>Average Thrust:</th>");\r
176                 out.print("<td>"\r
177                                 + RocketScience.ammountToRoundedString(bs.averageThrust())\r
178                                 + "</td>");\r
179                 out.print("<th>Fuel Mass:</th>");\r
180                 out.print("<td>"\r
181                                 + RocketScience.ammountToRoundedString(bs.getPropellantMass())\r
182                                 + "</td>");\r
183 \r
184                 out.print("</tr>");\r
185                 \r
186 \r
187                 out.print("<tr>");\r
188                 out.print("<td colspan='4' class='thrust'>");\r
189                 GraphSimplifier<Duration, Force> thrust = new GraphSimplifier<Duration, Force>(\r
190                                 b, "thrust", b.getData().keySet().iterator());\r
191 \r
192                 out.print(toChart(SI.SECOND, SI.NEWTON, thrust, "value", thrust\r
193                                 .getDomain().iterator(), null));\r
194                 out.print("</td>");\r
195                 /*\r
196                 out.print("<td colspan='3' class='pressure'>");\r
197                 GraphSimplifier<Duration, Pressure> pressure = new GraphSimplifier<Duration, Pressure>(\r
198                                 b, "pressure", b.getData().keySet().iterator());\r
199 \r
200                 out.print(toChart(SI.SECOND,\r
201                                 javax.measure.unit.SI.MEGA(javax.measure.unit.SI.PASCAL),\r
202                                 pressure, "value", pressure.getDomain().iterator(), "Pressure"));\r
203 \r
204                 out.print("</td>");\r
205                 */\r
206                 out.print("</tr>");\r
207                 /*\r
208                 out.print("<tr>");\r
209                 out.print("<th colspan='3'>.ENG File</th>");\r
210                 out.print("<th colspan='3'>MotorSim File</th>");\r
211                 out.print("</tr>");\r
212                 out.print("<tr>");\r
213                 out.print("<td colspan='3'>");\r
214                 out.print("<textarea>");\r
215                 out.flush();\r
216                 os.flush();\r
217                 ENGExporter.export(b, os);\r
218                 os.flush();\r
219                 out.print("</textarea>");\r
220                 out.print("</td>");\r
221                 out.print("<td colspan='3'>");\r
222                 out.print("<textarea>");\r
223                 out.print(MotorIO.writeMotor(b.getMotor()).replace("<", "&lt;").replace(">", "&gt;"));\r
224                 out.print("</textarea>");\r
225                 out.print("</td>");\r
226                 out.print("</tr>");\r
227                 */\r
228                 out.print("</table>");\r
229 \r
230                 out.println("\n<!--End motor " + b.getMotor().getName() + "-->");\r
231                 out.flush();\r
232                 out.close();\r
233         }\r
234 \r
235 }\r