001package sec.web.render.utilities;
002
003import android.util.SparseArray;
004import armyc2.c2sd.renderer.utilities.MilStdAttributes;
005import armyc2.c2sd.renderer.utilities.ModifiersTG;
006import armyc2.c2sd.renderer.utilities.SymbolUtilities;
007import armyc2.c2sd.graphics2d.*;
008import java.io.PrintWriter;
009import java.io.StringWriter;
010import java.io.Writer;
011import java.util.HashMap;
012import java.util.Locale;
013import java.util.Map;
014
015/**
016 *
017 * @author stephen.pinizzotto
018 */
019@SuppressWarnings("unused")
020public class JavaRendererUtilities {
021
022    public static final String HOSTILE_FILL_COLOR = "FFFF8080";
023    public static final String FRIENDLY_FILL_COLOR = "FF80E0FF";
024    public static final String NEUTRAL_FILL_COLOR = "FFAAFFAA";
025    public static final String UNKNOWN_FILL_COLOR = "FFFFFF80";
026
027    /**
028     * Converts ARGB string format to the Google used ABGR string format. Google
029     * reverses the blue and red positioning.
030     *
031     * @param rgbString A color string of the format AARRGGBB in hex value.
032     * @return the reverse of the input string in hex. The format should now be
033     * AABBGGRR
034     */
035    public static String ARGBtoABGR(String rgbString) {
036        if(rgbString.length()==6)
037        {
038            String s="FF";
039            rgbString=s.concat(rgbString);
040        }
041
042        String bgrString = rgbString.toUpperCase(Locale.US);
043        
044        if(rgbString.length() == 8)
045        {
046                char[] c = rgbString.toCharArray();
047                char temp1 = c[2];
048            char temp2 = c[3];
049            c[2] = c[6];
050            c[3] = c[7];
051            c[6] = temp1;
052            c[7] = temp2;
053            bgrString = new String(c);
054        }
055        else if(rgbString.length() == 6)
056        {
057                char[] c = rgbString.toCharArray();
058                char temp1 = c[0];
059            char temp2 = c[1];
060            c[0] = c[4];
061            c[1] = c[5];
062            c[4] = temp1;
063            c[5] = temp2;
064            bgrString = "FF" + new String(c);
065            //bgrString = "FF" + bgrString;
066        }
067        else
068        {
069                System.err.println("JavaRendererUtilties.ARGBtoABGR(): " + "\"" + String.valueOf(rgbString) + "\" is not a 6 or 8 character String in the format of RRGGBB or AARRGGBB");
070        }
071
072        return bgrString;
073    }
074
075    /**
076     * Returns a symbolId with just the identifiable symbol Id pieces. All
077     * variable information is returned as '*'. For example, a boundary,
078     * "GFGPGLB----KUSX" returns "G*G*GLB---****X";
079     *
080     * @param symbolCode A 15 character symbol ID.
081     * @return The normalized SymbolCode.
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(SparseArray<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, "MSL");
110    }
111
112    /**
113     *
114     * @param SymbolInfo something like
115     * "SymbolID?LineColor=0x000000&FillColor=0xFFFFFF&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     */
281    public static boolean is3dSymbol(String symbolCode, SparseArray<String> modifiers) {
282        boolean returnValue = false;
283
284        try {
285            String symbolId = symbolCode.substring(4, 10);
286
287            if (symbolId.equals("ACAI--") || // Airspace Coordination Area Irregular
288                    symbolId.equals("ACAR--") || // Airspace Coordination Area Rectangular
289                    symbolId.equals("ACAC--") || // Airspace Coordination Area Circular
290                    symbolId.equals("AKPC--") || // Kill box circular
291                    symbolId.equals("AKPR--") || // Kill box rectangular
292                    symbolId.equals("AKPI--") || // Kill box irregular
293                    symbolId.equals("ALC---") || // Air corridor
294                    symbolId.equals("ALM---") || // 
295                    symbolId.equals("ALS---") || // SAAFR
296                    symbolId.equals("ALU---") || // UAV
297                    symbolId.equals("ALL---") || // Low level transit route
298                    symbolId.equals("AAR---")
299                    || symbolId.equals("AAF---")
300                    || symbolId.equals("AAH---")
301                    || symbolId.equals("AAM---") || // MEZ
302                    symbolId.equals("AAML--") || // LOMEZ
303                    symbolId.equals("AAMH--")) {
304
305                try {
306                    if (modifiers != null) {
307
308                        // These guys store array values.  Put in appropriate data strucutre
309                        // for MilStdSymbol.
310                        if (modifiers.indexOfKey(ModifiersTG.X_ALTITUDE_DEPTH) >= 0) {
311                            String[] altitudes = modifiers.get(ModifiersTG.X_ALTITUDE_DEPTH).split(",");
312                            if (altitudes.length < 2) {
313                                returnValue = false;
314                            } else {
315                                returnValue = true;
316                            }
317                        }
318
319                    }
320                } catch (Exception exc) {
321                    System.err.println(exc.getMessage());
322                }
323            }
324        } catch (Exception e) {
325            System.err.println(e.getMessage());
326        }
327        return returnValue;
328    }
329
330    /**
331     * Determines if a String represents a valid number
332     *
333     * @param text
334     * @return "1.56" == true, "1ab" == false
335     */
336    public static boolean isNumber(String text) {
337        if (text != null && text.matches("((-|\\+)?[0-9]+(\\.[0-9]+)?)+")) {
338            return true;
339        } else {
340            return false;
341        }
342    }
343
344    /**
345     * Takes a throwable and puts it's stacktrace into a string.
346     *
347     * @param thrown
348     * @return
349     */
350    public static String getStackTrace(Throwable thrown) {
351        try {
352            Writer writer = new StringWriter();
353            PrintWriter printWriter = new PrintWriter(writer);
354            thrown.printStackTrace(printWriter);
355            return writer.toString();
356        } catch (Exception exc) {
357            //System.out.println("JavaRendererUtilties.getStackTrace()");
358            //return "Error - couldn't retrieve stack trace";
359            return "";
360        }
361    }
362
363    public static Point2D getEndPointWithAngle(Point2D ptStart,
364            //Point2D pt1,
365            //Point2D pt2,
366            double angle,
367            double distance) {
368        double newX = 0;
369        double newY = 0;
370        Point2D pt = new Point2D.Double();
371        try {
372            //first get the angle psi between pt0 and pt1
373            double psi = 0;//Math.atan((pt1.y - pt0.y) / (pt1.x - pt0.x));
374            //double psi = Math.atan((ptStart.getY() - ptStart.getY()) / (ptStart.getX() - (ptStart.getX()+100)));
375            //convert alpha to radians
376            double alpha1 = Math.PI * angle / 180;
377
378            //theta is the angle of extension from the x axis
379            double theta = psi + alpha1;
380            //dx is the x extension from pt2
381            double dx = distance * Math.cos(theta);
382            //dy is the y extension form pt2
383            double dy = distance * Math.sin(theta);
384            newX = ptStart.getX() + dx;
385            newY = ptStart.getY() + dy;
386
387            pt.setLocation(newX, newY);
388        } catch (Exception exc) {
389            System.out.println(exc.getMessage());
390            exc.printStackTrace();
391        }
392        return pt;
393    }
394
395    /**
396     *
397     * @param latitude1
398     * @param longitude1
399     * @param latitude2
400     * @param longitude2
401     * @param unitOfMeasure meters, kilometers, miles, feet, yards, nautical,
402     * nautical miles.
403     * @return
404     */
405    public static double measureDistance(double latitude1, double longitude1, double latitude2, double longitude2, String unitOfMeasure) {
406        // latitude1,latitude2 = latitude, longitude1,longitude2 = longitude
407        //Radius is 6378.1 (km), 3963.1 (mi), 3443.9 (nm
408
409        double distance = -1,
410                rad;
411
412        String uom = unitOfMeasure.toLowerCase();
413
414        if (uom.equals("meters")) {
415            rad = 6378137;
416        } else if (uom.equals("kilometers")) {
417            rad = 6378.137;
418        } else if (uom.equals("miles")) {
419            rad = 3963.1;
420        } else if (uom.equals("feet")) {
421            rad = 20925524.9;
422        } else if (uom.equals("yards")) {
423            rad = 6975174.98;
424        } else if (uom.equals("nautical")) {
425            rad = 3443.9;
426        } else if (uom.equals("nautical miles")) {
427            rad = 3443.9;
428        } else {
429            return -1.0;
430        }
431
432        latitude1 = latitude1 * (Math.PI / 180);
433        latitude2 = latitude2 * (Math.PI / 180);
434        longitude1 = longitude1 * (Math.PI / 180);
435        longitude2 = longitude2 * (Math.PI / 180);
436        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);
437
438        return distance;
439    }
440}