001package armyc2.c5isr.web.render.utilities;
002
003import armyc2.c5isr.renderer.utilities.MilStdAttributes;
004import armyc2.c5isr.graphics2d.Point2D;
005import armyc2.c5isr.renderer.utilities.Modifiers;
006
007import java.io.PrintWriter;
008import java.io.StringWriter;
009import java.io.Writer;
010import java.util.HashMap;
011import java.util.Locale;
012import java.util.Map;
013
014/**
015 *
016*
017 */
018@SuppressWarnings("unused")
019public class JavaRendererUtilities {
020
021    public static final String HOSTILE_FILL_COLOR = "FFFF8080";
022    public static final String FRIENDLY_FILL_COLOR = "FF80E0FF";
023    public static final String NEUTRAL_FILL_COLOR = "FFAAFFAA";
024    public static final String UNKNOWN_FILL_COLOR = "FFFFFF80";
025
026    /**
027     * Converts ARGB string format to the Google used ABGR string format. Google
028     * reverses the blue and red positioning.
029     *
030     * @param rgbString A color string of the format AARRGGBB in hex value.
031     * @return the reverse of the input string in hex. The format should now be
032     * AABBGGRR
033     */
034    public static String ARGBtoABGR(String rgbString) {
035        if(rgbString.length()==6)
036        {
037            String s="FF";
038            rgbString=s.concat(rgbString);
039        }
040
041        String bgrString = rgbString.toUpperCase(Locale.US);
042        
043        if(rgbString.length() == 8)
044        {
045                char[] c = rgbString.toCharArray();
046                char temp1 = c[2];
047            char temp2 = c[3];
048            c[2] = c[6];
049            c[3] = c[7];
050            c[6] = temp1;
051            c[7] = temp2;
052            bgrString = new String(c);
053        }
054        else if(rgbString.length() == 6)
055        {
056                char[] c = rgbString.toCharArray();
057                char temp1 = c[0];
058            char temp2 = c[1];
059            c[0] = c[4];
060            c[1] = c[5];
061            c[4] = temp1;
062            c[5] = temp2;
063            bgrString = "FF" + new String(c);
064            //bgrString = "FF" + bgrString;
065        }
066        else
067        {
068                System.err.println("JavaRendererUtilties.ARGBtoABGR(): " + "\"" + String.valueOf(rgbString) + "\" is not a 6 or 8 character String in the format of RRGGBB or AARRGGBB");
069        }
070
071        return bgrString;
072    }
073
074    /**
075     * Returns a symbolId with just the identifiable symbol Id pieces. All
076     * variable information is returned as '*'. For example, a boundary,
077     * "GFGPGLB----KUSX" returns "G*G*GLB---****X";
078     *
079     * @param symbolCode A 15 character symbol ID.
080     * @return The normalized SymbolCode.
081     * @deprecated
082     */
083    public static String normalizeSymbolCode(String symbolCode) {
084
085        String newSymbolCode = symbolCode;
086
087        if (symbolCode.startsWith("G") || symbolCode.startsWith("S")) {
088            // Remove Affiliation
089            newSymbolCode = newSymbolCode.substring(0, 1) + '*' + newSymbolCode.substring(2);
090            // Remove planned/present field
091            newSymbolCode = newSymbolCode.substring(0, 3) + '*' + newSymbolCode.substring(4);
092            // Remove echelon, special code and country codes
093            newSymbolCode = newSymbolCode.substring(0, 10) + "****" + newSymbolCode.substring(14);
094        }
095
096        // If a unit replace last character with *.
097        if (symbolCode.startsWith("S")) {
098            newSymbolCode = newSymbolCode.substring(0, 14) + '*';
099        }
100
101        return newSymbolCode;
102    }
103    
104    public static void addAltModeToModifiersString(Map<String,String> attributes, String altMode)
105    {
106        if(altMode.equals("relativeToGround"))
107            attributes.put(MilStdAttributes.AltitudeMode, "AGL");
108        else if(altMode.equals("absolute"))
109                attributes.put(MilStdAttributes.AltitudeMode, "AMSL");
110    }
111
112    /**
113     *
114     * @param SymbolInfo something like
115     * "SymbolID?LineColor=0x000000&amp;FillColor=0xFFFFFF&amp;size=35"
116     */
117    public static Map<String, String> createParameterMapFromURL(String SymbolInfo) {
118        Map<String, String> modifiers = new HashMap<String, String>();
119        String symbolID = null;
120        String parameters = null;
121        String key = null;
122        String value = null;
123        String arrParameters[] = null;
124        String arrKeyValue[] = null;
125        String temp = null;
126        int questionIndex = SymbolInfo.lastIndexOf('?');
127
128        try {
129            if (questionIndex == -1) {
130                symbolID = java.net.URLDecoder.decode(SymbolInfo, "UTF-8");
131            } else {
132                symbolID = java.net.URLDecoder.decode(SymbolInfo.substring(0, questionIndex), "UTF-8");
133            }
134
135        } catch (Exception exc) {
136            System.err.println("Error parsing SymbolID");
137            System.err.println(exc.getMessage());
138        }
139
140        try {   //build a map for the other createMilstdSymbol function to use
141            //to build a milstd symbol.
142            if (questionIndex > 0 && (questionIndex + 1 < SymbolInfo.length())) {
143                parameters = SymbolInfo.substring(questionIndex + 1, SymbolInfo.length());
144                arrParameters = parameters.split("&");
145                int n = arrParameters.length;
146                //for(int i = 0; i < arrParameters.length; i++)
147                for (int i = 0; i < n; i++) {
148                    arrKeyValue = arrParameters[i].split("=");
149                    if (arrKeyValue.length == 2 && arrKeyValue[1] != null && arrKeyValue[1].equals("") == false) {
150
151                        key = arrKeyValue[0];
152                        value = arrKeyValue[1];
153
154                        temp = java.net.URLDecoder.decode(value, "UTF-8");
155                        modifiers.put(key.toUpperCase(), temp);
156
157                        //System.out.println("key: " + key + " value: " + temp);
158                    }
159                }
160            }
161        } catch (Exception exc) {
162            System.err.println("Error parsing \"" + key.toUpperCase() + "\" parameter from URL");
163            System.err.println(exc.getMessage());
164        }
165        return modifiers;
166    }
167
168    /*
169     * Try to turn a bad code into something renderable.
170     *
171     * @param symbolID
172     * @return
173     * @deprecated use SymbolUtilties.reconcileSymbolID() 9/5/2013
174     */
175    /*public static String ReconcileSymbolID(String symbolID) {
176        StringBuilder sb = new StringBuilder("");
177        char codingScheme = symbolID.charAt(0);
178
179        if (symbolID.length() < 15) {
180            while (symbolID.length() < 15) {
181                symbolID += "-";
182            }
183        }
184        if (symbolID.length() > 15) {
185            symbolID = symbolID.substring(0, 14);
186        }
187
188        if (symbolID != null && symbolID.length() == 15) {
189            if (codingScheme == 'S' || //warfighting
190                    codingScheme == 'I' ||//sigint
191                    codingScheme == 'O' ||//stability operation
192                    codingScheme == 'E')//emergency management
193            {
194                sb.append(codingScheme);
195
196                if (SymbolUtilities.hasValidAffiliation(symbolID) == false) {
197                    sb.append('U');
198                } else {
199                    sb.append(symbolID.charAt(1));
200                }
201
202                if (SymbolUtilities.hasValidBattleDimension(symbolID) == false) {
203                    sb.append('Z');
204                    sb.replace(0, 1, "S");
205                } else {
206                    sb.append(symbolID.charAt(2));
207                }
208
209                if (SymbolUtilities.hasValidStatus(symbolID) == false) {
210                    sb.append('P');
211                } else {
212                    sb.append(symbolID.charAt(3));
213                }
214
215                sb.append("------");
216                sb.append(symbolID.substring(10, 15));
217
218            } else if (codingScheme == 'G')//tactical
219            {
220                sb.append(codingScheme);
221
222                if (SymbolUtilities.hasValidAffiliation(symbolID) == false) {
223                    sb.append('U');
224                } else {
225                    sb.append(symbolID.charAt(1));
226                }
227
228                sb.append('G');
229
230                if (SymbolUtilities.hasValidStatus(symbolID) == false) {
231                    sb.append('P');
232                } else {
233                    sb.append(symbolID.charAt(3));
234                }
235
236                sb.append("GPP---");//return an action point
237                sb.append(symbolID.substring(10, 15));
238
239            } else if (codingScheme == 'W')//weather
240            {//no default weather graphic
241                return "SUZP-----------";//unknown
242            } else//bad codingScheme
243            {
244                sb.append('S');
245                if (SymbolUtilities.hasValidAffiliation(symbolID) == false) {
246                    sb.append('U');
247                } else {
248                    sb.append(symbolID.charAt(1));
249                }
250
251                if (SymbolUtilities.hasValidBattleDimension(symbolID) == false) {
252                    sb.append('Z');
253                } else {
254                    sb.append(symbolID.charAt(2));
255                }
256
257                if (SymbolUtilities.hasValidStatus(symbolID) == false) {
258                    sb.append('P');
259                } else {
260                    sb.append(symbolID.charAt(3));
261                }
262
263                sb.append("------");
264                sb.append(symbolID.substring(10, 15));
265            }
266        } else {
267            return "SUZP-----------";//unknown
268        }
269
270        return sb.toString();
271
272    }//*/
273
274    /**
275     * Checks symbolID and if the relevant modifiers are present
276     *
277     * @param symbolCode
278     * @param modifiers
279     * @return
280     * @deprecated
281     */
282    public static boolean is3dSymbol(String symbolCode, Map<String,String> modifiers) {
283        boolean returnValue = false;
284
285        try {
286            String symbolId = symbolCode.substring(4, 10);
287
288            if (symbolId.equals("ACAI--") || // Airspace Coordination Area Irregular
289                    symbolId.equals("ACAR--") || // Airspace Coordination Area Rectangular
290                    symbolId.equals("ACAC--") || // Airspace Coordination Area Circular
291                    symbolId.equals("AKPC--") || // Kill box circular
292                    symbolId.equals("AKPR--") || // Kill box rectangular
293                    symbolId.equals("AKPI--") || // Kill box irregular
294                    symbolId.equals("ALC---") || // Air corridor
295                    symbolId.equals("ALM---") || // 
296                    symbolId.equals("ALS---") || // SAAFR
297                    symbolId.equals("ALU---") || // UAV
298                    symbolId.equals("ALL---") || // Low level transit route
299                    symbolId.equals("AAR---")
300                    || symbolId.equals("AAF---")
301                    || symbolId.equals("AAH---")
302                    || symbolId.equals("AAM---") || // MEZ
303                    symbolId.equals("AAML--") || // LOMEZ
304                    symbolId.equals("AAMH--")) {
305
306                try {
307                    if (modifiers != null) {
308
309                        // These guys store array values.  Put in appropriate data strucutre
310                        // for MilStdSymbol.
311                        if (modifiers.containsKey(Modifiers.X_ALTITUDE_DEPTH)) {
312                            String[] altitudes = modifiers.get(Modifiers.X_ALTITUDE_DEPTH).split(",");
313                            if (altitudes.length < 2) {
314                                returnValue = false;
315                            } else {
316                                returnValue = true;
317                            }
318                        }
319
320                    }
321                } catch (Exception exc) {
322                    System.err.println(exc.getMessage());
323                }
324            }
325        } catch (Exception e) {
326            System.err.println(e.getMessage());
327        }
328        return returnValue;
329    }
330
331    /**
332     * Determines if a String represents a valid number
333     *
334     * @param text
335     * @return "1.56" == true, "1ab" == false
336     */
337    public static boolean isNumber(String text) {
338        if (text != null && text.matches("((-|\\+)?[0-9]+(\\.[0-9]+)?)+")) {
339            return true;
340        } else {
341            return false;
342        }
343    }
344
345    /**
346     * Takes a throwable and puts it's stacktrace into a string.
347     *
348     * @param thrown
349     * @return
350     */
351    public static String getStackTrace(Throwable thrown) {
352        try {
353            Writer writer = new StringWriter();
354            PrintWriter printWriter = new PrintWriter(writer);
355            thrown.printStackTrace(printWriter);
356            return writer.toString();
357        } catch (Exception exc) {
358            //System.out.println("JavaRendererUtilties.getStackTrace()");
359            //return "Error - couldn't retrieve stack trace";
360            return "";
361        }
362    }
363
364    public static Point2D getEndPointWithAngle(Point2D ptStart,
365                                               //Point2D pt1,
366                                               //Point2D pt2,
367                                               double angle,
368                                               double distance) {
369        double newX = 0;
370        double newY = 0;
371        Point2D pt = new Point2D.Double();
372        try {
373            //first get the angle psi between pt0 and pt1
374            double psi = 0;//Math.atan((pt1.y - pt0.y) / (pt1.x - pt0.x));
375            //double psi = Math.atan((ptStart.getY() - ptStart.getY()) / (ptStart.getX() - (ptStart.getX()+100)));
376            //convert alpha to radians
377            double alpha1 = Math.PI * angle / 180;
378
379            //theta is the angle of extension from the x axis
380            double theta = psi + alpha1;
381            //dx is the x extension from pt2
382            double dx = distance * Math.cos(theta);
383            //dy is the y extension form pt2
384            double dy = distance * Math.sin(theta);
385            newX = ptStart.getX() + dx;
386            newY = ptStart.getY() + dy;
387
388            pt.setLocation(newX, newY);
389        } catch (Exception exc) {
390            System.out.println(exc.getMessage());
391            exc.printStackTrace();
392        }
393        return pt;
394    }
395
396    /**
397     *
398     * @param latitude1
399     * @param longitude1
400     * @param latitude2
401     * @param longitude2
402     * @param unitOfMeasure meters, kilometers, miles, feet, yards, nautical,
403     * nautical miles.
404     * @return
405     */
406    public static double measureDistance(double latitude1, double longitude1, double latitude2, double longitude2, String unitOfMeasure) {
407        // latitude1,latitude2 = latitude, longitude1,longitude2 = longitude
408        //Radius is 6378.1 (km), 3963.1 (mi), 3443.9 (nm
409
410        double distance = -1,
411                rad;
412
413        String uom = unitOfMeasure.toLowerCase();
414
415        if (uom.equals("meters")) {
416            rad = 6378137;
417        } else if (uom.equals("kilometers")) {
418            rad = 6378.137;
419        } else if (uom.equals("miles")) {
420            rad = 3963.1;
421        } else if (uom.equals("feet")) {
422            rad = 20925524.9;
423        } else if (uom.equals("yards")) {
424            rad = 6975174.98;
425        } else if (uom.equals("nautical")) {
426            rad = 3443.9;
427        } else if (uom.equals("nautical miles")) {
428            rad = 3443.9;
429        } else {
430            return -1.0;
431        }
432
433        latitude1 = latitude1 * (Math.PI / 180);
434        latitude2 = latitude2 * (Math.PI / 180);
435        longitude1 = longitude1 * (Math.PI / 180);
436        longitude2 = longitude2 * (Math.PI / 180);
437        distance = (Math.acos(Math.cos(latitude1) * Math.cos(longitude1) * Math.cos(latitude2) * Math.cos(longitude2) + Math.cos(latitude1) * Math.sin(longitude1) * Math.cos(latitude2) * Math.sin(longitude2) + Math.sin(latitude1) * Math.sin(latitude2)) * rad);
438
439        return distance;
440    }
441}